コマンドラインで C# をコンパイルしよう

ドキュメント制作用のマシンだったり、サーバーマシンだったり、他人のマシンだったりすると、Visual Studio が入っていないどころではなくて、Perl やら Ruby やらのスクリプト言語も入っていないので、バッチ処理をするときに大変なのです。

で、意外と【素】な環境で使えるプログラム言語を挙げてみると、wscript が一番なのですが、プログラミングっていう点では弱いんですよね。特に入出力関係を CreateObject で作らないといけないので、ちと面倒くさい。

なので、C# や Visual Basic を使います。と言いますか、この手のコンパイル系の言語が OS と共に入っているのは、なんだかなぁと思うのですが、それは別として使えます。
# セキュリティ的に、業務マシンには余分なものを入れないとう制限があるので。

C:\WINDOWS\Microsoft.NET\Framework なフォルダを見ると .NET Framework が入っています。

2011/02/15  10:08    <DIR>          v1.0.3705
2011/02/10  16:56    <DIR>          v1.1.4322
2011/02/15  12:54    <DIR>          v2.0.50727

この中に

 csc.exe C# のコンパイラ
 vbc.exe Visual Basic のコンパイラ
 jsc.exe Javascript.NET のコンパイラ
 
があります。

Java の場合はランタイムしか入らないので javac.exe を入れるためには JDK をインストールする必要があるのですが、.NET Framework は、【Windows Update】をしちゃうと入ってしまいます。ついでに言えば、MSBuild.exe も入っています。

なので、

SET PATH=%PATH%;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727

な風にパスを通しておけば、Hello.cs なんかがコンパイルできます。

using System;

pubic class Hello 
{
  public static void Main(string[] args) {
    Console.WriteLine("Hello 秘密な C# World.");
  }
}

このコマンドライン版で何を作るかというと、クラス設計のモックアップを作っています。クラス図を作っても、それが実際に動くかどうかわからないし、それを動かしたときにはどのようにクラスが関係するのかがわかりづらいので、スケルトンなモデルを作ります。これをコンパイルして、Console.WriteLine でトレース結果を出せば、どのメソッドをどのように呼び出しているのかを試すことができます。また、その結果をクラス図のほうに反映させることもできます。

まぁ、実は【シーケンス図】を書けばいいだけなんですけどね。リフレクションを使ったデータコンバーターを作る予定もあって、C# でモデルを作っているところなのです。

カテゴリー: 開発 | コマンドラインで C# をコンパイルしよう はコメントを受け付けていません

美女Linux Windows サイドバーガジェットを製作中

ちょっと製作途中ですが、Windows Vista/7 用のサイドバーガジェットです。


何のことはない、美女Linux の javascript をそのまま読み込ませるだけで OK なのです。

お試しのダウンロードはこちらに。

美女Linux v0.9
http://bijo-linux.com/bp/bijo-linux-0.9.gadget
美女Linux offshot版 v0.9
http://bijo-linux.com/bp/bijo-linux-off-0.9.gadget
美女Linux バレンタイン版 v0.9
http://bijo-linux.com/bp/bijo-linux-feb-0.9.gadget

こんな感じに、script タグで読み込ませればそのままガジェットとして表示できます。このあたりは、Flash でもできると思うんですがまだ試していません。あと、Silverlight もそのままいけそう。

<html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;>
    <head>
	    <title>美女linux v0.9</title>
	    <style type=&quot;text/css&quot;>
		    body
		    {
		        margin: 0;
		        width: 160px;
		        height: 262px;
		    }
	    </style>
	<head>
<body>
	<script src=&quot;http://bijo-linux.com/bp/js/bijo-0.9.js&quot;></script >
</body>
</html>

サイドバーガジェットの作り方は、本家とか

Windows Sidebar
http://msdn.microsoft.com/ja-jp/library/aa965850(v=VS.85).aspx
ガジェットに画像を貼りつける | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1493

を参照するとよいかも。色々テクニック的にはあるでしょうが、javascript で作っておくとこのあたりが楽ですね。

# Linux 版はさてどうするか、と思案中なわけですが、X Window で作るのもいいんですが、ひとまず Adobe AIR を使うのも良いかなぁと思ってみたり。

カテゴリー: サイドバーガジェット | 3件のコメント

ルイスはアリスにプラダを電送する

電話機能(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] = '&#92;&#48;';
		brandName[0] = '&#92;&#48;';
	}
	// アクセッサ
	bool getUsed() { return used; }
	string getOwnerName() { return ownerName; }
	void setOwnerName( string name ) {
		if ( ownerName[0] == '&#92;&#48;' ) {
			strcpy( ownerName, (const char*)name.c_str() );
		}
		
	}
	string getBrandName() { return brandName; }
	void setBrandName( string name ) {
		if ( brandName[0] == '&#92;&#48;' ) {
			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, &quot;Prada&quot; );
	}
};
class Tiffany : public Bag 
{
public:
	Tiffany() : Bag() {
		strcpy( brandName, &quot;Tiffany&quot; );
	}
};

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 << &quot;to &quot; << _name << &quot;: &quot; << (char*)data << endl;
	}
	// データ送信の関数
	virtual void sendPhone( const void *data, int size ) {
		if ( size == 0 ) {
			cout << &quot;from &quot; << _name << &quot;: &quot; << (char*)data << endl;
		} else {
			cout << &quot;from &quot; << _name << &quot;: send bag.&quot; << endl;
		}
		_phone.sendData( data, size );
	}
};

class Alice : public Person 
{
private:
	Bag *_bag;
public:
	Alice() : Person(&quot;alice&quot;), _bag(NULL) {
	}
	// データ受信のコールバック関数
	virtual void onPhone( const void *data, int size ) 
	{
		if ( size == 0 ) 
		{
			cout << _name << &quot;:&quot; << &quot;this is no bag!!!&quot; << endl;
		} else {
			Bag *bag = (Bag*)data;
			Prada *prada = dynamic_cast<Prada*>(bag);
			if ( prada == NULL ) {
				cout << _name << &quot;:&quot; << &quot;this is no Prada!!!&quot; << endl;
			} else {
				cout << _name << &quot;:&quot; <<	&quot;thank you Prada.&quot; << endl;
				this->_bag = Bag::Serialize((void*)data );
			}
		}
	}
	string getBrandName() {
		return _bag->getBrandName();
	}
		
};

int main(void)
{
	Alice alice;
	Person lewis(&quot;lewis&quot;);

	// アリスに電話を掛ける。
	lewis.setPhone( &alice );
	// hello と言う
	lewis.sendPhone( &quot;hello&quot;, 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 << &quot;alice has &quot; << alice.getBrandName() << endl;
	return 0;
}

送ってくるサイズをチェックせずに Bag クラス決め打ちなのは、別に考えるとして、この電送方式は転送元でコピーを作っておいて、転送先でそのメモリを使うっていう方式ですね。勿論、転送先でコピーを作ってもいいわけですが、懐かしの WM_COPYDATA は、この方式なのでこれにしてみました。と言うのは後付けでして、電送というのだから、

  1. 電送元で、一度、電子レベルに分解して
  2. 電送している間は、単なるビット列にして、
  3. 電送先で、なんらかの形で元に戻す。

というスタートレックな電送方式がイメージできるものがいいですよね。この Telephone だと、電話と電話の間の電送線のイメージが入っていないので、単なるビット列ってのが想像しづらいのです。いわば、この間は SOAP だったり XML データだったりするわけで、一番簡単なのが元のデータのフォーマットを維持したビット列(今回の方式)ですね。ビット列の場合、両方の端末が同じ CPU/OS である必要があるので(エンディアンとかパディングとか)、この転送している間は、別のフォーマットがよかったりします。というかそういう現実になっています。

ん~、これくらいになるとクラス図を示さないと分かりづらいですね。このあたりは後程。

カテゴリー: C++ | ルイスはアリスにプラダを電送する はコメントを受け付けていません

ルイスはアリスに電話する(2)

前回は、直接の継承を使って Phone の機能を使いましたが、今回は委譲を使ってます。
若干、回りくどくなる分だけソースコードがややこしいのですが。

// for ルイスはアリスに電話する(2)
// Telephone クラスを Person と独立に機能させる

#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 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 << &quot;to &quot; << _name << &quot;: &quot; << (char*)data << endl;
	}
	// データ送信の関数
	virtual void sendPhone( const void *data, int size ) {
		cout << &quot;from &quot; << _name << &quot;: &quot; << (char*)data << endl;
		_phone.sendData( data, size );
	}
};

int main(void)
{
	Person alice(&quot;alice&quot;);
	Person lewis(&quot;lewis&quot;);

	// アリスに電話を掛ける。
	lewis.setPhone( &alice );
	lewis.sendPhone( &quot;hello i'm lewis&quot;, 0 );

	return 0;
}

Telephone と Person クラスの間には、継承の関係はありません。Phone has a Telephone となって、has_a の関係になりますね。ITelephone を Person クラスが継承した場合は is_a の関係です。

この Phone 機能を使う環境では、main 関数の呼び出しは変わりません(メソッド名を変えたのでそこだけです)。Person クラスの中でコールバックを登録する Person::setPhone メソッドでは、メンバ関数ポインタを使いますが、C# なんかだとデリゲートで済みます。ここは、C++ なので若干ややこしいことになっているかと。ひとつ注意したいのが、リスナーパターンを利用した場合には、Person クラスはなんらかのインターフェースを継承する必要があります(Telephone クラスからコールバックのメソッドを呼び出すため)。ですが、Person クラスの継承元を使わずに/継承させずに、Phone の機能が使えるという点で、デリゲートや関数ポインタを使った場合は異なるのです。このあたり、差異が微妙ですが、継承元というのがポイントですね。

ここで、転送するデータは音声ということで文字列しか扱っていませんが、任意のデータを送ることができます。分かりますね、

アリスは物理転送機を持っている | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2025

のところで話した、シリアライズの機能を使ってプラダのバッグを送ることができるのです。

ってな訳で、この話は次回に。

カテゴリー: C++ | ルイスはアリスに電話する(2) はコメントを受け付けていません

ルイスはアリスに電話する

誰かが使ったらアリスに知らせろ | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2017
誰かが使ったらアリスに知らせろ(2) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2023

なところで、イベントを受信するパターンを書いたわけですが、これを使うと電話が作れます。いわゆる、

  • 電話を掛けるというコネクション
  • 電話を受けるという非同期イベント

な感じです。電話を受けるときには、電話番をしている訳ではなくて(電話番の場合はポーリングな訳ですが)、電話のベルが鳴ったら受話器を取るという、非同期的なイベントになります。

// for ルイスはアリスに電話する

#include <string.h>
#include <iostream>
using namespace std;

class ITelephone {
protected:
	ITelephone *_person;
public:
	// 相手先を登録
	void setPhone( ITelephone *person ) {
		this->_person = person ;
	}
	// データを送信
	virtual void sendData( const void *data, int size ) {
		if ( _person != NULL ) {
			_person->onPhone(data,size);
		}
	}
	// データを受信する仮想関数
	virtual void onPhone( const void *data, int size ) = 0;
};

class Person : public ITelephone
{
public:
	char _name[30];
public:
	Person( const char *name ) {
		strcpy( _name, name );
	}
	virtual void onPhone( const void *data, int size ) {
		cout << &quot;to &quot; << _name << &quot;: &quot; << (char*)data << endl;
	}
	virtual void sendData( const void *data, int size ) {
		cout << &quot;from &quot; << _name << &quot;: &quot; << (char*)data << endl;
		ITelephone::sendData( data, size );
	}
};

int main(void)
{
	Person alice(&quot;alice&quot;);
	Person lewis(&quot;lewis&quot;);

	// アリスに電話を掛ける。
	lewis.setPhone( &alice );
	lewis.sendData( &quot;hello i'm lewis&quot;, 0 );

	return 0;
}

これを実行するとこんな感じ。

D:\work\blog\src\alice>a
from lewis: hello i'm lewis
to alice: hello i'm lewis

ルイスからアリスに電話を掛けると、アリスの onPhone メソッドが呼び出されるという仕組みです。
実は、この ITelephone インターフェースは、Person で継承しているために、

人に電話が内蔵されている

って意味になっていますがw、ま、気にせずにッ!!! 単純に継承したほうが、機能拡張は楽だというオブジェクト指向の落とし穴に陥った例です。2,3 機能追加であれば、このように直で継承するほうがいいのですが、これの数が増えてしまう場合には、委譲という形で機能を分離したほうがよいのです。

人が電話を持っているパターンは次回に作り直します。

カテゴリー: C++ | ルイスはアリスに電話する はコメントを受け付けていません

アリスは物理転送機を持っている

まだまだ続くアリプラシリーズ。目次は後で作成します。既に自分で何を書いているのか忘れているし。
ひとまず、物理転送機を作ったところで、かつて流行ったオブジェクトパターンの再発明なのかなぁと思ったりします。このシリーズ、特にパターンを使っている訳ではなくて、自分がいままで業務で使ったパターンを書き残しているので、

  1. アリス&プラダという組み合わせで、C++ で現実をモデル化してみる。
  2. C++ でのコードを書いてみる。
  3. そういえば、オブジェクトパターンでこんなのがあったよなぁ。

という流れになっています。

さて、物理転送機、というのは別のところにコピーを作れるっていう方法です。今回は、ひとつの棚にプラダを入れて、そして取り出して、もう一度棚に入れる、という動作になります。取り出すっていうのは、いわゆる永続化なんですが、C++ の場合は単なるバイナリデータとして扱えますよって話です。

// for アリスは物理転送機を持っている

#include <string.h>
#include <iostream>
#include <string>
using namespace std;

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] = '&#92;&#48;';
		brandName[0] = '&#92;&#48;';
	}
	// アクセッサ
	bool getUsed() { return used; }
	string getOwnerName() { return ownerName; }
	void setOwnerName( string name ) {
		if ( ownerName[0] == '&#92;&#48;' ) {
			strcpy( ownerName, (const char*)name.c_str() );
		}
		
	}
	string getBrandName() { return brandName; }
	void setBrandName( string name ) {
		if ( brandName[0] == '&#92;&#48;' ) {
			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, &quot;Prada&quot; );
	}
};
class Tiffany : public Bag 
{
public:
	Tiffany() : Bag() {
		strcpy( brandName, &quot;Tiffany&quot; );
	}
};


class Alice 
{
private:
	Bag *bag;
public:
	Alice() : bag(NULL) {
	}
	void Present( Bag *b ) 
	{
		if ( this->bag == NULL ) {
			this->bag = b;
			this->bag->setOwnerName(&quot;Alice&quot;);
		}
	}
	void *SaveMyBag() {
		void *data = bag->Serialize();
		bag = NULL;
		return data;
	}
	void LoadMyBag(void *data) {
		if ( this->bag == NULL ) {
			Bag *bag = Bag::Serialize( data );
			this->bag = bag;
		}
	}
	string GetBrandName() {
		if ( bag == NULL ) {
			return &quot;none&quot;;
		} else {
			return bag->getBrandName();
		}
	}
};


int main(void)
{
	Alice alice;

	Bag *bag = new Prada();
	// アリスにプラダをプレゼント
	alice.Present( bag );
	cout << &quot;alice's bag brand is &quot; << alice.GetBrandName() << endl;
	// 一度、棚から降ろして持っていない状態にする
	void *prada = alice.SaveMyBag();
	cout << &quot;alice's bag brand is &quot; << alice.GetBrandName() << endl;
	// アリスにティファニーをプレゼント
	bag = new Tiffany();
	alice.Present( bag );
	cout << &quot;alice's bag brand is &quot; << alice.GetBrandName() << endl;
	// ティファニーを棚卸して
	void *tiffany = alice.SaveMyBag();
	// 再び、プラダを棚に入れる
	alice.LoadMyBag( prada );
	cout << &quot;alice's bag brand is &quot; << alice.GetBrandName() << endl;

	return 0;
}

ひとまず実行すると、こんな感じ。

D:\work\blog\src\alice>a
alice's bag brand is Prada
alice's bag brand is none
alice's bag brand is Tiffany
alice's bag brand is Prada

ここでは、永続化ということでシリアライズがミソです。インターフェースにしてあるのは、気分の問題でして、シリアライズのコードはこんなに簡単。

	// シリアライズ
	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;
	}

クラスのバイトデータを保持しているだけなんですが、実はこの技が使えるのは C/C++ だけなんですね。Java, C#, VB なんてのは使えません。シリアライズする時に何らかのフォーマッティング(大抵は XML なんですが)を使わない限り無理です。

この手のシリアライズ、C++ が得意とするとこで、データの読み書きがバイナリで直接行われるために非常に高速に動きます。っていうか、もともと C言語の struct 互換の動きになっているので、こういう風に動くわけでして、フォーマットを気にせずに単純に sizeof(…) でバイトデータを書き込めるのが得意なのですね。

# 勿論、このバイナリコードが人にとっては謎な値なので、XML フォーマットというのが流行りなわけですが。後、構造体/クラスをバイナリのままやり取りする方法は、固定長のデータしか無理なんですよね。なので、文字列のところで string じゃなくて char name[30] となっているのはそれが理由です。

データ構造を気にしなくて、高速に読み書きができるので、利用どころとしてはデータのヘッダ部分なんかに良く使われます。そう BMP 形式のヘッダとか、struct なんとかで読みだした後に、データ長を算出するときなんか、struct で読み込めば一発です、という話ですね。

カテゴリー: C++ | アリスは物理転送機を持っている はコメントを受け付けていません

誰かが使ったらアリスに知らせろ(2)

前回のソースはインターフェースを使っている例なんですが、実は C++ の場合はメンバ関数のポインタを使えるのでインタフェースは必要ありません…と言いますから、インタフェースの代わりになります。Java だと定番のリスナーパターンというとでインターフェースを利用、C# の場合はデリゲートを使います。このあたり、リスナーパターンは言語によって違った実装ができるという訳で。

メンバ関数を使って書き直したのが次のコードです。

// for 誰かが使ったらアリスに知らせろ

#include <iostream>
#include <list>
using namespace std;

class Person ;

class INotify {
protected:
	Person *person ;
	// メンバ関数ポインタ
	void (Person::*onTatch)();
public:	
	// メンバ関数の登録
	void setCatcher(Person *per, void (Person::*catcher)()) {
		person = per;
		onTatch = catcher;
	}
	// 呼出し
	virtual void OnTatch( void *bag ) {
		if ( onTatch != NULL ) {
			(person->*onTatch)();
		}
	}
};

class Bag : public INotify {
protected:
	void *_mark;
public:
	Bag() : _mark(NULL) {}
	virtual void setMark( void *mark ) {
		// 1回だけマークできる
		if ( _mark == NULL ) {
			_mark = mark;
		}
	}
	virtual void *getMark() {
		return _mark;
	}
	virtual void Used( void *user ) {
		if ( _mark != NULL && _mark != user ) {
			OnTatch( this );
		}
	}
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Person
{
private:
	list<Bag*> bags;
public:
	Person() {
	}
	void Present( Bag *bag ) 
	{
		if ( dynamic_cast<Prada*>(bag) != NULL &&
             bag->getMark() == NULL ) 
		{
			bag->setMark( this );
			// メンバ関数の登録
			bag->setCatcher( this, &Person::onTatch );
			bags.push_back( bag );
		}
	}
	int CountBags() 
	{
		return bags.size();
	}
	void Use( Bag *bag ) {
		bag->Used( this );
	}
	void onTatch() {
		cout << &quot;who use my Prada bag!!!&quot; << endl;
	}
};

int main(void)
{
	Person alice;
	Person lolita;
	
	Prada *bag = new Prada();
	// アリスにプレゼント
	alice.Present( bag );
	// アリスが使う分には問題なし
	alice.Use( bag );
	// 同じ鞄をロリータにプレゼント
	lolita.Present( bag );
	// ロリータが鞄を使うと...
	lolita.Use( bag );

	return 0;
}

これも実行すると、次のように誰が使ったんじゃッ!!!ってことになります。

D:\work\blog\src\alice>a
who use my Prada bag!!!

関数ポインタ、メンバ関数ポインタの書き方はちょっと難しくてコンパイルエラーとの格闘になります。慣れると関数部分をポインタにするだけだなぁ、慣れない場合はちょっと意味不明な感じがしますね。配列の配列のポインタなんかと一緒です。

カテゴリー: C++ | 誰かが使ったらアリスに知らせろ(2) はコメントを受け付けていません

誰かが使ったらアリスに知らせろ

怒涛のアリプラシリーズをもうひとつ。

鞄に対してマーキングをするのは消極的な手段でして、マーキングを上書きされてしまったり、マーキング自体を無視して Use メソッドを呼び出したりするとアリスはお手上げです。なので、もっと積極的に違法な Use メソッドが呼び出されたら、アラートメッセージがアリスに届くようにします。
そう、mvvm パターンの INotifyPropertyChanged と同じです。

// for 誰かが使ったらアリスに知らせろ

#include <iostream>
#include <list>
using namespace std;

class ICatch {
public:
	virtual void OnTatch( void *bag ) = 0;
};

class INotify {
protected:
	ICatch *catcher;
public:	
	void setCatcher( ICatch *user ) {
		catcher = user;
	}
	virtual void OnTatch( void *bag ) {
		if ( catcher != NULL ) {
			catcher->OnTatch( bag );
		}
	}
};

class Bag : public INotify {
protected:
	void *_mark;
public:
	Bag() : _mark(NULL) {}
	virtual void setMark( void *mark ) {
		// 1回だけマークできる
		if ( _mark == NULL ) {
			_mark = mark;
		}
	}
	virtual void *getMark() {
		return _mark;
	}
	virtual void Used( void *user ) {
		if ( _mark != NULL && _mark != user ) {
			OnTatch( this );
		}
	}
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Alice : public ICatch
{
private:
	list<Bag*> bags;
public:
	Alice() {
	}
	void Present( Bag *bag ) 
	{
		if ( dynamic_cast<Prada*>(bag) != NULL &&
             bag->getMark() == NULL ) 
		{
			bag->setMark( this );
			bag->setCatcher( this );
			bags.push_back( bag );
		}
	}
	int CountBags() 
	{
		return bags.size();
	}
	void Use( Bag *bag ) {
		bag->Used( this );
	}
	virtual void OnTatch( void *bag ) {
		cout << &quot;who use my Prada bag!!!&quot; << endl;
	}
};

class Lolita 
{
private:
	list<Bag*> bags;
public:
	void Present( Bag *bag ) 
	{
		bag->setMark( this );
		bags.push_back( bag );
	}
	void Use( Bag *bag ) {
		bag->Used( this );
	}
};

int main(void)
{
	Alice alice;
	Lolita lolita;
	
	Prada *bag = new Prada();
	// アリスにプレゼント
	alice.Present( bag );
	// アリスが使う分には問題なし
	alice.Use( bag );
	// 同じ鞄をロリータにプレゼント
	lolita.Present( bag );
	// ロリータが鞄を使うと...
	lolita.Use( bag );

	return 0;
}

鞄(Bag)のほうには、INotiry インターフェースを仕込んでおきます。アラートメッセージをを受け取る側(Alice)のほうは、ICatch インターフェースで受け取りの関数を用意しておきます。C++ で2つのインターフェースを使っているのは C# のデリゲートの機能がないので、こうなっています。static な関数ポインタを用意してもいいのですが、どうせならば、Alice のメソッドとして用意したいところですよね。
実は、モノである鞄(Bag)に機能を仕掛けておいて、人であるアリスに通知する、ってのがいい感じかなと。

これを実行すると、

D:\work\blog\src\alice>a
who use my Prada bag!!!

誰か私のバッグを使ったッ!!!って怒ります。ロリータからは Use メソッドの中身は見れないし、そもそも Lolita は ICatch を継承してないのでアラートメッセージを受け取りません。

似たようなパターンとしては、

  • Bag クラスのデストラクタに仕込んで、なんで、私の鞄を捨てるのよッ!!!とか。
  • List を改造して、なんで、私の鞄を持っているのよッ!!!とか

ができます。鞄というオブジェクトはひとつなので、Alice::bags と Lolita::bags の両方にあるってのは妙な感じですよね。実際は、ポインタが使われているので、鞄そのものが置いてあるというよりも、鞄を示すタグ(所有権)を撮り廻しているっていう意味になりますが。鞄そのものは実体なのでひとつしかないけど、ポインタ(指し示すもの)は、いくらでもできるから list のようにポインタのコレクションの場合は、所有自体を制限させるのはできないのかなと。写真みたいなものですから。肖像権を主張できても、対象そのものを写真で写すこを制限するのは難しい、っていう意味ですね。

カテゴリー: C++ | 誰かが使ったらアリスに知らせろ はコメントを受け付けていません

アリスはロリータと鞄を共有しない

引き続き、アリプラシリーズ。

ルイスが鞄をプレゼントする相手は、アリスだったりロリータだったりするわけですが、間違ってロリータにプレゼントしたものをアリスに渡したときの反応はどうでしょうか?ってな設定が次のコードです。

// for アリスはロリータと鞄を共有しない

#include <iostream>
#include <list>
using namespace std;

class Bag {
protected:
	void *_mark;
public:
	Bag() : _mark(NULL) {}
	virtual void setMark( void *mark ) {
		_mark = mark;
	}
	virtual void *getMark() {
		return _mark;
	}
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Alice
{
private:
	list<Bag*> bags;
public:
	Alice() {
	}
	void Present( Bag *bag ) 
	{
		if ( dynamic_cast<Prada*>(bag) != NULL &&
             bag->getMark() == NULL ) 
		{
			bag->setMark( this );
			bags.push_back( bag );
		}
	}
	int CountBags() 
	{
		return bags.size();
	}
};

class Lolita 
{
private:
	list<Bag*> bags;
public:
	void Present( Bag *bag ) 
	{
		bag->setMark( this );
		bags.push_back( bag );
	}
};

int main(void)
{
	Alice alice;
	Lolita lolita;
	
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Prada());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Tiffany());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	// 一度ロリータにあげる
	Bag *bag = new Prada();
	lolita.Present( bag );
	// そしてアリスにプレゼント
	alice.Present(bag); 
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	bag = new Prada();
	alice.Present(bag);
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;

	// 実は、アリスにプレゼントしたものをロリータにあげることもできる
	lolita.Present( bag );

	return 0;
}

バッグ自体にマーキングを付けているので、自分の鞄かどうかは分かるので、一度ロリータにプレゼントされた鞄を拒否ることができます。

が、問題は、アリスにプレゼントした鞄はロリータによってマーキングが上書きされてしまうので、その鞄はロリータのものになってしまうんですねぇ。困った。

これを防ぐには、Bag クラスの mark の処を次のように1回だけ書くように直します。

class Bag {
protected:
	void *_mark;
public:
	Bag() : _mark(NULL) {}
	virtual void setMark( void *mark ) {
		if ( _mark == NULL ) {
			_mark = mark;
		}
	}
	virtual void *getMark() {
		return _mark;
	}
};

ええ、何処かでみたようなコピーワンスの処理ですね。似たような感じで Use メソッドの中でカウンタを減らせば、ユーズワンス、の処理も可能です。ただし、コピーワンスの機能も、コピー対象(Prada とか DVD とか)を扱うモノ(Lolita とかビデオ機器とか)が Used をきちんと呼び出さなければ、利用は可能なのです。
このあたりのコピーワンスのガードを確実に呼び出させるためには、例えば初期の暗号解読のためのキーの読み出し自体にコピーワンスのガードを入れるとか、利用に必須なものに対してガードを入れ込みます。実は似たようところでは、SNS のログイン機能とか、ゲームソフトのコピーガードの仕方とかも似た方式です。最初にガードを置きます。
なので、実は最初にガードを置かずに、本体の所々にガードを分散させるってのもアリなんですよね。確か、販売 DVD のコピーガードはこの方式になっています。

カテゴリー: C++ | アリスはロリータと鞄を共有しない はコメントを受け付けていません

アリスは同じ鞄を抱えない

更にアリプラシリーズ。

同じ鞄をプレゼントした場合は、アリスは貰わない…と言いますから、同じ鞄なんだからなんでもう一度プレゼントするの?というコードです。

// for アリスは同じ鞄を抱えない
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

class Bag {
protected:
	bool used;
public:
	virtual bool getUsed() { return used; }  
	virtual void Used() { this->used = true; }  
};

class Prada : public Bag {};
class Tiffany : public Bag {};

class Alice
{
private:
	list<Bag*> bags;
public:
	Alice() {
	}
	void Present( Bag *bag ) 
	{
		if ( find(bags.begin(),bags.end(),bag) == bags.end() &&
             dynamic_cast<Prada*>(bag) != NULL ) {
			bags.push_back( bag );
		}
	}
	int CountBags() 
	{
		return bags.size();
	}
};

int main(void)
{
	Alice alice;
	
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	Bag *bag = new Prada();
	alice.Present(bag);
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Tiffany());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(bag); // 同じものをプレゼントする
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;
	alice.Present(new Prada());
	cout << &quot;alice has &quot; << alice.CountBags() << &quot; bags.&quot; << endl;

	return 0;
}

実行するとこんな感じ。

D:\work\blog\src\alice>a
alice has 0 bags.
alice has 1 bags.
alice has 1 bags.
alice has 1 bags.
alice has 2 bags.

プレゼントすると、一度、タンス(bags)を find 関数で照合します。何故か知らないけど、タンス(bags)にあったものが再び Present される訳で、変なのという感じなのですが、bags には追加しません。
データベースで言うところの、同じ名前だったらデータベースに insert しない処理ってのと同じですね。

SELECT @COUNT = count(*) FROM bags WHERE ...
IF @COUNT = 0 THEN
  INSERT bags VALUES ( ... )
END IF

のようなものです。重複チェックをして挿入ってな具合。これは、bags にある量が多くなるとだんだんと遅くなっていきます。いわゆる O(n) の確率。ただし、find のアルゴリズムを変えていけば O(log n) だっけ? になります。ハッシュテーブルとかバイナリツリーとかを使います。大変ですよね。不安ですね。

しかし、ちょっと工夫すると、一発で貰った鞄かどうかが分かります。Bag クラス自体にマーキングを示す mark 変数を用意して、これに自分のポインターを入れておきます。そうすると、mark を調べるだけで一発で分かりますよね。

class Bag {
protected:
	void *_mark;
public:
	Bag() : _mark(NULL) {}
	virtual void setMark( void *mark ) {
		_mark = mark;
	}
	virtual void *getMark() {
		return _mark;
	}
};

特性を知って、ちょっとクラスに工夫を加えるとうまくいくっていうパターンです。

カテゴリー: C++ | アリスは同じ鞄を抱えない はコメントを受け付けていません