C++ 如何使用CRTP自注册类实例?
我有一个与特定的C++ 如何使用CRTP自注册类实例?,c++,c++11,visual-studio-2017,template-meta-programming,crtp,C++,C++11,Visual Studio 2017,Template Meta Programming,Crtp,我有一个与特定的CommandId关联的lambda函数注册表,其中注册的函数应该创建命令执行器类的具体实例,并向其提供一些数据: 命令ID.h 我认为访问base的静态注册表adder成员会迫使编译器提供一个实例化,但事实并非如此 下面是对Command1和Command2 <h3>Command1.h</h3> #include "CommandBase.h" class Command1 : public CommandBase<Command1, Com
CommandId
关联的lambda函数注册表,其中注册的函数应该创建命令执行器类的具体实例,并向其提供一些数据:
命令ID.h
我认为访问base的静态注册表adder
成员会迫使编译器提供一个实例化,但事实并非如此
下面是对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_;
};
在中,构造函数将强制实例化基类::registryAdder
静态
基类成员。但显然不是,编译器只是将其剥离:
#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;
}
现在我的问题是: 如何强制编译器从
CommandBase
模板类实例化那些static
基类成员?
我当然做了一些研究,但这些问答并没有真正让我满意:
- 。。。也许更多
#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;
}
#包括
枚举类CommandId:uint16\t{
命令1,
命令2,
};
#包括
#包括
#包括
//#包括“CommandId.h”
类注册表
{
公众:
静态标准::映射和
GetCommands(){
静态std::map
职能登记处;
返回函数注册表;
}
};
//#包括“CommandId.h”
//#包括“Registry.h”
//通用命令执行界面
结构ICommand{
虚空执行(const std::string&data)=0;
虚拟~ICommand(){}
};
模板
类CommandBase:公共ICommand
{
公众:
虚拟~CommandBase(){}
无效寄存器(){}
受保护的:
//为了避免抽象实例化,伪函数可能会抛出
void Execute(const std::string&data)重写{
}
//用于具体命令类的受保护构造函数
CommandBase(int&derivedRef):derivedRef(&derivedRef){}
//应自动执行注册的静态成员
静态命令基寄存器加法器;
//此构造函数将从上述静态成员访问
//仅实例化,只需注册lambda函数
CommandBase():derivedRef_389;(nullptr){
//注册使用具体派生实例的lambda函数
注册表::GetCommands()[CmdId]=[](常量std::字符串和数据){
派生实例;
CommandBase*der=static_cast(&derivedInstance);
//操作派生实例数据并执行
*(der->derivedRef)=42;
命令->执行(数据);
};
}
//提供对派生类实例数据成员的访问并允许操作
int*derivedRef;
};
模板
CommandBase CommandBase::registryAdder;
//#包括“CommandBase.h”
类Command1:公共CommandBase
{
公众:
typedef命令基类;
好友类命令库;
公众:
Command1(CommandId){
基类::registryAdder_uz.Register();
}
virtual~Command1()重写{}
私人:
Command1():基类(成员)
{
基类::registryAdder_uz.Register();
}
void Execute(const std::string&data)重写{
}
私人:
国际会员组织;
};
//#包括“CommandBase.h”
类Command2:公共CommandBase
{
公众:
typedef命令基类;
好友类命令库;
公众:
命令2(命令ID){
基类::registryAdder_uz.Register();
}
virtual~Command2()重写{}
私人:
Command2():基类(成员)
{
基类::registryAdder_uz.Register();
}
void Execute(const std::string&data)重写{
}
私人:
国际会员组织;
};
#包括
//#包括“Command1.h”
//#包括“Command2.h”
//#包括“Registry.h”
int main()
{
std::我可以通过GitHub gist或其他渠道按需共享完整的VS2017项目吗?为什么每次有人调用GetCommands
,你都会返回一份空的静态映射副本?这有什么用?我想这会导致你每次问都说有0个命令,这可以解释你的代码。@yack THX指出,当我从头开始重新创建MCVE时,这只是一个打字错误。发布实际的s,而不是看起来有点像的随机代码。请生成一个指向在线编译器的链接,该编译器编译您的代码并生成您的症状,因为您看起来像MCVE的代码显然不是您实际运行和检查它的证据。@πάνταῥεῖ 您似乎很确定,但是类Command2
中的行Command12CommandId){
和虚拟析构函数~Command1()
却有所不同。
<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_;
};
#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();
#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.
#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;
}