我有一个有点讨厌的设置,我试图找到一个很好的方法来进行大修。
我有一个class Fractal
,有几个纯虚函数可以正常工作。每个实例都有一个人类可读的名称。我想构建一个所有这些子类的菜单,以便用户可以在它们之间切换 - 但是,懒惰,我想避免在源文件中定义每个实例并在另一个中再次列出它们。换句话说,我想在运行时动态地构建这个子类列表。
到目前为止,我所做的(并且有效)是:
class FractalRegistry
,它是std::map<std::string,Fractal*>
的单例封装(其中键是实例名称),Fractal
构造函数按名称向该注册表注册每个实例(为了完整性,基础~Fractal
注销它们),因此,对于每个新的分形,我都写了这样的东西(释义):
class SomeFractal : public Fractal {
public:
SomeFractal(std::string name, std::string desc) : Fractal(name,desc) {}
virtual void work_function(...) { ... }
}
SomeFractal sf_instance("Some fractal", "This is some fractal or other");
并且实例由基类构造函数添加到中央列表中,因此我不需要自己列出它。
然而,这会导致周围存在大量静态实例,如果我将此代码移动到库中,这些实例似乎会消失。 (是的,我可以为每个编译单元添加一个可怕的空函数,因此我可以强制它包含,或者使用链接器技巧,例如-Wl,--whole-archive
,但这些似乎也不是正确答案。)
有更好的方法吗?我想我正在寻找的是一种编写这些Fractal实现的方法 - 所有这些实现都具有相同的接口,因此我认为基类的子类是理想的 - 并且保留并填充它们的中央注册表但不留下我自己是静态实例的地雷。
我错过了什么? 我应该说我已经和C一起工作了很多年但是并没有真正拥有C ++的禅,所以很可能会有一些东西可以帮助我盯着我...(如果我写的是C我会考虑编写一个二阶宏复合体,它既声明了一些函数指针,又用它们和分形符的名称和描述填充了一个表,但这是一个更加Cish的事情,它看起来似乎不对对于C ++。)
修改 我希望实现的是重新排列代码的优雅方式,这样可以轻松添加新的分形类型并自动填充它们的中心列表,但不会强制程序员创建每个分形的静态实例。
答案 0 :(得分:1)
回想一下,静态库是pre-C ++技术,因此假设未引用的代码是不需要的(因为当一个人没有玩这个特殊技巧时,它仍然在C ++中)并不是不合理的。(
因此,您需要为每个需要它们的可执行文件显式指定目标文件,假设您不想探索涉及共享库,插件等的更复杂的方法。这不应该是繁重的:您必须已经有一个进入库的对象列表,因此不是构建库而是将该列表添加到可执行文件的链接器命令行。
答案 1 :(得分:1)
您需要在应用启动期间创建某种对象来处理Fractals的注册。通常当我需要这样做时,我创建一个单独的Registrar
类,它处理类型与工厂的注册,并在工厂应该知道的每种类型的cpp文件中创建一个实例。
<强> registrar.h 强>
class Registrar
{
Registrar(const std::string& name,
const std::string& desc,
Fractal*(*creator)())
{
FractalFactory::register(name, desc, creator);
}
};
#define REGISTER_FRACTAL(name, desc, type) \
namespace { \
Fractal *create##type() { \
return new type(); \
} \
Registrar register##type(name, desc, create##type); \
}
<强> myfractal.cpp 强>
class MyFractal : public Fractal
{
// fractal code
};
REGISTER_FRACTAL("MyFractal", "Creates a cool pattern", MyFractal);