Проблема следующая. Можно ли как-то заставить линкер включить в линковку модули из статической либы, которые по его мнению не используются в исполняемом файле. Т.е. примерный код:
classA123.cpp
ClassA123::ClassA123()
{
}
//ClassA123 methodsnamespace
{
auto p = new ClassA123();
auto reg = Repository::Instance().Register(p);
}
Если подобный код находится в ехе проекте, все отлично работает. Как только я выношу это в статическую либу, линкер считает, что classA123 у меня нигде не используется и не линкует модуль classA123.cpp. Фиаско.
Пробовал /FORCE:UNRESOLVED и /OPT:NOREF — не помогает.
Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.
df>namespace
df>{
df> auto p = new ClassA123();
df> auto reg = Repository::Instance().Register(p);
df>}
df>
Это вы в статической библиотеке создаете глобальные переменные? Да еще и надеясь на порядок инициализации (пусть эти переменные и находятся в одном файле)? Зачем там вообще переменная p? Избавляйтесь от всего этого безобразия. Сделайте нормальную функцию, которая создаст и зарегистрирует вам экземпляр класса.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
VTT>Это вы в статической библиотеке создаете глобальные переменные? Да еще и надеясь на порядок инициализации (пусть эти переменные и находятся в одном файле)?
Мне не важен порядок инициализации.
VTT>Зачем там вообще переменная p?
разве эта мелочь стоила упоминания? Вопрос вообще не в этом был.
VTT>Избавляйтесь от всего этого безобразия.
Какого?
VTT>Сделайте нормальную функцию, которая создаст и зарегистрирует вам экземпляр класса.
у меня 100500 таких классов и я их добавляю. "Нормальная" функция потребует включения каждого хэдера. Т.е. она должна будет знать о каждом классе. При добавлении/удалении класса я должен буду редактировать ее.
В моем случае — нет. Это удобно. Вообще это известный паттерн.
Здравствуйте, df, Вы писали:
df>Мне не важен порядок инициализации. df>разве эта мелочь стоила упоминания? Вопрос вообще не в этом был.
У вас там явная зависимость от порядка инициализации, если Repository::Instance().Register(p); будет вызван до инициализации p, то все сломается. Это на самом деле вряд ли произойдет, так как переменные объявляются в одном .cpp (а для одного .cpp стандарт вроде как устанавливает порядок). Но суть в том что тут задействуются два антипаттерна сразу: и глобальные переменные, и зависимость от порядка их инициализации.
Добавление по одному заголовочному файлу на каждый класс с объявлением одной единственной функции, создающей и регистрирующей соответствующий класс мне кажется вполне приемлемым решением. Реализацию этой функции можно было бы разместить в статической библиотеке, в том же .cpp. Написание 100500 заголовочных файлов с единственной функции в каждом я считаю меньшим злом чем такое же количество глобальных переменных. Тем более, что их генерацию можно атоматизировать. По-хорошему их можно было бы регистрировать и напрямую в общей функции типа Register_AllClasses(), знающей обо всех классах. Но такое решение наверняка вскроет проблемы с организацией заголовочных файлов, временем компиляции и т.д.
Если же хочется просто заставить это работать, то попробуйте объявлять свои глобальные переменные как extern.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Здравствуйте, df, Вы писали:
df>Привет всем.
df>Проблема следующая. Можно ли как-то заставить линкер включить в линковку модули из статической либы, которые по его мнению не используются в исполняемом файле. Т.е. примерный код:
можно
df>Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.
это тоже выход, попробуйте развить идею
df>Upd: Студия 2013
у нас используется следующий трюк:
ClassA123::ClassA123()
{
}
//ClassA123 methodsnamespace
{
auto p = new ClassA123();
auto reg = Repository::Instance().Register(p);
}
#if defined(_WIN64)
extern"C"void __cdecl _Dummy() {}
#else
extern"C"void __cdecl Dummy() {}
#endif
в том модуле, куда надо это влинковать мы добавляем команду для линкера:
module.cpp
#pragma comment(linker, "/include:_Dummy")
если классов много и они раскиданы по файлам, то на уровне одной статической либы лучше сделать одну регистрирующую функцию, а не много. за нее уже можно линкером цепляться
Здравствуйте, df, Вы писали:
df> auto p = new ClassA123();
у меня с кучей компиляторов (включая msvc2013) работает
static int PPMD_x = AddCompressionMethod (parse_PPMD); // Зарегистрируем парсер метода PPMD
разница в том что это obj а не lib, namespace глобальный, AddCompressionMethod из другого obj. думаю что причина именно в первом — к lib линкер относится более агрессивно
ps: по карйней мере gcc делает именно так: "Also, gcc will ALWAYS link .o files, but it will only search libraries and link from them if there are undefined names still to resolve."
pps: изучение списка опций линкера даёт нам только один вариант. как я понимаю, библиотека рассматривается как набор всякого мусора, из которого следует включить только то, что непосредственно используется программой, и никакого способа принудительно включить все obj из библиотеки у нас нет
можешь попробовать -Gy- при компиляции, но как я понимаю с opt:noref это роли уже не играет
Здравствуйте, uzhas, Вы писали:
U>если классов много и они раскиданы по файлам, то на уровне одной статической либы лучше сделать одну регистрирующую функцию, а не много. за нее уже можно линкером цепляться
кстати да. в моём коде вызывается AddCompressionMethod, добавляющий записи в глобальную таблицу, используемую FindCompressionMethod, который вызывается главной программой. т.е. у вызовов AddCompressionMethod налицо видимый в основной программе побочный эфект, исчезающий при их удалении. может, компилятор замечает эти зависимости и именно поэтому подцепляет этот код? можно попробовать сделать такую же схему
Здравствуйте, BulatZiganshin, Вы писали:
BZ>кстати да. в моём коде вызывается AddCompressionMethod, добавляющий записи в глобальную таблицу, используемую FindCompressionMethod, который вызывается главной программой. т.е. у вызовов AddCompressionMethod налицо видимый в основной программе побочный эфект, исчезающий при их удалении. может, компилятор замечает эти зависимости и именно поэтому подцепляет этот код? можно попробовать сделать такую же схему
ты же вроде правильно уже отметил, что линковщик .obj линкует без анализа. это должно все объяснять
что тебя заставило думать в другом направлении?
Здравствуйте, uzhas, Вы писали:
U>ты же вроде правильно уже отметил, что линковщик .obj линкует без анализа. это должно все объяснять U>что тебя заставило думать в другом направлении?
я использую -Gy и opt:ref:
When /OFT:REF is enabled, LINK removes unreferenced packaged functions and data. An object contains packaged functions and data (COMDATs) if it was compiled by using the /Gy option.
т.е. по идее вещей моя статпеременная вместе со всей своей инициализацией должна элиминироваться? это как раз тот момент, который я ни черта не понимаю
U>ClassA123::ClassA123()
U>{
U>}
U>//ClassA123 methods
U>namespace
U>{
U> auto p = new ClassA123();
U> auto reg = Repository::Instance().Register(p);
U>}
U>#if defined(_WIN64)
U>extern"C"void __cdecl _Dummy() {}
U>#else
U>extern"C"void __cdecl Dummy() {}
U>#endif
U>
U>в том модуле, куда надо это влинковать мы добавляем команду для линкера: U>module.cpp U>
U>#pragma comment(linker, "/include:_Dummy")
U>
U>если классов много и они раскиданы по файлам, то на уровне одной статической либы лучше сделать одну регистрирующую функцию, а не много. за нее уже можно линкером цепляться
Ну это то, что VTT предлагал. Хотелось бы уйти от этого.
Прелесть решения (когда оно в ехе) в том, что классов много и они "живые". Т.е. добавляются, удаляются постоянно, и никто ни кому не мешает. Пока не было нужды выносить это в либу, все было просто замечательно... да вы это и сами знаете.
BZ>у меня с кучей компиляторов (включая msvc2013) работает BZ>static int PPMD_x = AddCompressionMethod (parse_PPMD); // Зарегистрируем парсер метода PPMD
BZ>разница в том что это obj а не lib, namespace глобальный, AddCompressionMethod из другого obj. думаю что причина именно в первом — к lib линкер относится более агрессивно
да, здесь все ясно.
BZ>как я понимаю, библиотека рассматривается как набор всякого мусора, из которого следует включить только то, что непосредственно используется программой, и никакого способа принудительно включить все obj из библиотеки у нас нет
грусть-засада. Придется общий сортир добавлять видимо.
Здравствуйте, BulatZiganshin, Вы писали:
BZ>кстати да. в моём коде вызывается AddCompressionMethod, добавляющий записи в глобальную таблицу, используемую FindCompressionMethod, который вызывается главной программой. т.е. у вызовов AddCompressionMethod налицо видимый в основной программе побочный эфект, исчезающий при их удалении. может, компилятор замечает эти зависимости и именно поэтому подцепляет этот код? можно попробовать сделать такую же схему
если упростить — модуль orphan:
static int x = add();
модуль resident:
int y = 0;
add() {y++;}
основная программа:
main() {return y;}
здесь инициализация x приводит к изменению переменной y, используемой в main. можно проэкспериментировать с различными вариантами компиляции (Gy/Gy-, ref/noref, lib/obj) и посмотреть какое y будет возвращаться
BZ>When /OFT:REF is enabled, LINK removes unreferenced packaged functions and data. An object contains packaged functions and data (COMDATs) if it was compiled by using the /Gy option.
BZ>т.е. по идее вещей моя статпеременная вместе со всей своей инициализацией должна элиминироваться? это как раз тот момент, который я ни черта не понимаю
почему вообще /OPT:NOREF не работает так как от него ждешь....
Ведь вся проблема в линковке. 1000 чертей!
BZ>если упростить — модуль orphan:
BZ>static int x = add();
BZ>модуль resident:
BZ>int y = 0; BZ>add() {y++;}
BZ>основная программа:
BZ>main() {return y;}
BZ>здесь инициализация x приводит к изменению переменной y, используемой в main. можно проэкспериментировать с различными вариантами компиляции (Gy/Gy-, ref/noref, lib/obj) и посмотреть какое y будет возвращаться
Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.
Здравствуйте, df, Вы писали:
df>почему вообще /OPT:NOREF не работает так как от него ждешь.... df>Ведь вся проблема в линковке. 1000 чертей!
по идее вещей, obj-файл — это неотъемлемая часть программы, исключение части его кода — уже рассматривается как оптимизация, которая не должна ничего менять (вероятно поэтмоу у меня инициализхация и срабатывает, поскольку она модифицирует глобальную переменную). opt:noref — это способ отключить оптимизацию, если она всё же начудила, и вернуть всё на свои места
библиотека — это набор полезных артефактов, которые могут пригодиться в программе. по умолчанию, пока на них нет ссылок, они в программу включаться не должны. соответственно opt:[no]ref влияет только на оптимизацию obj-файлов, но ничего не меняет для lib. что собственно логично — представь что ты внезапно получил бы все msvc*.lib включёнными в свой exe
Здравствуйте, df, Вы писали:
df>Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.
My current solution (hack?) is to use a Ruby script as a post build step on my libraries. This script uses the dumpbin.exe tool to find all symbols containing the word 'forceLink', and it outputs a header file containing
#pragma comment(linker, "/include:symbol")
for each of the symbols. I then include that header file from my main project, and then everything just works.
Здравствуйте, df, Вы писали:
df>Привет всем.
df>Проблема следующая. Можно ли как-то заставить линкер включить в линковку модули из статической либы, которые по его мнению не используются в исполняемом файле.
...
auto __declspec(dllexport) ClassA123_registered = Repository::Instance().Register(p);
Похожая практика применяется в boost.serialization
как уже было справедливо замечено, выбранный путь череповат граблями да и просто "некрасив".
когда имеем кучу классов, которые нужно регать где-то там, применяем паттерн "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.
Но я лично склоняюсь к длл.
Просто в качестве размышления. Интересно, почему нет инструкции линкеру принудительно линковать тот или иной модуль, не "умничать"...