Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to apply an action to N C++ class members in a loop over member names (probably via pre-processor)?

The problem:

I have a C++ class with gajillion (>100) members that behave nearly identically:

  • same type

  • in a function, each member has the same exact code done to it as other members, e.g. assignment from a map in a constructor where map key is same as member key

  • This identicality of behavior is repeated across many-many functions (>20), of course the behavior in each function is different so there's no way to factor things out.

  • The list of members is very fluid, with constant additions and sometimes deletions, some (but not all) driven by changing columns in a DB table.

As you can imagine, this presents a big pain-in-the-behind as far as code creation and maintenance, since to add a new member you have to add code to every function where analogous members are used.

Example of a solution I'd like

Actual C++ code I need (say, in constructor):

MyClass::MyClass(SomeMap & map) { // construct an object from a map 
    intMember1 = map["intMember1"];
    intMember2 = map["intMember2"];
    ... // Up to 
    intMemberN = map["intMemberN"];
}

C++ code I want to be able to write:

MyClass::MyClass(SomeMap & map) { // construct an object from a map 
#FOR_EACH_WORD Label ("intMember1", "intMember2", ... "intMemberN")
    $Label = map["$Label"];
#END_FOR_EACH_WORD
}

Requirements

  • The solution must be compatible with GCC (with Nmake as make system, if that matters). Don't care about other compilers.

  • The solution can be on a pre-processor level, or something compilable. I'm fine with either one; but so far, all of my research pointed me to the conclusion that the latter is just plain out impossible in C++ (I so miss Perl now that I'm forced to do C++ !)

  • The solution must be to at least some extent "industry standard" (e.g. Boost is great, but a custom Perl script that Joe-Quick-Fingers created once and posted on his blog is not. Heck, I can easily write that Perl script, being much more of a Perl expert than a C++ one - I just can't get bigwigs in Software Engineering at my BigCompany to buy into using it :) )

  • The solution should allow me to declare a list of IDs (ideally, in only one header file instead of in every "#FOR_EACH_WORD" directive as I did in the example above)

  • The solution must not be limited to "create an object from a DB table" constructor. There are many functions, most of them not constructors, that need this.

  • A solution of "Make them all values in a single vector, and then run a 'for' loop across the vector" is an obvious one, and can not be used - the code's in a library used by many apps, the members are public, and re-writing those apps to use vector members instead of named members is out of the question, sadly.

like image 822
DVK Avatar asked Sep 02 '09 13:09

DVK


People also ask

How do you loop over a class attributes in Python?

Use dir() and a for-loop to iterate through all attributes of a class. Create an object of a class. Call dir(object) to return a list of all attributes of that object . Use a for-loop to loop through each attribute in the list.

Can you iterate over sets?

There is no way to iterate over a set without an iterator, apart from accessing the underlying structure that holds the data through reflection, and replicating the code provided by Set#iterator...


1 Answers

Boost includes a great preprocessor library that you can use to generate such code:

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/cat.hpp>

typedef std::map<std::string, int> SomeMap;

class MyClass
{
public:
    int intMember1, intMember2, intMember3;

    MyClass(SomeMap & map) 
    {
        #define ASSIGN(z,n,_) BOOST_PP_CAT(intMember, n) = map[ BOOST_PP_STRINGIZE(BOOST_PP_CAT(intMember, n))];
        BOOST_PP_REPEAT_FROM_TO(1, 4, ASSIGN, nil)
    }
};
like image 112
Bojan Resnik Avatar answered Sep 20 '22 00:09

Bojan Resnik