static lib. force linkage
От: df Россия  
Дата: 26.03.15 10:29
Оценка:
Привет всем.

Проблема следующая. Можно ли как-то заставить линкер включить в линковку модули из статической либы, которые по его мнению не используются в исполняемом файле. Т.е. примерный код:

classA123.cpp

ClassA123::ClassA123()
{
}

//ClassA123 methods

namespace
{
    auto p = new ClassA123();
    auto reg = Repository::Instance().Register(p);
}


Если подобный код находится в ехе проекте, все отлично работает. Как только я выношу это в статическую либу, линкер считает, что classA123 у меня нигде не используется и не линкует модуль classA123.cpp. Фиаско.
Пробовал /FORCE:UNRESOLVED и /OPT:NOREF — не помогает.

Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.

Help!

Upd: Студия 2013
Отредактировано 26.03.2015 10:37 df . Предыдущая версия .
Re: static lib. force linkage
От: VTT http://vtt.to
Дата: 26.03.15 10:44
Оценка:
Здравствуйте, df, Вы писали:

df>classA123.cpp

df>

df>namespace
df>{
df>    auto p = new ClassA123();
df>    auto reg = Repository::Instance().Register(p);
df>}

df>


Это вы в статической библиотеке создаете глобальные переменные? Да еще и надеясь на порядок инициализации (пусть эти переменные и находятся в одном файле)? Зачем там вообще переменная p? Избавляйтесь от всего этого безобразия. Сделайте нормальную функцию, которая создаст и зарегистрирует вам экземпляр класса.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 26.03.2015 10:50 VTT . Предыдущая версия .
Re[2]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 11:04
Оценка:
Здравствуйте, VTT, Вы писали:



VTT>Это вы в статической библиотеке создаете глобальные переменные? Да еще и надеясь на порядок инициализации (пусть эти переменные и находятся в одном файле)?


Мне не важен порядок инициализации.


VTT>Зачем там вообще переменная p?


разве эта мелочь стоила упоминания? Вопрос вообще не в этом был.


VTT>Избавляйтесь от всего этого безобразия.


Какого?


VTT>Сделайте нормальную функцию, которая создаст и зарегистрирует вам экземпляр класса.


у меня 100500 таких классов и я их добавляю. "Нормальная" функция потребует включения каждого хэдера. Т.е. она должна будет знать о каждом классе. При добавлении/удалении класса я должен буду редактировать ее.
В моем случае — нет. Это удобно. Вообще это известный паттерн.
Re[3]: static lib. force linkage
От: VTT http://vtt.to
Дата: 26.03.15 11:28
Оценка:
Здравствуйте, df, Вы писали:

df>Мне не важен порядок инициализации.

df>разве эта мелочь стоила упоминания? Вопрос вообще не в этом был.

У вас там явная зависимость от порядка инициализации, если Repository::Instance().Register(p); будет вызван до инициализации p, то все сломается. Это на самом деле вряд ли произойдет, так как переменные объявляются в одном .cpp (а для одного .cpp стандарт вроде как устанавливает порядок). Но суть в том что тут задействуются два антипаттерна сразу: и глобальные переменные, и зависимость от порядка их инициализации.

Добавление по одному заголовочному файлу на каждый класс с объявлением одной единственной функции, создающей и регистрирующей соответствующий класс мне кажется вполне приемлемым решением. Реализацию этой функции можно было бы разместить в статической библиотеке, в том же .cpp. Написание 100500 заголовочных файлов с единственной функции в каждом я считаю меньшим злом чем такое же количество глобальных переменных. Тем более, что их генерацию можно атоматизировать. По-хорошему их можно было бы регистрировать и напрямую в общей функции типа Register_AllClasses(), знающей обо всех классах. Но такое решение наверняка вскроет проблемы с организацией заголовочных файлов, временем компиляции и т.д.

Если же хочется просто заставить это работать, то попробуйте объявлять свои глобальные переменные как extern.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: static lib. force linkage
От: uzhas Ниоткуда  
Дата: 26.03.15 11:50
Оценка:
Здравствуйте, df, Вы писали:

df>Привет всем.


df>Проблема следующая. Можно ли как-то заставить линкер включить в линковку модули из статической либы, которые по его мнению не используются в исполняемом файле. Т.е. примерный код:

можно

df>Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.


это тоже выход, попробуйте развить идею

df>Upd: Студия 2013


у нас используется следующий трюк:
ClassA123::ClassA123()
{
}

//ClassA123 methods

namespace
{
    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")


если классов много и они раскиданы по файлам, то на уровне одной статической либы лучше сделать одну регистрирующую функцию, а не много. за нее уже можно линкером цепляться
Re: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 12:23
Оценка:
Здравствуйте, 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 это роли уже не играет
Люди, я люблю вас! Будьте бдительны!!!
Отредактировано 26.03.2015 12:41 BulatZiganshin . Предыдущая версия . Еще …
Отредактировано 26.03.2015 12:24 BulatZiganshin . Предыдущая версия .
Re[2]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 12:46
Оценка:
Здравствуйте, uzhas, Вы писали:

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


кстати да. в моём коде вызывается AddCompressionMethod, добавляющий записи в глобальную таблицу, используемую FindCompressionMethod, который вызывается главной программой. т.е. у вызовов AddCompressionMethod налицо видимый в основной программе побочный эфект, исчезающий при их удалении. может, компилятор замечает эти зависимости и именно поэтому подцепляет этот код? можно попробовать сделать такую же схему
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: static lib. force linkage
От: uzhas Ниоткуда  
Дата: 26.03.15 13:04
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>кстати да. в моём коде вызывается AddCompressionMethod, добавляющий записи в глобальную таблицу, используемую FindCompressionMethod, который вызывается главной программой. т.е. у вызовов AddCompressionMethod налицо видимый в основной программе побочный эфект, исчезающий при их удалении. может, компилятор замечает эти зависимости и именно поэтому подцепляет этот код? можно попробовать сделать такую же схему


ты же вроде правильно уже отметил, что линковщик .obj линкует без анализа. это должно все объяснять
что тебя заставило думать в другом направлении?
Re[4]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 13:19
Оценка:
Здравствуйте, 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.

т.е. по идее вещей моя статпеременная вместе со всей своей инициализацией должна элиминироваться? это как раз тот момент, который я ни черта не понимаю
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 13:22
Оценка:
Здравствуйте, uzhas, Вы писали:



U>у нас используется следующий трюк:

U>
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 предлагал. Хотелось бы уйти от этого.
Прелесть решения (когда оно в ехе) в том, что классов много и они "живые". Т.е. добавляются, удаляются постоянно, и никто ни кому не мешает. Пока не было нужды выносить это в либу, все было просто замечательно... да вы это и сами знаете.
Re[2]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 13:27
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:


BZ>у меня с кучей компиляторов (включая msvc2013) работает

BZ>static int PPMD_x = AddCompressionMethod (parse_PPMD); // Зарегистрируем парсер метода PPMD

BZ>разница в том что это obj а не lib, namespace глобальный, AddCompressionMethod из другого obj. думаю что причина именно в первом — к lib линкер относится более агрессивно


да, здесь все ясно.


BZ>как я понимаю, библиотека рассматривается как набор всякого мусора, из которого следует включить только то, что непосредственно используется программой, и никакого способа принудительно включить все obj из библиотеки у нас нет


грусть-засада. Придется общий сортир добавлять видимо.
Re[3]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 13:29
Оценка:
Здравствуйте, 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 будет возвращаться
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 13:32
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:


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 чертей!
Re[4]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 13:36
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:




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(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.

Re[6]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 13:42
Оценка: +1
Здравствуйте, df, Вы писали:

df>почему вообще /OPT:NOREF не работает так как от него ждешь....

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

по идее вещей, obj-файл — это неотъемлемая часть программы, исключение части его кода — уже рассматривается как оптимизация, которая не должна ничего менять (вероятно поэтмоу у меня инициализхация и срабатывает, поскольку она модифицирует глобальную переменную). opt:noref — это способ отключить оптимизацию, если она всё же начудила, и вернуть всё на свои места

библиотека — это набор полезных артефактов, которые могут пригодиться в программе. по умолчанию, пока на них нет ссылок, они в программу включаться не должны. соответственно opt:[no]ref влияет только на оптимизацию obj-файлов, но ничего не меняет для lib. что собственно логично — представь что ты внезапно получил бы все msvc*.lib включёнными в свой exe
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: static lib. force linkage
От: BulatZiganshin  
Дата: 26.03.15 13:44
Оценка:
Здравствуйте, df, Вы писали:

df>Если добавить в classA123.cpp любую void f(){}; и соответственно вызвать ее в любом модуле ехе проекта, то опять все работает, но это не выход, конечно.


да в том-то и дело, что я делаю иначе!
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: static lib. force linkage
От: uzhas Ниоткуда  
Дата: 26.03.15 14:02
Оценка: 12 (1)
Здравствуйте, BulatZiganshin, Вы писали:

BZ>т.е. по идее вещей моя статпеременная вместе со всей своей инициализацией должна элиминироваться?


если ты явно линкуешь .obj, то статпеременную линкер не выкинет имхо
ссылка с аналогичным вопросом: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2aa2e1b7-6677-4986-99cc-62f463c94ef3/linkexe-bug-optnoref-option-doesnt-work?forum=vclanguage
Re[6]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 14:20
Оценка:
Здравствуйте, uzhas, Вы писали:


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


оттуда:

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.


охренеть..
Re: static lib. force linkage
От: ArtDenis Россия  
Дата: 26.03.15 14:30
Оценка:
Здравствуйте, df, Вы писали:

df>Привет всем.


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


...
auto __declspec(dllexport) ClassA123_registered = Repository::Instance().Register(p);


Похожая практика применяется в boost.serialization
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: static lib. force linkage
От: df Россия  
Дата: 26.03.15 15:21
Оценка:
Здравствуйте, ArtDenis, Вы писали:


AD>
AD>...
AD>auto __declspec(dllexport) ClassA123_registered = Repository::Instance().Register(p);
AD>


AD>Похожая практика применяется в boost.serialization


не вышло.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.