c ++ 11 — функция C ++ с побочным эффектом, используемым в области видимости файла, доступ к синглтону

Я написал класс со следующим статическим методом:

MyMap& Manager::GetMap( void )
{
static MyMap* factories = new MyMap();

return ( *factories );
}

Где «MyMap» — это typedef для:

unordered_map<string, function<Base* ( Dependency& d )>>

Есть также множество типов, полученных из Base, например

class Derived1 : public Base
{
public:
Derived1( Dependency& d );
};

Рассмотрим следующее использование.

Я определяю следующее в файле реализации для Derived1:

#include "Derived1.h"#include "Manager.h"
int RegisterDerived1( void )
{
Manager::GetMap()["Test"] = []( Dependency& d ){ return new Derived1( d ); };

return 0;
}

int Reg = RegisterDerived1();

Вы не можете вызывать функции в области видимости файла, но вы можете присвоить возвращаемое значение функции глобальной переменной, даже если эта функция имеет побочные эффекты. Следовательно, к тому времени, когда «Менеджер» используется, «MyMap» будет содержать пары строк / функций для различных производных типов «Base» (пока). Намерение состоит в том, чтобы новые производные типы «Base» регистрировались в «Manager», способном создавать экземпляры этого типа и выбирать, какой тип основывался на имени.

Мне интересно, представляет ли это безопасное поведение и / или есть ли альтернативные реализации для получения желаемого эффекта?

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

http://accu.org/index.php/journals/597

3

Решение

Принцип в порядке.

Несколько вещей, которые вы можете рассмотреть:

  1. возвращать необработанные указатели — плохая идея — вместо этого используйте unique_ptr.

  2. Вы действительно хотите зависимость?& ссылка будет неконстантной?

  3. Скрыть внутреннюю реализацию. Пользователям не нужно знать (или заботиться), что это unordered_map.

Немного измененная версия с встроенными комментариями для рассмотрения:

#include <functional>
#include <unordered_map>
#include <memory>
#include <string>

struct Base
{
virtual ~Base() = default;
};

struct Dependency
{

};

struct Manager
{
// I notice that Depdendency& is not const. Was that what you wanted?
using factory_function = std::function<std::unique_ptr<Base> ( Dependency& d )>;

// public registration function hides internal implementation of map
static bool register_function(const std::string ident, factory_function f)
{
return GetMap().emplace(std::move(ident), std::move(f)).second;
}

// public create function hides internal implementation of map
// returns a unique_ptr - much better!
static std::unique_ptr<Base> create(const std::string& ident, Dependency& d)
{
// this will throw an exception if the factory does not exist.
// another implementation could substitute a known version of Base,
// for example. But now it's under your control and the user does
// not have to think about it.
return GetMap().at(ident)(d);
}

private:

using MyMap = std::unordered_map<std::string, factory_function>;

// private map implementation. In future we may want to add a mutex
// (in case the map can be dynamically updated?)
// so let's encapsulate
static MyMap& GetMap()
{
// no need for new here. Static variables are cleanly destructed at
// the end of the program, and initialised the first time the code
// flows over them.
static MyMap _map;
return _map;
}
};

struct Derived1 : Base
{
Derived1(Dependency&) {}
};

// now we don't need to care about Manager's implementation.
// this is better - we are decoupled.
bool derived1_registered = Manager::register_function("Derived1",
[](Dependency& d)
{
return std::make_unique<Derived1>(d);
});

int main()
{
Dependency d;
auto p = Manager::create("Derived1", d);

return 0;
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]