У меня есть реестр для лямбда-функций, связанных с конкретным CommandId
, где зарегистрированная функция должна создать конкретный экземпляр класса исполнителя команд и предоставить ему некоторые данные:
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
только для регистрации связанной лямбда-функции (поскольку рабочий процесс для вызова производных классов будет просто шаблонным кодом) :
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_;
Я думал, что доступ к элементу 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.
Теперь мой вопрос:
Как заставить компилятор создавать экземпляры этих static
членов базового класса из CommandBase
класса шаблона?
Я, конечно, провел некоторое исследование, но эти вопросы и ответы меня не очень удовлетворили:
GetCommands
? Чем это... полезно? Я предполагаю, что это приведет к тому, что вы всегда будете говорить, что есть 0 команд каждый раз, когда вы спрашиваете, что объясняет ваш код. - person Yakk - Adam Nevraumont   schedule 09.06.2017Command12CommandId) {
и виртуальный деструктор~Command1()
в классеCommand2
начинают отличаться. - person François Andrieux   schedule 09.06.2017registryAdder_
. Мне кажется, что все, что вам нужно сделать, это закончить свою функциюCommandBase::Register
. Пусто. - person François Andrieux   schedule 09.06.2017