2010-12-12 27 views
5

Czy można replikować ogólną tablicę w czystym ANSI-C?Czysta ANSI-C: generic array

Mam tę strukturę, która posiada tablicę (w tej chwili dla pływaków) i niektóre zmienne, takie jak rozmiar i pojemność mutacji w tablicy.

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; 

używam tej struktury, więc mogę zrobić tablicę w czystym C, gdzie mogę dodać/usunąć elementy, dynamicznie rozwinąć rozmiar tablicy, gdy są potrzebne itd. Wszystkie rzeczy „standard” tablica ma, oprócz tego, że jest wykonane tylko w C. A teraz chcę to zrobić, aby po zainicjowaniu tej struktury można było ustawić typ danych elementów, które powinien on posiadać, w tej chwili jest on zdolny do przechowywania typów danych typu float, ale chcę, aby był on przechowywany typ danych/inne struktury. Ale nie wiem, czy to nawet możliwe.

W tej chwili funkcja do tej tablicy jest:

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; 
} 

Czy to możliwe, aby podać typ danych jako parametr, więc mogę malloc pamięci dla tego typu danych i rzucił ją jak podano typ danych w tablicy dynamicznie ?

Dzięki z góry,

Marnix van Rijswijk

+0

nie myślcie w czystym C można przekazać typy danych w ten sposób. Patrząc na języki, które obsługują heterogeniczną listę tablic, np. C#, działa ona tylko dla nie podstawowych typów danych, to jest Klasy, a nie na int, float itp. Ponieważ C nie jest zorientowana obiektowo, jest wysoce nieprawdopodobne, aby uzyskać ten obiekt. –

+2

nie uruchamiaj identyfikatorów z podkreśleniami: takie nazwy są zarezerwowane dla implementacji (kompilator + libc); używanie podkreślenia i wielkie litery są podwójnie złe: nazwy te są zarezerwowane w dowolnym kontekście, ponieważ używają nowych funkcji językowych (np. '_Pragma',' _Complex', '_Bool', ...); Prostym sposobem obejścia tego problemu jest używanie końcowych znaków podkreślenia, co jest również przyjemne z prefiksem opartym na prefiksach. – Christoph

+1

Na stronie jest wiele pytań dotyczących tego, jak można konstruować zachowania obiektowe w c: [Object-Orientation in C] (http: // /stackoverflow.com/q/415452/2509) i [Czy można napisać kod obiektowy w języku C?] (http://stackoverflow.com/q/351733/2509) i inne. Możesz osiągnąć pożądany rezultat dzięki rozsądnemu użyciu 'sizeof' i mechanizmowi wskaźnika funkcji omawianego w linkach, ale będzie to więcej pracy, która jest warta. Interfejs 'qsort' i' bsearch' jest kompromisem. – dmckee

Odpowiedz

8

Twój kod ma poważny problem ... wracasz adres zmiennej lokalnej (CustomArray) i gdy funkcja zwraca, że ​​zmienna jest zniszczony, więc nie można używaj go ze wskaźnikiem. Musisz zdekonocować także tę strukturę, aby pamięć pozostała dostępna po przywróceniu funkcji.

O co typ parametr, który można dostać nieco blisko użyciu makr ... na przykład o coś takiego:

#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;\ 
} 

Następnie można użyć go w ten sposób

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

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

pamiętać, że Aby używać makr do definiowania wszystkich własnych funkcji. Zauważ też, że to podejście powiela kod w każdym module (powiedziałbym, że nie jest to duży problem, ale jeśli nie możesz używać C++ prawdopodobnie twoja platforma docelowa jest całkiem mała). Przy nieco bardziej złożonym podejściu można wygenerować oddzielne pliki .h i pliki .c dla implementacji.

+0

Cholera, na pewno też spróbuję. dzięki za to. –

+0

to działa, a także pokazał mi zupełnie nowy sposób robienia rzeczy. niesamowite. –

+3

Hehe ... witaj w świecie metaprogramowania (pisanie kodu, który pisze kod). Preprocesor C to potwornie słaba forma metaprogramowania, maszyny szablonów C++ są tylko trochę lepsze. Do prawdziwej magii trzeba użyć zewnętrznych generatorów (pisanie generatorów C/C++ na przykład w python/perl jest łatwe) lub przejść do innych języków, w których dostępne jest poważne metaprogramowanie (np. Lisp). – 6502

2

Boy, to naprawdę brzmi jak zadanie dla C++.

Myślę, że najbliższym, do którego można dojść w C, jest nie podanie typu, ale raczej wielkość (sizeof (type)).

Możesz uczynić swoją funkcję bardziej ogólną, aby mogła zrobić to, co musi, jeśli wszystko, co wie, to rozmiar każdego elementu w tablicy. Tak działają funkcje takie jak bsearch().

+0

hmm dzięki, wiem, że to więcej pracy dla C++. ale zastanawiałem się, czy to możliwe. : [Niestety, nie jest. –

+0

Czy można bezpiecznie przyjąć założenia dotyczące rodzaju danych, patrząc tylko na rozmiar? –

+0

Strzelec: Można bezpiecznie założyć, ile pamięci zużywa każdy przedmiot. To powinno wystarczyć do przydzielania i przenoszenia pamięci. Jedynym problemem jest to, czy typ danych jest wskaźnikiem. W takim przypadku wskazany element powinien również zostać skopiowany. Ale dla wszystkich podstawowych typów to podejście jest całkowicie poprawne. –

2

Jednym ze sposobów osiągnięcia tego jest użycie tak zwanego X-macros.

Here to (prawdopodobnie błędna) implementacja wektora generycznego przy użyciu tej techniki.

Następnie jest on używany jako

// 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

Pomyślałem o ogólnym programowaniu w C kilka lat temu, tylko dla do cholery.

W zasadzie wykorzystałem preprocesor. Wydaje mi się, że osiągnąłem niewielki sukces: wykonałem notację makr dla kilku najważniejszych ogólnych struktur danych.

Co ja zdecydowanie NIE osiągnąć (w każdym automatyczny sposób co najmniej) został uruchomiony rekurencyjnie makra - czyli tworzenie tablicą-of-tablic lub tablicy-of-mieszań itp to ze względu na ciekawą kaszel szalony kaszel semantyka makr preprocesora C.

Jeśli jesteś zainteresowany, oto kod: https://github.com/christianfriedl/CGenerics/blob/master/src/cgArray.h