Sign in to follow this  
ZealousEngine

[Solved] C++ Macro question...

Recommended Posts

There are many times where I find myself writing dozens of lines of code where there is only a minor difference from line to line (a name for example). A good example is my function map. I have a list of component names, and I want to map each unique name to a unique function (since its faster than a big switch statement)...
Quote:
mFuncMap[meshId] = &funcMesh; mFuncMap[particleEffectId] = &funcParticleEffect; mFuncMap[rigidBodyId] = &funcRigidBody;
In the above case each unique component (mesh, particle effect, ect..) maps to a unique function. But it really bugs me that I have to write all this out long hand, when there SHOULD be a way to create some kind of loop...
Quote:
for ( int i = 0; i < numComponents; ++i ) { mFuncMap[getComId(i)] = &func'MACRO_getComName(i)'; }
Is there anyway to do the above? Thanks! [Edited by - ZealousEngine on August 17, 2008 11:38:15 PM]

Share this post


Link to post
Share on other sites
You could use boost::preprocessor to create a list of names and then expand the assignment for each id. Though in this case I'm not sure how much that would gain you over just using an array to initialize your map.

Share this post


Link to post
Share on other sites
Hrmm I skimmed over boost::preprocessor, never used it before.. Could you please give an example showing how my above code could work with boost::preprocessor?

Here is another example of where I want to 'macro-ize' things.

One of my functions converts a typeId (integer) to a name (string). When I initialize the map, I have to do...

Quote:


mIdToStringMap[0] = "Mesh";
mIdToStringMap[1] = "ParticleEffect";
...

std::string toString( int id ) { return mIdToStringMap[id]; }



Now my whole point is - wouldnt it be nice if I could generate ONE list of 'names' (in macro form, or whatever), then use that ONE list to save me from having to write 40+ lines every time I do some kind of mapping like this. The only thing that really changes from line to line is the name, so how could I write a loop like the following...

Quote:

for ( int i = 0; i < numComponents; ++i ) {
//I want to REUSE the same MACRO_getComName() from the earlier function map example
mIdToStringMap[i] = 'MACRO_getComName(i)';
}

Share this post


Link to post
Share on other sites
For your second example, an alternative would be to read the integer-string pairs from a file. Also, in both cases you could clean up the code a bit by using the Boost Assignment library:
myFuncMap = boost::assign::map_list_of
( meshId, &funcMesh )
( particleEffectId, &funcParticleEffect )
( rigidBodyId, &funcRigidBody );

Share this post


Link to post
Share on other sites
Quote:

myFuncMap = boost::assign::map_list_of
( meshId, &funcMesh )
( particleEffectId, &funcParticleEffect )
( rigidBodyId, &funcRigidBody );


Yes but the whole point is I have MULTIPLE maps like this, and I dont want to have to write them out more than once. I just want to create ONE list of unique names, and somehow use that.

Share this post


Link to post
Share on other sites
Quote:

#define LIST (w, (x, (y, (z, BOOST_PP_NIL))))

#define MACRO(r, data, elem) BOOST_PP_CAT(elem, data)

BOOST_PP_LIST_FOR_EACH(MACRO, _, LIST) // expands to w_ x_ y_ z_


I dont quite see how this macro would allow me to insert JUST the names into an existing loop or a block of code. Can somebody please post a little demonstration if it is possible?

It seems like BOOST_PP_REPEAT would work IF I could figure out how to use the index counter to iterate through some kind of 'name list' as well..

Share this post


Link to post
Share on other sites
Quote:
Original post by ZealousEngine
There are many times where I find myself writing dozens of lines of code where there is only a minor difference from line to line (a name for example). A good example is my function map. I have a list of component names, and I want to map each unique name to a unique function (since its faster than a big switch statement)...

Quote:

mFuncMap[meshId] = &funcMesh;
mFuncMap[particleEffectId] = &funcParticleEffect;
mFuncMap[rigidBodyId] = &funcRigidBody;


In the above case each unique component (mesh, particle effect, ect..) maps to a unique function. But it really bugs me that I have to write all this out long hand, when there SHOULD be a way to create some kind of loop...

Quote:

for ( int i = 0; i < numComponents; ++i ) {
mFuncMap[getComId(i)] = &func'MACRO_getComName(i)';
}


Is there anyway to do the above?

Thanks!


I usually achieve this like so:

#define LIST_MY_STUFF( F, A, B, C ) \
F( mesh, Mesh, A, B, C ) \
F( particleEffect, ParticleEffect, A, B, C ) \
F( rigidBody, RigidBody, A, B, C ) //

#define FUNC_MAP( L, U, A, B, C ) \
mFuncMap[L##Id] = &func##U; //

LIST_MY_STUFF( FUNC_MAP, unused, unused, unused );

^^ A, B and C are just there if you need to pass in any arguments. They're not used so I've just chucked 'unused' there - you can put any crap you want (boost chooses to use ~ for unused macro arguments) or if none of your macros require extra arguments you can get rid of A,B,C altogether (or if you need more than 3 arguments you can add D,E,F,G...).

If you need to call another function for each thing in the list, just make a new macro like FUNC_MAP, and then do the for-each using LIST_MY_STUFF.

[EDIT]N.B. if you're copy/pasting my code, I had to insert a space after each back-slash to make the forum accept the code ;) Make sure to remove these spaces if you want to try and compile it.

[EDIT2] I'm sure this can be done with boost as well, but IMO this is a lot simpler and easier for non-boost-acquainted people to come to grips with.

[EDIT3] I'm not sure if this is portable, but it works with MSVC 6 - MSVC 2008 (I haven't tested with GCC, etc.)

Share this post


Link to post
Share on other sites
I would recommend using BOOST_PP_ it's much more flexible.



// local callback macro to generate a function map, elem is a tuple of the form (id prefix, function name)
#define LOCAL_MACRO_WITH_A_MEANINGFUL_NAME(r, data, elem) mFuncMap[BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 0, elem), Id)] = &BOOST_PP_CAT(func, BOOST_PP_TUPLE_ELEM(2, 1, elem));

// generate the function map from a sequence of tuples, calling LOCAL_MACRO_WITH_A_MEANINGFUL_NAME
// on each element in the sequence
BOOST_PP_SEQ_FOR_EACH(LOCAL_MACRO_WITH_A_MEANINGFUL_NAME, _,
((mesh, Mesh))
((particleEffect, ParticleEffect))
((rigidBody, RigidBody)))

// don't pollute the global namespace
#undef LOCAL_MACRO_WITH_A_MEANINGFUL_NAME




You are going to want a fair few elements in the sequence though to make it worth your while. Unless of course the code that you are using in the callback macro gets more complicated and it becomes a hassle / maintenance issue to keep typing it over and again.

Share this post


Link to post
Share on other sites
Quote:
You are going to want a fair few elements in the sequence though to make it worth your while


And why would having a large list of names be a problem with Hodgman's method? I define the list ONCE, then I can use it with ANY macro. Keep in mind the onlything I need is the NAME of the component (upper and lower case versions are nice to have too). Although with Hodgman's method it seems like it would be trivial to pass additional information to the macros if I ever needed too.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this