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:
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.
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.
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.)
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
How to Create a N Way Cartesian Product of Type Lists in C++
When Did "And" Become an Operator in C++
What Is a Cross-Platform Way to Get the Current Directory
Why Default Return Value of Main Is 0 and Not Exit_Success
What Does the C++ New Operator Do Other Than Allocation and a Ctor Call
C++ Unions VS. Reinterpret_Cast
Why How to Implicitly Convert an Int Literal to an Int * in C But Not in C++
What Does a Comma Separated List of Values, Enclosed in Parenthesis Mean in C? a = (1, 2, 3);
How to Format a Datetime to String Using Boost
How to Find an Object with Specific Field Values in a Std::Set
Specializing Single Method in a Big Template Class
Why Doesn't the Program Crash When I Call a Member Function Through a Null Pointer in C++
How to Stop Windows from Blocking the Program During a Window Drag or Menu Button Being Held Down
How to Load Bmp File Using X11 Window Background
How to Initialize All Tuple Elements by the Same Arguments
Could We Use Extern "C" in C File Without #Ifdef _Cplusplus