views:

50

answers:

1

I'm trying to engage in some C-preprocessor-only templating efforts in order to type-specialize some code. I've tried to boil it down a bit, so this example seems trivial and pointless, but the real challenge is getting the "include" blocking.

Say I have a "template" file, that gets #included from other source files that define T_ELEMENT_TYPE before including the template.

// Template file...
#ifndef T_ELEMENT_TYPE
#error #define T_ELEMENT_TYPE
#endif

#define PASTER(x,y) x ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define SYMBOLNAME EVALUATOR(SymbolFor, T_ELEMENT_TYPE)

#ifndef SYMBOLNAMEISDEFINED
#define SYMBOLNAMEISDEFINED EVALUTOR(DEFINEDFOR, T_ELEMENT_TYPE)

int SYMBOLNAME(T_ELEMENT_TYPE arg)
{
    // do something with arg
    return 0;
}

#endif // Guard #ifdef

Then I want to include that template from multiple instantiation sites, but I only want the templated function to be generated ONCE per unique T_ELEMENT_TYPE (so as not to create duplicate symbols.) Like, say this:

// Template-using file...    
#define T_ELEMENT_TYPE int
#include "Template.c"
#undef T_ELEMENT_TYPE

#define T_ELEMENT_TYPE float
#include "Template.c"
#undef T_ELEMENT_TYPE

#define T_ELEMENT_TYPE int
#include "Template.c"
#undef T_ELEMENT_TYPE

int someOtherFunc()
{
    int foo = 42;
    foo = SymbolForint(foo);
    float bar = 42.0;
    bar = SymbolForfloat(bar);
    return foo;
}

So I'm looking for something I can use in the template code. I imagined it might look something like this (although this does not work):

// Template file...
#ifndef T_ELEMENT_TYPE
#error #define T_ELEMENT_TYPE
#endif

#define PASTER(x,y) x ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define SYMBOLNAME EVALUATOR(SymbolFor, T_ELEMENT_TYPE)

#ifndef SYMBOLNAMEISDEFINED
#define SYMBOLNAMEISDEFINED EVALUTOR(DEFINEDFOR, T_ELEMENT_TYPE)

int SYMBOLNAME(T_ELEMENT_TYPE arg)
{
    // do something with arg
    return 0;
}

#endif // Guard #ifdef

This particular incantation blocks ALL multiple instantiations of the template, not just for different values of T_ELEMENT_TYPE.

Is there a trick I can use to get this effect? Or am I just off the C-Preprocessor reservation, so to speak?

Thanks! Jour

+1  A: 

I think you're off the reservation. The first "argument" to #define, the macro name, isn't subject to macro-expansion. So I don't think the preprocessor can define a different symbol according to the value of T_ELEMENT_TYPE. Neither can the preprocessor construct a "list" of already-seen types and check for existence in that.

So I think the include-guard will have to be outside the file:

#ifndef included_mytemplatefile_h_int
    #undef T_ELEMENT_TYPE
    #define T_ELEMENT_TYPE int
    #include "mytemplatefile.h"
    #define included_mytemplatefile_h_int
#endif

Alternatively, if your template file header only declares the function SymbolFor_int, instead of defining it, then multiple inclusion isn't harmful. You could have a normal include guard around the parts of the file that don't depend on the current value of T_ELEMENT_TYPE, including the definitions of PASTER, EVALUATOR, SYMBOLNAME. You'd need a separate template file containing definitions, which the program (rather than each translation unit) needs to have exactly once:

template_%.c :
    echo "#define T_ELEMENT_TYPE $*" > $@
    echo "#include \"mytemplatedefinitions.c\"" >> $@

Then add template_int.o to the list of files linked into your program.

Steve Jessop
Most definitely off the reservation. The rules for #ifdef/#ifndef/defined are carefully worded such that there is no possibility of performing a macro-expansion on the identifier being tested, and that is what you need for the idea from the OP to work.
Bart van Ingen Schenau