我正在写一个与网络相关的课程。我的应用程序接收表格
的网络消息 [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;
}
在main中,我们为消息ID注册回调,然后通过网络接收消息,获取适当的MessageHandler,按变量解析data
变量,将每个变量附加到回调绑定,以及何时绑定所有内容 - 拨打回叫。
所以,关心我的事情:
是否有可能有一个映射(或其他一些基于整数键结构值的数据结构,具有近似常量查找),其中值是模板结构,并且您希望存储不同类型的结构它? (即存储在地图中的值不是同类型的。)
如何使parse
和parseHelper
功能发挥作用?
parse
中调用回调后,如何取消绑定所有绑定值? (或者他们在通话后自动取消绑定?)如何使此代码生效?
如果有人可以将我的伪代码修复为工作代码,解释为什么我的代码不起作用以及它是如何修复的,那将是很好的,但只是解释也非常有用!
答案 0 :(得分:1)
MessageHandler<int>
和MessageHandler<float>
是不同的类型,不共享可用于另一个的通用定义(“基础”类)。所以不,你不能创建一个可以存储MessageHandler
不同参数的容器。还要记住,静态类型也意味着知道声明的大小。如果不将参数解析为实际的“值”,这是不可能的。
所以没有。如果没有实际指定T,则不能有map<key, MessageHandler<T...>>
,这禁止使用T...
的多个值。
要解决此问题,您可以使用类型橡皮擦。我们使用它作为例子:
https://github.com/aerys/minko/blob/master/framework/include/minko/Any.hpp
所以我们可以创建一个map<key, Any>
。
https://github.com/aerys/minko/blob/master/framework/include/minko/Signal.hpp
它使用可变参数模板以相应的参数作为参数调用回调。
对于你的parseHelper函数,我认为它有多个问题:
callback
而不是设置它吗?我以为callback
是用户定义的值?我认为您要做的是“反序列化”来自网络的值集,然后将这些值作为回调的参数传递。这是对的吗?
如果是这种情况,你可以看一下:https://stackoverflow.com/a/1547118/4525791
答案 1 :(得分:0)
您可以通过模板参数轻松解析内存中的动态数据(请参阅第1部分)。关于如何使用元组调用函数,answer非常有用并且可以应用(参见第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
,但它们没有问题。