LINUX.ORG.RU

[C++][Singleton][Фабрика объектов]Проблема с формированием библиотеки

 


0

0

Имеем синглтон фабрики объектов и кучу однотипных объектов. Эти объекты регистрируются в фабрике так:

namespace
{
    classA* createClass() { return new classB() };
    const bool registered = factory::instance().registerClass( "B", createClass );
}
Проблема в следующем: если все это напрямую компилируется в бинарник или динамическую библиотеку, то все нормально. Но если это скомпилировать в статическую библиотеку, то регистрации компонентов не происходит. Вопрос: почему и как это обойти.

P.S. если все объекты скомпилировать в динамическую библиотеку и самому программно ее загружать, то внутри библиотеки создается свой экземпляр фабрики и уже в ней происходит регистрация объектов. Соответственно из основной программы я их достать не могу.


Ответ на: комментарий от Torvus

Он левел свой недостаточно прокачал, не сможет тебе помоч если не расслабишся и шапочку не снимешь. Иначе придётся ждать более крутых телепатов, а они часто заняты.

ixrws ★★★
()
Ответ на: комментарий от ixrws

Ну так скажите какая еще нужна информация и я вам ее предоставлю

Torvus
() автор топика

Хорошо, сделаем так:

Файл factory.hpp

class Factory
{
public:
    typedef classA* (*Creator)();
    ~Factory()
    {
        if( instancePtr )
            delete instancePtr;
    }
    static Factory& instance()
    {
        if( !instancePtr )
            instancePtr = new Factory();
        return *instancePtr;
    }
    bool registerClass( const std::string &id, Creator cr )
    {
        return creatorMap.insert( CreatorMapType::value_type( id, cr ) ).second();
    }

    ...

private:
    Factory() : instancePtr(0) {}
    static Factory *instancePtr;
    typedef std::map< std::string, Creator > CreatorMapType;
    CreatorMapType creatorMap;
};
class classA { ... };
class classB : public classA { ... }
namespace
{
    classA* createClass() { return new classB };
    const bool registered = Factory::instance().registerClass( "B", createClass );
}
class classC : public classA { ... }
namespace
{
    classA* createClass() { return new classC };
    const bool registered = Factory::instance().registerClass( "C", createClass );
}
Проблема в следующем: если все это скомпилировать в статическую библиотеку и подключить к основному проекту, то автоматической регистрации классов classB и classC в фабрике Factory не происходит. А вот с динамической библиотекой все нормально. Чем это объяснить?

Torvus
() автор топика
Ответ на: комментарий от dimon555

если вам нечего сказать по-существу, то проходим мимо и не задерживаемся.

Torvus
() автор топика
Ответ на: комментарий от dilmah

Это значит, что я объявил класс classB как наследника класса classA. А затем объявил безымянную область видимости, где и объявил глобальную переменную registered, которая, по-идее должна инициализироваться до начала самой программы. Как-то так.

P.S. s/public classA{ ... }/public classA{ ... };/

Torvus
() автор топика

Похоже на косяк с созданием глобальных/статических объектов (он не определён стандартом). В действительности порядок вызова конструкторов у таких объектов часто зависит от порядки линковки объектников.

Каким компилятором пользуешься?

anonymous
()
Ответ на: комментарий от Torvus

При статической компоновке компонуются только используемые символы. Если registered не используется - он не компонуется.

anonymous
()
Ответ на: комментарий от Torvus

void do_register_stuff(void) __attribute__((constructor));
static void do_register_stuff(void)
{

/* stuff */

}

anonymous
()

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

QLibrary lib("mylibrary");
lib.load(); // тут создается фабрика и регистрируются все классы
...
при последующем вызове Factory::instance() создается новый экземпляр фабрики.

Torvus
() автор топика
Ответ на: комментарий от Divius

думаете, если бы у меня был выбор, я бы сидел на этом говне мамонта?

Torvus
() автор топика
Ответ на: комментарий от Torvus

> Хорошо. Тогда что можете предложить для автоматической регистрации классов в фабрике? Статическую компоновку делать через условную компиляцию и регистрировать все явно. Правда, сходу, не скажу как это все автоматизировать. Тем более не известна структура проекта.

anonymous
()
Ответ на: комментарий от anonymous

Тогда чем фабрика лучше тупого:

classA* getClass( const std::string &id )
{
    if( id == "B" )
        return new classB;
    else if( id == "C" )
        return new classC;
    ...
}

Torvus
() автор топика
Ответ на: комментарий от Torvus

Я уже писал выше что архитектура проекта неизвестна. Я предполагал что после регистрации создание объектов происходит клонированием. Фабрика - для создания. А приведенный вами код - для неявной регистрации в фабрике.

anonymous
()
Ответ на: комментарий от anonymous

с помощью фабрики я создаю объекты (через new). А целью является автоматизация регистрации классов в фабрике

Torvus
() автор топика
Ответ на: комментарий от Torvus

При статической компоновке, боюсь, автоматизировать не получится. При динамической - может использовать API экспортируемое приложением для получения доступа к его фабрике?

anonymous
()

Старые грабли. Переменные не экспортируются, поэтому не попадают в .exe, поэтому не инициализируются, поэтому не вызываются функции регистрации.

yatagarasu
()
Ответ на: комментарий от yz

У компилятора есть опции, в которых можно указать, какие имена надо инициализировать

Так. На этом, пожалуйста, поподробнее.

Torvus
() автор топика
Ответ на: комментарий от Torvus

Как вариант решения - можно линковать не со статической библиотекой а непосредственно с объектными файлами. Проверил - вроде работает. По поводу ключей - вряд-ли это задача компилятора, а у компоновщика я ничего похожего не нашел.

anonymous
()
Ответ на: комментарий от xydo

>учить синтаксис объявление классов немедленно.

я не копипастил сюда из реального проекта, а заново писал схожую структуру. отсюда такие ошибки и некрасивости

Torvus
() автор топика
Ответ на: комментарий от anonymous

> При статической компоновке, боюсь, автоматизировать не получится.

Получится. Я уже привёл пример, как.

Есть ещё один вариант. Вместо __attribute__((constructor)) поместить функцию в новую секцию, дальше собрать все такие секции в кучу, и при старте программы пробежаться и вызвать всё, что вам нужно. Будет работать 100%

Примеры можете посмотреть в исходниках ядра линукс (смотреть на макросы для модулей и разные initcall)

anonymous
()
Ответ на: комментарий от Torvus

Я знаю насчёт линкера из старых бин-утилс, но можешь попробовать передать ключ --whole-archive линкеру.

В любом случае, ты можешь добиться того же самого руками:

static void module_init_function(void) { /* some code */; }

#define register_initcall(fname) void* __attribute__((section(".module.initcall"))) module_init_symbol_##fname = fname;

Дальше, тебе нужно в линкере собрать всё «в кучу» и пометить начало и конец. Ну и пробежаться и вызвать всё.

anonymous
()
Ответ на: комментарий от anonymous

>нужно в линкере собрать всё «в кучу» и пометить начало и конец. Ну и пробежаться и вызвать всё

А можно поподробнее. Желательно с примерами.

Torvus
() автор топика
Ответ на: комментарий от anonymous

нашел в исходниках ядра следующее:

extern initcall_t __initcall_start, __initcall_end;
...

static void __init do_initcalls(void)
{
        initcall_t *call;

        call = &__initcall_start;
        do {
                (*call)();
                call++;
        } while (call < &__initcall_end);
        ...
}
не совсем понятно откуда берутся __initcall_start и __initcall_end.

Torvus
() автор топика
Ответ на: комментарий от Torvus

> не совсем понятно откуда берутся __initcall_start и __initcall_end.

Смотри внимательно на линковку модуля и на ld-script-ы. verbose включается при отстройке с помощью make V=1 modules

anonymous
()

Дурить оптимизирующие компиляторы, чтобы они не выкидывали «неиспользуемоые» символы, можно, к примеру, так:

if (random() % 2 > 10)
{
// делаем что-то с символом
}
MaxCom
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.