Re: static lib. force linkage
От: zaufi Земля  
Дата: 26.03.15 15:43
Оценка: -1
как уже было справедливо замечено, выбранный путь череповат граблями да и просто "некрасив".
когда имеем кучу классов, которые нужно регать где-то там, применяем паттерн "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 stuff
namespace reg {

/// Some registry for classes
struct 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 test

int main()
{
    std::cout << "Nothing to do in main()" << std::endl;
    return 0;
}


пример работы:
zaufi@gentop〉/work/tests〉g++ -std=c++11 -o static_xtors_test static_xtors_test.cc

zaufi@gentop〉/work/tests〉 ./static_xtors_test
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〉/work/tests〉g++ --version
g++ (Gentoo 4.9.2 p1.2, pie-0.6.2) 4.9.2


Дальнейшие улучшения: если "статический деструктор" не нужен, можно заморочиться и определять в compile time присутствует ли в Derived функция static_dtor(), и если нет, порождать static_xtors::helper от соответствующей специализации... ну типа упростить задачу линкеру по выкидыванию пустых функций.

... не за что ...
Отредактировано 26.03.2015 15:52 zaufi . Предыдущая версия .
Re[7]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 15:54
Оценка:
Здравствуйте, df, Вы писали:

U>>ссылка с аналогичным вопросом: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2aa2e1b7-6677-4986-99cc-62f463c94ef3/linkexe-bug-optnoref-option-doesnt-work?forum=vclanguage


там кстати предпоследний ответ описывает вроде работающую техзнологию. а как альтернативный вариант — в момент комплияции грепнуть по всем исходжникам и создать init-функцию, которая все конструкторы вызывает. тогда из main достатточно эту функцию позвать
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 15:57
Оценка:
Здравствуйте, uzhas, Вы писали:

U>если ты явно линкуешь .obj, то статпеременную линкер не выкинет имхо


согласно процитированной мной доке, и переменные и функции должны выкидываться при Gy+opt:ref. иначе какой в них смысл? можно конечно принудительно сохранять именно static объекты, но это не очень-то логично — они могут применяться как рахз элиминированными non-static функциями
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 15:59
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>... не за что ...


ага. проблема именно в том, что это работает в obj, но не lib. читай тред
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: static lib. force linkage
От: ArtDenis Россия  
Дата: 26.03.15 15:59
Оценка:
Здравствуйте, df, Вы писали:

df>не вышло.


Да, похоже что это работает если из статического lib-файла используется хоть что-то. Если не используется вообще ничего, то даже __declspec(dllexport) бессилен. Возможно, что нормального решения просто нету.
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[7]: static lib. force linkage
От: uzhas Ниоткуда  
Дата: 26.03.15 17:17
Оценка: 18 (1)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>согласно процитированной мной доке, и переменные и функции должны выкидываться при Gy+opt:ref.


предположу, что дока неполная и для явных .obj файлов эта оптимизация не применяется
Re[8]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 17:44
Оценка:
Здравствуйте, uzhas, Вы писали:

BZ>>согласно процитированной мной доке, и переменные и функции должны выкидываться при Gy+opt:ref.


U>предположу, что дока неполная и для явных .obj файлов эта оптимизация не применяется


неявные obj-файлы — это библиотеки? возможно. у меня отключение -Gy на "явных .obj файлах" меняет рахзмер exe на 100 кб, но это может быть из-за более точного решения что из библиотек нужно включать, а что нет

... проверил твою гипотезу включением заведомо неиспользуемой константы

static char*aaaa="Done event on the destruction";

сначала в main модуль, затем в другой — её текст в exe в обоих случаях нашёлся. так что ты на 100% прав! спасибо!
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: static lib. force linkage
От: zaufi Земля  
Дата: 26.03.15 18:37
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, zaufi, Вы писали:


Z>>... не за что ...


BZ>ага. проблема именно в том, что это работает в obj, но не lib. читай тред


не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру...
  1. дергая из библы какую-либо функцию (метод реестра объектов) -- например factory method какой-нить
  2. дергая из библы какую-либо функцию, которая допустим даже опосредованно обращается к реестру объектов
  3. и самый крайний вариант: из исполняемого модуля вызывается функция, которая запускает отдельный 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()


и самое главное, это все не завязано на какие-либо хитрожопые ключики конкретного компилятора!
Отредактировано 26.03.2015 18:41 zaufi . Предыдущая версия .
Re[4]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 19:01
Оценка: +1
Здравствуйте, zaufi, Вы писали:

Z>не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру...


к реестру-то обращения есть. проблема в том что линкер выкидывает сами операции регистрации. в твоём случае, если ты поместишь код наследника в библиотеку, то весь наследник со своей регистрацией будет выкинут нафиг. я приводил на второй странице самый простой пример с x,y — можешь попробовать
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: static lib. force linkage
От: zaufi Земля  
Дата: 26.03.15 19:11
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, zaufi, Вы писали:


Z>>не вижу никакой проблемы... я не представляю зачем нужно регать эти классы, если из исполняемого файла не обращаться к этому реестру...


BZ>к реестру-то обращения есть. проблема в том что линкер выкидывает сами операции регистрации. в твоём случае, если ты поместишь код наследника в библиотеку, то весь наследник со своей регистрацией будет выкинут нафиг. я приводил на второй странице самый простой пример с x,y — можешь попробовать


именно так и сделано тут!
все регистрируемые классы находятся в отдельной единице трансляции, которая и составляет библиотеку.
в исполняемом модуле ничего этого не видно, ни процедуры регистрации, ни даже типов регистрируемых классов!

повтыкай приведенный пример более внимательно... попробуй собрать для разнообразия...
Re[6]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 19:33
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>именно так и сделано тут!

Z>все регистрируемые классы находятся в отдельной единице трансляции, которая и составляет библиотеку.
Z>в исполняемом модуле ничего этого не видно, ни процедуры регистрации, ни даже типов регистрируемых классов!

у тебя main явно вызывавет функцию, определённую в библиотеке:

main()
#ifdef COMPILE_AS_STATIC
test::access_registry_indirectly_mt();

благодаря этому цепляется obj-файл, в котором она определена. если таких obj-файлов много, то придётся вызывать функцию в каждом из них

ps: и извини, неужели ты не можешь сделать пример на 10-20 строчек и собрать его явно без смаке? думаешь охота разбираться в километрах кода и логов? boost, вот это всё. ведь очень просто:

lib.c: int a=printf("lib.c\n");
main.c: main(){}

слинкуй lib.c как obj/lib файл и ты увидишь разницу. а дальше пробуй конструкторы и т.д.
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: static lib. force linkage
От: zaufi Земля  
Дата: 26.03.15 19:46
Оценка:
Здравствуйте, 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) показывающую, что в случае со статическими и тем более динамическими библами тоже все работает... какие еще остались вопросы?
Отредактировано 26.03.2015 19:53 zaufi . Предыдущая версия .
Re[8]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 21:22
Оценка:
Здравствуйте, zaufi, Вы писали:

Здравствуйте, 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 для сборки моих кроссплатформенных проектов
Люди, я люблю вас! Будьте бдительны!!!
Отредактировано 26.03.2015 21:28 BulatZiganshin . Предыдущая версия .
Re[6]: static lib. force linkage
От: df Россия  
Дата: 27.03.15 11:40
Оценка:
Здравствуйте, df, Вы писали:

df>Ведь вся проблема в линковке. 1000 чертей!


Позволю себе некоторое резюме:

1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"
#define FORCE_LINK_THIS(x) extern "C" void __cdecl Dummy_##x(){}; //для win64:  extern "C" void __cdecl _Dummy_##x(){}
#define FORCE_LINK_THAT(x) __pragma(comment(linker, "/include:_Dummy_"#x))

небольшой плюс в том, что при при указании FORCE_LINK_THAT(1)..FORCE_LINK_THAT(100500) не нужно указывать хэдеры классов 1..100500. Слабое утешение, конечно. В противном случае — регистрационный метод, который будет знать и регистрировать всех.

2. Отказаться от статической библиотеки в пользу Dll. Здесь, как мне кажется, плюсы и минусы очевидны.
Re[7]: static lib. force linkage
От: BulatZiganshin  
Дата: 27.03.15 13:12
Оценка:
Здравствуйте, df, Вы писали:

df>1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"


а как насчёт сканирования кода и автоформирования необходимой функции инициализации?
Люди, я люблю вас! Будьте бдительны!!!
Re[8]: static lib. force linkage
От: df Россия  
Дата: 27.03.15 13:25
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:



df>>1. Решение проблемы со статической библиотекой найти не удалось. В качестве некоего облегчения жизни можно использовать вариант предложенный коллегой uzhas. Можно "облагородить" парочкой макросов"


BZ>а как насчёт сканирования кода и автоформирования необходимой функции инициализации?


Как вариант №3.
Но я лично склоняюсь к длл.

Просто в качестве размышления. Интересно, почему нет инструкции линкеру принудительно линковать тот или иной модуль, не "умничать"...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.