Вначале пример использования:
struct A : class_initializer<A>
{
static void static_ctor()
{
std::cout << __FUNCTION__ << std::endl;
}
static void static_dtor()
{
std::cout << __FUNCTION__ << std::endl;
}
};
struct B : class_initializer<B>
{
static void static_ctor()
{
std::cout << __FUNCTION__ << std::endl;
}
static void static_dtor()
{
std::cout << __FUNCTION__ << std::endl;
}
};
int main()
{
std::cout << __FUNCTION__ << std::endl;
}
Выводит:
A::static_ctor
B::static_ctor
main
B::static_dtor
A::static_dtor
Теперь реализация (для любителей хардкорного С++):
template<typename derived_t, typename target_t = derived_t>
class class_initializer
{
struct helper
{
helper()
{
target_t::static_ctor();
}
~helper()
{
target_t::static_dtor();
}
};
static helper helper_;
static void use_helper()
{
(void)helper_;
}
template<void(*)()>
struct helper2 {};
helper2<&class_initializer::use_helper> helper2_;
virtual void use_helper2()
{
(void)helper_;
}
};
template<typename derived_t, typename target_t>
typename class_initializer<derived_t, target_t>::helper
class_initializer<derived_t, target_t>::helper_;
Основная идея достаточна прямолинейна — создаём статическую переменную, конструктор которой вызывает static_ctor(), а деструктор static_dtor(). Но дьявол, как говорится, в деталях. Как заставить компилятор инстанциировать статическую переменную шаблонного класса, так сказать, "изнутри" этого же класса? У меня на это ушло примерно 2 года непрерывных медитаций

Понятно, что надо как-то использовать эту статическую переменную (helper_), что бы заставить компилятор её инстанциировать. Со студией всё достаточно просто. Достаточно создать виртуальную функцию (use_helper2), которая упоминает статическую переменную. По стандарту будет ли инстанциироваться виртуальная функция — implementation defined. Но студия инстанциирует, поэтому всё замечательно.
Основная засада с gcc, т.к. он, сволочь, не инстанциирует виртуальные функции шаблонных классов без особой надобности. При наследовании от шаблонного класса гарантированно по стандарту инстанциируются только объявления всех членов. Поэтому надо как-то из объявления члена класса использовать статическую переменную. Решение следующее. Параметризуем вспомогательный шаблонный класс (helper2) адресом статической функции (use_helper). Поскольку от функции берётся адрес, gcc её инстанциирует (логично). Далее просто — функция упоминает статическую переменную (helper_).
В принципе тут можно пойти более коротким путём — параметризовать вспомогательный класс сразу адресом нужной статической переменной (helper_). Но я столкнулся с тем, что не все компиляторы хорошо это переваривают, хотя на некоторых gcc это работает как полагается.
В итоге это решение работает на msvc7.1, msvc8, msvc9, gcc3.4, gcc 4.1. На Comeau online — компилируется без ошибок, что радует.