5

I was thinking about how to create a list of all class which derive from a template base class.

First I want to have a template Base class:

template <typename T>
class Base
{
public:
    Base() {};
    virtual ~Base() {};
};

and a class which inherits from the base template class:

class Foo : public Base<Foo >
{
public:
    Foo () {};
    virtual ~Foo () {};
};

There might be any number of other subclasses like Foo. The result should look like something:

std::vector<std::string> allTemplates = Base<Base>::getAllTemplateClasses();

My question is if its possible to create an list of all sub-classes at compile time? The Magic should happed in the Base class or with a really little effort in the child class.

I was thinking in different directions before. First I thought it is possible to use constexpr. Like every child class needs a static function with the signature:

constexpr static std::string name() { "Foo";}

Or thought maybe it is possible with meta programming and create a compile time list like the Example of A Compile Time Data Structure Using,Template-Meta. The problem here is that I don't know the head of for the template creation.

Next I was thinking about to use macros and build an enum struct like this way Enum structs expanding. So I can't find any solution for this problem I want to ask you if its even possible?

Edit:

To say it clear: I want to have a list of the child objects, without any need to create them.

Community
  • 1
  • 1
PeterNL
  • 630
  • 6
  • 21
  • Ok, I deleted my answer because it doesn't quite work. The part that is missing is what is described here: http://stackoverflow.com/questions/5803953/static-constructor-in-c `// C++ needs to define static members externally. has_static_constructor::constructor has_static_constructor::cons;` The `registrar` object wasn't actually being constructed in static initialization because when it appears in the template class there, it is only a declaration and not a definition. I still think this static registration thing is the way to go, but you will need to do something ugly with a macro afaik. – Chris Beck Jan 19 '16 at 00:08
  • (The point of the macro would be, when you declare a class to be a child of this base class, you also want, outside of the class definition, to declare a `static` registrar type object whose constructor registers its name, and the macro should help make sure that these two things happen together. But idk if this is a nice enough solution to be worth writing up anyways.) – Chris Beck Jan 19 '16 at 00:11
  • Just as an additional comment -- I would be interested if there is an elegant way to do this, but right now I wouldn't bet that there really is one. – Chris Beck Jan 19 '16 at 00:30
  • Thanks for your efforts, how I really said I was thinking in different directions but no one was really helpful. I think it is a nice problemb but with no nice solution. :) Thought it get maybe upvoted a bit more. – PeterNL Jan 19 '16 at 11:05
  • Thx fo your help @Chris Beck with your idea I found some solution. – PeterNL Jan 19 '16 at 20:26

1 Answers1

4

With this nice post static constructor and an already answer which was similiar to this one, I was able to find a solution. The magic is static constructors. First I will have to create an container which holds the subclass and add the subclasses:

//base.h
std::set<std::string> &get_objects();
void add_object(const char *name);

And the implementations:

// base.cpp

std::set<std::string> &get_objects()
{
    static std::set<std::string> theSet;
    return theSet;
}

void add_object(const char *name)
{
    get_objects().emplace(name);
}

So now we have to create a static class which adds the strings to the list. It is similar to the post static constructor:

//base.h
class StaticClassType {
public:
    StaticClassType(const char *name) {
        // Notify when the static member is created
        add_object(name);
    }
};

The base class is an template class which creates an static object of the 'StaticClassType'. C++ guaranteed that static initialization is done before main() is called.

//base.h
template<typename T>
class Base {
protected:
    // Static member in a template class
    static StaticClassType m;
    Base()
    {
        (void)m;
    }
};

Without the next row the m doesnt get createad:

template<typename T>
StaticClassType Base<T>::m = StaticClassType(typeid(T).name());

Now we can create two classes and the main:

class TestClass1 : public Base<TestClass1> {
public:
    TestClass1() :Base() {}
};

class TestClass1 : public Base<TestClass1> {
public:
    TestClass1() :Base() {}
};

int main()
{
    std::set<std::string> &test = get_objects();
    for(auto str : test)
        std::cout << str.c_str() << std::endl;
    return 0;
}

The output is without any construction of any object:

class TestClass1
class TestClass2

There is one think to take care of. We have to use m somewhere. Otherwise the compiler is optimating the code and removing m. I force this behavior by writting the constructor call:

TestClass1() :Base() {}

Hope you like it, it is not really compile time but we have the object list without doing anything by writting the constructor and use base as parent class.

Community
  • 1
  • 1
PeterNL
  • 630
  • 6
  • 21
  • 3
    While this is a nice solution to your problem, this is not actually a "compile time" solution. The list is built during runtime, even though as you say it runs before main is executed. – Morgan Jun 27 '20 at 22:25