пытается вызвать инициализацию статического объекта - PullRequest
6 голосов
/ 04 марта 2012

Я пытаюсь инициализировать статический объект без успеха. Цель состоит в том, чтобы автоматически зарегистрировать фабричный класс в репозитории (который является одноэлементным).

Я уже посмотрел на: Как принудительно инициализировать статический элемент?

В одном из комментариев говорится, что (есть также пример, которому я следовал):

Я прочитал это в стандарте C ++ (14.7.1): если только член шаблона класса или шаблон члена не был явно создан или явно специализирован, специализация члена создается неявно, когда на специализацию ссылаются в контекст, который требует, чтобы определение члена существовало; в частности, инициализация (и любые связанные побочные эффекты) элемента статических данных не происходит, если только сам элемент статических данных не используется таким образом, который требует определения элемента статических данных.

Итак, я пытаюсь сделать что-то подобное, но мне не удалось вызвать инициализацию объекта. Вот код Я не знаю, что мне не хватает. Это шаблон, который я использую.

namespace my_lib
{
    template <typename T>
    struct FactoryHelper
    {
        FactoryHelper ();
        static FactoryHelper<T> _helper;
    };
}

И это макрос, который пользователь библиотеки будет использовать для определения фабричного класса и одновременно регистрации объекта в репозитории:

#define CREATE_FACTORY(ClassName)\
namespace my_lib\
{\
    class ClassName##Factory;\
    template<> FactoryHelper<ClassName##Factory>::FactoryHelper () { std::cout << "object initialized!" << std::endl; }\
    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\
    struct ClassName##Factory : public FactoryBase<ClassName> {\
      ...\
    };\
} 

Предыдущий код определен в заголовочном файле (Factory.h).

В файле .cpp (Example.cpp) у меня есть:

CREATE_FACTORY(UnitTestExample)
...

Когда я запускаю программу, я не вижу сообщения, которое конструктор печатает, когда она вызывается. Любая помощь приветствуется.

Заранее спасибо.

Ответы [ 4 ]

4 голосов
/ 04 марта 2012

Это сложная область C ++. Что вы сделали, так это попытались определить статический член здесь:

template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\

но на самом деле это декларация, а не определение. Чтобы C ++ рассматривал это как определение, вы должны передать что-то конструктору. Как правило, это значение, которое вы хотите инициализировать:

template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\

Но в вашем случае вы хотите, чтобы это был одноэлементный файл, поэтому вы, вероятно, не хотите, чтобы он был копируемым. В этом случае вам понадобится фиктивный параметр:

template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\

и вы должны соответствующим образом изменить конструктор:

template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\

Вот полный рабочий пример:

#include <iostream>

namespace my_lib
{
    template<typename> struct FactoryBase { };
    template <typename T>
    struct FactoryHelper
    {
        FactoryHelper (int);
        static FactoryHelper<T> _helper;
    };
}

#define CREATE_FACTORY(ClassName)\
namespace my_lib\
{\
    class ClassName##Factory;\
    template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\
    template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
    struct ClassName##Factory : public FactoryBase<ClassName> {\
    };\
} 

struct UnitTestExample {
};

CREATE_FACTORY(UnitTestExample);

int main(int argc,char **argv)
{
  return 0;
}

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

Более подробную информацию о явном объявлении специализации и определении можно найти здесь: Инициализация статического члена для специализированного шаблонного класса

1 голос
/ 04 марта 2012

Что делает ваш макрос, так это объявляет специализации некоторых членов класса. Это не создаст какой-либо объект и, вероятно, не то, что вы действительно хотите в любом случае. То, что вам нужно, это определение FactoryHelper<SomeClass>::_helper где-то. Определение статического члена будет выглядеть примерно так:

FactoryHelper<foo> FactoryHelper<foo>::_helper;

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

Вот как бы я это сделал:

template <typename T>
struct factory_helper
{
    std::auto_ptr<base> create_fuction() { return std::auto_ptr<base>(new T()); }
    factory_helper(std::string const& name) {
        factory.register_class(name, create_function);
    }
};

Предполагается, что вы хотите создать объекты, производные от типа base, и что ваша фабрика использует сопоставление с функциональным объектом, возвращающим std::auto_ptr<base> в качестве функций конструктора, и что она имеет функцию register_class(), которая принимает имя и конструктор функционировать как параметры. Однако ни одно из этих предположений не присуще оценке: это просто для того, чтобы заполнить некоторые пробелы, которые вы не упомянули. Вы бы зарегистрировали фабричную функцию для класса foo примерно так:

static factor_helper<foo> foo_helper("foo");
0 голосов
/ 04 марта 2012

Ну, во-первых, большое спасибо за предложения и объяснения.Я добавил решения, которые вы мне дали, в код и не работал.Тогда я попробовал ваши решения как самостоятельные программы и работал.

Разница в том, что реализуемые мной классы компилируются, а затем связываются с исполняемым файлом как статические библиотеки.Если я собираю код все вместе (без использования статических библиотек), то это работает.

Я нашел ответ здесь: Статическая инициализация и уничтожение глобальных объектов статической библиотеки, не происходящих с g ++

.o файлы не связаны, если на них нет ссылок изОсновное применение.Я использовал опцию ld -Wl,--whole-archive, и теперь она работает.

-Wl,--whole-archive -lmy_static_library ... -Wl,--no-whole-archive

Относительно второго вопроса, я до сих пор не понимаю, почему я должен указать фиктивный параметр в конструкторе.

template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\

Вместо этого:

emplate<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\

Спасибо!

0 голосов
/ 04 марта 2012

Вместо статического члена (вы должны где-то его создать) рассмотрите возможность статической переменной в статическую функцию, например

namespace my_lib
{
    template <typename T>
    struct FactoryHelper
    {
        FactoryHelper () { ... };
        static FactoryHelper<T>& helper()
        { static FactoryHelper<T> h; return h; }
    };
}

Не так, как вы просите, но нет необходимости во внеполосной инициализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...