Jump to content
  • Advertisement
Sign in to follow this  
Houdini

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

This topic is 4811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
Advertisement
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Excellent, I did not realize g++ did this.

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


- Houdin

Share this post


Link to post
Share on other sites
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) x

MYMACRO(,)


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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!