4

So, this is basically what I want to do:

#define RS03(obj, a1, a2, a3) {if (_str1 == #a1) _file >> _##a1; if (_str1 == #a2) _file >> _##a2;if (_str1 == #a3) _file >> _##a3; obj (_##a1, _##a2, _##a3);}

This is the case of three arguments, but I also need:

#define RS04(obj, a1, a2, a3, a4)
#define RS05(obj, a1, a2, a3, a4, a5)
#define RS06(obj, a1, a2, a3, a4, a5, a6)
...

So a variadic macro.

There are a number of questions on Stackoverflow on such topic, but they do not apply to my case.

In the code above, the three parameters a1, a2 and a3 are used both as string (in the "if" condition) and as variable (in the assignments and in the constructor), while obj is a class (so the last command in the macro is a constructor call).

The point is that: suppose I have twenty different classes, requiring each of them a different number of input to construct.

The macro receive the name of the class and the names of the arguments required to build an object of this class.

The point is that (see "reason of the design" below) I need to use the name of the argument also like string in a "if" condition. This is the reason why I use a macro (which have the nice feature of the # and ## special characters) instead of a template function.

Basically, I need a plain text conversions (so a macro, not a function), but with a variable name of arguments.


Reason of this design

Let us suppose I have twenty different classes, each of one is very different from the other (for example, class 1 can be constructed with two double, class 2 requires a double and two integer, class 3 can be constructed with two bool, etc...).

All these classe have a "run" member function which produce an output of the same format.

My program should do the following:

1 - read a text file

2 - launch the run for each model described in the file

The file should be something like that:

car {
    name = model1
    speed = 0.05
}

man {
    name = model2
    speed = 0.03
    male = true
    ageclass = 3
}

...

So I need to read such file and initialize each class described in the file.

Parameters should be written in whatever order the user like.

Also, they may appear more than one time, for example:

car {
    name = pippo
    speed = 0.05
    speed = 0.06
    speed = 0.07
}

(in this case, the last will overwrite the other)

If the user forget some parameters the program should stop with a clear error message.

There may be different model of the same type (for example, 4 different flat model).

For example, in such situation:

car {
    name = pippo
    speed = 0.05
}

car {
    name = pluto
}

The program should say that the second model is incomplete.

There are of course a number of ways to do that.

I do in this ways:

1 - create a template class (let's call it "field") with a T member (which store a value) and a bool member (which tell me if the variable is present or not)

2 - create an object (the reader, the object which read the file) with a number of "fields", one for each possible model attribute (_name, _speed, _male, etc..)

3 - then, while reading the file, for the portion within the brackets, I first read a string (the "label"), then I read the "=", and finally I read the value. The value will be stored in the variable with the same name of the string

(if I found the line "speed = 0.03", the I'll save 0.03 in the field _name of the reader).

This should be the pseudo-code (there may be erros; it's just for illustrating purpose):

if (car) {
    while (str != "}") {
        if (str == "speed")    { _file >> _speed;     _file >> _str; }
        if (str == "male")     { _file >> _male;      _file >> _str; }
        if (str == "ageclass") { _file >> _ageclass;  _file >> _str; }
        ERROR;
    }
    car (_speed.get (), _male.get (), _ageclass.get ());
}

get () is a member function of the "field" class, which raise an exception if not available (i.e., not present in the file), or return the value (read from file) otherwise.

Class "field" also has an overloaded operator >>, which apply the standard operator >> on the value, and set to true the boolean attribute.

As the models are so many, I'd like to trasform that code in a macro :-)

a3f
  • 8,517
  • 1
  • 41
  • 46
  • 2
    Boost.Preprocessor has tools for this. – chris Feb 06 '14 at 15:54
  • Can't this be resolved using (variadic) templates? Those should always be preferred over macros when dealing with C++. – JorenHeit Feb 06 '14 at 16:04
  • @JorenHeit Not really (in this case), notice the use of the preprocessor token concatenation operator. – Angew is no longer proud of SO Feb 06 '14 at 16:05
  • @Angew All those macro's could be a sign of bad (i.e. not pure C++) design ;-) – JorenHeit Feb 06 '14 at 16:18
  • I use macro just as text conversion: I have to repeat the same code about twenty times, but with slighly modifications. I want to write the code in a single location, so that I can edit this in just a single location. I use the macro instead of a function for the reason I've explained in the end of the main topic (I require to use the same variable in two different form) – Richter Bernadell Feb 06 '14 at 16:29
  • @RichterBernadell Apparently, you also use them to obscure constructor calls... (was a reply to your unedited comment) – JorenHeit Feb 06 '14 at 16:34
  • @JorenHeit: it's very complex to describe all the stuff. Ok, I'll try to better describe the problem in the main topic (for better visibility) – Richter Bernadell Feb 06 '14 at 16:36
  • @RichterBernadell I read the edits in your question, but I can't imagine a situation in which you would need to let the variable-names depend on a macro-argument. Maybe if you'd tell us what exactly it is you're trying to accomplish, we can help you figure out a more elegant way to do so. Macro's generally obscure code, hide what's actually going on, and complicate debugging. – JorenHeit Feb 06 '14 at 16:38
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46949/discussion-between-jorenheit-and-richter-bernadell) – JorenHeit Feb 06 '14 at 16:42
  • Do not use macro for this. Use variadic template. This is C++! –  Feb 06 '14 at 16:44
  • I completely update the main topic. Let me say if know is more clear (I hope). Sure it's longer, I'm sorry :-( – Richter Bernadell Feb 06 '14 at 17:15
  • Do you need some `else` operations in the loop? Otherwise, you're going to execute the `ERROR` action on every iteration. Is it better to dress up the `ERROR` action as an argument-less function (`ERROR()`)? – Jonathan Leffler Feb 06 '14 at 17:22
  • It may be related to [this question](http://stackoverflow.com/questions/15609533/c-and-preprocessor-macros-variadic-type), but the FOR_EACH seems simpler [here](http://stackoverflow.com/a/5958315/2684539). – Jarod42 Feb 06 '14 at 17:25

1 Answers1

0

I'm not sure if you have the freedom to change your implementation as drastically as I'm about to propose, but here's what I would do. It does not require any exotic template techniques whatsoever.

First, you create a structure called Properies (for example), that contains all properties that any class can inhibit.

struct Properties
{
    enum Types
    {
        MAN,
        CAR,
        // and more                                                                              
    };

    enum Gender
    {
        MALE, FEMALE
    };

    Types type;
    string name;
    double speed;
    Gender gender;
    int ageClass;
};

As you can see, it also contains an enum that describes every existing type.

Next, you define a Base-type, from which you derive all other types like Man, Car etc.

class Base
{};

class Man: public Base
{
    string d_name;
    Properties::Gender d_gender;
    int d_ageClass;
    double speed;

public:
    Man(Properties const &properties)
    {
        // Set properties that apply to the "Man"-class                                          
    }
};

class Car: public Base
{
    string d_name;
    double d_speed;

public:
    Car(Properties const &properties)
    {
        // Set properties that apply to the "Car"-class                                          
    }
};

The constructors of every class expect a Properties object, from which they extract the fields that are appropriate to them. For example, the Car constructor won't be inspecting the gender field, whereas the Man constructor will.

Now, you define a Loader class which will handle the parsing. It contains a member readFile that returns a vector of Base*, such that you have pointers to all initialized objects in one container. I've chosen for shared_ptr to handle the ownership problem, but you should decide what's best for your application.

class Loader
{
public:
    static vector<shared_ptr<Base>> readFile(string const &fileName)
    {
        vector< shared_ptr<Base> > result;
        ifstream file(fileName);
        // Parse the file, creating a "Properties" object, called                                
        // "props" here                                                                          

        while (file) // while EOF not reached.                                                   
        {
            Properties props = parse(file); // implement your parse                              
                                            // routine, returning Properties.                    
            switch (props.type)
            {
            case Properties::CAR:
                result.push_back(shared_ptr<Base>(new Car(props)));
                break;
            case Properties::MAN:
                result.push_back(shared_ptr<Base>(new Man(props)));
                break;
            // etc for all classes derived from Base                                             
            default:
                throw string("error: unknown type");
        }
    }
};

Hope this helps.

JorenHeit
  • 3,877
  • 2
  • 22
  • 28
  • Ok. It's not so drastic. In fact, I think I do not need to build any new class at all. Basically, correct me if I'm wrong, you are suggesting me to read all the possible fields (in the "parse" function I need as many "if" condition as all possible fields of all possible object) and pass all of them to the objects, which in turn will use only the fields they really need. – Richter Bernadell Feb 07 '14 at 09:48
  • @RichterBernadell Exactly. The functions that the classes have in common should of course be virtual, in order to do `vec[i]->speed()` for example (you'll have to provide an implementation in the Base class as well, or make it an abstract class with pure virtuals). To call functions that are specific to a certain class, the Base pointer should be cast first. In general you won't know in advance what it points to, so there `dynamic_cast` is in order. – JorenHeit Feb 07 '14 at 10:38