(SOLVED) Advanced macro/preprocessor help on g++

Started by
6 comments, last by mfawcett 18 years, 7 months ago
I've been dabbling in using the preprocessor to auto-generate code. What I have written compiles and works great on VC++ 7.1, but when I tried compiling under g++ 3.4.2 I'm getting compile errors. After spending a full day getting nowhere, I'm hoping one of you g++ and preprocessor gurus may be able to help me out. ;) I've whittled the code down to a bare minimum to reproduce the error and added a bunch of comments to hopefully explain how it works. Anyone familiar with the Boost Preprocessor should be able to understand what the code is doing. But let me briefly explain: I wrote, for the sake of an example, a function which create will create any object the user specifies, like so:

template<typename ClassType>
ClassType *CreateObject()
{
   return new ClassType;
}
I'd like to use function template overloading to allow the user to pass any number of constructor parameters to this function, like so:

template<typename ClassType, typename A1>
ClassType *CreateObject(A1 a1)
{
   return new ClassType(a1);
}

template<typename ClassType, typename A1, typename A2>
ClassType *CreateObject(A1 a1, A2 a2)
{
   return new ClassType(a1, a2);
}
Etc. However, instead of rewriting this function 16 times to allow 16 parameters I am instead using the preprocessor do that work. Below is a full source code example which does just that, and compiles fine in VC++ 7.1.

#include <iostream>

using namespace std;

// Commonly used pre-defined characters to seperate repeating code.
// For exmple, the comma seperate is used when repeating function 
// arguments (ie, Function(A1 a1, A2 a2, A3 a3, ...) )
// 
// The empty seperator is used when there doesn't need to be any
// characters between repeating code, such as repeating functions.
#define MACRO_EMPTY_SEPERATOR 
#define MACRO_COMMA_SEPERATOR ,

// Commonly used pre-defined code to be repeated.  For example, the
// MACRO_FUNCTION_PARAMETER is used to repeat function parameters
// (ie, Function(A1 a1, A2 a2, A3 a3, ...) ).
#define MACRO_TEMPLATE_PARAMETER(num) typename A##num
#define MACRO_FUNCTION_PARAMETER(num) A##num a##num
#define MACRO_FUNCTION_ARGUMENT(num) a##num

// The perform the actual repetition.  Each macro will continuously
// call the preceding macro until the MACRO_REPEAT_0 is called, at
// which point each macro will write the seperator followed by the
// macro, passing the repitition number.
#define MACRO_REPEAT_0(begin_seperator, seperator, macro) 
#define MACRO_REPEAT_1(begin_seperator, seperator, macro) begin_seperator macro(0)
#define MACRO_REPEAT_2(begin_seperator, seperator, macro) MACRO_REPEAT_1(begin_seperator, seperator, macro) seperator macro(1)
#define MACRO_REPEAT_3(begin_seperator, seperator, macro) MACRO_REPEAT_2(begin_seperator, seperator, macro) seperator macro(2)
#define MACRO_REPEAT_4(begin_seperator, seperator, macro) MACRO_REPEAT_3(begin_seperator, seperator, macro) seperator macro(3)

// User called macros that uses the above macros to perform the
// desired work.  MACRO_LIST is primarily used to list template
// or function arguments, where each argument is seperated by
// a comma (ie, Function(A1 a1, A2 a2, A3 a3, ...) ).
// MACRO_LIST_APPEND does the same as MACRO_LIST, except it is
// used to append arguments onto an existing argument, thus all
// code is preceded by a comma (ie, Function(int x, A1 a1, A2 a2, ...) ).
// MACRO_REPEAT simply repeats code not using any seperating
// characters.
#define MACRO_LIST(num, macro) MACRO_REPEAT_##num(MACRO_EMPTY_SEPERATOR, MACRO_COMMA_SEPERATOR, macro)
#define MACRO_LIST_APPEND(num, macro) MACRO_REPEAT_##num(MACRO_COMMA_SEPERATOR, MACRO_COMMA_SEPERATOR, macro)
#define MACRO_REPEAT(num, macro) MACRO_REPEAT_##num(MACRO_EMPTY_SEPERATOR, MACRO_EMPTY_SEPERATOR, macro)

// This creates a multiple CreateObject function using function
// template overloading.  Basically, it allows a user to specify
// the object the function will create as well as any number
// of parameters to pass to that objects constructor.  When the
// macros unroll this function, it will look similar to this:
// 
// template<typename ClassType>
// ClassType *CreateObject()
// {
//    return new ClassType();
// }
// 
// template<typename ClassType, typename A1>;
// ClassType *CreateObject(A1 a1)
// {
//    return new ClassType(a1);
// }
// 
// template<typename ClassType, typename A1, typename A2>
// ClassType *CreateObject(A1 a1, A2 a2)
// {
//    return new ClassType(a1, a2);
// }
// ...
//
// Note the macro below uses the \ to span multiple lines,
// but the source tags are removing them.
#define CREATEOBJECT(num)
   template<typename ClassType MACRO_LIST_APPEND(num, MACRO_TEMPLATE_PARAMETER)>
   ClassType *CreateObject(MACRO_LIST(num, MACRO_FUNCTION_PARAMETER))
   {
      return new ClassType( MACRO_LIST(num, MACRO_FUNCTION_ARGUMENT) );
   }

// Generate 3 instances of this function, one which takes no
// constructor params, one which takes one param, and the
// last which takes two constructor parameters.                                                                                              
MACRO_REPEAT(3, CREATEOBJECT)


int main (int argc, char *argv[])
{
	// Create an integer, passing 42 as a constructor parameter
	int *ptr = CreateObject<int>(42);

	cout << *ptr << endl;
	return(0);
}




As mentioned, this compiled perfectly in VC++ 7.1, but I get the following errors in g++ 3.4.2: ../test4.cpp:75:1: macro "MACRO_REPEAT_1" passed 5 arguments, but takes just 3 ../test4.cpp:75: error: expected nested-name-specifier before "ClassType" ../test4.cpp:75: error: ISO C++ forbids declaration of `ClassType' with no type ../test4.cpp:75: error: expected `>' before "MACRO_REPEAT_1" ../test4.cpp:75: error: expected constructor, destructor, or type conversion before '*' token ../test4.cpp:75: error: expected `;' before '*' token ../test4.cpp:75:1: macro "MACRO_REPEAT_1" passed 4 arguments, but takes just 3 ../test4.cpp:75:1: macro "MACRO_REPEAT_1" passed 4 arguments, but takes just 3 ../test4.cpp: In function `int main(int, char**)': ../test4.cpp:83: error: no matching function for call to `CreateObject(int, int)' *** Errors occurred during this build *** Now, the strange thing is that if I change the MACRO_REPEAT(3, CREATEOBJECT) line and to create just TWO CreateObject functions (ie, MACRO_REPEAT(2, CREATEOBJECT)) then the code compiles and runs fine. But repeating code more than twice to allow 2 or more constructor parameters to be passed causes the errors displayed above. Anyone have any ideas what the issue could be??? - Houdini [Edited by - Houdini on September 13, 2005 9:41:26 AM]
- Houdini
Advertisement
Have you tried studying the preprocessor's output (i.e. by invoking cpp separately)?
Quote:Original post by doynax
Have you tried studying the preprocessor's output (i.e. by invoking cpp separately)?


doynax, I'm not sure I follow. Are you saying it's possible to capture the source code generated by the preprocessor? I'm not sure what you mean by "invoking cpp seperately".


- Houdini
- Houdini
Quote:Original post by Houdini
doynax, I'm not sure I follow. Are you saying it's possible to capture the source code generated by the preprocessor? I'm not sure what you mean by "invoking cpp seperately".
Yes, the C preprocessor is a language of it's own which you can run separately. It can even be used with other languages or replaced by a more powerful system.
Anyway, at times (such as in this case) it's useful to study the generated code on it's own. Most compiler suits provide either a command line option or another utility altogheter for doing this.

GCC should come with an application called 'cpp' (the c preprocessor). Try running your code through it.
Quote:Original post by doynax
Quote:Original post by Houdini
doynax, I'm not sure I follow. Are you saying it's possible to capture the source code generated by the preprocessor? I'm not sure what you mean by "invoking cpp seperately".
Yes, the C preprocessor is a language of it's own which you can run separately. It can even be used with other languages or replaced by a more powerful system.
Anyway, at times (such as in this case) it's useful to study the generated code on it's own. Most compiler suits provide either a command line option or another utility altogheter for doing this.


Yeah as you are using GCC it's the -E flag/option.
Excellent, I did not realize g++ did this.

Thanks, you guys, I'll check that out and hopefully find my issue.


- Houdin
- Houdini
Just wanted to let you know that thanks to the preprocessor output I was able to track down the issue. Turns out you can't pass commas to macros (duh!). Ie, doing this:

#define MYMACRO(x) xMYMACRO(,) 


Makes the preprocessor think your passing two empty parameters, not a single comma parameter. Of course, VC++ 7.1 allows this when it shouldn't, otherwise I would have caught this long ago.

For anyone who may be interested in passing commas in macros, I was able to do this with a small fix. Here's the working code:

#include <iostream>using namespace std;// Commonly used pre-defined characters to seperate repeating code.// For exmple, the comma seperate is used when repeating function // arguments (ie, Function(A1 a1, A2 a2, A3 a3, ...) )// // The empty seperator is used when there doesn't need to be any// characters between repeating code, such as repeating functions.#define MACRO_EMPTY_SEPERATOR_DISPLAY #define MACRO_COMMA_SEPERATOR_DISPLAY ,// Commonly used pre-defined code to be repeated.  For example, the// MACRO_FUNCTION_PARAMETER is used to repeat function parameters// (ie, Function(A1 a1, A2 a2, A3 a3, ...) ).#define MACRO_TEMPLATE_PARAMETER(num) typename A##num#define MACRO_FUNCTION_PARAMETER(num) A##num a##num#define MACRO_FUNCTION_ARGUMENT(num) a##num// The perform the actual repetition.  Each macro will continuously// call the preceding macro until the MACRO_REPEAT_0 is called, at// which point each macro will write the seperator followed by the// macro, passing the repitition number.#define MACRO_REPEAT_0(begin_seperator, seperator, macro) #define MACRO_REPEAT_1(begin_seperator, seperator, macro) begin_seperator##_DISPLAY macro(0)#define MACRO_REPEAT_2(begin_seperator, seperator, macro) MACRO_REPEAT_1(begin_seperator, seperator, macro) seperator##_DISPLAY macro(1)#define MACRO_REPEAT_3(begin_seperator, seperator, macro) MACRO_REPEAT_2(begin_seperator, seperator, macro) seperator##_DISPLAY macro(2)#define MACRO_REPEAT_4(begin_seperator, seperator, macro) MACRO_REPEAT_3(begin_seperator, seperator, macro) seperator##_DISPLAY macro(3)// User called macros that uses the above macros to perform the// desired work.  MACRO_LIST is primarily used to list template// or function arguments, where each argument is seperated by// a comma (ie, Function(A1 a1, A2 a2, A3 a3, ...) ).// MACRO_LIST_APPEND does the same as MACRO_LIST, except it is// used to append arguments onto an existing argument, thus all// code is preceded by a comma (ie, Function(int x, A1 a1, A2 a2, ...) ).// MACRO_REPEAT simply repeats code not using any seperating// characters.#define MACRO_LIST(num, macro) MACRO_REPEAT_##num(MACRO_EMPTY_SEPERATOR, MACRO_COMMA_SEPERATOR, macro)#define MACRO_LIST_APPEND(num, macro) MACRO_REPEAT_##num(MACRO_COMMA_SEPERATOR, MACRO_COMMA_SEPERATOR, macro)#define MACRO_REPEAT(num, macro) MACRO_REPEAT_##num(MACRO_EMPTY_SEPERATOR, MACRO_EMPTY_SEPERATOR, macro)// This creates a multiple CreateObject function using function// template overloading.  Basically, it allows a user to specify// the object the function will create as well as any number// of parameters to pass to that objects constructor.  When the// macros unroll this function, it will look similar to this:// // template<typename ClassType>// ClassType *CreateObject()// {//    return new ClassType();// }// // template<typename ClassType, typename A1>;// ClassType *CreateObject(A1 a1)// {//    return new ClassType(a1);// }// // template<typename ClassType, typename A1, typename A2>// ClassType *CreateObject(A1 a1, A2 a2)// {//    return new ClassType(a1, a2);// }// ...//// Note the macro below uses the \ to span multiple lines,// but the source tags are removing them.  To compile you MUST// re-add these \ tags#define CREATEOBJECT(num)   template<typename ClassType MACRO_LIST_APPEND(num, MACRO_TEMPLATE_PARAMETER)>   ClassType *CreateObject(MACRO_LIST(num, MACRO_FUNCTION_PARAMETER))   {      return new ClassType( MACRO_LIST(num, MACRO_FUNCTION_ARGUMENT) );   }// Generate 3 instances of this function, one which takes no// constructor params, one which takes one param, and the// last which takes two constructor parameters.                                                                                              MACRO_REPEAT(3, CREATEOBJECT) int main (int argc, char *argv[]){	// Create an integer, passing 42 as a constructor parameter	int *ptr = CreateObject<int>(42);	return(0);}



- Houdini
- Houdini
Glad you solved your problem. I just thought I would post some code since your output looks almost exactly the same as mine, just using different preprocessor techniques.

#define MRF_INTERNAL_USE_ONLY__CREATE_OVERLOAD(z, i, data)template <typename R BOOST_PP_COMMA_IF(i) BOOST_PP_ENUM_PARAMS(i, typename T)>static boost::shared_ptr<R> create(BOOST_PP_ENUM_BINARY_PARAMS(i, T, t)) {boost::shared_ptr<R> nrv(new R(BOOST_PP_ENUM_PARAMS(i, t)));return nrv; }#define MRF_INTERNAL_USE_ONLY__CREATE_DEFINITIONS(num)BOOST_PP_REPEAT(BOOST_PP_ADD(num, 1), MRF_INTERNAL_USE_ONLY__CREATE_OVERLOAD, ~)class SomeClass{public:	// Support arg lists from 0-10 arguments	MRF_INTERNAL_USE_ONLY__CREATE_DEFINITIONS(10);private:};


edit: pretend the macros have the '\' line seperator at the end...I couldn't get the post to keep them in the source.
--Michael Fawcett

This topic is closed to new replies.

Advertisement