0

Here are 2 classes, a parent and a subclass:

class Model
{
    protected:
    static std::list<char *>   list;
}

class SubModel : public Model
{
    // Should add itself to Model::list...
}

So, here are 2 example classes. I would like to create a generic model in a way every subclass (correctly implemented) would register themselves to the static Model::list. That way, any other subclass is able to identify other "existing" models (and type names).

I've tried many ways, but I have no clue how to populate a static member in many other files only once.

Any idea, if it's possible?

EDIT: I'm looking for a way to do something like Model::list.push_back("SubModel"); somewhere in each sub model (with the according name of course)

EDIT2: Apparently, only feasible with all push_back in the same function. What I wanted is to make the subclass automatically register themselves. A workaround would be to create a virtual register which would contain a static bool init(false); and register the class. Then, set to true, if the constructors would return if init is true...

Max13
  • 919
  • 2
  • 9
  • 27
  • There is no one-size-fits-all, out-of-the-box solution. It depends on your use case and your tool suite. The most portable way would be to just register all of them in a function in a separate source file, which you'd call at some point in your program. – spectras Aug 04 '17 at 00:20
  • That's what I would like (registering them separately), but how can I "append" elements to a static only once? wouldn't they be initialized each time I redefine them in each sources? – Max13 Aug 04 '17 at 00:22
  • Nope, there is only one list, that's the point of `static` keyword. Just `Model::list.push_back(foo)`. Better yet, create a `static void registerModel(std::string, somethingrelevant_like_a_factory)`. – spectras Aug 04 '17 at 00:25
  • That's it! I'm exactly looking for that, a way in `SubClass1` to write `Model::list.push_back("SubClass1");` in `SubClass2` would be `Model::list.push_back("SubClass2");` and so on. But I just don't know where and what would be the syntax to be sure it would be called only ONCE per class... – Max13 Aug 04 '17 at 00:29
  • the only way to do it portably is to do it from your `main()`, or some setup function run from your main. – spectras Aug 04 '17 at 00:31
  • Ok, so... It's possible to initialize a static class member at compile time (defining it in the according cpp file), but not possible to populate them at compile time, is it? – Max13 Aug 04 '17 at 00:32
  • > you can initialize it from one point like you said (it will still be done at runtime though, unless the variable is a `constexpr`). But not from multiple points. To populate it, your best bet is to have, for instance, a `setupModels` function somewhere, in which you do all your `push_back()`s. – spectras Aug 04 '17 at 00:42

2 Answers2

3

Cheap Hack trick. For the cost of a variable hanging around unused in each subclass constructor you can ensure that the registration runs once and only once per class. A static variable is only initialized once, so the function called to initialize it is only called once.

#include <iostream>
#include <list>

class Model
{
public:
    static std::list<const char *>   list; // made public for ease of demonstration
                                           // const char * to store string literals
protected:
    // registration method
    bool reg(const char * name)
    {
        list.push_back(name);
        return true;
    }

};
std::list<const char *>   Model::list; // allocate storage for list


class SubModel1 : public Model
{
public:
    SubModel1()
    {
        // once will only be initialized once, so the reg method will only run once
        static bool once = reg("SubModel1");
        (void) once; // silence unused variable warning.
    }
};

class SubModel2 : public Model
{
public:
    SubModel2()
    {
        static bool once = reg("SubModel2");
        (void) once;
    }
};

class SubModel3 : public Model
{
public:
    SubModel3()
    {
        static bool once = reg("SubModel3");
        (void) once;
    }
};

int main()
{
    SubModel1 one;
    SubModel1 two;
    SubModel2 three;
    // note no SubModel3

    for (const char * model: Model::list)
    {
        std::cout << model << '\n';
    }
    return 0;
}

Output:

SubModel1
SubModel2
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Until now, it was just another idea. I wasn't going to prioritize that idea because for now, EVERYTHING is in the parent constructor, so for now, all my subclasses are just declared but nothing need to be defined (the default constructors and inherited members are OK. I will explore that finally. – Max13 Aug 04 '17 at 00:55
  • @Max13 Shoot. I didn't even see you'd edited your question to add you'd been considering this possibility. – user4581301 Aug 04 '17 at 00:59
  • That's because I wanted to avoid that (considering I don't need any constructor yet). But as my question THE WAY I WOULD LIKE seems not being possible, well... I have to cheap hack, isn't it? – Max13 Aug 04 '17 at 01:00
  • @Max13> The point being: submodels will only appear in the list **after** at least one instance of the class has been constructed. You still have to trigger registration from somewhere (here it is triggered by creating `one`, `two` and `three` in `main()`). – spectras Aug 04 '17 at 01:03
  • I may have read this wrong. If you don't care whether they've been constructed or not, all of the subclasses go into the list so you have a list of possibilities, use the same trick, but the static variable becomes a class member. – user4581301 Aug 04 '17 at 01:06
  • @spectras Yes, and it would be done the same way, when I use them in code (even if not in `main()`) – Max13 Aug 04 '17 at 01:07
  • It's okay, was just worth noting. This technique will work great for keeping track of known types, but it won't work for, say, registering plugins for later use. So depending on use case… :) – spectras Aug 04 '17 at 01:11
  • Been playing around with the static member idea. Works, but a Bad Idea. Requires too many parts. You'd be better off loading the list by hand. – user4581301 Aug 04 '17 at 01:33
0
class Model
{
protected:
    static std::vector<std::string> list;

};

// this line initializes 'list'
// this line must be outside the class 
std::vector<std::string> Model::list;

class SubModel : public Model
{
    // in the constructor, push the name of this class on to the vector:
    SubModel()
    {
       // you could check for duplicates first,
       // if you don't want the name to appear
       // multiple times (if you create multiple instances of this class)

       list.push_back("SubModel");
    }
};

class SubModel2 : public Model
{
    SubModel2()
    {
       list.push_back("SubModel2");
    }
};



int main()
{
    SubModel mod1;
    SubModel2 mod2;

    // list now contains two entries:
    // "SubModel"
    // "SubModel2"


}
ttemple
  • 852
  • 5
  • 20