Подтвердить что ты не робот

Псевдо-дженерики в C

Мне нужно реализовать некоторые методы, которые делают вещи с различными типами массивов чисел. Обычно я использую generics для этой работы, но поскольку C не предоставляет их, я теперь пытаюсь имитировать их с помощью макросов.

Вот пример того, что я пытаюсь сделать:

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

Однако это не будет компилироваться. Сообщение об ошибке clang:

error: expected ';' после объявления верхнего уровня

Есть ли способ сделать это в C? Или мне нужно реализовать это для каждого типа вручную?

4b9b3361

Ответ 1

Вы можете сделать что-то подобное в файле заголовка:

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

а затем #include в исходном файле для каждого требуемого типа, например:

//
// generic.c
//

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

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

Вы можете проверить это, выполнив его через препроцессор:

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

Ответ 2

Собственно, самое лучшее, что вы можете сделать, это определить макрос, который будет генерировать функцию для данного типа.

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

Затем вы можете вызвать этот макрос, чтобы определить необходимые вам специализации (с помощью шаблона С++ аналогичная операция выполняется автоматически компилятором).

define_get_minimum(int)
define_get_minimum(double)
define_get_minimum(float)

Другая вещь, которую компилятор С++ делает автоматически, выводит перегруженную функцию, в которой вы нуждаетесь. Вы не можете иметь это в C, поэтому вам придется сказать, что вы используете его специализацию. Вы можете имитировать синтаксис типа шаблона для вашей функции со следующим макросом (С++ <> просто заменяется на ()):

#define get_minimum(T) get_minimum_##T

Затем вы сможете называть его следующим образом:

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

Я не тестировал этот код, но он должен работать.

Ответ 3

Вы также можете использовать указатели функций (Массив указателей функций), кроме оператора switch, и передать аргумент коммутатора в качестве индекса в массив.