Статическая карта, используемая до ее инициализации - PullRequest
0 голосов
/ 16 октября 2018

Я пытаюсь реализовать фабричный шаблон, такой как this .

. Проблема сейчас в том, что программа завершается с segfault в функции регистра, потому что карта еще не инициализирована.

// initialise the registered names map
std::map<std::string, factoryMethod> SourceFactory::registeredClasses_ = { };

bool SourceFactory::Register(std::string name, factoryMethod createMethod) {
    // registeredClasses_ = { }; // This prevents the segfault but does not work for obvious reasons
    auto temp = std::make_pair(name.c_str(), createMethod);

    std::pair<std::map<std::string, factoryMethod>::iterator, bool> registeredPair =
            SourceFactory::registeredClasses_.insert(temp);

    return registeredPair.second;
}

Почему можно вызывать Register() до инициализации карты?Я попытался инициализировать карту в заголовочном файле, но затем я получил ошибку компоновщика

множественное определение SourceFactory :: selectedClasses_

Решением было бы установить статический булisInitialized=false и затем инициализируйте карту соответственно.Но я надеюсь, что этого можно избежать.

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Это распространенная проблема, известная как статический порядок инициализации fiasco .

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

Вместо того, чтобы ваш контейнер находился в области имен, сделайте его статическим (возможно, возвращенным из новогоGetRegistry() функция?) Так что она построена при первом использовании.Это может быть использование из main, использование из инициализации другой «вещи» статической длительности (вероятно, откуда поступает ваш вызов Register), использование с Луны…

Это также почемуправильный способ написать одиночный код - это иметь GetInstance() функцию, которая объявляет (static ly!) экземпляр в пределах области действия функции.

Решение будетустановить статическое значение bool isInitialized = false и затем соответствующим образом инициализировать карту.Но я надеюсь, что этого можно избежать.

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

0 голосов
/ 16 октября 2018

Возможно, когда Register вызывается из другого модуля перевода до инициализации реестра.
К сожалению, добавление статического флага не решит ничего, потому что это тоже не будет инициализировано.

Удобным решением является добавление уровня косвенности:

// static
std::map<std::string, factoryMethod>& SourceFactory::registry()
{
    static std::map<std::string, factoryMethod> registeredClasses;
    return registeredClasses;
}

bool SourceFactory::Register(const std::string& name, factoryMethod createMethod) {
    auto temp = std::make_pair(name, createMethod);
    return registry().insert(temp).second;
}
...