使用变分模板的困难
Difficulties using Variadic Templates
我正在编写一个与网络相关的类。我的应用程序接收形式为的网络消息
[uint8_t message id, uint8_t/uint16_t/uint32_t data ...]
我的类允许其用户为特定的消息id注册回调。
由于有各种不同的消息,具有不同数量的不同数据条目(数据条目仅限于uint8_t、uint16_t和uint32_t),我决定使用C++11的可变模板来减轻重复代码的负担。
这是我想做的伪代码(没有编译,怀疑它是否编译)
#include <arpa/inet.h>
#include <stdexcept>
using namespace std;
template<class ...T>
struct MessageHandler {
size_t size;
std::function<void(T...)> callback;
template<class Head, class... Tail>
void parseHelper(uint8_t *data)
{
if (sizeof(Head) == 1) {
uint8_t val;
memcpy(&val, data, sizeof(Head));
// set next unset argument to the value of val
callback = std::bind(callback, val);
data += sizeof(Head);
} else if (sizeof(Head) == 2) {
uint16_t val;
memcpy(&val, data, sizeof(Head));
val = ntohs(val);
// set next unset argument to the value of val
callback = std::bind(callback, val);
data += sizeof(Head);
} else if (sizeof(Head) == 4) {
uint32_t val;
memcpy(&val, data, sizeof(Head));
val = ntohl(val);
// set next unset argument to the value of val
callback = std::bind(callback, val);
data += sizeof(Head);
} else {
throw std::invalid_argument("We support only 1, 2 and 4 byte integers!");
}
// repeat for the rest of arguments
parseHelper<Tail...>(data);
}
template<class ...Empty>
void parseHelper(uint8_t *data)
{
// do nothing, terminating case of recursion
}
template<class ...T>
void parse(utin8_t *data)
{
// parse `data` into T... arguments and bind them into `callback`
parseHelper<T...>(data);
// at this point `callback` has all arguments binded from `data`
// invoke the callback
callback();
}
}
// <message id, callback-holding helper struct>
std::unordered_map<uint8_t, MessageHandler> myMap;
template<class...T>
void dummy(T&&...)
{
// a dummy, does nothing
}
template<class...T>
void addMessageHandler(uint8_t messageId, std::function<void<T... arg>> callback)
{
MessageHandler<arg> mh;
mh.size = 0;
// order of execution is undefined, but we don't care
dummy( (mh.size += sizeof(arg))... );
mh.callback = callback;
myMap[messageId] = mh;
}
void foo(uint16_t a, uint8_t b, uint16_t c, uint32_t d)
{
// do stuff with the parsed message
}
void bar(uint32_t a)
{
// do stuff with the parsed message
}
int main()
{
// register callbacks
addMessageHandler<uint16_t, uint8_t, uint16_t, uint32_t>(0, std::bind(&foo));
addMessageHandler<uint32_t>(1, std::bind(&bar));
...
// get message over the network
uint8_t messageId = some_network_library.read.first_byte();
MessageHandler mh = myMap[messageId];
uint8_t *data = some_network_library.read.bytes(mh.size);
// parses and calls the callback with parsed values
mh.parse(data);
return 0;
}
主要来说,我们为消息ID注册回调,然后通过网络接收消息,获取适当的MessageHandler,逐个变量解析data
,将每个回调附加到回调绑定中,当我们绑定了所有内容时,调用回调。
所以,我关心的事情:
是否可以有一个映射(或其他基于整数键结构值的数据结构,具有近似常量查找),其中的值是一个模板结构,并且您希望在其中存储不同类型的结构?(即存储在映射中的值不是同质类型的)。
我需要什么才能使
parse
和parseHelper
函数工作?- 我不确定是否可以像那样将绑定值附加到std::函数中
- 调用
parse
中的回调后,如何解除所有绑定值的绑定?(或者他们在通话后自动解除绑定?)
如何使此代码正常工作?
如果有人能把我的伪代码修复成一个有效的代码,解释为什么我的代码不起作用以及它是如何修复的,那就太好了,但只是解释也非常有帮助!
- 参数多态性(模板)不是包含多态性(继承):
MessageHandler<int>
和MessageHandler<float>
是不同的类型,没有一个可用于另一个("基类")的通用定义。因此,不能创建一个可以存储具有不同参数的MessageHandler
的容器
还要记住,静态类型还意味着要知道声明的大小。如果不将参数求解为其实际"值",这是不可能的。
所以没有。如果没有实际指定T,就不能有map<key, MessageHandler<T...>>
,这禁止为T...
使用多个值。
为了解决这个问题,你可以使用一个类型橡皮擦。我们使用这个例子:
https://github.com/aerys/minko/blob/master/framework/include/minko/Any.hpp
所以我们可以创建一个CCD_ 11。
- 如果你想有一个可变回调方法,你可以看看我们的Signal类:
https://github.com/aerys/minko/blob/master/framework/include/minko/Signal.hpp
它使用可变模板调用回调,并将相应的参数作为参数。
在您的parseHelper函数的情况下,我认为它有多个问题:
- 它只需要"head"值,难道不需要某种循环/递归调用吗
- 如果你做这样的循环,它什么时候应该停止?你需要指针和你正在阅读的"消息"的大小
- 我认为您应该使用lambdas而不是std::bind:在您的情况下,一切都是单态的,所以std::绑定将免费占用更多的内存/CPU
- 您不想调用
callback
而不是设置它吗?我以为callback
是用户定义的值
我认为您想要做的是"反序列化"来自网络的值集,然后将这些值作为回调的参数传递。这是正确的吗?
如果是这样的话,你可以看看这个:https://stackoverflow.com/a/1547118/4525791
您可以通过模板参数轻松地解析内存中的动态数据(请参阅第1部分)。关于如何使用元组调用函数,答案非常有用,并且可以应用(请参阅第2部分)。现在,您只需要存储有关函数的信息,并使用动态解析的值进行调用。此外,从我的角度来看,在阅读时查找消息的大小可能会有另一个问题,所以我为它制作了简单的助手struct
template< typename T1, typename... T2 >
struct size_of {
enum {
size = sizeof (T1) + size_of < T2... >::size
};
};
template< typename T >
struct size_of< T > {
enum {
size = sizeof (T)
};
};
这是带有一些注释的代码
分析程序
template< typename T1, typename... T2 > struct parser { static std::tuple< T1, T2... > parse(void* data) { // get value from pointer T1* p = (T1*) data; std::cout << typeid (*p).name() << " " << *p << std::endl; // concatenate current value with next one return std::tuple_cat(std::make_tuple(*p), parser < T2... >::parse(p + 1)); } }; template< typename T1 > struct parser< T1 > { static std::tuple< T1 > parse(void* data) { T1* p = (T1*) data; std::cout << typeid (*p).name() << " " << *p << std::endl; return std::make_tuple(*p); } };
此外,您还可以重新实现这个类,以返回解析值的大小,以确保一切正常
函数调用
// function call using tuple template < int N > struct __apply_impl { template < typename... ArgsF, typename... ArgsT, typename... Args > static void apply(const std::function<void( ArgsF...)>& f, const std::tuple<ArgsT...>& t, Args... args) { __apply_impl < N - 1 > ::apply(f, t, std::get < N - 1 > (t), args...); } }; template <> struct __apply_impl<0> { template < typename... ArgsF, typename... ArgsT, typename... Args > static void apply(const std::function<void( ArgsF...)>& f, const std::tuple<ArgsT...>& /* t */, Args... args) { // actual call f(args...); } }; // wrapper function template < typename... ArgsF, typename... ArgsT > void call_with_tuple(const std::function<void( ArgsF...)>& f, std::tuple<ArgsT...> const& t) { __apply_impl<sizeof...(ArgsT)>::apply(f, t); }
消息分配器或消息处理器
// message dispatcher class message_dispatcher { protected: // callback interface struct callback_t { public: virtual void call(void*) = 0; }; // and implementation template< typename... Ty > struct callback_impl : public callback_t { typedef std::function< void(Ty...) > function_t; callback_impl(const function_t& f) { m_f = f; } virtual void call(void* data) { // parse to tuple auto t = parser < Ty... >::parse(data); // call function call_with_tuple(m_f, t); } function_t m_f; }; public: // process incoming data void process(int t, void* data) { m_c[t]->call(data); } // register callback for type t template< typename... Ty > void add(int t, const std::function< void(Ty...) >& f) { m_c[t] = new callback_impl < Ty... >(f); } protected: std::map< int, callback_t* > m_c; };
示例
void foo(int a, float b, char c) { std::cout << "in foo(int,float,char) with args: "; std::cout << "1: " << a << ", " << "2: " << b << ", " << "3: " << c << std::endl; } struct foo_t { void foo(int a, double b) { std::cout << "in foo_t::foo(int,double) with args: "; std::cout << "1: " << a << ", " << "2: " << b << std::endl; } }; int main(int argc, char** argv) { // pack data char* b1 = new char[size_of< int, float, char >::size]; int a1 = 1; float a2 = 2.; char a3 = 'a'; memcpy(b1, &a1, sizeof (a1)); memcpy(b1 + sizeof (a1), &a2, sizeof (a2)); memcpy(b1 + sizeof (a1) + sizeof (a2), &a3, sizeof (a3)); // pack data char* b2 = new char[size_of< int, double >::size]; int a4 = 10; double a5 = 20.; memcpy(b2, &a4, sizeof (a4)); memcpy(b2 + sizeof (a4), &a5, sizeof (a5)); // create callbacks std::function<void(int, float, char) > f1(&foo); foo_t foo; std::function<void(int, double) > f2 = std::bind(&foo_t::foo, &foo, std::placeholders::_1, std::placeholders::_2); message_dispatcher md; // register callbacks md.add(0, f1); md.add(1, f2); // call md.process(0, b1); md.process(1, b2); return 0; }
输出
i 1 f 2 c a in foo(int,float,char) with args: 1: 1, 2: 2, 3: a i 10 d 20 in foo_t::foo(int,double) with args: 1: 10, 2: 20
当然,它只适用于POD类型。我没有使用uint8_t
、uint16_t
和uint32_t
,但它们不会有问题。
- 没有找到相关文章