C++ 删除虚拟呼叫

C++ 删除虚拟呼叫,c++,templates,c++11,template-meta-programming,C++,Templates,C++11,Template Meta Programming,数据以以下形式从给定通道到达: void DispatchIncomingChannelData(uint8_t const typeId, void * payload, uint32_t const payloadSize); 有效载荷可能有几种类型: struct PayloadA { /* Various Data */ }; struct Paylo

数据以以下形式从给定通道到达:

void DispatchIncomingChannelData(uint8_t const typeId, 
                                 void * payload, 
                                 uint32_t const payloadSize);
有效载荷可能有几种类型:

struct PayloadA { /* Various Data */ };
struct PayloadB { /* Various Data */ };
struct PayloadC { /* Various Data */ };
// Other PODs... 
然后发送到相应的处理程序:

void ProcessPayload_A(PayloadA * payload) { /* PayloadA code */ }
void ProcessPayload_B(PayloadB * payload) { /* PayloadB code */ }
void ProcessPayload_C(PayloadC * payload) { /* PayloadC code */ }
没有模板,你可以简单地提供一个开关,然后扔掉!有了一个模板,我开始做如下工作:

struct BasePayloadProcessor abstract
{
public:
    virtual void ProcessPayload(void * const payload, uint32_t const payloadSize) = 0;
};

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

template<typename T>
struct TypedPayloadProcessor : BasePayloadProcessor
{
public:
    using PayloadHandler = void (*)(T * message);

    TypedPayloadProcessor(uint8_t const typeId, PayloadHandler payloadHandler) :
        _payloadHandler { payloadHandler },
        _type { typeid(T) },
        _typeId { typeId }
        { }

    virtual void ProcessPayload(void * const payload, uint32_t const payloadSize) override
    {
        ASSERT(_payloadHandler);
        ASSERT(payloadSize == sizeof(T));
        T * t = reinterpret_cast<T *>(payload);
        _payloadHandler(t);
    }

private:
    PayloadHandler _payloadHandler;
    type_info const & _type;         // <-- These two members are 
    uint8_t const _typeId;           // <-- not really necessary
};
void DispatchIncomingChannelData(uint8_t const typeId, 
                                 void * payload, 
                                 uint32_t const payloadSize)
{
    ASSERT(typeId > static_cast<uint8_t>(PayloadTypes::Invalid));
    ASSERT(typeId < static_cast<uint8_t>(PayloadTypes::TotalTypes));
    payloadProcessors[typeId]->ProcessPayload(payload, payloadSize);
}
struct BasePayloadProcessor摘要
{
公众:
虚拟无效进程有效负载(无效*常量有效负载,uint32_t常量有效负载大小)=0;
};
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
模板
结构类型DpayLoadProcessor:BasePayloadProcessor
{
公众:
使用PayloadHandler=void(*)(T*消息);
TypedPayloadProcessor(uint8_t const typeId,PayloadHandler PayloadHandler):
_payloadHandler{payloadHandler},
_类型{typeid(T)},
_typeId{typeId}
{ }
虚拟无效进程有效负载(无效*常量有效负载,uint32\u t常量有效负载大小)覆盖
{
断言(_payloadHandler);
断言(payloadSize==sizeof(T));
T*T=重新解释(有效载荷);
_有效载荷处理器(t);
}
私人:
PayloadHandler\u PayloadHandler;
type_info const&_type;//进程有效负载(有效负载,有效负载大小);
}

一切正常。但是,我对这个解决方案不满意。我更愿意去掉基类,而不是保留指针数组(由于缓存线)。我相信有更好的办法。最后,我打算允许开发人员“注册”他们的类型处理程序。谢谢。

我认为使用包装器构建一个
函数数组会更容易:

using CallbackFn = std::function<void(void*, const uint32_t)>;

template <typename T>
CallbackFn make_processor(void (*func)(T*))
{
    return [=](void* payload, const uint32_t size){);
        ASSERT(payloadSize == sizeof(T));
        func(static_cast<T*>(payload));
    };
}
使用CallbackFn=std::函数;
模板
CallbackFn生成处理器(void(*func)(T*))
{
返回[=](无效*有效负载,常数32_t size){);
断言(payloadSize==sizeof(T));
func(静态广播(有效载荷));
};
}
这样,您就可以直接将已有的处理函数传递到中,让所有功能都正常工作:

std::array<CallbackFn, 
    static_cast<uint8_t>(PayloadTypes::TotalTypes)> payloadProcessors;

payloadProcessors[static_cast<uint8_t>(PayloadTypes::A)] = 
    make_processor(ProcessPayload_A);
payloadProcessors[static_cast<uint8_t>(PayloadTypes::B)] =
    make_processor(ProcessPayload_B);
payloadProcessors[static_cast<uint8_t>(PayloadTypes::C)] = 
    make_processor(ProcessPayload_C);
std::阵列付费处理器;
payloadProcessors[static_cast(PayloadTypes::A)]=
使_处理器(ProcessPayload_A);
payloadProcessors[static_cast(PayloadTypes::B)]=
制造处理器(处理有效载荷);
payloadProcessors[static_cast(PayloadTypes::C)]=
制造处理器(处理有效载荷);

使用
开关

默认值:
捕获-理想情况下是信号-不支持的类型。
它是实现您期望目标的最简单的代码。“每个人”都会理解的

它还几乎照亮了所有其他方面


与switch相比,还有哪些地方可以改进?我能想到的是:

  • 引入新的有效负载类型需要引入新的处理程序函数、该类型的新枚举,并将其添加到switch语句中。您可能忘记了添加到switch语句

  • 如果有十几个值,则很难直观地验证所有枚举和处理程序是否匹配,例如,您可能调用了错误的处理程序

最后一个问题仍然是现在发布的所有备选方案,我想不出一个聪明的模板解决方案

如果有更多的情况,我会考虑<强>代码生成< /强> . 有档案

A
B
C
可以为
struct
s和处理程序函数生成有效负载类型的枚举(转发)声明,并使用开关实现中央处理程序

向该文件添加另一个有效负载类型将破坏构建,直到您实现该处理程序,然后所有内容都将重新就位

缺点是更复杂的构建过程,以及生成代码的常见问题


**[编辑]*别误会我的意思-这个问题肯定很有趣-也许我们可以想出一个漂亮的解决方案,并且/或者从中学习一些东西。这只是我对生产的建议

我已经读了你的问题好几次了,我仍然不明白为什么使用模板意味着你不能
切换
类型ID
并分派给相应的处理程序。你当然可以。我指出,你可以完全删除模板(“没有模板,你可以简单地提供一个开关,然后扔掉”)。上面的解决方案避免了切换。这基本上相当于OP的代码,但使用标准库:-)很好。这正是我想要的。这样就去掉了基类和虚拟调用。我会坚持一点之前,标记是回答,让任何其他人插话。谢谢巴里。@杰夫:注意,
std::function
internal可能会使用类似于虚拟调用的东西。@YoungJohn这对你来说不是一个糟糕的折衷吗?我想这取决于项目。如果您有一个由许多成员组成的大型团队不断更新不同的
PayloadTypes
,那么易于维护的成本可能是值得的,特别是如果这不是性能关键代码的一个方面。发现有人在运行时未能更新
PayloadTypes::TotalTypes
,这一点都不好玩。我感谢您的回答(+1)。我从一个开关开始,但希望使解决方案更具动态性和通用性(允许用户向相应的处理程序注册其类型)。我不喜欢失败,不喜欢测试每一个案例。
A
B
C