как уже было справедливо замечено, выбранный путь череповат граблями да и просто "некрасив".
когда имеем кучу классов, которые нужно регать где-то там, применяем паттерн "static constructor" (искать по этому форуму).
тогда код инициализации будет жить в самом классе (в статической функции, которая будет "автоматом" вызываться просто по факту объявления класса).
итак:
вот тебе пример с использованием статического конструктора (авторство уточнить поиском по форуму):
#include <iostream>
namespace pattern {
/**
* \brief Helper class to inialize static variables w/o explicit variables declaration
*
* \note \e 'xtors' in class name means \e ctor (constructor) and \e dtor (destructor)
*/template <
typename Derived
, typename Target = Derived
>
class static_xtors
{
struct helper
{
helper()
{
Target::static_ctor();
}
~helper()
{
Target::static_dtor();
}
};
static helper s_helper;
template <void(*)()>
struct helper2 {};
static void use_helper()
{
(void)s_helper;
}
helper2<&static_xtors::use_helper> s_helper2;
virtual void use_helper2()
{
(void)s_helper2;
}
public:
virtual ~static_xtors() {}
};
template <
typename Derived
, typename Target
>
typename static_xtors<Derived, Target>::helper
static_xtors<Derived, Target>::s_helper;
} // namespace pattern
/// Registration related stuffnamespace reg {
/// Some registry for classesstruct registry
{
/// Dummy implementation. The real one should add a given pointer
/// to some registry...template <typename T>
static void register_it(T* const)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
/// Generalize registration for any partucular child of this base.
/// It uses \c static_xtors to make an instance of \c Derived and
/// register it.template <typename Derived>
struct registrar : public pattern::static_xtors<registrar<Derived>>
{
static void static_ctor()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
static auto* p = new Derived();
/// \attention Fix it according your case!
registry::register_it(p);
}
static void static_dtor()
{
// Nothing to do...
// \attention In this particular example pointer \c p will be leaked!
// A real code may do cleanup...
}
};
} // namespace reg
/// \note Declare your particular classes and don't care about registration anymore!
/// Adding removing any new class as easy as pie!namespace test {
struct class_1 : public reg::registrar<class_1> {};
struct class_2 : public reg::registrar<class_2> {};
struct class_123 : public reg::registrar<class_123> {};
} // namespace testint main()
{
std::cout << "Nothing to do in main()" << std::endl;
return 0;
}
Дальнейшие улучшения: если "статический деструктор" не нужен, можно заморочиться и определять в compile time присутствует ли в Derived функция static_dtor(), и если нет, порождать static_xtors::helper от соответствующей специализации... ну типа упростить задачу линкеру по выкидыванию пустых функций.
там кстати предпоследний ответ описывает вроде работающую техзнологию. а как альтернативный вариант — в момент комплияции грепнуть по всем исходжникам и создать init-функцию, которая все конструкторы вызывает. тогда из main достатточно эту функцию позвать
Здравствуйте, uzhas, Вы писали:
U>если ты явно линкуешь .obj, то статпеременную линкер не выкинет имхо
согласно процитированной мной доке, и переменные и функции должны выкидываться при Gy+opt:ref. иначе какой в них смысл? можно конечно принудительно сохранять именно static объекты, но это не очень-то логично — они могут применяться как рахз элиминированными non-static функциями
Да, похоже что это работает если из статического lib-файла используется хоть что-то. Если не используется вообще ничего, то даже __declspec(dllexport) бессилен. Возможно, что нормального решения просто нету.
Здравствуйте, uzhas, Вы писали:
BZ>>согласно процитированной мной доке, и переменные и функции должны выкидываться при Gy+opt:ref.
U>предположу, что дока неполная и для явных .obj файлов эта оптимизация не применяется
неявные obj-файлы — это библиотеки? возможно. у меня отключение -Gy на "явных .obj файлах" меняет рахзмер exe на 100 кб, но это может быть из-за более точного решения что из библиотек нужно включать, а что нет
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, zaufi, Вы писали:
Z>>... не за что ...
BZ>ага. проблема именно в том, что это работает в obj, но не lib. читай тред
не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру... дергая из библы какую-либо функцию (метод реестра объектов) -- например factory method какой-нить
дергая из библы какую-либо функцию, которая допустим даже опосредованно обращается к реестру объектов
и самый крайний вариант: из исполняемого модуля вызывается функция, которая запускает отдельный thread, который, в свою очередь, обращается к реестру объектов (пусть даже косвенно)
для демонстрации последнего (самого извращенного из перечисленных) вариантов, я выложил проектик демонстрирующий использование здесь.
пример сборки и демонстрация работы:
zaufi@gentop〉/work/GitHub/static_xtors_demo〉 mkdir build
zaufi@gentop〉/work/GitHub/static_xtors_demo〉 cd build
zaufi@gentop〉.../GitHub/static_xtors_demo/build〉 cmake ..
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/lib/outproc/bin/cc -- works
-- Detecting C compiler ABI info - done
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/lib/outproc/bin/c++ -- works
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features - done
-- Boost version: 1.57.0
-- Found the following Boost libraries:
-- system
-- thread
-- Configuring done
-- Generating done
-- Build files have been written to: /work/GitHub/static_xtors_demo/build
zaufi@gentop〉.../GitHub/static_xtors_demo/build〉 make
Scanning dependencies of target static_xtors_lib_d
[ 25%] Building CXX object CMakeFiles/static_xtors_lib_d.dir/static_xtors_lib.cc.o
Linking CXX shared library libstatic_xtors_lib_d.so
[ 25%] Built target static_xtors_lib_d
Scanning dependencies of target static_xtors_lib_s
[ 50%] Building CXX object CMakeFiles/static_xtors_lib_s.dir/static_xtors_lib.cc.o
Linking CXX static library libstatic_xtors_lib_s.a
[ 50%] Built target static_xtors_lib_s
Scanning dependencies of target static_xtors_test_d
[ 75%] Building CXX object CMakeFiles/static_xtors_test_d.dir/static_xtors_test.cc.o
Linking CXX executable static_xtors_test_d
[ 75%] Built target static_xtors_test_d
Scanning dependencies of target static_xtors_test_s
[100%] Building CXX object CMakeFiles/static_xtors_test_s.dir/static_xtors_test.cc.o
Linking CXX executable static_xtors_test_s
[100%] Built target static_xtors_test_s
zaufi@gentop〉.../GitHub/static_xtors_demo/build〉 ./static_xtors_test_d
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_1]
static void reg::registry::register_it(T*) [with T = test::class_1]
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_2]
static void reg::registry::register_it(T*) [with T = test::class_2]
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_123]
static void reg::registry::register_it(T*) [with T = test::class_123]
Nothing to do in main()
zaufi@gentop〉.../GitHub/static_xtors_demo/build〉./static_xtors_test_s
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_1]
static void reg::registry::register_it(T*) [with T = test::class_1]
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_2]
static void reg::registry::register_it(T*) [with T = test::class_2]
static void reg::registrar<Derived>::static_ctor() [with Derived = test::class_123]
static void reg::registry::register_it(T*) [with T = test::class_123]
Nothing to do in main()
void test::access_registry_indirectly_mt()
void test::access_registry_indirectly()
и самое главное, это все не завязано на какие-либо хитрожопые ключики конкретного компилятора!
Здравствуйте, zaufi, Вы писали:
Z>не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру...
к реестру-то обращения есть. проблема в том что линкер выкидывает сами операции регистрации. в твоём случае, если ты поместишь код наследника в библиотеку, то весь наследник со своей регистрацией будет выкинут нафиг. я приводил на второй странице самый простой пример с x,y — можешь попробовать
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, zaufi, Вы писали:
Z>>не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру...
BZ>к реестру-то обращения есть. проблема в том что линкер выкидывает сами операции регистрации. в твоём случае, если ты поместишь код наследника в библиотеку, то весь наследник со своей регистрацией будет выкинут нафиг. я приводил на второй странице самый простой пример с x,y — можешь попробовать
именно так и сделано тут!
все регистрируемые классы находятся в отдельной единице трансляции, которая и составляет библиотеку.
в исполняемом модуле ничего этого не видно, ни процедуры регистрации, ни даже типов регистрируемых классов!
повтыкай приведенный пример более внимательно... попробуй собрать для разнообразия...
Здравствуйте, zaufi, Вы писали:
Z>именно так и сделано тут! Z>все регистрируемые классы находятся в отдельной единице трансляции, которая и составляет библиотеку. Z>в исполняемом модуле ничего этого не видно, ни процедуры регистрации, ни даже типов регистрируемых классов!
у тебя main явно вызывавет функцию, определённую в библиотеке:
благодаря этому цепляется obj-файл, в котором она определена. если таких obj-файлов много, то придётся вызывать функцию в каждом из них
ps: и извини, неужели ты не можешь сделать пример на 10-20 строчек и собрать его явно без смаке? думаешь охота разбираться в километрах кода и логов? boost, вот это всё. ведь очень просто:
lib.c: int a=printf("lib.c\n");
main.c: main(){}
слинкуй lib.c как obj/lib файл и ты увидишь разницу. а дальше пробуй конструкторы и т.д.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, zaufi, Вы писали:
Z>>именно так и сделано тут! Z>>все регистрируемые классы находятся в отдельной единице трансляции, которая и составляет библиотеку. Z>>в исполняемом модуле ничего этого не видно, ни процедуры регистрации, ни даже типов регистрируемых классов!
BZ>у тебя main явно вызывавет функцию, определённую в библиотеке:
BZ>main() BZ>#ifdef COMPILE_AS_STATIC BZ> test::access_registry_indirectly_mt();
BZ>благодаря этому цепляется obj-файл, в котором она определена. если таких obj-файлов много, то придётся вызывать функцию в каждом из них
ок. разделил библиотеку на 2 файла (объектника): код регистрации и доступ к реестру (в main'a используюется функция только из этой единицы трансляции!).
BZ>ps: и извини, неужели ты не можешь сделать пример на 10-20 строчек и собрать его явно без смаке?
извини, как ты себе представляешь код на 10-20 строчек, при том, что участвует 2 единицы трансляции (для формирования библиотеки) + 1 header (для того, чтобы main знал про реестр) + собственно код исполняемого файла, пытающийся использовать указанную библиотеку...
BZ>думаешь охота разбираться в километрах кода и логов? boost, вот это всё. ведь очень просто:
я могу написать на `make` -- CMake просто для того, чтобы на убогих платформах можно было сгенерить файл проекта для самой лучшей IDE в мире...
BZ>lib.c: int a=printf("lib.c\n"); BZ>main.c: main(){}
BZ>слинкуй lib.c как obj/lib файл и ты увидишь разницу. а дальше пробуй конструкторы и т.д.
ну а я что делаю??? static_xtors_lib.cc и static_xtors_lib_access.cc транслируются в .o, и далее из них собирается библа (.a, или .lib, по вашему) -- в чем я должен увидеть разницу?
линковка объектников напрямую к исполняемому файлу, очевидно работает. я сделал (кроссплатформенную) демку (не потратив на это кучу времени, спасибо CMake) показывающую, что в случае со статическими и тем более динамическими библами тоже все работает... какие еще остались вопросы?
Здравствуйте, zaufi, Вы писали:
Z>ок. разделил библиотеку на 2 файла (объектника):
не помогло. первый файл цепляется из-за того что main вызывает access_registry_indirectly_mt, а второй — потому что первый файл использует reg::registry::get_registry(), а reg::registry::s_registry находится во втором файле. и чстно говоря, мне надоело выискивать твои ошибки. ты ведь уверен что можешь корркектный пример найти. так проверь его сам, а не спеши вываливать первый пришедший в голову код
Z>извини, как ты себе представляешь код на 10-20 строчек, при том, что участвует 2 единицы трансляции (для формирования библиотеки) + 1 header (для того, чтобы main знал про реестр) + собственно код исполняемого файла, пытающийся использовать указанную библиотеку...
а вот так как я ниже написал, этого достаточно для демонстрации отлиячий между lib и obj:
BZ>>lib.c: int a=printf("lib.c\n"); BZ>>main.c: main(){}
Z>я могу написать на `make` -- CMake просто для того, чтобы на убогих платформах можно было сгенерить файл проекта для самой лучшей IDE в мире...
а, из лучших намерений. у меня как рах cmake не работает, а gcc вполне себе. тут ведь достаточно несколько команд чтобы откомпилировать всё
BZ>>слинкуй lib.c как obj/lib файл и ты увидишь разницу. а дальше пробуй конструкторы и т.д.
Z>какие еще остались вопросы?
из-за рахзмеров этого кода ты не видишь в нём ошибок. в нём куча ненужных в данном случпае вещей — наслежование шаблоны треды. поменяй мой lib.c на такой:
class a{
struct b{
b() {printf("be");}
}
static b c;
}
и посмотри как оно работает из obj/lib при пустом main(). нам ведь не нужен полный механизм — нужна лишь демонстрация кода, который срабатывает без каких-лиюо внешних ссылок к этому obj-файлу
ps: спасибо за ответы. мне понравилось как ты испольтзовал github для удобства ведения дискуссии и я понял, что мне надо смотреть в сторолну cmake для сборки моих кроссплатформенных проектов
Здравствуйте, df, Вы писали:
df>Ведь вся проблема в линковке. 1000 чертей!
Позволю себе некоторое резюме:
1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"
небольшой плюс в том, что при при указании FORCE_LINK_THAT(1)..FORCE_LINK_THAT(100500) не нужно указывать хэдеры классов 1..100500. Слабое утешение, конечно. В противном случае — регистрационный метод, который будет знать и регистрировать всех.
2. Отказаться от статической библиотеки в пользу Dll. Здесь, как мне кажется, плюсы и минусы очевидны.
Здравствуйте, df, Вы писали:
df>1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"
а как насчёт сканирования кода и автоформирования необходимой функции инициализации?
df>>1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"
BZ>а как насчёт сканирования кода и автоформирования необходимой функции инициализации?
Как вариант №3.
Но я лично склоняюсь к длл.
Просто в качестве размышления. Интересно, почему нет инструкции линкеру принудительно линковать тот или иной модуль, не "умничать"...