基于C++11的RPC参数反序列化与函数调用实现咨询
问题:基于函数签名自动实现RPC参数反序列化与调用
EDIT2:已更新底部代码及示例,用于解决最终问题(参数出现反转)
EDIT:针对「问题过多」的关闭投票,请阅读加粗部分。除参数反序列化与函数调用实现外,其余部分已有通用解决方案,仅提供背景信息。
核心需求
寻求一种简洁的参数序列化与反序列化机制,尽可能减少重复样板代码。
背景说明
我们有一款自定义ASIC,包含多个相同核心,希望为每个核心提供统一软件接口,将函数调用编组到负责特定外设的核心执行。客户端调用函数时,需封装参数并通过IPC机制发送至服务端,服务端解包后执行目标函数。
已实现的参数序列化方案
我已经实现了较为合理的参数序列化逻辑:
template<typename ... Args> auto Serialize(uint32_t FunctionID, Args ...args ) { auto args_tuple = std::tuple<Args...>(args...); send_to_IPC_Pipe(FunctionID); //foreach tuple element // send_to_IPC_Pipe(get<n>(args_tuple)); } void EntryPoint(int a, float b, bool c) { uint32_t uFunctionID = hash(__PRETTY_FUNCTION__); Serialize(uFunctionID,a,b,c); }
我意识到每个入口点都需要重复编写类似EntryPoint的结构,但暂时无法避免。
反序列化与函数调用的瓶颈
我在反序列化并触发函数调用的机制上遇到了瓶颈。目前能想到的最佳思路如下(注:我们使用constexpr字面量运算符将函数名转换为32位哈希值):
void OnServerSideRPCReceived() { uint32_t uFunctionID = get_from_IPC_Pipe<uint32_t>(); switch(uFunctionID): { case "EntryPoint"__hash: //需自动从IPC管道读取参数并调用同名本地函数 break; } }
尽管能确定服务端需要调用的函数,但找不到简洁的方式,利用函数签名来驱动从IPC管道读取参数的逻辑。
我设想可以这样实现:
case "EntryPoint"_hash: Deserialize(EntryPoint); break;
其中Deserialize的期望定义如下:
template<typename F> DeSerialize(F function) { //自动获取所有参数 function(a,b,c); }
但我无法实现根据传入函数的参数签名自动适配模板。我知道可以手动为不同调用签名编写模板特化,但希望模板能自动完成适配。
请问有什么实现建议?
用于展示问题的简化示例代码
#include <stdint.h> #include <stdio.h> #include <tuple> #include <iostream> #include <cstring> class ByteQueue { uint8_t storage[1024]; uint8_t *writeptr; public: template<typename T> void push(T &data) { memcpy(writeptr,&data,sizeof(data)); writeptr += sizeof(data); } template<typename T> void pull(T&data) { memcpy(&data,storage,sizeof(data)); uint32_t uAmountToCopy = uintptr_t(writeptr)-uintptr_t(storage)-sizeof(data); memmove(storage,storage+sizeof(data),uAmountToCopy); writeptr -= sizeof(data); } ByteQueue() { writeptr = storage; } }; ByteQueue g_ByteQueue; template<typename T> void send_to_IPC_Pipe(T first) { g_ByteQueue.push(first); } template<typename T> T get_from_IPC_Pipe() { T var; g_ByteQueue.pull(var); return var; } namespace detail { // FNV-1a 32bit hashing algorithm. inline constexpr uint32_t fnv1a_32(char const *s, size_t count) { return count ? (fnv1a_32(s, count - 1) ^ s[count - 1]) * 16777619u : 2166136261u; } } // namespace detail inline constexpr uint32_t operator"" _hash(const char * s, size_t count) { return detail::fnv1a_32(s, count); } constexpr uint32_t hash(char const *s,size_t count) { return detail::fnv1a_32(s,count); } template<typename ... Args> void Serialize(uint32_t FunctionID, Args ... args) { send_to_IPC_Pipe(FunctionID); //some magical syntax for C++11 int dummy[]= { (send_to_IPC_Pipe(args),0)...}; //hide the warning (void)dummy; } template<typename ... Args> void Invoke(void(*function)(Args...)) { function(get_from_IPC_Pipe<Args>()...); } void MyFunction(int a, int b, int c) { printf("ClientSide: %s, %d, %d, %d\n",__PRETTY_FUNCTION__,a,b,c); Serialize(hash(__PRETTY_FUNCTION__,strlen(__PRETTY_FUNCTION__)),a,b,c); } void MyFunctionServerSide(int a, int b, int c) { printf("ServerSide: %s, %d, %d, %d\n",__PRETTY_FUNCTION__,a,b,c); } void runRPCs() { uint32_t uFunctionID = get_from_IPC_Pipe<uint32_t>(); switch (uFunctionID) { case "void MyFunction(int, int, int)"_hash: Invoke(MyFunctionServerSide); break; } } int main() { MyFunction(2,4,6); runRPCs(); return 0; }
内容的提问来源于stack exchange,提问作者Russ Schultz
相关产品推荐
相关产品推荐

