電話機能(Telephone)とシリアライズ機能(ISerialize)を同時に利用すると、電送ができます(笑)。「物理転送」っていう言葉を使っているのは、最近 SF を読んでいないなぁと思いつつ本屋さんに行ったらミステリーしか並んでいないじゃんッ!!! 早川SF文庫は何処へと探していたら、アーサー・C・クラークのベスト集が売っていたので、仕方がなく「えッ!!! 1000円なんですか??? 文庫なのに、こんなに高いんじゃ講談社学術文庫や筑摩文庫と同じじゃないですかッ!!!」ってな感じで仕方なく買ってきて、100頁ほど一気読み、したからです。なるほど、SF ちっくな本を読もうとすると、ラノベのほうがそれらしい(インデックスとかレールガンとか白井黒子とか…)なんだなぁ、と思ったり思わなかったり。
そんな訳で、電送しているのが次のコードです。
// for ルイスはアリスにプラダを電送する #include <string.h> #include <iostream> using namespace std; class Person ; class Telephone { protected: Telephone *_telephone; Person *_person ; void (Person::*_onPhone)(const void *, int); public: // 自分の onPhone を登録 void setMyPhone(Person *person, void (Person::*onPhone)(const void *, int)) { _person = person ; _onPhone = onPhone; } // 相手先を登録 void setPhone( Telephone *telephone ) { this->_telephone = telephone ; } // データを送信 virtual void sendData( const void *data, int size ) { if ( _telephone != NULL ) { _telephone->onPhone(data,size); } } // データを受信する関数 virtual void onPhone( const void *data, int size ) { if ( _onPhone != NULL ) { (_person->*_onPhone)( data, size ); } } }; class ISerialize { // シリアライズ化 void *Serialize(); // データから復元 static void Serialize( void * ); }; class Bag : public ISerialize { public: // 利用フラグ bool used ; // 持ち主の名前 char ownerName[30]; // ブランド名 char brandName[30]; public: // コンストラクタ Bag() : used(false) { ownerName[0] = '\0'; brandName[0] = '\0'; } // アクセッサ bool getUsed() { return used; } string getOwnerName() { return ownerName; } void setOwnerName( string name ) { if ( ownerName[0] == '\0' ) { strcpy( ownerName, (const char*)name.c_str() ); } } string getBrandName() { return brandName; } void setBrandName( string name ) { if ( brandName[0] == '\0' ) { strcpy( brandName, (const char*)name.c_str() ); } } // 利用する virtual void Use() { this->used = true; } // シリアライズ virtual void *Serialize() { int size = sizeof(Bag); void *data = new char[size]; memcpy( data,this, size ); return data; } // 逆シリアライズ static Bag *Serialize(void *data) { Bag *bag = (Bag*)data; return bag; } }; class Prada : public Bag { public: Prada() : Bag() { strcpy( brandName, "Prada" ); } }; class Tiffany : public Bag { public: Tiffany() : Bag() { strcpy( brandName, "Tiffany" ); } }; class Person { public: char _name[30]; Telephone _phone; public: // コンストラクタ Person( const char *name ) { strcpy( _name, name ); // 自分のコールバックを登録 _phone.setMyPhone( this, &Person::onPhone ); } // 相手に電話を掛ける void setPhone( Person *person ) { _phone.setPhone(person->getPhone()); } // 電話を受け取るアクセッサ Telephone *getPhone() { return &(this->_phone); } // データ受信のコールバック関数 virtual void onPhone( const void *data, int size ) { cout << "to " << _name << ": " << (char*)data << endl; } // データ送信の関数 virtual void sendPhone( const void *data, int size ) { if ( size == 0 ) { cout << "from " << _name << ": " << (char*)data << endl; } else { cout << "from " << _name << ": send bag." << endl; } _phone.sendData( data, size ); } }; class Alice : public Person { private: Bag *_bag; public: Alice() : Person("alice"), _bag(NULL) { } // データ受信のコールバック関数 virtual void onPhone( const void *data, int size ) { if ( size == 0 ) { cout << _name << ":" << "this is no bag!!!" << endl; } else { Bag *bag = (Bag*)data; Prada *prada = dynamic_cast<Prada*>(bag); if ( prada == NULL ) { cout << _name << ":" << "this is no Prada!!!" << endl; } else { cout << _name << ":" << "thank you Prada." << endl; this->_bag = Bag::Serialize((void*)data ); } } } string getBrandName() { return _bag->getBrandName(); } }; int main(void) { Alice alice; Person lewis("lewis"); // アリスに電話を掛ける。 lewis.setPhone( &alice ); // hello と言う lewis.sendPhone( "hello", 0 ); // ティファニーを転送 Bag *bag = new Tiffany(); lewis.sendPhone( bag->Serialize(), sizeof(Tiffany)); delete bag; // プラダを転送 bag = new Prada(); lewis.sendPhone( bag->Serialize(), sizeof(Prada)); delete bag; // アリスが持っているのはプラダの鞄 cout << "alice has " << alice.getBrandName() << endl; return 0; }
送ってくるサイズをチェックせずに Bag クラス決め打ちなのは、別に考えるとして、この電送方式は転送元でコピーを作っておいて、転送先でそのメモリを使うっていう方式ですね。勿論、転送先でコピーを作ってもいいわけですが、懐かしの WM_COPYDATA は、この方式なのでこれにしてみました。と言うのは後付けでして、電送というのだから、
- 電送元で、一度、電子レベルに分解して
- 電送している間は、単なるビット列にして、
- 電送先で、なんらかの形で元に戻す。
というスタートレックな電送方式がイメージできるものがいいですよね。この Telephone だと、電話と電話の間の電送線のイメージが入っていないので、単なるビット列ってのが想像しづらいのです。いわば、この間は SOAP だったり XML データだったりするわけで、一番簡単なのが元のデータのフォーマットを維持したビット列(今回の方式)ですね。ビット列の場合、両方の端末が同じ CPU/OS である必要があるので(エンディアンとかパディングとか)、この転送している間は、別のフォーマットがよかったりします。というかそういう現実になっています。
ん~、これくらいになるとクラス図を示さないと分かりづらいですね。このあたりは後程。