Кратчайший пример использования шаблонизирующей функции в C? - PullRequest
2 голосов
/ 23 мая 2010

Как мне подойти к функции echo_tpl, которая может принять 1 параметр типа int или string и распечатать его?

Ответы [ 4 ]

8 голосов
/ 23 мая 2010

C не имеет шаблонов.Я думаю, что лучшее, что вы могли бы сделать, это использовать объединение или иметь имена функций с разными именами.Последний способ иметь разные имена - это квази-стандартный метод (например, fabs fabsf fabsl, также интенсивно используемый OpenGL, который также объясняет тот факт, что C не можетфункции перегрузки)

void echo_tpl_s(char const *string) { /* ... */ }
void echo_tpl_i(int number) { /* ... */ }

int main(void) {
  echo_tpl_s("Hello world");
  echo_tpl_i(42);
}

Если общего кода много, вы можете решить выделить его в отдельные функции

void echo_tpl_s(char const *string) { 
  prepare_output_device();
  printf("%s", string);
  unprepare_output_device();
}

void echo_tpl_i(int number) { 
  prepare_output_device();
  printf("%d", number);
  unprepare_output_device();
}

Или вы можете взять union путь, который будет иметь имена функций равными, но вместо этого взорвать тип параметра с метаинформацией.

enum Type {
  Number,
  String
};

struct Value {
  enum Type type;
  union { 
    int number;
    char const *string;
  } u;
};

void echo_tpl(struct Value value) {
  switch(value.type) {
    case Number: printf("%d", value.u.number); break;
    case String: printf("%s", value.u.string); break;
  }
}

int main(void) {
  echo_tpl((struct Value) { 
    .type = String, 
    .u.string = "Hello world" 
  });
}

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

int main(void) {
  struct Value value;
  value.type = String;
  value.u.string = "Hello world";
  echo_tpl(value);
}

Это хорошая идея для создания функций, хотя

struct Value stringval(char const *string) {
  struct Value value;
  value.type = String;
  value.u.string = string;
  return value;  
}

struct Value numberval(int number) {
  struct Value value;
  value.type = Number;
  value.u.number = number;
  return value;  
}

int main(void) {
  echo_tpl(stringval("Hello world!"));
}

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

void echo_tpl(int value) __attribute__((overloadable)) {
  printf("%d", value);
}

void echo_tpl(char const *value) __attribute__((overloadable)) {
  printf("%s", value);
}

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

4 голосов
/ 23 мая 2010

Традиционный способ перевода шаблонов в C - использование препроцессора. Я бы сделал это примерно так:

// this creates each template "instance"
#define ECHO_TPL_IMPLEMENT(t) void echo_tpl_##t(t param){\
/* this is where you write your function that uses param */ \
}

// this calls the specific template instance
#define ECHO_TPL(t, val) echo_tpl_##t(val)

// as i wrote it, the function only accepts a 1 word parameter type
// so for simplicity, i'm defining char* to be string
typedef char *string;

// i implement the function for the types int and string
ECHO_TPL_IMPLEMENT(int)     // creates echo_tpl_int
ECHO_TPL_IMPLEMENT(string)  // creates echo_tpl_string

main()
{
  // then i just call them and let the preprocessor handle it
  ECHO_TPL(string, "meep");  // will call echo_tpl_string
  ECHO_TPL(int, 10);         // will call echo_tpl_int
}

Вот как исходные компиляторы C ++ обрабатывали шаблоны, только у них были (и до сих пор действуют) более сложные правила искажения типов, где я просто предположил, что типы - это 1 слово, а если нет, вам придется typedef им.

edit : Обратите внимание, что я оставил функцию пустой. Это действительно то, как вы пишете «шаблонные функции» в C, но я не могу написать параметр, как вы просили, потому что C не имеет независимого от типа API для записи файла. printf и write запрашивают информацию о фактическом типе (через %d или %s и через длину в байтах того, что писать соответственно), и у нас этого нет.

Также обратите внимание, что это относится и к C ++. Вы также не можете использовать C api для записи в файл из шаблона, вы можете использовать только cout (или альтернативу формата Boost или что-то подобное). Вам нужно будет подумать, что вы хотите сделать с фактической функцией.

0 голосов
/ 25 апреля 2013

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

#define echo_tpl(X) _Generic((X), int: echo_tpl_i, \
                               char *: echo_tpl_s)(X)

void echo_tpl_s(char const *string) { /* ... */ }
void echo_tpl_i(int number) { /* ... */ }

int main(void) {
  echo_tpl("Hello world");
  echo_tpl(42);
}

Вам все еще нужно определить реализации функций, используя отдельные имена, как в C99, но вы можете определить макрос с именем C ++ - ish, который вставляет выражение _Generic в каждую точку использования, чтобы выбрать правильная версия для этого сайта вызова, не заставляя пользователя думать о типе аргумента.

По-видимому, для полного принятия стандартов C требуется вечность, и я понятия не имею, какие компиляторы реализуют эту функцию, но она станет широко распространенной раньше, если больше людей выйдет и будет ее использовать!

0 голосов
/ 23 мая 2010
template <typename T>
void echo_tpl(const T& t) { std::cout << t; }

РЕДАКТИРОВАТЬ: я не заметил тег c.Приведенный выше ответ работает только с C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...