如何使用CRTP自注册类实例? [英] How to self register class instances using the CRTP?

查看:49
本文介绍了如何使用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 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:

推荐答案

我决定根据 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;
}

一个人需要利用这些静态个实例。

One needs to make use of these static instances in the derived classes.

这篇关于如何使用CRTP自注册类实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆