2

How do you write a macro with variable number of arguments to define a function? Suppose that we define the class class1 with 2 parameters and class class2 with three parameters.

class class1 {
public:
   int arg1;
   int arg2;
   class1(int x1, int x2): arg1(x1), arg2(x2) {}
};
class class2 {
public:
   int arg1;
   int arg2;
   int arg3;
   class1(int x1, int x2, int x3): arg1(x1), arg2(x2), arg3(x3) {}
};

For each class that I define or even classes that have been defined before I want to write the following:

template<> inline void writeInfo<class1>(const class1& obj, FILE* fp) {
    writeAmount(2, fp);
    writeName("arg1", fp);
    writeInfo(obj.arg1, fp);
    writeName("arg2", fp);
    writeInfo(obj.arg2, fp);
}
template<> inline void writeInfo<class2>(const class2& obj, FILE* fp) {
    writeAmount(3, fp);
    writeName("arg1", fp);
    writeInfo(obj.arg1, fp);
    writeName("arg2", fp);
    writeInfo(obj.arg2, fp);
    writeName("arg3", fp);
    writeInfo(obj.arg3, fp);
}

We do not need to care about the definitions of writeAmount, writeName or writeInfo. What I would like to do is write something like:

MACROWRITEINFO(class1, 2, arg1, arg2);
MACROWRITEINFO(class2, 3, arg1, arg2, arg3);

Is it possible to create such macro so that it can expand to the above template definitions? I've read in a lot of places that macros are evil, but in this case I believe that they are very helpful since they'll reduce the amount of code I type and thus the amount of typos I'll make during the creation of the template functions.

jmlopez
  • 4,853
  • 4
  • 40
  • 74
  • You might want to check out Boost Serialization. This looks like reinventing the wheel. – MSalters Jul 22 '11 at 08:13
  • You can also check out Boost Preprocessor which should give you the tools to create such a macro. – visitor Jul 22 '11 at 08:36
  • @visitor and MSalter, could any of you show me an example using the Boost Preprocessor or Serialization? I'm sorry for sounding lazy and not googleing but I would like to see how I can apply it without having to do a lot of research about this. – jmlopez Jul 22 '11 at 08:48

2 Answers2

5

First of all you should improve your formatting/code. Your code lacks "class" keywords and semicolons after classes definitions - when you post a snippet make sure it's proper code, because some people (i.e. me) will try to compile it.

Second of all, dont use function template specialization. If macros are evil, then they must be satan incarnation. Just stick to the good old overloads. See here for details.

And at least - an answer. You could mess around with variadic macros if all args were of the same type - for example, you could create an array inside writeInfo function and iterate over elements. Since it's cleary not the case here you can define many variants of MACROWRITEINFO macro for different number of parameteres, using some common blocks to reduce code repetition. For example:

#define MACROWRITEINFO_BEGIN(type, amount)  \
void writeInfo(const type& obj, FILE* fp)   \
{                                           \
    writeAmount(amount, fp);

#define MACROWRITEINFO_NAMEINFO(name)       \
    writeName(#name, fp);                   \
    writeInfo(obj.##name, fp);

#define MACROWRITEINFO_END()                \
}

Using those you can now define variants based on number of arguments.

#define MACROWRITEINFO1(type, arg1) \
    MACROWRITEINFO_BEGIN(type, 1)   \
    MACROWRITEINFO_NAMEINFO(arg1)   \
    MACROWRITEINFO_END()

#define MACROWRITEINFO2(type, arg1, arg2) \
    MACROWRITEINFO_BEGIN(type, 2)   \
    MACROWRITEINFO_NAMEINFO(arg1)   \
    MACROWRITEINFO_NAMEINFO(arg2)   \
    MACROWRITEINFO_END()

And so on...

EDIT: Well I guess it is possible to use variadic macros here. Take at look at this SO question. It's pure madness, but you should be able to achieve what you want.

EDIT: My idea was to expand variadic arguments into array then iterate over them; if they were of the same type, let's say int, you could write:

#define VAARGSSAMPLE(...) \
    int args[] = { __VA_ARGS__ }; \
    for (int i = 0; i < sizeof(args)/sizeof(int); ++i) \
    { \
        printf("%d\n", args[i]); \
    }

VAARGSSAMPLE(1, 5, 666);

So if all your variables were of the same type you could put them in an array. But they are not, so it won't do. If you really, really want to stick to variadic arguments go to my first edit.

Community
  • 1
  • 1
gwiazdorrr
  • 6,181
  • 2
  • 27
  • 36
  • Thank you for pointing out my mistakes and for showing linking the details about function template specialization and thank you of course for your answer. About "You could mess around with variadic macros if all args were of the same type", all we are writing here is "arg1", "arg2", ... does it matter what type they are? they are supposed to be written down by the macro. Could you write a variadic macro? I can't seem to find how to iterate through the array. – jmlopez Jul 22 '11 at 09:07
  • I think I'll stick with your first edit. I found something [interesting](http://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments/2124433#2124433), but this requires me to know the variable type ahead of time. Any way of storing the variable type in a variable? In any case, I think I'll stick with your first answer and will avoid using template specializations. – jmlopez Jul 22 '11 at 10:01
  • To clarify: template *type* specializations are great. Template *functions* specializations are pure evil. – gwiazdorrr Jul 23 '11 at 10:06
1

I don't think it's possible to do that with a macro. You can use variable arguments (variadic) but you can't generate code which depends on the arguments.

I'd suggest you create a DSL (e.g. simple xml..) and generate code out of it. this is much cleaner and good practice.

You could do this:

<writeInfos>
    <writeInfo class="class1" amount="3">
        <arguments>
            <argument>arg1</argument>
            <argument>arg2</argument>
        </arguments>
    </writeInfo>
</writeInfos>

Then create source code out of this. You should add this step in your build process..

But you can also define something much simpler.. you could put your MACROWRITEINFO "functions" in a text file and parse it yourself..

duedl0r
  • 9,289
  • 3
  • 30
  • 45
  • I'm not familiar with DSL... could you show me an example that applies to my question? – jmlopez Jul 22 '11 at 08:28
  • I'm sorry I wasn't clear. Domain specific language is a generic expression. You can define it yourself.. e.g. using an xml with all your arguments. I'll update my answer. – duedl0r Jul 22 '11 at 08:31
  • I think I would prefer the second options. Put MACROWRITEINFO functions in another file and parse it to create a cpp file that can be included into the main file. Not so satisfied though. I guess I'll just keep writing the whole function for now. – jmlopez Jul 22 '11 at 08:51