Last active
December 15, 2015 22:29
-
-
Save y-oda-oni-juba/5333614 to your computer and use it in GitHub Desktop.
msgpack で polymorphic なクラスの pack/unpack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
msgpack で polymorphic なクラスの pack/unpack を行う例。型情報、バージョン情報つきでオブジェクトをpackするかんじ。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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