tips
id:viverさんの作ってるmsgpackライブラリを使っていてのtips
既に実装されている機能などを知らずに僕が再発明してる可能性もあります。
packされている内容をバイナリ列で出力する
データが化けていた場合、多くは通信などのIOまわりでケアレスミスをしています。
原因を追うために送信前・受信後のデータ列を比較するのはお手軽な手段です。
template<class tuple> void tuple_dump(const tuple& t){ msgpack::sbuffer sb; msgpack::pack(sb, t); const char* ptr = sb.data(); for(std::size_t i=0; i<sb.size(); ++i){ printf("%02x",*ptr++ & 255); } } // 使いかた msgpack::type::tuple<int, double, std::string> mytuple(12,0.3f,"hello"); tuple_dump(mytuple); // 出力 930ccb3fd3333340000000a568656c6c6f
これによりtupleの中身がどうなってるのか逐次probeできます。
http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec
と照らし合わせる事でバイナリ列の意味は目視で確認できます。
tuple単位で情報をやりとりするときはメッセージ名を定義する
大規模なアプリケーションを作っているとtuple単位で情報をやりとりすることがメインになります。
書くべき内容が膨大になること自体は構わないにしても、初歩的でやりがちなミスが誤シリアライズ/誤デシリアライズです(卒論でskip graphを作っていたときは6割ほどがこのミスでした)
C++には強い静的型付けがあるので遠慮無く恩恵を受けましょう。
// --- 宣言 --- namespace operation{ // 使うメッセージ名を規定 enum type{ set, get, delete, }; // データのフォーマットを規定 // set, key, value, client_address typedef msgpack::type::tuple<int, std::string, std::string, address> Set; // get, key, client_address typedef msgpack::type::tuple<int, std::string, address> Get; // delete, key, client_address typedef msgpack::type::tuple<int, std::string, address> Delete; }// namespace operation // シリアライズ時 // もしここで変換不能な引数を渡してしまった場合でもコンパイルが通らないので安全 const operation::Set set_query(operation::set, "key1", "value1", myaddress); // デシリアライズ時 // まずどのタイプのメッセージが送られてきたのかチェック msgpack::type::tuple<int> op(msg); const int message = op.get<0>(); switch(message){ // メッセージのタイプに応じて処理を分ける case: operation::set:{ const operation::Set set(msg); // もしデータが化けていたならここでbad_castがthrowされる // 中身を順次デシリアライズ。もし変換不能な引数で受け取ろうとしてもコンパイルが通らない const std::string& key = set.get<1>(); const std::string& value = set.get<2>(); const address& = set.get<3>(); ....// } case: operation::get:{ // 以下同様 }
typedefでメッセージに名前を付けて定義しておくことによりプログラム全体の見通しが良くなると共に、受信・送信時でインターフェイスを整え、プログラマ自身にそれに従う事を強要できるため、誤シリアライズや誤デシリアライズに速い段階で気づけます。この方法に乗り換えてからこの手のケアレスミスは完全に無くなりました。
勘違いやもう少し効率の良いやりかたなどありましたらコメントで教えて頂けると幸いです。
■eldeshさんに「namespace type」となっていたことを指摘して頂きました。正しくは「enum type」でした。修正しました。