如何使用 CRTP 自注册类实例?
How to self register class instances using the CRTP?
>我有一个与特定CommandId
关联的lambda函数的注册表,其中注册的函数应该创建一个命令执行器类的具体实例并为其提供一些数据:
CommandId.h
#include <cstdint>
enum class CommandId : uint16_t {
Command1 ,
Command2 ,
};
注册表.h
#include <map>
#include <functional>
#include <string>
#include "CommandId.h"
class Registry
{
public:
static std::map<CommandId,std::function<void (std::string)>>&
GetCommands() {
static std::map < CommandId, std::function<void(std::string)>>
theFunctionRegistry;
return theFunctionRegistry;
}
};
现在我的想法是提供具体的命令实现作为来自 CRTP 模板库的派生类,它提供自身的static
成员只是为了注册关联的 lambda 函数(因为调用派生类的工作流只是样板代码):
CommandBase.h
#include "CommandId.h"
#include "Registry.h"
// The general command execution interface
struct ICommand {
virtual void Execute(const std::string& data) = 0;
virtual ~ICommand() {}
};
template<typename Derived, CommandId CmdId>
class CommandBase : public ICommand
{
public:
virtual ~CommandBase() {}
void Register() {}
protected:
// Dummy function to avoid abstract instantiations, should probably throw
void Execute(const std::string& data) override {
}
// Protected constuctor meant for concrete command classes
CommandBase(int& derivedRef) : derivedRef_(&derivedRef) {}
// The static member that should perform the registation automagically
static CommandBase<Derived, CmdId> registryAdder_;
// This constructor is meant to be accessed from the above static member
// instantiation only, and just register the lambda function
CommandBase() : derivedRef_(nullptr) {
// Register a lambda function that ususe a concrete Derived instance
Registry::GetCommands()[CmdId] = [](const std::string& data) {
Derived derivedInstance;
CommandBase<Derived, CmdId>* der = static_cast<CommandBase<Derived, CmdId>*>(&derivedInstance);
// Manipulate the derived instances data and execute
*(der->derivedRef_) = 42;
der->Execute(data);
};
}
// Provides access to the derived class instances data members and allows manipulation
int* derivedRef_;
};
template<typename Derived, CommandId CmdId>
CommandBase<Derived, CmdId> CommandBase<Derived, CmdId>::registryAdder_;
我以为访问 base 的static registryAdder_
成员会强制编译器提供实例化,但这并没有发生。
这是Command1
和Command2
的试用版
<h3>Command1.h</h3>
#include "CommandBase.h"
class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
typedef CommandBase<Command1, CommandId::Command1> BaseClass;
friend class BaseClass;
public:
Command1(CommandId) {
BaseClass::registryAdder_.Register();
}
virtual ~Command1() override {}
private:
Command1() : BaseClass(member_)
{
BaseClass::registryAdder_.Register();
}
void Execute(const std::string& data) override {
}
private:
int member_;
};
命令2.h
#include "CommandBase.h"
class Command2 : public CommandBase<Command2, CommandId::Command2>
{
public:
typedef CommandBase<Command2, CommandId::Command2> BaseClass;
friend class BaseClass;
public:
Command12CommandId) {
BaseClass::registryAdder_.Register();
}
virtual ~Command1() override {}
private:
Command2() : BaseClass(member_)
{
BaseClass::registryAdder_.Register();
}
void Execute(const std::string& data) override {
}
private:
int member_;
};
我希望这条线
BaseClass::registryAdder_.Register();
在构造函数中将强制实例化BaseClass::registryAdder_
static
基类成员。但它显然没有,编译器只是把它剥离掉:
#include <iostream>
#include "Command1.h"
#include "Command2.h"
#include "Registry.h"
int main()
{
std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
return 0;
}
输出:
There are 0 registered commands.
现在我的问题是:
如何强制编译器从CommandBase
模板类实例化这些static
基类成员?
当然,我做过一些研究,但这些问答并没有真正让我满意:
- 如何强制初始化静态成员?
- 强制模板静态成员实例化
- 派生类中的显式模板静态成员实例化
- 。也许更多
我决定根据 Yakk 的修复重新打开问题和自我回答。似乎是一个更常见的问题(例如,请参阅此处)
Yakk的例子解决了它:
#include <cstdint>
enum class CommandId : uint16_t {
Command1 ,
Command2 ,
};
#include <map>
#include <functional>
#include <string>
//#include "CommandId.h"
class Registry
{
public:
static std::map<CommandId,std::function<void (std::string)>>&
GetCommands() {
static std::map < CommandId, std::function<void(std::string)>>
theFunctionRegistry;
return theFunctionRegistry;
}
};
// #include "CommandId.h"
// #include "Registry.h"
// The general command execution interface
struct ICommand {
virtual void Execute(const std::string& data) = 0;
virtual ~ICommand() {}
};
template<typename Derived, CommandId CmdId>
class CommandBase : public ICommand
{
public:
virtual ~CommandBase() {}
void Register() {}
protected:
// Dummy function to avoid abstract instantiations, should probably throw
void Execute(const std::string& data) override {
}
// Protected constuctor meant for concrete command classes
CommandBase(int& derivedRef) : derivedRef_(&derivedRef) {}
// The static member that should perform the registation automagically
static CommandBase<Derived, CmdId> registryAdder_;
// This constructor is meant to be accessed from the above static member
// instantiation only, and just register the lambda function
CommandBase() : derivedRef_(nullptr) {
// Register a lambda function that ususe a concrete Derived instance
Registry::GetCommands()[CmdId] = [](const std::string& data) {
Derived derivedInstance;
CommandBase<Derived, CmdId>* der = static_cast<CommandBase<Derived, CmdId>*>(&derivedInstance);
// Manipulate the derived instances data and execute
*(der->derivedRef_) = 42;
der->Execute(data);
};
}
// Provides access to the derived class instances data members and allows manipulation
int* derivedRef_;
};
template<typename Derived, CommandId CmdId>
CommandBase<Derived, CmdId> CommandBase<Derived, CmdId>::registryAdder_;
// #include "CommandBase.h"
class Command1 : public CommandBase<Command1, CommandId::Command1>
{
public:
typedef CommandBase<Command1, CommandId::Command1> BaseClass;
friend class CommandBase<Command1, CommandId::Command1>;
public:
Command1(CommandId) {
BaseClass::registryAdder_.Register();
}
virtual ~Command1() override {}
private:
Command1() : BaseClass(member_)
{
BaseClass::registryAdder_.Register();
}
void Execute(const std::string& data) override {
}
private:
int member_;
};
// #include "CommandBase.h"
class Command2 : public CommandBase<Command2, CommandId::Command2>
{
public:
typedef CommandBase<Command2, CommandId::Command2> BaseClass;
friend class CommandBase<Command2, CommandId::Command2>;
public:
Command2(CommandId) {
BaseClass::registryAdder_.Register();
}
virtual ~Command2() override {}
private:
Command2() : BaseClass(member_)
{
BaseClass::registryAdder_.Register();
}
void Execute(const std::string& data) override {
}
private:
int member_;
};
#include <iostream>
//#include "Command1.h"
//#include "Command2.h"
//#include "Registry.h"
int main()
{
std::cout << "There are " << Registry::GetCommands().size() << " registered commands." << std::endl;
return 0;
}
需要在派生类中使用这些static
实例。
相关文章:
- 从C++实例化QML
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 无法将结构注册为增强几何体3D点
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 无法创建抽象类的实例
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 如何使用AngelScript注册SFML Vector2运算符
- 我收到以下错误:抛出'std::bad_alloc'实例后终止调用
- 建议在运行时将带有类实例的列表从c++导入qml
- 约束和显式模板实例化
- 通过实例理解std::move及其目的
- 如何将不同的成员函数指针分配给注册类的不同实例?
- 如何使用 CRTP 自注册类实例?
- 从C (例如Main.CPP)访问注册类型实例
- C++逻辑错误:试图为创建的所有实例生成唯一的ObjectID,计数器注册错误