2013-05-13 16 views
24

Muszę zaimplementować niektóre metody, które robią rzeczy z różnymi rodzajami tablic liczbowych. Zwykle używałbym generycznych dla tego zadania, ale ponieważ C ich nie dostarcza, próbuję teraz emulować je za pomocą makr.Pseudo-generics in C

Oto przykład tego, co próbuję zrobić:

#ifndef TYPE 
#define TYPE int 
#endif 

TYPE get_minimum_##TYPE (TYPE * nums, int len){ 
    TYPE min = nums[0]; 

    for (int i = 1; i < len; i++) { 
     if (nums[i] < min) { 
      min = nums[i]; 
     } 
    } 

    return min; 
} 

Jednak nie będzie to skompilować. Komunikat o błędzie klang:

error: expected ';' after top level declarator

Czy jest jakiś sposób to zrobić w C? Czy muszę wdrożyć to dla każdego typu ręcznie?

+0

Generics fyi w języku C# i java są obiektami środowiska wykonawczego. –

+0

Jeśli chcesz przeczytać trochę więcej na temat "templates"/"generics" w C, [możesz przeczytać to pytanie] (http://stackoverflow.com/questions/10950828/simulation-of-templates-in-c). Jest kilka interesujących odpowiedzi :) – Jehan

Odpowiedz

25

można zrobić coś takiego w pliku nagłówka:

// 
// generic.h 
// 

#define TOKENPASTE(x, y) x ## y 

#define GET_MINIMUM(T) TOKENPASTE(get_minimum_, T) 

TYPE GET_MINIMUM (TYPE) (TYPE * nums, size_t len){ 
    TYPE min = nums[0]; 

    for (size_t i = 1; i < len; i++) { 
     if (nums[i] < min) { 
      min = nums[i]; 
     } 
    } 

    return min; 
} 

a następnie #include go w pliku źródłowym dla każdego wymaganego typu, np:

// 
// generic.c 
// 

#define TYPE int 
#include "generic.h" 
#undef TYPE 

#define TYPE float 
#include "generic.h" 
#undef TYPE 

Można to sprawdzić uruchamiając przez preprocesor:

$ gcc -E generic.c 

int get_minimum_int (int * nums, size_t len){ 
    int min = nums[0]; 

    for (size_t i = 1; i < len; i++) { 
     if (nums[i] < min) { 
      min = nums[i]; 
     } 
    } 

    return min; 
} 

float get_minimum_float (float * nums, size_t len){ 
    float min = nums[0]; 

    for (size_t i = 1; i < len; i++) { 
     if (nums[i] < min) { 
      min = nums[i]; 
     } 
    } 

    return min; 
} 
23

Właściwie najlepsze, co możesz zrobić, to zdefiniować acro, które wygeneruje funkcję dla danego typu.

#define define_get_minimum(T) \ 
T get_minimum_##T(T* nums, int len){ \ 
    T min = nums[0]; \ 
    for (int i = 1; i < len; i++) { \ 
     if (nums[i] < min) { \ 
      min = nums[i]; \ 
     } \ 
    } \ 
    return min; \ 
} 

Następnie można nazwać tego makra do określenia specjalizacji potrzebne (z szablonu C++ podobna sprawa odbywa się automagicznie przez kompilator).

Inną rzeczą, którą kompilator C++ robi automagicznie, jest dedukcja przeciążonej funkcji, której potrzebujesz. Nie możesz tego mieć w C, więc będziesz musiał powiedzieć, że używasz tej specjalizacji. Można symulować szablonu podobną składnię dla funkcji z następującym makro (C++ <> są po prostu zastąpione przez ()):

#define get_minimum(T) get_minimum_##T 

Następnie powinieneś być w stanie nazwać w następujący sposób:

int main() 
{ 
    // Define arr as char* array... 
    // Do stuff... 
    int res = get_minimum(int)(arr, 3); 
} 

Nie przetestowałem tego kodu, ale powinien zadziałać.

+0

+1: Właśnie to przetestowałem i wygląda na to, że działa. –

+0

Podoba mi się styl ostatniego połączenia, ale próbowałem zarchiwizować to bez dużych makr. – fb55

+0

@ fb55 Cóż, ogólny i nie duży nie jest zgodny w C :) – Jehan

0

Można również użyć wskaźników funkcji (Tablica wskaźników funkcji), innych niż instrukcja switch, i przekazać argument przełącznika jako indeks do tablicy.