define macro with template as variable - c++

i'm trying to use a macro to create some static variables.
my problem is, how do i do define a macro with 2 parameters, the first is a template and the second a static variable. the template should have more than 1 type.
for example:
#define macro(x, y, z, v) x::y z = v;
int main(int argc, char *argv[]) {
// this works
macro(std::queue<int>, value_type, test, 4)
// this also works
std::pair<int, int>::first_type x = 3;
// this is produsing some compiler errors
macro(std::pair<int, int>, first_type, test2, 4)
return 0;
}
and is it even possible to do this?
here is the error:
main.cpp(47) : warning C4002: too many actual parameters for macro 'macro'
main.cpp(47) : error C2589: 'int' : illegal token on right side of '::'
main.cpp(47) : error C2589: 'int' : illegal token on right side of '::'
main.cpp(47) : error C2143: syntax error : missing ',' before '::'
main.cpp(50) : error C2143: syntax error : missing ';' before '}'
main.cpp(51) : error C2143: syntax error : missing ';' before '}'
inspired by Joachim Pileborg
#define macro(x, y, z, v, ...) x<__VA_ARGS__>::y z = v;
...
// now it works
macro(std::pair, first_type, test2, 4, int, int)
thx Joachim

It's not really a solution but merely a work-around:
#define COMMA ,
macro(std::pair<int COMMA int>, first_type, test2, 4)
or a little bit more readable:
#define wrap(...) __VA_ARGS__
macro(wrap(std::pair<int, int>), first_type, test2, 4)

It's because the preprocessor that handles macros is quite stupid. It sees five arguments in the second macro "call", the first one being std::pair<int and the second one int>. You can't have macro arguments that contains comma.
You might want to look into variadic macros, and re-arrange so that the class is last in the macro.

There are a couple of ways to get rid of that top-level comma.
typedef std::pair<int, int> int_pair;
macro(int_pair, first_type, test2, 4)
macro((std::pair<int, int>), first_type, test2, 4);
#define macro2(x1, x2, y, z, v) x1, x2::y z = v;
macro2(std::pair<int, int> first_type, test2, 4)
Incidentally, I'd leave off the ; from the macro, and use it wherever the macro is used. That makes the code look more natural.

Related

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

I'm working with CPPUNIT 1.12.1.
It has those macro defined:
#define CPPUNIT_TEST_SUITE_ADD_TEST( test ) \
context.addTest( test )
#define CPPUNIT_TEST( testMethod ) \
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller<TestFixtureType>( \
context.getTestNameFor( #testMethod), \
&TestFixtureType::testMethod, \
context.makeFixture() ) ) )
I want to add many tests to the same test suite using templates (as CPPUNIT works, every test must be a void function, so using template makes it possible to call the same void function with different "parameters"...).
This works perfectly:
class MyTestSuite1 : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE(MyTestSuite1);
CPPUNIT_TEST(doTest<false>);
CPPUNIT_TEST(doTest<true>);
CPPUNIT_TEST_SUITE_END();
template<bool param> void doTest() { /* test here */ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(MyTestSuite1);
while this does not:
class MyTestSuite2 : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE(MyTestSuite2);
CPPUNIT_TEST(doTest<false,false>);
CPPUNIT_TEST(doTest<true,false>);
CPPUNIT_TEST_SUITE_END();
template<bool param1,bool param2> void doTest() { /* test here */ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(MyTestSuite2);
Compiler (Visual Studio 2015) reports:
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(20):
warning C4002: too many actual parameters for macro 'CPPUNIT_TEST'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(21):
warning C4002: too many actual parameters for macro 'CPPUNIT_TEST'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(20):
error C2059: syntax error: ')'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(21):
error C2059: syntax error: ')'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(22):
error C2143: syntax error: missing ';' before '}'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(22):
error C2065: 'namer': undeclared identifier
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(22):
error C2065: 'factory': undeclared identifier
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(22):
error C2059: syntax error: ')'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(29):
error C2143: syntax error: missing ';' before '{'
1>b:\dev\vobs_diabeloop\private\tst\regulation\cppunit\hyper_ftac3\test.cpp(30):
error C2143: syntax error: missing ';' before '{'
Why is that? How could the macro handle correctly 1 template parameter, but fails for two? Any idea how I could easily have it compile and work?
Edit:
Already tried CPPUNIT_TEST((doTest<false,false>)); without success (getting error C2143: syntax error: missing ';' before ')')
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
, is parsed as separator in MACRO (except when surrounded by parent).
way of work-around
using intermediate MACRO:
#define COMMA ,
CPPUNIT_TEST(doTest<false COMMA false>);
Or fix your original MACRO to handle comma:
#define CPPUNIT_TEST(testMethod, ...) \
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller<TestFixtureType>( \
context.getTestNameFor( #testMethod), \
&TestFixtureType::testMethod , ##__VA_ARGS__, \
context.makeFixture() ) ) )
does this work?
CPPUNIT_TEST((doTest<false,false>));
CPPUNIT_TEST((doTest<true,false>));
sometimes Macros can be tricky when parsing commas...

Q_DECLARE_METATYPE a boost::multi_array

I am trying to pass a multi-dimensional array represented as boost::multi_array using Qt's signals and slots mechanism. I attempted to declare meta-type using the following piece of code:
Q_DECLARE_METATYPE(boost::multi_array<double, 2>)
However I get the following compilation errors (on MSVC 2015):
path\to\project\metatypes.h(7): error C2976: 'boost::multi_array': too few template arguments
..\..\ml_project\boost-libs\include\boost/multi_array.hpp(111): note: see declaration of 'boost::multi_array'
path\to\project\metatypes.h(7): error C2332: 'enum': missing tag name
path\to\project\metatypes.h(7): error C2065: 'Defined': undeclared identifier
path\to\project\metatypes.h(7): error C2143: syntax error: missing '>' before ';'
path\to\project\metatypes.h(7): error C2059: syntax error: '>'
path\to\project\metatypes.h(7): error C2976: 'QMetaTypeId': too few template arguments
c:\qt\qt-everywhere-opensource-src-5.5.0\qtbase\include\qtcore\../../src/corelib/kernel/qmetatype.h(1576): note: see declaration of 'QMetaTypeId'
path\to\project\metatypes.h(7): error C2913: explicit specialization; 'QMetaTypeId' is not a specialization of a class template
..\..\ml_project\boost-libs\include\boost/multi_array.hpp(111): note: see declaration of 'boost::multi_array'
..\..\ml_project\boost-libs\include\boost/multi_array.hpp(111): note: see declaration of 'boost::multi_array'
path\to\project\metatypes.h(7): error C2226: syntax error: unexpected type 'quintptr'
path\to\project\metatypes.h(7): error C2143: syntax error: missing ')' before ';'
path\to\project\metatypes.h(7): error C2143: syntax error: missing ';' before '}'
The comma between double, 2 is parsed as part of the macro definition. The solutions are as follows:
Option #1
typedef boost::multi_array<double, 2> my_name;
Q_DECLARE_METATYPE( my_name );
Option #2
#include <boost/utility/identity_type.hpp>
Q_DECLARE_METATYPE( BOOST_IDENTITY_TYPE( (boost::multi_array<double, 2>) ) );
Option #3
Hand-written BOOST_IDENTITY_TYPE:
template <typename T> struct identity_type;
template <typename T> struct identity_type<void(T)> { typedef T type; };
#define IDENTITY_TYPE(T) typename identity_type<void T>::type
Q_DECLARE_METATYPE( IDENTITY_TYPE( (boost::multi_array<double, 2>) ) );
Option #4
Replace a comma by a preprocessor macro:
#define COMMA ,
Q_DECLARE_METATYPE( boost::multi_array<double COMMA 2> );
#undef COMMA

using boost foreach with items that are themselves templates

I have a std::deque< std::pair<int, int> > that I would like to iterate over using BOOST_FOREACH.
I tried the following:
#define foreach_ BOOST_FOREACH
// declaration of the std::deque
std::deque< std::pair<int, int> > chosen;
foreach_( std::pair<int,int> p, chosen )
{
...
}
But when I compile this (in Visual Studio ) I get the following errors:
warning C4002: too many actual parameters for macro 'BOOST_FOREACH'
1>c:\users\beeband\tests.cpp(133): error C2143: syntax error : missing ')' before '>'
1>c:\users\beeband\tests.cpp(133): error C2059: syntax error : '>'
1>c:\users\beeband\tests.cpp(133): error C2059: syntax error : ')'
1>c:\users\beeband\tests.cpp(133): error C2143: syntax error : missing ';' before '{'
1>c:\users\beeband\tests.cpp(133): error C2181: illegal else without matching if
What is the correct way to use BOOST_FOREACH with this deque?
The problem is the , as it is being used by the preprocessor to separate the macro arguments.
Possible solutions using typedef:
typedef std::pair<int, int> int_pair_t;
std::deque<int_pair_t> chosen;
foreach_( int_pair_t p, chosen )
// Or (as commented by Arne Mertz)
typedef std::deque<std::pair<int, int>> container_t;
container_t chosen;
foreach_(container_t::value_type p, chosen)
Possible replacements, both introduced in c++11, are:
range-for loop:
for (auto& : chosen)
{
}
lambdas:
std::for_each(std::begin(chosen),
std::end(chosen)
[](std::pair<int, int>& p)
{
});
As the author of BOOST_FOREACH, I ask you to please stop using it. It was a hack whose time has come and gone. Please, please, please use C++11's range-base for loops and let BOOST_FOREACH die.
Boosts foreach is a macro, which means it's handled by the preprocessor. And the preprocessor is pretty simple, and can't handle symbols with commas in them as it is used as a macro argument separator.

What's the typedef syntax for an array type?

I have this C++11 code:
using swallow = int[];
but MSVS2013 Preview barfs on it:
error C2143: syntax error : missing ';' before '='
So I tried
typedef int[] swallow;
But that got me:
warning C4091: 'typedef ' : ignored on left of 'int' when no variable is declared
So I tried reversing the typedef stuff, as I never remember (hence the reason using is so great):
typedef swallow int[];
And got:
m:\development\source\ambrosia\libambrosia\Ambrosia/utility.h++(33) : error C2144: syntax error : 'int' should be preceded by ';'
I'm already disappointed in MSVS2013. How can I write this so the MS compiler will understand this simple code?
typdef is a declaration, and follows the same syntax as a declaration:
extern int a[];
typedef int b[];
(Note that b is an incomplete type, and that a is only declared, not defined.)

Strange behaviour with templates and #defines

I have the following definitions:
template<typename T1, typename T2>
class Test2
{
public:
static int hello() { return 0; }
};
template<typename T>
class Test1
{
public:
static int hello() { return 0; }
};
#define VERIFY_R(call) { if (call == 0) printf("yea");}
With these, I try to compile the following:
VERIFY_R( Test1<int>::hello() );
this compiles fine
VERIFY_R( (Test2<int,int>::hello()) );
this also compiles fine, notice the parentheses around the call.
VERIFY_R( Test2<int,int>::hello() );
This, without the parentheses produces a warning and several syntax errors:
warning C4002: too many actual parameters for macro 'VERIFY_R'
error C2143: syntax error : missing ',' before ')'
error C2059: syntax error : ')'
error C2143: syntax error : missing ';' before '}'
error C2143: syntax error : missing ';' before '}'
error C2143: syntax error : missing ';' before '}'
fatal error C1004: unexpected end-of-file found
What's going on here?
This happens with VS2008 SP1.
The comma inside a macro can be ambiguous: an extra set of parentheses (your second example) is one way of disambiguating. Consider a macro
#define VERIFY(A, B) { if ( (A) && (B) ) printf("hi"); }
then you could write VERIFY( foo<bar, x> y ).
Another way of disambiguating is with
typedef Test1<int,int> TestII;
VERIFY_R( TestII::hello() );
The preprocessor is a dumb text replacement tool that knows nothing about C++. It interprets
VERIFY_R( Test1<int,int>::hello() );
as
VERIFY_R( (Test1<int), (int>::hello()) );
which calls VERIFY_R with too many parameters. As you noted, additional parentheses fix this:
VERIFY_R( (Test1<int,int>::hello()) );
The question remains, however, why you need the preprocessor anyway. The macro you used in your question could just as well be an inline function. If you real code doesn't do anything requiring the preprocessor, try to get rid of macros. They just cause pain.
The comma in <int, int> is treated as an argument separator for the macro, rather than for the template. The compiler therefore thinks you're calling VERIFY_R with two arguments (Test1<int and int>::hello()), when it requires only one. You need to use variadic macros to expand everything supplied to the macro:
#define VERIFY_R(...) { if ((__VA_ARGS__) == 0) printf("yea");}
It is generally a good idea to wrap macro arguments in parentheses, as well, to prevent other kinds of weird substitution errors.
The preprocessor doesn't know that < and > are supposed to be brackets, so it interprets the expression as two macro arguments, Test1<int and int>::hello(), separated by the ,. As you say, it can be fixed by surrounding the entire expression with parentheses, which the preprocessor does recognise as brackets.
I'm not sure if this is an error in your reporting here or the actual problem, but your last VERIFY_R is still referencing Test1, rather than Test2.