设为首页 加入收藏

TOP

C++中模拟反射填充消息struct(一)
2014-11-24 01:40:33 来源: 作者: 【 】 浏览:3
Tags:模拟 反射 填充 消息 struct

问题
我正做的一个项目需要在Erlang 节点和C++ 节点之间传输大量的事件,在C++这一侧,使用struct储存这些消息。



自然的,我需要很多struct,例如:


struct msg_greeting{


std::string sender;


std::string content;


int content_length;


std::string content_type;


};


struct msg_bye{


std::string sender;


};



在Erlang这一侧,使用tuple储存,由于Erlang是动态类型的,所以不需要定义,这里只是说明:


view sourceprint 1 {greeting, Sender ::string(), Content ::string(), ContentLength ::int(), ContentType ::atom() }


2 {bye, Sender ::string() }



消息的传输可以使用tinch_pp (http://www.adampetersen.se/code/tinchpp.htm)


如果你第一次使用tinch_pp,下面这一段是一个简单的接收和匹配的过程,即使不了解tinch_pp也可以看懂:



void connect::msg_receiver_routine()


{


try{


while(1) {


matchable_ptr msg = mbox->receive();


int token;



std::string type;


matchable_ptr body;


if(msg->match(


make_e_tuple(atom("event"),


e_string(&type)),


any(&body)))


//do something here


else


//some log here


}


}catch(boost::thread_interrupted e){


// @todo output some log here


}


};



我们使用event标识一个erlang事件,type是这个事件的类型,body是事件内容,也就是我们之前定义的greeting或者bye。


接下来,我们需要实现事件的处理,首先,我们需要把tinch_pp匹配出来的tuple填入我们的c++结构。


我们这样做:


msg_ptr on_greeting(matchable_ptr p){


std::string sender;


std::string content;


int contentLength;


std::string contentType;


bool matched = p->match(make_e_tuple(


erl::string(&sender),


erl::string(&content),


erl::int_(&contentLength),


erl::atom(&contentType)


));



if(matched){


msg_ptr = shared_ptr(new msg_greeting());


msg_ptr->Sender = sender;


msg_ptr->Content = content;


msg_ptr->ContentLength = contentLength;


msg_ptr->ContentType = contentType;


return msg_ptr;


}



return shared_ptr();


}



问题在于,我们需要为每个消息写这么一大段代码。假如我们的C Node需要处理几十种消息,我们就需要把这个代码重复几十遍,而实际上只有一小部分才是有用的(有差异的)。


提取通用代码


怎样才能省去重复的部分,只保留其中的精华呢?这就需要元编程和预处理器了,我们稍后再介绍。


首先,最显著的差异就是不同的消息中的信息不一样,用c++的说法是:他们具有不同的成员。


去掉这个差异后,我们的代码可以简化为:
msg_ptr on_greeting(matchable_ptr p){


if(matched){


msg_ptr mp = msg_greeting::make(p);


return mp;


}



return shared_ptr();


}



看似简洁了许多,但实际上,我们只是把msg_greeting特有的处理(差异)隐藏在msg_greeting定义的静态方法里了。


至少,我们的on_xxxx方法看起来干净点了。


但是,我们还是需要在某处(msg_greeting内部)定义这些代码。


更好的方案


反射是很多语言都具有的特性,反射意味着类具有了自省的能力,即一个类知道自己有哪些成员,这些成员有哪些属性。



如果C++支持反射,我们这个问题就好解决了,我们可以定义一个msg_fill方法,按照msg成员的属性,从matchable_ptr获取成员的值。等等,C++可没有反射支持,至少我不知道。


那么,我们自己来实现一个吧。


成员属性


我们需要一个能保存成员属性的,符合C++语法的物件。有两种选择:对象,类型。



对象对应着运行时,类型对应着编译时。考虑到速度和效率,我们选择类型。


在C++进行元编程,主要是依靠模板来实现的,首先我们声明一个模板,用来表示成员


template


struct auto_field;



这个模板有三个参数:Type表示成员的C++类型,Struct表示这个成员所属的结构,Field是成员指针,用来记住这个成员在所属结构中所处的位置。



光有声明没有什么作用,所以我们需要一些实现(或者说模板定义):


template


struct auto_field{


typedef tinch_pp::erl::atom field_e_type;


typedef std::string field_c_type;



static void fill(Struct* s, field_c_type& c){


s->*Field = (c == "true");


};


};



可以看出,我们通过模板特化,为bool类型的成员提供了:


C++类型


Erlang类型


填充C++类型的fill方法


这里其实隐藏了一个问题,怎么知道需要定义这几个类型和静态成员函数呢?稍后再介绍。



类似的,我们可以为更多的类型提供特化,不再重复。


至此,我们已经知道怎么定义类型成员,并记住成员的属性。


填充数据
有了成员的属性,我们就可以解析消息tuple了,参考最初的代码,填充方法的伪实现应该长这样:


template


bool fill(Msg* e){


field_0_c_type field_0_c;


field_1_c_type field_1_c;


field_2_c_type field_2_c;



bool matched = p->match(make_e_tuple(


field_0_e_type(&fi

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇如何在Hadoop的MapReduce程序中处.. 下一篇MinGW 编译FFmpeg 1.2.1 的H.264 ..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: