Ah yes, I forgot about a few of those odd static initialization rules, especially with templates.
I got something working though:
Meta.hpp:
#ifndef META_HPP
#define META_HPP
#include <typeinfo>
#include <iostream>
template<typename T>
class MetaBuilder
{
public:
MetaBuilder()
{
std::cout << "MetaBuilder<" << typeid(T).name() << ">::MetaBuilder()\n";
T::registerClass(*this);
}
};
template<typename T>
class Meta
{
public:
static void doWork()
{
metaBuilder;
std::cout << "Meta<" << typeid(T).name() << ">::doWork()\n";
}
private:
static MetaBuilder<T> metaBuilder;
};
template<typename T>
MetaBuilder<T> Meta<T>::metaBuilder;
#endif
main.cpp:
#include <iostream>
#include "Meta.hpp"
class Test
{
public:
static void registerClass(MetaBuilder<Test>& builder)
{
std::cout << "Test::registerClass()\n";
registered = true;
}
static bool isRegistered()
{
return registered;
}
private:
static bool registered;
};
bool Test::registered = false;
int main(int argc, char** argv)
{
std::cout << "enter main\n";
Meta<Test>::doWork();
std::cout << std::boolalpha << "Test::isRegistered(): " << Test::isRegistered() << '\n';
std::cout << "exit main\n";
return 0;
}
Output:
MetaBuilder<4Test>::MetaBuilder()
Test::registerClass()
enter main
Meta<4Test>::doWork()
Test::isRegistered(): true
exit main
Basically, two things are required: an out-of-class provision of
Meta<T>::metaBuilder, and a (non-) use of the member. (Above, in
Meta<T>::doWork()). Basically, for the compiler/runtime to decide for it to be statically initialized as desired, it must be
directly accessed. Plain-Old-Data types don't have this restriction. Kinda weird. But it works, and it's not even that much work.
tl;dr: The above code will have
T::registerClass() called for any class used as
Meta<T>'s template parameter.
EDIT:
Test::registered was just for testing, it wouldn't ever actually be needed, as it will always be
true.
So, we came to quite similar conclusions. The constructor being called when desired is the only difference!