Основная идея достаточна прямолинейна — создаём статическую переменную, конструктор которой вызывает 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 — компилируется без ошибок, что радует.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Если я правильно понимаю ситуацию, то вся реализация лежит в заголовчном файле. Представим что этот заголовочник используется в статической библиотеке libMyLib.lib. В ней инстанцируется class_initializer<> для какого-нибудь типа Foo, объявленного и определённого в этой же библиотеке. А теперь главное не дать пользователю библиотеки самому инстанцировать class_initializer<Foo> и запретить ему включать заголовочник библиотеки, в котором объявлен class_initializer<Foo>. Иначе получим двойное опредление переменной class_initializer<Foo, Foo>::helper_.
Здравствуйте, Mazay, Вы писали:
M>Если я правильно понимаю ситуацию, то вся реализация лежит в заголовчном файле. Представим что этот заголовочник используется в статической библиотеке libMyLib.lib. В ней инстанцируется class_initializer<> для какого-нибудь типа Foo, объявленного и определённого в этой же библиотеке. А теперь главное не дать пользователю библиотеки самому инстанцировать class_initializer<Foo> и запретить ему включать заголовочник библиотеки, в котором объявлен class_initializer<Foo>. Иначе получим двойное опредление переменной class_initializer<Foo, Foo>::helper_.
Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).
Могу привести как я это использую в библиотеке асинхронного агентного программирования.
Пользователь описывает свой класс агента следующим образом:
class my_agent : public zzz::agent<my_agent>
{
// помимо вкусностей, которые нам даёт наследование и CRTP,
// у агента есть функция статической подписки на сообщения.
// Она вызывается из "конструктора базового класса" zzz::agent<>,
// который сам делает некую работу по регистрации данного типа агента,
// по инициализации необходимых структур данных,
// а потом вызывает пользовательскую функцию zzz_subscribe()static void zzz_subscribe()
{
subscribe<msg1>();
subscribe<msg2>();
subscribe<msg3>();
}
};
По-моему, получается очень красиво и удобно для пользователя. Никаких дополнительных макросов, никаких дополнительных вызовов функций, которые надо "не забыть" сделать. Вся инициализация гарантированно происходит "до main", т.o. в main я гарантированно знаю ВСЕ типы агентов (и аналогично для типов сообщений), которые есть в программе, это позволяет строить необходимые вспомогательные таблицы в виде массивов, не боясь и не предусматривая возможность динамического роста.
Ну или классический пример, если в программе есть много типов чего-либо — ну там типов запросов на сервере, или типов команд в клиентском приложении, тогда описывать их можно следующим образом:
class request_123 : public request<request_123>
{
// тут уже никаких дополнительных действий не надо,
// т.к. наш базовый класс request<> сам в своём "статическом конструкторе"
// зарегистрирует этот тип обработчика в фабрике обработчиков
};
Здравствуйте, remark, Вы писали:
R>Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).
R>
Вот если бы ещё и dll не было...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
приложении, тогда описывать их можно следующим образом: R>
R> // тут уже никаких дополнительных действий не надо,
R> // т.к. наш базовый класс request<> сам в своём "статическом конструкторе"
R> // зарегистрирует этот тип обработчика в фабрике обработчиков
R>
R>
Супер! Так бы сразу и писал, что это всё проолжение темы о построении списка деклараций в CT. В CT похоже облом, и пришлось отступить до этапа статических конструкторов. В целом прикольно. Единственный существенный недостаток для пользователя -- труно врубиться когда и кто зовёт твою функцию ну да RTFM!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
R>>Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).
E>Вот если бы ещё и dll не было...
Здравствуйте, remark, Вы писали:
R>Тут есть какие-то проблемы с dll'ями?
Если я заведу клиентсякий класс, то в каждой dll в которую будет включаться его хедер добавится вызоывалка инициализации...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
R>>Тут есть какие-то проблемы с dll'ями?
E>Если я заведу клиентсякий класс, то в каждой dll в которую будет включаться его хедер добавится вызоывалка инициализации...
По-моему, логично. А как классу жить без вызванного конструктора? А если ты включишь заголовок с определением объекта в несколько длл, в каждой длл тоже будет свой объект...
Здравствуйте, remark, Вы писали:
R>По-моему, логично. А как классу жить без вызванного конструктора? А если ты включишь заголовок с определением объекта в несколько длл, в каждой длл тоже будет свой объект...
R>
IMHO логично бы было, если бы инициализация вызывалась только в той dll которая экспортирует эту штуку...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, remark
R>В итоге это решение работает на msvc7.1, msvc8, msvc9, gcc3.4, gcc 4.1. На Comeau online — компилируется без ошибок, что радует.