在C中使用模板功能的最短例子?

时间:2010-05-23 11:34:39

标签: c syntax

如何处理可以采用echo_tplint类型的 1 参数并将其打印出来的函数string

4 个答案:

答案 0 :(得分:8)

C没有模板。我认为你能做的最好的事情就是使用一个联合或让函数有不同的名字。后一种具有不同名称的方法是这样做的准标准方法(例如fabs fabsf fabsl,也是OpenGL的heavily used,它也解释了事实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" 
  });
}

如果你想将值存储在某处然后执行print函数而不关心传递给它的值类型,那么union方式特别适合。在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中提供function overloading

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

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

这解决了函数的调用方不依赖于类型。在定义方面,您仍然需要编写两次代码。这主要是因为(另一个答案解释)C没有类型通用输出函数。当然,如果您使用此功能,您的代码将变为不可移植。

答案 1 :(得分:4)

将模板转换为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他们。

编辑:请注意,我将该功能留空了。这确实是你在C中编写“模板化函数”的方法,但是我不能像你要求的那样编写参数,因为C没有编写api的类型独立文件。 printfwrite需要有关实际类型的信息(通过%d%s以及分别写入的字节长度),我们没有这一点。

另请注意,这也适用于C ++。您也不能使用C api从模板写入文件,您只能使用cout(或者使用boost格式替代或类似的东西)。你必须考虑你想用实际的功能做什么。

答案 2 :(得分:0)

最近,但值得补充的是,从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标准,我不知道哪个编译器实现了这个功能,但如果有更多的人出去使用它,它会越来越普遍!

答案 3 :(得分:-1)

template <typename T>
void echo_tpl(const T& t) { std::cout << t; }

编辑:我没有发现c标签。以上答案仅适用于C ++。