C++ 虚拟模板函数最佳解决方法

问题描述 投票:0回答:1

我有一个 ComInterface 类,它有一个重载函数 send。对于许多不同的枚举类类型,此函数已重载。

class ComInterface{
public:
    virtual void send(MotorCommand cmd);
    virtual void send(ValveCommand cmd);
    virtual void send(ThrottleCommand cmd);
};

然后我有一个派生类CanCom

class CanCom: public ComInterface{
public:
    void send(uint32_t CanID, uint8_t cmd);
    virtual void send(MotorCommand cmd){
        send(1, (uint8_t) cmd); //1 is the Motor specific address
    }
    virtual void send(ValveCommand cmd){
        send(2, (uint8_t) cmd); //2 is the Valve specific address
    }
    virtual void send(ThrottleCommand cmd){
        send(3, (uint8_t) cmd); 
    }
};

这是到目前为止的代码。由于我有许多这样的枚举类(如 MotorCommand),因此我更喜欢使用模板。比如:

class ComInterface{
public:
    template <typename T>
    virtual void send(T cmd);
};

然后将使用模板变量插入特定地址,如此处建议的那样 C++ 模板函数将参数类型与整数相关联 所以类似[如链接问题中 Jarod42 提出的建议]

template <typename T>
constexpr uint32_t can_address = [](){ throw "Should be specialized"; }();
template <> constexpr uint32_t can_address<MotorCommand> = 1;
template <> constexpr uint32_t can_address<ValveCommand> = 2;
template <> constexpr uint32_t can_address<ThrottleCommand> = 3;


template <typename T> send(T cmd){
    send(can_address<T>, (uint8_t) msg);
}

但是现在我遇到了无法使用模板和虚函数的问题。经过一些研究,我认为解决这个问题的最佳方法是使用基于策略的设计。所以 ComInterface 是一个模板类,并且有类似 CanImplementation 类的东西作为模板参数传递。不幸的是,这意味着 ComInterface 的类型必须在使用它的每个地方进行调整,这在我的情况下是不可能的。

我的下一个想法是创建一个新类“TypeToIDConverter”,它将类型转换为 ID。但是,这个类必须再次派生为“TypeToCanIDConverter”,而且,由于它必须是虚拟的,因此不能使用模板。我考虑使用普通覆盖,然后使用 static_cast 将存储在 ComInterface 中的基类型转换器转换为派生类。然而,我不确定这是否有效,而且这肯定是一个丑陋的解决方案!

我发现的其他想法是类型错误和访问者原则。但是,我不确定在这里如何使用它们。

有一个优雅的解决方案吗?我不必为每个枚举类类型编写发送函数,但也可以保持接口的通用性,并为其他协议(如 LinCom 或 I2CCom)的新实现留出空间?

提前非常感谢!这个问题已经困扰我好几天了......

c++ templates virtual-functions policy-based-design
1个回答
0
投票

根据评论中的信息,我可能会做这样的事情:

namespace Motor {
    uint32_t addr() { return 1; }

    enum class Command { Start, MaxSpeed, Off };
}

namespace Throttle { 
    uint32_t addr() { return 2; }

    enum class Command { Open, Close };
}

namespace Valve { 
    uin32_t addr() { return 3; }

    enum class Command { Open = 17, Close = 13 };
}

class CanInterface {
public:
    // should probably use a Concept to assure that `Cmd` is
    // an `enum class`
    template<class Cmd>
    void send(Cmd const &command) {
        send(addr(), static_cast<uint8_t>(command));
    }
};

addr()
将通过参数相关查找找到,因此当您传递
Motor::Command
时,它会找到
Motor::addr()
,但是当您传递
Valve::Command
时,它会找到
Valve::addr()
,依此类推。

© www.soinside.com 2019 - 2024. All rights reserved.