C++ C++;

C++ C++;,c++,c++11,callback,rpc,variadic-functions,C++,C++11,Callback,Rpc,Variadic Functions,我需要在带有参数的函数列表中注册如下函数 void func1( int a , char* b ) {} void func2( vec3f a , std::vector<float> b , double c) {} ... 解决这个问题最优雅的方法是什么? 我知道std::bind/std::function具有类似的功能,但我认为内部数据隐藏在std的深处。我只需要一些基本的数据类型,不必是任意类型的。如果预处理器使用##VA#u参数或使用模板来解决问题,我也同意这一点。

我需要在带有参数的函数列表中注册如下函数

void func1( int a , char* b ) {}
void func2( vec3f a , std::vector<float> b , double c) {}
...
解决这个问题最优雅的方法是什么? 我知道std::bind/std::function具有类似的功能,但我认为内部数据隐藏在std的深处。我只需要一些基本的数据类型,不必是任意类型的。如果预处理器使用##VA#u参数或使用模板来解决问题,我也同意这一点。优先考虑的是它最易于使用


Edit1:我发现assembly可以解决()问题,但我更喜欢平台无关的解决方案

如果您的目标是创建您自己的小型临时“rpc”解决方案,那么决策的主要驱动因素之一可能是:1。代码2的最小数量。尽量简单

记住这一点,思考以下两种场景之间的区别是值得的:

  • “真实”RPC:处理程序应与您使用RPC方法特定签名编写的一样

  • “消息传递”:处理程序接收“端点确定类型”或简单的统一消息类型的消息

  • 现在,需要做什么才能得到类型1的解决方案

    传入的字节流/网络数据包需要被解析为与所选协议相关的某种消息。然后,根据{serviceContract,serviceMethod},使用一些元信息(契约),需要在数据包中确认一组特定的数据项,如果存在,则需要调用相应的已注册处理程序函数。在该基础架构中的某个地方,您通常有一个(可能是代码生成的)函数,该函数执行以下操作:

    void CallHandlerForRpcXYCallFoo( const RpcMessage*message )
    {
         uint32_t arg0 = message->getAsUint32(0);
         // ...
         float argN = message->getAsFloat(N);
         Foo( arg0, arg1, ... argN );
    }
    
    当然,所有这些都可以打包到类和虚拟方法中,这些类是从服务契约元数据生成的。也许,还有一种方法是通过一些过度的模板巫术来避免生成代码和使用更通用的元实现。但是,所有这些都是工作,真正的工作。工作太多了,不能只是为了好玩。与其这样做,不如使用已有的几十种技术中的一种

    到目前为止值得注意的是:在这件艺术品的某个地方,可能有一个(代码生成的)函数,看起来像上面给出的函数

    现在,需要做什么才能得到类型2的解决方案

    小于案例1。为什么?因为您只需在调用这些处理程序方法时停止实现,这些方法都将
    RpcMessage
    作为它们的单个参数。因此,您不需要在这些方法之上生成“make-it-look-like-a-function-call”层就可以离开

    它不仅减少了工作量,而且在合同发生变化的某些情况下,它也更加健壮。如果要向“rpc解决方案”添加更多数据项,“rpc函数”的签名必须更改。重新生成代码,调整应用程序代码。无论应用程序是否需要该新数据项。另一方面,在方法2中,代码中没有破坏性的更改。当然,根据你的选择和合同中的变更类型,合同仍然会破裂

    因此,最优雅的解决方案是:不做RPC,做消息传递。最好是在某种程度上

    此外,如果您更喜欢“统一”的rpc消息而不是许多rpc合约特定的消息类型,那么您可以删除代码膨胀的另一个原因

    以防万一,我说的似乎有点太抽象了,这里有一些模拟虚拟代码,草图解决方案2:

    #include <cstdio>
    #include <cstdint>
    #include <map>
    #include <vector>
    #include <deque>
    #include <functional>
    
    // "rpc" infrastructure (could be an API for a dll or a lib or so:
    
    // Just one way to do it. Somehow, your various data types need 
    // to be handled/represented.
    class RpcVariant
    {
    public:
        enum class VariantType
        {
            RVT_EMPTY,
            RVT_UINT,
            RVT_SINT,
            RVT_FLOAT32,
            RVT_BYTES
        };
    private:
        VariantType m_type;
        uint64_t m_uintValue;
        int64_t m_intValue;
        float m_floatValue;
        std::vector<uint8_t> m_bytesValue;
    
        explicit RpcVariant(VariantType type)
            : m_type(type)
        {
    
        }
    public:
        static RpcVariant MakeEmpty()
        {
            RpcVariant result(VariantType::RVT_EMPTY);
            return result;
        }
        static RpcVariant MakeUint(uint64_t value)
        {
            RpcVariant result(VariantType::RVT_UINT);
            result.m_uintValue = value;
            return result;
        }
        // ... More make-functions
    
        uint64_t AsUint() const
        {
            // TODO: check if correct type...
            return m_uintValue;
        }
        // ... More AsXXX() functions
    
        // ... Some ToWire()/FromWire() functions...
    };
    typedef std::map<uint32_t, RpcVariant> RpcMessage_t;
    typedef std::function<void(const RpcMessage_t *)> RpcHandler_t;
    
    void RpcInit();
    void RpcUninit();
    
    // application writes handlers and registers them with the infrastructure. 
    // rpc_context_id can be anything opportune - chose uint32_t, here.
    // could as well be a string or a pair of values (service,method) or whatever.
    void RpcRegisterHandler(uint32_t rpc_context_id, RpcHandler_t handler);
    
    // Then according to taste/style preferences some receive function which uses the registered information and dispatches to the handlers...
    void RpcReceive();
    void RpcBeginReceive();
    void RpcEndReceive();
    
    // maybe some sending, too...
    void RpcSend(uint32_t rpc_context_id, const RpcMessage_t * message);
    
    int main(int argc, const char * argv[])
    {
        RpcInit();
        RpcRegisterHandler(42, [](const RpcMessage_t *message) { puts("message type 42 received."); });
        RpcRegisterHandler(43, [](const RpcMessage_t *message) { puts("message type 43 received."); });
        while (true)
        {
            RpcReceive();
        }
        RpcUninit();
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    //“rpc”基础结构(可以是dll或lib的API等:
    //只有一种方法。不知何故,您的各种数据类型需要
    //被处理/代表。
    类rpc变量
    {
    公众:
    枚举类VariantType
    {
    RVT_为空,
    RVT_UINT,
    RVT_SINT,
    RVT_浮动32,
    RVT_字节
    };
    私人:
    变量类型m_类型;
    uint64μmμtμt值;
    int64_t m_intValue;
    浮动m_浮动值;
    std::向量m_字节值;
    显式RpcVariant(VariantType类型)
    :m_类型(类型)
    {
    }
    公众:
    静态rpc变量MakeEmpty()
    {
    RPC变量结果(VariantType::RVT_EMPTY);
    返回结果;
    }
    静态RPC变量生成(uint64_t值)
    {
    RPC变异结果(变异类型::RVT_UINT);
    result.m_uintValue=值;
    返回结果;
    }
    //…更多的功能
    uint64\u t AsUint()常量
    {
    //TODO:检查类型是否正确。。。
    返回m_uintValue;
    }
    //…更多AsXXX()函数
    //…一些ToWire()/FromWire()函数。。。
    };
    typedef std::map RpcMessage\u t;
    typedef std::函数RpcHandler\u t;
    void RpcInit();
    void RpcUninit();
    //应用程序编写处理程序并向基础结构注册它们。
    //rpc_context_id可以是任何合适的-在这里选择uint32_t。
    //也可以是一个字符串或一对值(服务、方法)或其他任何内容。
    void RpcRegisterHandler(uint32\u t rpc\u context\u id,RpcHandler\u t handler);
    //然后根据口味/风格偏好,一些接收函数使用注册的信息并发送给处理程序。。。
    void RpcReceive();
    void RpcBeginReceive();
    void RpcEndReceive();
    //也许也会有人送。。。
    void RpcSend(uint32_t rpc_context_id,const RpcMessage_t*消息);
    int main(int argc,const char*argv[]
    {
    RpcInit();
    RpcRegisterHandler(42,[](const RpcMessage_t*message){puts(“接收到的消息类型42”);});
    RpcRegisterHandler(43,[](const RpcMessage_t*message){puts(“消息类型43 received.”);});
    while(true)
    {
    RpcReceive();
    }
    RpcUninit();
    返回0;
    }
    
    如果
    RpcMessage
    被交易,当打包在std::shared_ptr中时,您甚至可以有多个处理程序或转发(到其他线程)相同的消息实例。这是一件特别烦人的事情,需要
    #include <cstdio>
    #include <cstdint>
    #include <map>
    #include <vector>
    #include <deque>
    #include <functional>
    
    // "rpc" infrastructure (could be an API for a dll or a lib or so:
    
    // Just one way to do it. Somehow, your various data types need 
    // to be handled/represented.
    class RpcVariant
    {
    public:
        enum class VariantType
        {
            RVT_EMPTY,
            RVT_UINT,
            RVT_SINT,
            RVT_FLOAT32,
            RVT_BYTES
        };
    private:
        VariantType m_type;
        uint64_t m_uintValue;
        int64_t m_intValue;
        float m_floatValue;
        std::vector<uint8_t> m_bytesValue;
    
        explicit RpcVariant(VariantType type)
            : m_type(type)
        {
    
        }
    public:
        static RpcVariant MakeEmpty()
        {
            RpcVariant result(VariantType::RVT_EMPTY);
            return result;
        }
        static RpcVariant MakeUint(uint64_t value)
        {
            RpcVariant result(VariantType::RVT_UINT);
            result.m_uintValue = value;
            return result;
        }
        // ... More make-functions
    
        uint64_t AsUint() const
        {
            // TODO: check if correct type...
            return m_uintValue;
        }
        // ... More AsXXX() functions
    
        // ... Some ToWire()/FromWire() functions...
    };
    typedef std::map<uint32_t, RpcVariant> RpcMessage_t;
    typedef std::function<void(const RpcMessage_t *)> RpcHandler_t;
    
    void RpcInit();
    void RpcUninit();
    
    // application writes handlers and registers them with the infrastructure. 
    // rpc_context_id can be anything opportune - chose uint32_t, here.
    // could as well be a string or a pair of values (service,method) or whatever.
    void RpcRegisterHandler(uint32_t rpc_context_id, RpcHandler_t handler);
    
    // Then according to taste/style preferences some receive function which uses the registered information and dispatches to the handlers...
    void RpcReceive();
    void RpcBeginReceive();
    void RpcEndReceive();
    
    // maybe some sending, too...
    void RpcSend(uint32_t rpc_context_id, const RpcMessage_t * message);
    
    int main(int argc, const char * argv[])
    {
        RpcInit();
        RpcRegisterHandler(42, [](const RpcMessage_t *message) { puts("message type 42 received."); });
        RpcRegisterHandler(43, [](const RpcMessage_t *message) { puts("message type 43 received."); });
        while (true)
        {
            RpcReceive();
        }
        RpcUninit();
        return 0;
    }