2010-12-12 8 views
5

È possibile replicare un array generico in puro ANSI-C?Pure ANSI-C: crea array generico

Ho questa struttura che contiene un array (per i float al momento) e alcune variabili come dimensione e capacità per la mutazione nell'array.

typedef struct _CustomArray 
{ 
    float* array; //the array in which the objects will be stored 
    int size; //the current size of the array 
    int capacity; //the max capacity of the array 
} CustomArray; 

Io uso questa struct in modo da poter fare una matrice in puro C dove posso aggiungere/rimuovere elementi, espandere dinamicamente la dimensione della matrice, se necessario, ecc tutte le cose che un allineamento "standard" fa, tranne che è fatto solo in C. E ora voglio fare in modo che quando inizializzi questa struct puoi impostare il tipo di dati degli elementi che dovrebbe contenere, in questo momento è solo in grado di memorizzare i tipi di dati float, ma voglio farlo in modo che possa memorizzare qualsiasi tipo di dati/altre strutture. Ma non so se questo è possibile.

In questo momento la possibilità di eseguire questa matrice è:

CustomArray* CustomArray_Create(int initCapacity, /*type elementType*/) 
{ 
    CustomArray* customArray_ptr; //create pointer to point at the structure 
    float* internalArray = (float*)malloc(sizeof(float) * initCapacity); //create the internal array that holds the items 
    if(internalArray != NULL) 
    { 
     CustomArray customArray = { internalArray, 0, initCapacity }; //make the struct with the data 
     customArray_ptr = &customArray; //get the adress of the structure and assign it to the pointer 
     return customArray_ptr; //return the pointer 
    } 
    return NULL; 
} 

E 'possibile che invia un tipo di dati come parametro così posso malloc memoria per tale tipo di dati e espressi come quella proposta tipo di dati in un array dinamico ?

Grazie in anticipo,

Marnix van Rijswijk

+0

Non pensare in puro C è possibile passare i tipi di dati in questo modo. Esaminando le lingue che supportano l'elenco di array eterogenei, ad esempio C#, funziona solo per tipi di dati non di base, vale a dire Classi e non su int, float ecc. Poiché C non è orientato agli oggetti, è altamente improbabile che si ottenga questa funzione. –

+2

non avviare identificatori con underscore: tali nomi sono riservati per l'implementazione (compilatore + libc); l'uso di un carattere di sottolineatura e un caso di maiuscole è doppiamente negativo: questi nomi sono riservati in qualsiasi contesto perché è quello che usano le nuove funzionalità della lingua (ad esempio '_Pragma',' _Complex', '_Bool', ...); una soluzione semplice è quella di utilizzare caratteri di sottolineatura finali, che suona anche con il namespace basato su prefisso – Christoph

+1

Ci sono un certo numero di domande sul sito su come si possono costruire comportamenti orientati agli oggetti in c: [Object-Orientation in C] (http://stackoverflow.com/q/415452/2509) e [È possibile scrivere codice orientato agli oggetti in C?] (http://stackoverflow.com/q/351733/2509) e altri. Potresti ottenere il risultato desiderato con un uso giudizioso di 'sizeof' e il meccanismo del puntatore a funzioni discusso nei collegamenti, ma sarà più un lavoro che vale la pena. L'interfaccia di 'qsort' e' bsearch' è un compromesso. – dmckee

risposta

8

Il codice ha un problema serio ... stai tornando l'indirizzo di una variabile locale (CustomArray), e quando la funzione restituisce tale variabile è distrutta così non si può continua a usarlo con il puntatore. Devi malloc anche quella struttura in modo che la memoria sia ancora disponibile una volta che la funzione ritorna.

di fare il tipo di un parametro è possibile ottenere un po 'vicino con le macro ... per esempio con qualcosa di simile:

#include <stdlib.h> 
#define DefArray(type) \ 
typedef struct T_##type##Array {\ 
    type *array; \ 
    int size, capacity; \ 
} type##Array; \ 
static type##Array *type##ArrayCreate(int capacity)\ 
{\ 
    type##Array *s = malloc(sizeof(type##Array));\ 
    if (!s) return NULL;\ 
    s->array = malloc(sizeof(type) * capacity);\ 
    if (!s->array) { free(s); return NULL; }\ 
    s->size=0; s->capacity = capacity;\ 
    return s;\ 
} 

Quindi è possibile utilizzare in questo modo

#include "customarray.h" 
DefArray(float); 
DefArray(double); 

void foo() 
{ 
    floatArray *fa = floatArrayCreate(100); 
    ... 
} 

Si noti che è 'usare le macro per definire tutte le tue funzioni personalizzate. Nota anche che questo approccio duplicherà il codice in ogni modulo (direi non un grosso problema, ma se non puoi usare C++ probabilmente la tua piattaforma di destinazione è piuttosto piccola). Con un approccio leggermente più complesso è possibile generare file .h e .c file separati per l'implementazione.

+0

dannatamente bello, ci proverò anche questo di sicuro. grazie per questo. –

+0

funziona, e mi ha anche mostrato un modo completamente nuovo di fare le cose. eccezionale. –

+3

Hehe ... benvenuto nel mondo di metaprogrammazione (scrittura di codice che scrive codice). Il preprocessore C è una forma orribilmente debole di metaprogrammazione, il macchinario modello C++ è leggermente migliore. Per la vera magia devi usare entrambi i generatori esterni (scrivere generatori C/C++ per esempio in python/perl è facile) o passare ad altre lingue dove è disponibile una metaprogrammazione seria (ad esempio Lisp). – 6502

2

Boy, questo suona davvero come un lavoro per C++.

Penso che il più vicino possibile a questo in C è quello di non passare il tipo, ma piuttosto la dimensione (sizeof (tipo)).

È possibile rendere la propria funzione più generica in modo che possa fare ciò che deve fare se tutto ciò che conosce è la dimensione di ciascun elemento nell'array. Questo è il modo in cui funzionano funzioni come bsearch().

+0

hmm grazie, so che è più un lavoro per C++. ma mi stavo chiedendo se fosse possibile. : [purtroppo non lo è. –

+0

È sicuro fare assunzioni sul tipo di dati semplicemente osservando le dimensioni? –

+0

Gunner: è sicuro fare supposizioni su quanta memoria utilizza ogni oggetto. Questo dovrebbe essere sufficiente per allocare e spostare la memoria. L'unico problema è se il tipo di dati è un puntatore. In tal caso, l'oggetto puntato dovrebbe anche essere copiato. Ma per tutti i tipi di base, questo approccio è perfettamente valido. –

2

Un modo per ottenere ciò è utilizzare il cosiddetto X-macros.

Here è un'implementazione vettoriale generica (probabilmente con errori) che utilizza questa tecnica.

Viene poi utilizzato come

// defining generic parameters 
#define PREFIX tv 
#define ITEM token 
#define NAME token_vector 
#include "vector.h" 

... 
token_vector tv = tv_new(100); 
*(tv.front) = some_token; 
tv_push_back(&tv, other_token); 
0

Ho lavorato con la programmazione generica in C qualche anno fa, solo per il gusto di farlo.

Fondamentalmente, ho finito per sfruttare il preprocessore. Credo di aver avuto un lieve successo: ho realizzato alcune notazioni macro per molte delle più importanti strutture di dati generici.

Quello che sicuramente non ha compire (in qualsiasi modo automatico almeno) è stato in modo ricorsivo l'esecuzione delle macro - vale a dire, la creazione di una matrice-di-array o array di hash-ecc Questo è dovuto al interessante tosse pazzo tosse semantica di macro preprocessore C.

Se siete interessati, ecco il codice: https://github.com/christianfriedl/CGenerics/blob/master/src/cgArray.h