httpinterpret
httpinterpret

Reputation: 6709

Shortest example to use templating function in C?

How do I approach a function echo_tpl that can take 1 parameter of type int or string ,and print it out?

Upvotes: 2

Views: 230

Answers (4)

Alex Celeste
Alex Celeste

Reputation: 13370

Late, but worth adding to this that as of the C11 standard, C now has some very limited support for overloading by using _Generic expressions, that select the right result expression at compile-time based on the type of an input. Nothing like templates but they can answer this old question like this:

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

You still have to define the function implementations using separate names as you would in C99, but you can define a macro with the C++-ish name that inserts a _Generic expression at every point of use, in order to choose the right version for that call site, without making the function user think about the argument type.

It seemingly takes forever for C standards to be fully adopted and I have no idea which compilers implement this feature, but it will become widespread sooner if more people go forth and use it!

Upvotes: 0

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506905

C doesn't have templates. I think the best you could do is to use an union or to have the functions have different names. The latter way of having different names is the quasi-standard method of doing it (for instance fabs fabsf fabsl, also heavily used by OpenGL which also accounts for the fact C can't overload functions)

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

If there is a lot of common code, you may decide to factor it out in separate functions

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();
}

Or you can take the union way, which will have the function names be equal but instead blow up the parameter type with meta informations.

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" 
  });
}

The union way is particular well-suited if you want to store the value somewhere and then execute the print function without caring what value type you pass to it. In C89 you would need to create the value separately since it doesn't have compound literals

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

It's a good idea to create functions for that, though

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!"));
}

Some compilers may provide extensions for writing such things. For instance Clang provides function overloading in C.

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

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

This solves the call-side of the function not to depend on the type. On the definition side, you still have to write the code twice. That's mainly because (as another answer explains) C doesn't have type-generic output functions. Of course if you use this feature, your code becomes nonportable.

Upvotes: 8

Blindy
Blindy

Reputation: 67380

The traditional way to translate templates to C is using the preprocessor. I'd do it something like this:

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

This is how the original C++ compilers handled templates, only they had (and still do to this day) more complex type mangling rules, where I just assumed types are 1 word and if they aren't, you'll have to typedef them.

edit: Note that I left the function empty. This is indeed how you write "templated functions" in C, but I cant really write the parameter like you asked because C doesn't have a type-independent file writing api. printf and write require information about the actual type (through the %d or %s and through the length in bytes of what to write respectively), and we don't have that.

Also note that this applies to C++ too. You can't use the C api to write to a file from a template either, you can only really use cout (or the boost format alternative or something similar). You'll have to think what you want to do with the actual function.

Upvotes: 4

Marcelo Cantos
Marcelo Cantos

Reputation: 185852

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

EDIT: I didn't spot the c tag. The above answer only works with C++.

Upvotes: -1

Related Questions