2013-08-24 9 views
8

Sto scrivendo un semplice oggetto vettoriale 2D. Avrà componenti xey, e lunghezza, cross product, ecc. Il fatto è che voglio avere la struttura per avere molti tipi possibili (char, int, float, double, ecc.). Mi chiedevo quale sarebbe stata la scelta migliore, dal punto di vista del design, per interagire con l'oggetto? Ecco quello che sto valutando:Miglior approccio per il polimorfismo strutturale in C

1. Avere l'utente passa l'oggetto vettoriale per funzioni specializzate, come:

Vector2Dt_Dot(Vec2Dt* vector1, Vec2Dt* vector2); 

dove 't' è il tipo di vettore. Tuttavia, il problema con questo approccio è che non consente a tipi diversi di interagire tra loro, quindi non posso dire di calcolare il prodotto punto di un vettore float2d e un vettore doppio double. Un secondo approccio, e quello che sto appoggiato verso:

2. che l'utente passare l'oggetto vettore (s) puntatori come void, insieme con i loro tipi, come:

Vector2D_Dot(void* vector1, unsigned vector1_type, void* vector2, unsigned vector2_type); 

ovviamente questo l'approccio è più compatto in termini di API e risolve anche il problema di cui sopra, ma al costo di alcuni parametri aggiuntivi e sicurezza del tipo.

Potrebbero esserci altre soluzioni di cui non sono a conoscenza, tuttavia queste sono quelle che sto considerando. Quale pensi che sia il miglior approccio a questo?

+0

@WhozCraig la questione è aggiunto 'C', non' 'C++ –

+0

Non utilizzare il' 'void * alternativo; ci sono troppi modi in cui puoi commettere errori che il compilatore non catturerà (perché qualsiasi tipo di puntatore può essere convertito in un 'void *', in C o C++). Una volta, questa domanda (brevemente) conteneva un tag C++ e per un lungo periodo utilizzava la notazione del tipo di riferimento C++. Se C++ è rilevante, considera un vettore 'vector >'. –

+0

@johnathan leffler: Sì, è stato un mio errore. Passo spesso tra C e C++. Definitivamente utilizzerei un'interfaccia vettoriale come se fosse un'opzione in C. – Shokwav

risposta

11

Quello che puoi fare è usare oggetti polimorfi. Definire le strutture come questa:

#define INT_TYPE 0 
#define DOUBLE_TYPE 1 
//more type constants 

typedef struct Vector2D { 
    int type; 
} Vector2D; 

typedef struct Vector2D_int { 
    Vector2D super; 
    int x, y; 
} Vector2D_int; 

typedef struct Vector2D_double { 
    Vector2D super; 
    double x, y; 
} Vector2D_double; 

//more typed vector structures 

Quindi è possibile scrivere le funzioni di accettare Vector2D puntatori, ispezionare i loro rispettivi campi di tipo e falle scendere alla variante digitato appropriata per accedere ai dati di payload.

double Vector2D_length(const Vector2D* vector) { 
    if(vector->type == TYPE_INT) { 
     const Vector2D_int* intVector = (Vector2D_int*)vector; 
     return sqrt(intVector->x * intVector->x + intVector->y * intVector->y); 
    } 
    if(vector->type == TYPE_DOUBLE) { 
     const Vector2D_double* doubleVector = (Vector2D_double*)vector; 
     return sqrt(doubleVector->x * doubleVector->x + doubleVector->y * doubleVector->y); 
    } 
    //other cases follow 
} 

Questo è il polimorfismo codificato a mano. Tutto ciò che è necessario assicurarsi è che il campo type sia sempre impostato sul valore corretto (impostare una volta quando viene creato un vettore digitato).

Il vantaggio di questo approccio alla tua seconda idea è che non devi passare attorno al tipo di vettori in un'altra variabile che renderebbe noioso e soggetto ad errori l'uso dei vettori.

In alternativa, è possibile definire il campo type per contenere un puntatore a una struttura di puntatori di funzione.Si creerebbe un oggetto di questa struttura del puntatore di funzione per tipo di vettore tipizzato che si definisce e lo si utilizzerà per cercare la funzione da utilizzare con il vettore specificato. Questo approccio sarebbe molto vicino a ciò che C++ fa sotto il cofano.

+0

Fantastico! Grazie. – Shokwav

3

È possibile utilizzare un elenco di argomenti variabili, il suo prototipo è codificato, ad esempio, come:

int xyz(int a, ...); 

Questo stile ha bisogno di un parametro definito, in questo esempio a, seguito da un qualsiasi numero di parametri i cui tipi di dati può essere determinato in fase di esecuzione.

Vedere le funzioni e gli oggetti: va_list; va_start; va_args; e va_end, per una descrizione completa di come elaborare gli elenchi di argomenti variabili.

Spero che questo aiuti. Per domande su va_list, ecc. Si prega di chiedere.

0

Cosa Io sono andato con era la seguente:

ho creato una classe base Vector2D, con la seguente disposizione:

struct Vector2D_Base; 
typedef struct Vector2D_Base{ 
    M_double (*Vector2D_Get_X)(struct Vector2D_Base* vec); 
    M_double (*Vector2D_Get_Y)(struct Vector2D_Base* vec); 
} Vector2D; 

Come si può vedere, questo permette le funzioni vettoriali generici a chiamare questi per ottenere i valori x e y delle classi derivate convertiti in doppi, il che impedisce alla funzione generica di doversi preoccupare della differenza tra le dimensioni di tipi come char e float. Poi ogni classe derivata:

#define DEFINE_VECTOR2D(type, name)\ 
typedef struct{\ 
Vector2D_Base vec_base;\ 
type x, y;\ 
} Vector2D##name\ 
Problemi correlati