Thursday, June 11, 2009

雜記: Variadic Template, Boost FunctionTypes, Boost MPL, AMQP

最近滿腦子除了球球的事之外,就是一堆C++ template,還有各式各樣的messaging system,整個快爆炸了。來筆記一下這幾天在看的東西好了;首先是為了要做比較直覺的RPC framework,不想要讓使用者自己encode parameter list,所以看了各式各樣的template的技巧,像是Variadic Template,之前完全沒用過,用了才發現其實這很強大...


template< typename T >
void print_comma_separated_list(T value)
{
std::cout << value << std::endl;
}

template< typename First, typename ... Rest >
void print_comma_separated_list(First first,Rest ... rest)
{
std::cout << first << ",";
print_comma_separated_list(rest...);
}

// 然後就可以這樣寫...
print_comma_separated_list(42,"hello",2.3,'a');


參考自: http://www.devx.com/cplus/Article/41533/1954
不過在g++裡要再多加個-std=c++0x或是-std=gnu++0x才能編譯就是了。

然後看到某個神人在GameDev發的文章,在講把C/C++的function bind進Lua的方式,用到了我之前沒看過的Boost.FunctionTypes
http://www.gamedev.net/reference/articles/article2629.asp
作者有附上原始碼,雖然在Ubuntu上因為libluabind-dev要用boost,而內建boost版本又太舊不能編譯之外,應該還蠻容易看懂的。我覺得比較神奇的是Executor的部份,原來可以用這樣的方式依據不同數量的parameter來選擇template specialization啊...每次看別人寫的template都有種:「原來可以這樣啊!!」的感覺 Orz

截取一小段程式碼:

// 這是Executor的部份...
template< class Arity, class ParamTypes, typename ResultType >
struct Executor;

// 0 parameters
template< class ParamTypes, typename ResultType >
struct Executor< Int2Type< 0 >, ParamTypes, ResultType > {
template< class Fn > ResultType operator()(Fn& fn, const AnyParams& /*params*/) {
return fn();
}
};

// 1 parameter
template< class ParamTypes, typename ResultType >
struct Executor< Int2Type< 1 >, ParamTypes, ResultType > {
template< class Fn > ResultType operator()(Fn& fn, const AnyParams& params) {
return fn(
boost::any_cast< boost::mpl::at< ParamTypes, boost::mpl::int_< 0 > >::type >(params[0]));
}
};

// 這是Command的部份
template< class Fn >

struct CommandT : public Command {

CommandT(const std::string& name, const Fn& fn)

: Command(name)

, fn_(fn)

{}


// ...略...

// Executer for void return type

int Execute(lua_State* lua_state, Int2Type) const {

AnyParams params;

try {

boost::mpl::for_each< ParamTypes >(Extractor(lua_state, params));

} catch( std::exception& /*ex*/) {

//LOG_ERROR_LN("Error in extract");

throw;

}

Executor f;

f(fn_, params);

return 0;

}

// ...略...


Boost.FunctionTypes搭配Boost.MPL ==> 整個非常強大,這樣就可以很容易的bind RPC callback而不用自己去decode network buffer;不過麻煩的在於呼叫的時候,現在的想法大概是類似Boost.Function + Boost.Bind,用類似的宣告方式,但是內部真的在呼叫的時候是會自動去encode network buffer,不過這樣寫起來實在太麻煩了,不知道有沒有比較簡單的方法....還在想要怎麼辦才好...


然後本來要自己寫的broker最後放棄了,實在太花時間了,看那些TRAM、TOTEM、各式各樣的reliable broadcast,還有各式各樣的reliable membership dissemination,頭都昏了;其實用現成的MQ就夠了,反正這個不太要求效能,重點是要reliable跟fault-tolerant啊! 所以再次重新研究了一下現在能用的messaging system,大概有幾套:
1. Spread Toolkit (但是已經不maintain非常久了..不太敢用...囧)
2. OpenAIS (作者的進度一直delay)
3. JMS
3.1 ActiveMQ (歷史悠久啊...也有ActiveMQ-CPP支援C++ client,CMS的介面定的還不錯)
3.2 FioranoMQ (最近有出了個報告說它是市面上最快的MQ...只是要錢 XD)
4. AMQP
4.1 OpenAMQ (應該是AMQP的第一個實作吧,感覺好像蠻有那麼一回事的,應該要來試用看看)
4.2 ZeroMQ (又一個號稱地表上最快的AMQP實作,雖然他有圖有真相,還號稱支援Infiniband...不過怎麼沒什麼提到fault-tolerance的部份勒...)
3.3 Qpid (這個是Apache的C++ AMQP實作,我研究過他的原始碼,其實寫的還可以,只是居然depend on OpenAIS,然後似乎有打算支援Infiniband,但是雖然程式碼有寫,可是根本沒有使用...囧)

其實AMQP裡還有很多有名的實作,像是之前twitter使用的RabbitMQ,只可惜他沒有C++ client,要不然真的可以考慮一下。目前最看好的大概就是ActiveMQ,接著是FioranoMQ,然後是OpenAMQ,Spread Toolkit,最後才是ZeroMQ (沒辦法,ZeroMQ似乎reliability的部份不清不楚)。其實照理論上,我覺得如果OpenAIS有好好的、認真的把它寫完,而且還有人繼續maintain的話,應該會是最強大的,畢竟它和Spread Toolkit一樣是唯二實作totem ring protocol的;當然用totem跟TRAM或是其它的方式都有好有壞啦,只是我個人比較偏好totem的reliability跟recoverability。

反正不管用什麼MQ,上面應該都會用類似JMS/CMS的介面包起來吧,畢竟AMQP是JMS的generalization,應該不成問題才是;但是麻煩的是reliability跟system administration,這個還是留到之後再考慮吧!

2 Comments:

孫德華 (Walter Suen) said...

AMQP並不是JMS的generalization;因為兩者本質是不同的:JMS是Java API, 而AMQP則是連線協定。請參考下列Wikipedia中AMQP條目"Comparative specifications"一節中的說明:
http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol

因此,只要Client與Broker兩者間,以相同版本AMQP進行通訊的話,兩者可以是不同語言或平台的。例如Client是JMS/AMQP(以AMQP通訊標準0-10所實作的JMS service provider),那Broker只要也是遵循AMQP 0-10的標準,那兩者應該就可以進行溝通的。

小迪克/Mark said...

我知道啊~AMQP是wire level的spec,不過很多AMQP的實作大都有類似的interface,因為他們的protocol就已經定義的communication model了;而這個communication model比JMS所定義的再更有彈性一點就是了...