如何使用CRTP自注册类实例? [英] How to self register class instances using the CRTP?
问题描述
我有一个与特定 CommandId
关联的lambda函数的注册表,其中已注册的函数应该创建命令执行器类的具体实例并为其提供一些实例数据:
I have a registry for lambda functions associated with a specific CommandId
, where the registered function is supposed to create a concrete instance of a command executor class and supply it with some data:
#include <cstdint>
enum class CommandId : uint16_t {
Command1 ,
Command2 ,
};
Registry.h
Registry.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函数(因为调用派生类的工作流只是样板代码):
Now my idea was to provide concrete command implementations as derived classes from a CRTP template base, that provides a static
member of itself just for the purpose of registering an associated lambda function (because the workflow for calling the derived classes will be just boilerplate code):
#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_;
我认为访问静态registryAdder _
成员
这里是 Command1 $ c $的试用版c>和
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_;
};
Command2.h
Command2.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 _
静态
基类成员。但这显然不是,编译器只是将其剥离:
in the constructors would force the instantiation of the BaseClass::registryAdder_
static
base class member. But it obviously doesn't, and the compiler just strips it away:
#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.
现在我的问题是:
Now my question is:
如何强制编译器从 CommandBase实例化那些
静态
基类成员。 code>模板类?
How can I force the compiler to instantiate those static
base class members from the CommandBase
template class?
我当然已经做过一些研究,但这些问答; A并没有真正让我满意:
I've done some research of course, but these Q&A's didn't really satisfy me:
- 如何强制初始化静态成员?
- 强制使用静态模板成员实例
- 派生类
中的显式模板静态成员实例化 - ...甚至更多
- How to force a static member to be initialized?
- Force template static member instantiation
- Explicit template static member instantiation in a derived class
- ... and maybe more
推荐答案
我决定根据 Yakk的修复程序来自动注册类实例。似乎是一个更常见的问题(请参阅这里为例)
I decided to reopen the question and self answer based on Yakk's fixes. Seems to be a more common problem (see here for example)
Yakk的示例解决了该问题:
Yakk's example solved it:
#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;
}
一个人需要利用这些静态派生类中的code>个实例。
One needs to make use of these static
instances in the derived classes.
这篇关于如何使用CRTP自注册类实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!