Trouble with Template Parameters Used in MACros

How to pass multi-argument templates to macros?

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

pass method with template arguments to a macro

the problem is the extra comma, you will need to protect it from the macro. Try

ASSERT_THROW((Matrix<5,1>()), std::runtime_error);

Parenthesis in templated type macro argument and I cannot use variadic macros

Use a little tricky macros:

#define A2(t1,t2) t1,t2
#define A3(t1,t2,t3) t1,t2,t3
#define A4(t1,t2,t3,t4) t1,t2,t3,t4
#define INHERIT(t) foo< t >
template<typename A, typename B>
struct bar : INHERIT(bar<A2(A, B)>) {};

These macros cause that preprocessor stop treating comma as its syntax - the comma starts be treated as just part of argument.

An alternative way:

#define bar_A_B bar<A,B>
template<typename A, typename B>
struct bar : INHERIT(bar_A_B) {};

NOTE
These examples these does not compile without:

template <class T>
class foo {};

C++ preprocessors are not aware of template arguments?

The C/C++ preprocessor recognizes commas as macro argument separators unless they are nested inside parentheses. Just parentheses. Brackets, braces and template markers don't count:

The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments. (C++14 §16.3/11; C11 §6.10.3/11)

(A side effect of the above is that you can use unbalanced braces and brackets as macro arguments. That's usually not a very good idea, but you can do it if you have to.)

Problems occasionally crop up as a result; a common one is unwanted multiple arguments when the argument is supposed to be a block of code:

MY_FANCY_MACRO(1000, { int i=0, j=42; ... })

Here, the macro is called with (at least) 3 arguments, although it was probably written to accept 2.

With modern C++ (and C) compilers, you have a few options. In a fairly subjective order:

  1. Rewrite the macro as an inline function. If the argument is a code block, consider using a templated function which could accept a lambda or other functor. If the argument is a type, make it a template argument instead.

  2. If surrounding the argument with redundant parentheses is syntactically valid, do that. But in such a case it is almost certainly the case that suggestion (1) above would have worked.

  3. Define:

    #define COMMA ,

    and use it where necessary:

     FOO(baz<20 COMMA 30>);

    This doesn't require modifying the macro definition in any way, but it will fail if the macro passes the argument to another macro. (The replacement will be done before the inner macro call is parsed, so the multiple argument problem will just be deferred to the inner call.)

  4. If you expect that one macro argument might contain unprotected commas, and it is the last or only argument, and you're in a position to modify the macro, and you're using C++11/C99 or better (or gcc, which has allowed this as an extension for some time), make the macro variadic:

    #define FOO(...) printf("%d\n",__VA_ARGS__::val())

Why this macro accepts a template with 1 parameter and refuses a template with 2 parameters?

CPPUNIT_TEST(doTest<false,false>);

This one doesn't work because macro thinks you are passing 2 macro parameters: doTest<false and false>.


CPPUNIT_TEST((doTest<false,false>));

This doesn't work because &TestFixtureType::testMethod will expand to &TestFixtureType::(doTest<false,false>) which is invalid.


As mentioned by Piotr in comment, you can use this code:

#define COMMA ,
class MyTestSuite2 : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE(MyTestSuite2);
CPPUNIT_TEST(doTest<false COMMA false>);
CPPUNIT_TEST(doTest<true COMMA false>);
CPPUNIT_TEST_SUITE_END();

template<bool param1, bool param2> void doTest() { /* test here */ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(MyTestSuite2);

Because pre-processor sees that you want to pass 1 parameter

C++ macro that inserts typename if necessary

From the comments:

A way to rewrite this where typename can be specified through the macro arguments is to use a helper template class that simply reports its own template argument.

template <typename T>
struct id { typedef T type; };

#define INST_TMPL(NAME,...) \
struct NAME : id<__VA_ARGS__>::type { \
typedef typename id<__VA_ARGS__>::type Base; \
using Base::Base; \
};

Use it like INST_IMPL(MyType,LongName<int,float,x::y::LongInnerType>), or like INST_IMPL(MyType,typename T::LongName<int,float,x::y::LongInnerType>).

The use of typename id<...>::type is allowed even if this is a non-dependent type.

Passing template argument commas through macros safely?

one of many solutions using boost pp

#define INSTANTIATE_MYTYPE(DATA)                          \
template <BOOST_PP_SEQ_ELEM(0,DATA)> \
struct MyType <BOOST_PP_SEQ_ELEM(1,DATA)> { \
static const bool value = BOOST_PP_SEQ_ELEM(2,DATA); \
}

INSTANTIATE_MYTYPE((typename T, ...)
(std::vector<T>, ....)
(true));

further read: http://www.boostpro.com/mplbook/preprocessor.html

template deduction failure in variadic macro

You can do something like this to avoid having to find the template overload:

template <class T>
auto add_pointer(T&& t) {
if constexpr (std::is_pointer_v<std::remove_reference_t<T>>) {
return t;
}
else {
return &t;
}
}

#define PRINT_IF(printer, cond, ...) if(cond) add_pointer(printer)->print(__VA_ARGS__)

This lets ->print deduce the template arguments to avoid having to find an overload and just use add_pointer to convert printer to a pointer if it's not already.



Related Topics



Leave a reply



Submit