tags:

views:

96

answers:

3

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

A: 
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++.

Marcelo Cantos
Isn't this C++?
httpinterpret
Yes it is. That's why I deleted my similar answer...
Axel Gneiting
+6  A: 

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.

Johannes Schaub - litb
+1 , I was going to suggest using a union.
Tim Post
Would this approach be ok for 64bit machines?
Gary Willoughby
@Gary both techniques would be alright for 64bit machines. The members of an union aren't required to have the same size.
Johannes Schaub - litb
But they would anyway wouldn't they? Both `int` and `const char *` have the same size on a PC.
Blindy
@Blindy on the two [most common data models](http://en.wikipedia.org/wiki/64-bit#64-bit_data_models), they wouldn't have the same size on 64bit.
Johannes Schaub - litb
Hm interesting, wonder why they chose not to upgrade the meaning of `int` from 32 to 64 bits like they did from 16 to 32.
Blindy
+4  A: 

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.

Blindy
Er yea you're right, it's late :(
Blindy