在文件范围内使用具有副作用的 C++ 函数,访问单例
C++ Function with side-effect used at file scope, accesses singleton
我用下面的静态方法写了一个class:
MyMap& Manager::GetMap( void )
{
static MyMap* factories = new MyMap();
return ( *factories );
}
其中 "MyMap" 是一个类型定义:
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();
您不能在文件范围内调用函数,但您可以将函数的 return 值分配给全局变量,即使该函数有副作用。因此,在使用 "Manager" 时,"MyMap" 将包含 string/function 对,用于 "Base" 的各种派生类型(到目前为止)。目的是 "Base" 的新派生类型向 "Manager" 注册自己,能够构造该类型的实例和 select 哪个类型基于名称。
我想知道这是否代表安全行为and/or是否有替代实现来获得预期效果?
我已经知道这篇文章提出了一个通用注册对象,该对象在其构造函数中采用上述对并进行注册,然后为每个 class 定义一个静态实例已注册。
原理没问题
您可能需要考虑的一些事项:
返回原始指针是个坏主意 - 请改用 unique_ptr。
您真的希望 Dependency& 引用是非常量吗?
隐藏内部实现。用户无需知道(或关心)它是 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;
}
我用下面的静态方法写了一个class:
MyMap& Manager::GetMap( void )
{
static MyMap* factories = new MyMap();
return ( *factories );
}
其中 "MyMap" 是一个类型定义:
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();
您不能在文件范围内调用函数,但您可以将函数的 return 值分配给全局变量,即使该函数有副作用。因此,在使用 "Manager" 时,"MyMap" 将包含 string/function 对,用于 "Base" 的各种派生类型(到目前为止)。目的是 "Base" 的新派生类型向 "Manager" 注册自己,能够构造该类型的实例和 select 哪个类型基于名称。
我想知道这是否代表安全行为and/or是否有替代实现来获得预期效果?
我已经知道这篇文章提出了一个通用注册对象,该对象在其构造函数中采用上述对并进行注册,然后为每个 class 定义一个静态实例已注册。
原理没问题
您可能需要考虑的一些事项:
返回原始指针是个坏主意 - 请改用 unique_ptr。
您真的希望 Dependency& 引用是非常量吗?
隐藏内部实现。用户无需知道(或关心)它是 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;
}