Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass multi-argument templates to macros?

Say I have a macro like this:

#define SET_TYPE_NAME(TYPE, NAME) \
    template<typename T>          \
    std::string name();           \
                                  \
    template<>                    \
    std::string name<TYPE>() {    \
        return NAME;              \
    }

This won't work if I pass it a template that has more than one parameter, because the comma in the <int, int> is interpreted as separating the macro arguments, not the template arguments.

SET_TYPE_NAME(std::map<int, int>, "TheMap")
// Error: macro expects two arguments, three given

This problem seems to be solved by doing this:

SET_TYPE_NAME((std::map<int, int>), "TheMap")

But now another problem arises, one that I really did not expect:

 template<>
 std::string name<(std::map<int, int>)>()
 // template argument 1 is invalid

It seems that the extra parentheses make the template argument invalid. Is there any way around this?

like image 820
Paul Manta Avatar asked Jan 20 '12 14:01

Paul Manta


People also ask

How many arguments can macro have?

For portability, you should not have more than 31 parameters for a macro. The parameter list may end with an ellipsis (...).

Can there be more than one argument to template?

Can there be more than one argument to templates? Yes, like normal parameters, we can pass more than one data type as arguments to templates.

How many arguments we pass in macro definition?

macro (array[x = y, x + 1]) passes two arguments to macro : array[x = y and x + 1] .

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.


3 Answers

You could use typedef:

typedef std::map<int, int> int_map;

SET_TYPE_NAME(int_map, "TheMap");

boost's BOOST_FOREACH suffers from the same issue.

like image 36
hmjd Avatar answered Nov 08 '22 21:11

hmjd


Besides typedef, you could switch the order of the arguments and use variadic macros (requires C99 or C++11-compatible compiler):

#define SET_TYPE_NAME(NAME, ...) \
template<typename T>          \
std::string name();           \
                              \
template<>                    \
std::string name<__VA_ARGS__>() {    \
    return NAME;              \
}

...

SET_TYPE_NAME("TheMap", std::map<int, int>)
like image 80
kennytm Avatar answered Nov 08 '22 23:11

kennytm


Some time ago (while searching the web for the utility of Identity<T>) I got to this page.

In brief, to answer the question, if you don't have C++11 support and/or can't (or don't want to) use a typedef, you call your macro the "right" way:

// If an argument contains commas, enclose it in parentheses:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
// For an argument that doesn't contain commas, both should work:
SET_TYPE_NAME((SomeType1), "TheType1")
SET_TYPE_NAME(SomeType2, "TheType2")

and then to get rid of the (possible) unwanted parentheses around the type, you can use a "helper" like this:

template<typename> struct RemoveBrackets;
template<typename T> struct RemoveBrackets<void (T)> {
    typedef T Type;
};

and in your macro change the line:

    std::string name<TYPE>() {    \

to:

    std::string name< RemoveBrackets<void (TYPE)>::Type >() {    \

(or define a helper macro, say

#define REMOVE_BRACKETS(x) RemoveBrackets<void (x)>::Type

then replace the line with

    std::string name< REMOVE_BRACKETS(TYPE) >() {    \

).

(For the full story read the paragraph "An even better solution" at the end of the article linked above.)

Edit: just found that. But it uses <void X> when it really should use <void (X)> (in get_first_param<void X>::type); indeed the parentheses are necessary if you pass a "simple", non-bracketed argument (like SomeType2 in my code above) -- and they don't hurt if X is already bracketed (e.g., void ((SomeType1)) is equivalent to void (SomeType1); again, see the article). (By the way, I noticed that many answers on the other SO page are in essence "Macros are dumb". I won't comment, though.)

like image 24
gx_ Avatar answered Nov 08 '22 21:11

gx_