Skip to content

Instantly share code, notes, and snippets.

@y-oda-oni-juba
Last active December 15, 2015 22:29
Show Gist options
  • Save y-oda-oni-juba/5333614 to your computer and use it in GitHub Desktop.
Save y-oda-oni-juba/5333614 to your computer and use it in GitHub Desktop.
msgpack で polymorphic なクラスの pack/unpack
msgpack で polymorphic なクラスの pack/unpack を行う例。型情報、バージョン情報つきでオブジェクトをpackするかんじ。
//
// compile:
//
// g++ -I$(MSGPACK)/include -L$(MSGPACK)/lib mpci-test3.cpp -lmsgpack
//
#include <iostream>
#include <fstream>
#include <map>
#include <tr1/unordered_map>
#include <string>
#include <sstream>
#include <tr1/functional>
#include <tr1/memory>
#include <typeinfo>
#include <msgpack.hpp>
// tr1 のコンテナは明示的に include する必要あり
#include <msgpack/type/tr1/unordered_map.hpp>
using namespace std;
//
// polymorphic な型を自動的に型判定して復元
//
// constructor
struct msgpack_allocator_base {
virtual void* construct() = 0;
};
typedef tr1::shared_ptr<msgpack_allocator_base> msgpack_allocator_ptr;
template<typename T>
struct msgpack_allocator: msgpack_allocator_base {
virtual void* construct() {
return new T();
}
};
// 型 uid から constructor へのマッピング
typedef map<string, msgpack_allocator_ptr> allocator_map_t;
allocator_map_t allocator_map;
// type_info.name から 型 uid へのマッピング
typedef map<string, string> type_uid_map_t;
type_uid_map_t type_uid_map;
// 型 uid の登録
template<typename T>
void register_type() {
type_uid_map[typeid(T).name()] = T::type_uid();
allocator_map[T::type_uid()] = msgpack_allocator_ptr(new msgpack_allocator<T>());
}
// packされた型 uid から型の復元
template<typename T>
void convert_polymorphic(msgpack::object o, T** v) {
// if not registered type, convert by msgpack::object::convert()
string type_name = typeid(T).name();
type_uid_map_t::iterator uid_found = type_uid_map.find( type_name );
if ( uid_found == type_uid_map.end() ) {
o.convert(*v);
return;
}
// get allocator by stored type
string type_uid;
o.via.array.ptr[0].convert(&type_uid);
allocator_map_t::iterator alloc_found = allocator_map.find( type_uid );
if ( alloc_found == allocator_map.end()) throw bad_cast();
T* alloced_v = (T*)(alloc_found->second->construct());
alloced_v->msgpack_unpack(o);
*v = alloced_v;
}
//
// サンプル
//
typedef tr1::unordered_map<string,string> map_t;
typedef msgpack::packer<stringstream> packer_impl_t;
class BaseClass {
public:
BaseClass() {}
virtual ~BaseClass() {}
BaseClass* add(const string &key, const string &value) {
map_[key] = value;
return this;
}
virtual void show() {
cout << "{ ";
for( map_t::iterator begin = map_.begin(), end = map_.end(), p = begin;
p != end; ++p ) {
if ( p != begin ) cout << ", ";
cout << p->first << "=>" << p->second;
}
cout << " }" << endl;
}
protected:
map_t map_;
public:
// 型 uid, version, 固有のメンバ数定義
static string type_uid() { return "BaseClass"; }
static size_t type_version(){ return 0; }
static size_t member_num() { return 1; }
// 各メンバの pack
void msgpack_pack_inner( packer_impl_t& pk) const {
pk.pack(map_);
}
// 各メンバの unpack
void msgpack_unpack_inner(msgpack::object o, size_t &offset) {
o.via.array.ptr[offset++].convert(&map_);
}
// msgpack::pack インターフェイス実装
//
// NOTE: MSGPACK_DEFINE() で定義すると、msgpack_pack() は
// template method として定義されるため、virtual にできない。
// virtual にしないと面倒なので、非template method として
// 定義する.
//
// msgpack_allocator と同様のトリックを使えば template method に
// することもできる
virtual void msgpack_pack( packer_impl_t& pk) const {
// 型 uid, version を pack
const int member_n = BaseClass::member_num();
pk.pack_array(member_n + 2);
pk.pack( BaseClass::type_uid() );
pk.pack( BaseClass::type_version() );
// メンバを pack
msgpack_pack_inner(pk);
}
// msgpack::unpack インターフェイス実装
virtual void msgpack_unpack(msgpack::object o) {
size_t version;
o.via.array.ptr[1].convert(&version);
// バージョンチェック
if ( version == 0 ) {
size_t offset = 2;
// メンバを unpack
msgpack_unpack_inner(o, offset);
}
}
};
class DerivedClass: public BaseClass {
public:
DerivedClass() {}
DerivedClass(const string &name): name_(name) {}
virtual ~DerivedClass() {}
virtual void show() {
cout << name_ << ": ";
BaseClass::show();
}
void name(const string &new_name) { name_ = new_name; }
private:
string name_;
public:
// 型 uid, version, 固有のメンバ数定義
static string type_uid() { return "DerivedClass"; }
static size_t type_version(){ return 0; }
static size_t member_num() { return 1; }
// 各メンバの pack
void msgpack_pack_inner( packer_impl_t& pk) const {
// まず 親クラスのメンバを pack
BaseClass::msgpack_pack_inner(pk);
// 固有メンバをpack
pk.pack(name_);
}
// 各メンバの unpack
void msgpack_unpack_inner(msgpack::object o, size_t &offset) {
// まず 親クラスのメンバを unpack
BaseClass::msgpack_unpack_inner(o, offset);
// 固有メンバをunpack
o.via.array.ptr[offset++].convert(&name_);
}
// msgpack::pack インターフェイス実装
virtual void msgpack_pack( packer_impl_t& pk) const {
const int member_n = BaseClass::member_num() + DerivedClass::member_num();
pk.pack_array( member_n + 2);
// 型 uid, version を pack
pk.pack( DerivedClass::type_uid());
pk.pack( DerivedClass::type_version());
// メンバを pack
msgpack_pack_inner(pk);
}
// msgpack::unpack インターフェイス実装
virtual void msgpack_unpack(msgpack::object o) {
size_t version;
o.via.array.ptr[1].convert(&version);
// バージョンチェック
if ( version == 0 ) {
size_t offset = 2;
// メンバを unpack
msgpack_unpack_inner(o, offset);
}
}
};
int main(int argc, char **argv) {
register_type<BaseClass>();
register_type<DerivedClass>();
BaseClass *obj = new DerivedClass( "<derived>" );
// BaseClass *obj = new BaseClass();
obj->add( "key1", "val1")->add("key2", "val2");
cout << "Origin: ";
obj->show();
stringstream buf;
packer_impl_t pk(buf);
pk.pack(*obj);
string pack_result = buf.str();
if ( argv[1] ) {
ofstream f(argv[1]);
f << pack_result;
}
////
msgpack::unpacked unpack_buf;
msgpack::unpack( &unpack_buf, pack_result.c_str(), pack_result.size());
msgpack::object unpack_result = unpack_buf.get();
BaseClass *restored = NULL;
// unpack_result.convert() の 代わりに convert_polymorphic() を使う
convert_polymorphic(unpack_result, &restored);
cout << "Restored: ";
restored->show();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment