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

Есть ли эквивалент в C для шаблонов С++?

В коде, который я пишу, мне нужны функции foo(int, char*) и foo(int, int).

Если бы я кодировал это в С++, я бы использовал шаблоны. Существует ли какой-либо эквивалент для C? Или я должен использовать указатели void? Как?

4b9b3361

Ответ 1

Вы не можете этого сделать.
В C нет перегрузок, одной функции, одного имени, вам нужно будет использовать тип, который поддерживает все ваши потребности, например. (void *)

Либо это, либо сделать foo_int(int,int) и a foo_char(int, char*)

Ответ 2

Я думаю, что самое близкое, что вы можете получить в шаблонах C к шаблонам, - это какой-то уродливый макрокод. Например, чтобы определить простую функцию, которая возвращает дважды свой аргумент:

#define MAKE_DOUBLER(T)  \
    T doubler_##T(T x) { \
        return 2 * x;    \
    }

MAKE_DOUBLER(int)
MAKE_DOUBLER(float)

Обратите внимание, что поскольку C не имеет перегрузки функций, вам нужно сыграть трюки с именем функции (вышесказанное делает как doubler_int, так и doubler_float, и вам придется их так называть).

printf("%d\n", doubler_int(5));
printf("%f\n", doubler_float(12.3));

Ответ 3

Да, есть. Вы можете использовать типовое выражение в C11:

#include <stdio.h>

void foo_char_ptr(int a, char *b) {
  printf("Called int, char*.\n");
}

void foo_int(int a, int b) {
  printf("Called int, int.\n");
}

#define foo(a, b) _Generic((b), char*: foo_char_ptr, int: foo_int)(a, b)

int main() {
  foo(1, 1);
  foo(1, "foo");
}

// Output:
// Called int, int.
// Called int, char*.

Ответ 4

Другие обсудили внутреннее ограничение c в отношении перегрузки. Обратите внимание, однако, что если вы можете вывести, какой случай нужен, вы можете использовать varargs:

#include <stdarg.h>
foo(int, ...);

Если вы не можете его вывести, вы можете передать дополнительный аргумент:

foo(int, char *spec, ...);

где spec сообщает функции, что ожидать в последующих аргументах. Подобно семействам функций printf и scanf. На самом деле вам может показаться удобным повторное использование соглашений printf/scanf для указания типа, таким образом, чтобы ваши пользователи не могли наклонить другой мини-язык.

Ответ 5

Вместо void * вы также можете использовать объединение для хранения любых типов данных, которые вам нужны:

typedef struct {
    int type;
    union {
        char* char_ptr;
        int int_val;
        // etc...
    };
} foo_data;

void foo(foo_data data)
{
    switch (data.type) {
        case 0:
            printf("%s\n", data.char_ptr);
            break;
        case 1:
            printf("%i\n", data.int_val);
            break;
    }
}

void main()
{
    foo_data data;

    data.type = 0; data.char_ptr = "hello";
    foo(data);
    data.type = 1; data.int_val  = 12;
    foo(data);
}

Конечно, вы должны создать константы для значений типа.

Ответ 6

Шаблоны могут быть реализованы с использованием заголовков шаблонов.

Пусть foo.h вот так:

#ifndef PREFIX
    #define PREFIX
#endif
#define CCAT2(x, y) x ## y
#define CCAT(x, y) CCAT2(x, y)
#define FN(x) CCAT(PREFIX, x)

#ifndef T
    #error Template argument missing.
#endif

void FN(foo)(int x, T t)
{
    // Whatever.
}


#undef T
#undef PREFIX
#undef CCAT2
#undef CCAT
#undef FN

Чтобы использовать его, вы можете:

#define T char*
#define PREFIX pchar_
#include "foo.h"

#define T int
#define PREFIX int_
#include "foo.h"

Теперь у вас есть pchar_foo() и int_foo(), которые вы можете использовать.

Преимущества этого в том, что если есть проблема с сборкой, вы получаете номер строки в заголовке шаблона вместо компилятора, просто говоря, что макрос ошибочен, а также завершение кода в некоторых IDE.

Макросы PREFIX, CCAT и FN очень распространены, поэтому я выделил их определение в отдельный заголовок, а их неопределенность на другой.

Я реализовал части STL, используя этот шаблон для удовольствия и использую его в некоторых моих проектах C.