2012-02-06 18 views
6

Czy jest jakiś sposób, że mogę wykryć typ zmiennej automatycznie w C, albo przez jakiś mechanizm w samym programie, albo - bardziej prawdopodobny - poprzez wstępną kompilację skrypt, który wykorzystuje przepustki kompilatora do punktu, w którym przeanalizował zmienne i przypisał im ich typy? Szukam ogólnych sugestii na ten temat. Poniżej znajduje się więcej informacji o tym, czego potrzebuję i dlaczego.Uzyskiwanie typu zmiennej w kodzie C

Chciałbym zmienić semantykę klauzuli redukcji OpenMP. W tym momencie wydaje się najłatwiej po prostu zamienić klauzulę w kodzie źródłowym (poprzez skrypt) na wywołanie funkcji, a następnie mogę zdefiniować funkcję implementacji semantyki redukcji, jakiej chcę. Na przykład, mój skrypt będzie przekonwertować ten

#pragma omp parallel for reduction(+:x) 

w tym:

my_reduction(PLUS, &x, sizeof(x)); 
#pragma omp parallel for 

gdzie wcześniej, mam (powiedzmy)

enum reduction_op {PLUS, MINUS, TIMES, AND, 
    OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */}; 

I my_reduction ma podpis

void my_reduction(enum reduction_op op, void * var, size_t size); 

Wśród innych t hings, my_reduction musiałby zastosować operację dodawania do zmiennej redukcji, jak pierwotnie zamierzał programista. Ale moja funkcja nie może wiedzieć, jak to zrobić poprawnie. W szczególności, chociaż zna rodzaj operacji (PLUS), lokalizacja oryginalnej zmiennej (var), a także rozmiar typu zmiennej, nie zna samego typu zmiennej. W szczególności nie wiadomo, czy var ma typ całkowy czy zmiennoprzecinkowy. Z POV o niskim poziomie operacja dodawania dla tych dwóch klas typów jest zupełnie inna.

Jeśli tylko niestandardowy operator typeof, który obsługuje GCC, działałby tak, jak działa sizeof - zwracając rodzaj zmiennej - mógłbym łatwo rozwiązać ten problem. Ale typeof nie jest tak naprawdę sizeof: może być użyty tylko w deklaracjach L-value.

Teraz kompilator oczywiście nie zna typu x przed zakończeniem generowania kodu wykonywalnego. Prowadzi mnie to do zastanawiania się, czy mogę w jakiś sposób wykorzystać parser GCC, aby uzyskać typ x i przekazać go do mojego skryptu, a następnie ponownie uruchomić GCC, aby skompilować mój zmieniony kod źródłowy. Byłoby wtedy być proste wystarczy zadeklarować

enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE}; 
void my_reduction(enum reduction_op op, void * var, enum var_type vtype); 

And my_reduction może oddać odpowiednio przed dereferencji i stosowania operatora.

Jak widać, próbuję stworzyć rodzaj mechanizmu "dyspozytorskiego" w C. Dlaczego po prostu nie używać przeciążania C++? Ponieważ mój projekt ogranicza mnie do pracy ze starszym kodem źródłowym napisanym w C. Mogę zmienić kod automatycznie za pomocą skryptu, ale nie mogę go przepisać na inny język.

Dzięki!

+2

Co powiesz na przetwarzanie kodu źródłowego za pomocą niektórych narzędzi/skryptów? Na przykład. przeanalizować go za pomocą języka, znaleźć typy, wstawić/dostosować kod specyficzny dla typu, skompilować? –

+0

Dzięki, Alex. To brzmi jak idzie w dobrym kierunku. –

+1

Czytałem gdzieś, że zdefiniowana przez użytkownika redukcja będzie częścią standardu 3.1 lub 4.0. Hm 3.1 mówi: 'reduction ({operator | nazwa_procedury_wirtualnej}: list)' .. once tried nazwa_procedury_wejściowej. chociaż tylko częściowo rozwiązuje wykrywanie typu. – Bort

Odpowiedz

2

GCC udostępnia rozszerzenie typeof. Nie jest standardem, ale dość często (ma kilka innych kompilatorów, np. Clang/llvm).

Być może warto rozważyć dostosowanie GCC poprzez rozszerzenie go o MELT (język specyficzny dla danej domeny w celu rozszerzenia GCC), aby pasował do twoich celów.

+1

Dzięki, Basile. Jak widzisz z mojego postu, mam świadomość typeof, ale typeof nie da mi tego, czego potrzebuję: nie zwraca niczego; nie możesz przekazać jej lub jej informacji do funkcji. Możesz to zrobić: #define dodaj (x, y, z) {\ typeof (x) _x = x; \ typeof (y) _y = y; \ z = _x + _y; \ } Wydaje mi się, że operator typu typeof ma bardzo ograniczoną przydatność. –

+1

To nie jest standard i nie będzie działać na większości kompilatorów języka C. Z mojego doświadczenia wynika, że ​​używanie kodów GCC do kodu produkcyjnego jest złym pomysłem. – Lundin

2

Można również rozważyć dostosowanie GCC za pomocą wtyczki lub rozszerzenia MELT do swoich potrzeb. Wymaga to jednak zrozumienia niektórych wewnętrznych reprezentacji GCC (Gimple, Tree), które są złożone (co zajmie Ci przynajmniej dni pracy).

Ale typy są kompilacją tylko w C. Nie są reifikowane.

+1

To jest mniej więcej to, co zrobiłem - nie plug-in, ale hakowanie kodu w samym GCC. Pomyślałem, jak uzyskać informacje o typie z drzewa Gimple tree_nodes podczas kompilacji. To wymagało sporo kopania. Dzięki! –

4

C tak naprawdę nie ma sposobu, aby wykonać to w czasie przed kompilacją, chyba że napisze się powódź makr. Chciałbym nie polecam zalew podejścia makr, byłoby po prostu iść tak:

void int_reduction (enum reduction_op op, void * var, size_t size); 

#define reduction(type,op,var,size) type##_reduction(op, var, size) 

... 
reduction(int, PLUS, &x, sizeof(x)); // function call 

Zauważ, że to jest bardzo zła praktyka i powinny być używane tylko jako ostateczność przy zachowaniu źle napisany kod starszego typu, jeśli nawet następnie. W przypadku tego podejścia nie ma bezpieczeństwa typu ani innych takich gwarancji.

Bezpieczniejsze podejście jest jawnie wywołać int_reduction() od rozmówcy, lub zadzwonić rodzajowe funkcji, która decyduje typ w czasie wykonywania:

void reduction (enum type, enum reduction_op op, void * var, size_t size) 
{ 
    switch(type) 
    { 
    case INT_TYPE: 
     int_reduction(op, var, size); 
     break; 
    ... 
    } 
} 

Jeśli int_reduction jest inlined i różne inne optymalizacje są wykonane, to ocenę wykonania niekoniecznie jest o wiele wolniejszy od zaciemnianych makr, ale jest o wiele bezpieczniejszy.

+1

Tak, dzięki za twoje myśli. Ale jak się dowiedzieć, jaki jest typ? To moje wielkie wyzwanie. Reszta to po prostu składnia - i oczywiście zgadzam się z twoimi komentarzami na temat najlepszej składni. W powyższym przypadku, na przykład, skąd mam wiedzieć, że dana zmienna to INT_TYPE? Skrypt, który piszę, aby przenieść "redukcję (+: x)" do osobnego wywołania "redukcja (INT_TYPE, PLUS, & var, sizeof (x));" musiałby wiedzieć, że x ma typ INT_TYPE, ale w jaki sposób? - jeśli nie mam pisania całego parsera, żeby uzyskać typ x? –

+1

@AmittaiAviram Skąd wiadomo, kiedy zadeklarować zmienną jako 'int'? Nie mam pojęcia o charakterze twoich danych ani o pochodzeniu, więc nie mogę odpowiedzieć na to pytanie. Jeśli jest to jakiś rodzaj zewnętrznych danych surowych, musisz oczywiście określić typ danych przed wykonaniem jakichkolwiek obliczeń. Jest to problem algorytmiczny, który nie ma nic wspólnego ze składnią programowania C jako taką. – Lundin

+1

@ Lundin - Nie, nie. Obraz jest bardziej podobny do tego. Załóżmy, że masz funkcję 'main', która ma deklarację' int x' w pobliżu góry. Powiedzmy 100 linii w dół, masz '#pragma omp równolegle do redukcji (+: x)'. 'X' ma typ' int', ponieważ tak został zadeklarowany w bieżącym zakresie. Programista wie o tym i może sam opisywać wywołanie funkcji, ale chcę to zrobić automatycznie. –

7

C11 _Generic nie jest bezpośrednim rozwiązaniem, ale nie pozwala na osiągnięcie pożądanego efektu, jeśli pacjent kodować wszystkie rodzaje jak w:

#define typename(x) _Generic((x), \ 
    int:  "int", \ 
    float: "float", \ 
    default: "other") 

int i; 
float f; 
void* v; 
assert(strcmp(typename(i), "int") == 0); 
assert(strcmp(typename(f), "float") == 0); 
assert(strcmp(typename(i), "other") == 0); 

dobrym punktem wyjścia z tonami typów można znaleźć in this answer.

GCC dodał wsparcie tylko w wersji 4.9.

4

Możesz użyć funkcji sizeof do określenia typu, niech zmienna nieznanego typu będzie zmienna. następnie

if(sizeof(var)==sizeof(char)) 
     printf("char"); 
    else if(sizeof(var)==sizeof(int)) 
     printf("int"); 
    else if(sizeof(var)==sizeof(double)) 
     printf("double"); 

Ty to będzie prowadziło do powikłań, gdy dwa lub więcej podstawowe typy mogą mieć taką samą wielkość.