<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>株式会社エス・スリー・フォー &#187; article</title>
	<atom:link href="http://www.s34.co.jp/category/cpptechdoc/article/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.s34.co.jp</link>
	<description>株式会社エス・スリー・フォーは、一人一人のスキルを重視したシステム・ネットワーク構築のプロフェッショナル集団です。蓄積されたノウハウと最新技術により、お客様が考えられているシステムを明確にし、ご提案させて頂きます。</description>
	<lastBuildDate>Mon, 23 Jan 2012 07:59:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
		<item>
		<title>C++の新しいキャスト</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/newcast/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/newcast/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=202</guid>
		<description><![CDATA[従来のキャストの問題点 異なる型への変換において、C/C++ではキャストが用いられます。 // intからlongへのキャスト int ival; int lval = (long)ival; ご存知のとおり、キャストは非常に危険です。 本来ならば型の不一致によるコンパイルエラーをねじ伏せるのですから。 キャストの使われ方(意味)は、大きく3種(型変換/型変更/const外し)に分類されます。 型変換 // int から double へ int ival; double dval = (double)ival; 型変更 // long から int* へ long lval; int* iptr = (int*)lval; const外し // const int* から int* へ const int* ciptr; int* iptr = (int*)ciptr; 上記のどの目的でキャストしても、構文としてはどれも同じ (型)式 です。つまりコードからは&#8221;何が目的でキャストしたのかはっきりしない&#8220;のですね。 4種類の新しいキャスト [...]]]></description>
			<content:encoded><![CDATA[<h3>従来のキャストの問題点</h3>
<p>異なる型への変換において、C/C++ではキャストが用いられます。</p>
<pre>// intからlongへのキャスト
int ival;
int lval = (long)ival;</pre>
<p>ご存知のとおり、キャストは<strong>非常に危険</strong>です。 本来ならば型の不一致によるコンパイルエラーをねじ伏せるのですから。</p>
<p><span id="more-202"></span></p>
<p>キャストの使われ方(意味)は、大きく3種(型変換/型変更/const外し)に分類されます。</p>
<ol>
<li>型変換
<pre>// int から double へ
int ival;
double dval = (double)ival;</pre>
</li>
<li>型変更
<pre>// long から int* へ
long lval;
int* iptr = (int*)lval;</pre>
</li>
<li>const外し
<pre>// const int* から int* へ
const int* ciptr;
int* iptr = (int*)ciptr;</pre>
</li>
</ol>
<p>上記のどの目的でキャストしても、構文としてはどれも同じ</p>
<pre>(型)式</pre>
<p>です。つまりコードからは&#8221;何が目的でキャストしたのか<strong>はっきりしない</strong>&#8220;のですね。</p>
<h3>4種類の新しいキャスト</h3>
<p>キャストは使わずに済ませられるならそれに越したとはないのですが、どうしても使わざるを得ないシチュエーションは少なからず存在します。<br />
ならばせめて少しでも安全にキャストするために新たに定められたキャスト構文が、以下に挙げる<strong>static_cast</strong> / <strong>reinterpret_cast</strong> / <strong>const_cast</strong> / <strong>dynamic_cast</strong> です。</p>
<h4>static_cast</h4>
<pre><strong>static_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>static_cast</strong>は<em>expr</em>の型から<em>type</em>への暗黙の型変換、あるいは<em>type</em>から<em>expr</em>への暗黙の型変換が存在する場合にだけキャストします。キャスト不可能であればコンパイルエラーとなります。</p>
<pre>// int から long へ
int ival;
long lval = <strong>static_cast</strong>&lt;long&gt;(ival);</pre>
<h4>reinterpret_cast</h4>
<pre><strong>reinterpret_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>reinterpret_cast</strong>は<em>type</em>(<em>expr</em>)が許されるなら、<em>expr</em>を<em>type</em>に単にキャストします。</p>
<pre>// long から int* へ
long lval;
int* iptr = <strong>reinterpret_cast</strong>&lt;int*&gt;(lval);</pre>
<p><strong>reinterpret_cast</strong>は単なる型変更であり、たとえ派生関係があったとしてもポインタのアドレス自体はキャスト前と変わりません。<br />
その意味で<strong>reinterpret_cast</strong>は非常に危険なキャストといえるでしょう。</p>
<h4>const_cast</h4>
<pre><strong>const_cast</strong>&lt;<em>type</em>&gt;(<em>expr</em>)</pre>
<p><strong>const_cast</strong>はconstおよびvolatile修飾子を無効にするだけのキャストを行ないます。そのほかのときはコンパイルエラーとします。</p>
<pre>// const int* から int* へ
const int* ciptr;
int* iptr = <strong>const_cast</strong>&lt;int*&gt;(ciptr);</pre>
<h4>dynamic_cast</h4>
<pre><strong>dynamic_cast</strong>&lt;type&gt;(expr)</pre>
<p><strong>dynamic_cast</strong>は基底クラスへのポインタ(or 参照)から派生クラスへのポインタ(or 参照)への型保証キャストを行ないます。</p>
<p>上記3種のキャストはコンパイル時にキャストしますが、<strong>dynamic_cast</strong>は実行時に型の検査が行なわれ、変換不可能であれば0を返します。</p>
<pre>class Base { ... };
class Derived : public Base { ... };

Base base;
Derived derived;

Derived* pd1 = <strong>dynamic_cast</strong>&lt;Derived*&gt;(&amp;base); // 失敗 pd1 == 0
Derived* pd2 = <strong>dynamic_cast</strong>&lt;Derived*&gt;(&amp;derived);
</pre>
<p>参照のキャストに失敗すると、std::bad_cast例外がthrowされます。</p>
<pre>
try {
  Derived&amp; rd1 = <strong>dynamic_cast</strong>&lt;Derived&amp;&gt;(base);
} catch ( const std::bad_cast&amp; e ) {
  std::cout &lt;&lt; e.what() &lt;&lt; std::endl;
};</pre>
<p><strong>dynamic_cast</strong>により、従来のキャストでは不可能であった クロス・キャスト、そして抽象基底クラスからのダウン・キャストが可能になりました。</p>
<h5>クロス・キャスト</h5>
<pre>/*
 * Shape と Drawable は派生関係にない
 */
class Shape {
 ...
};

class Drawable {
public:
  virtual void draw() =0;
};

/*
 * Circle は Drawable にキャスト可能
 */
class Circle : public Shape, public Drawable {
public:
  virtual void draw();
};

Shape* shape = new Circle;
/* 従来のキャスト (Drawable*)shape では、
 * 場合によっては暴走する */
Drawable* drawable = <strong>dynamic_cast</strong>&lt;Drawable*&gt;(shape);
if ( drawable )
  drawable-&gt;draw();</pre>
<h5>抽象基底クラスからのダウン・キャスト</h5>
<pre>class Machine {
  ...
};

class TV : virtual public Machine {
public:
  void tune(int channel);
};

class VTR : virtual public Machine {
public:
  void record();
};

class TeleVideo : public TV, public VTR {
  ...
};

Machine* machine = new TeleVideo;
/* 従来のキャスト (TeleVideo*)machine はコンパイルエラー */
TeleVideo* televideo = <strong>dynamic_cast</strong>&lt;TeleVideo*&gt;(machine);
if ( televideo ) {
  televideo-&gt;tune(8);
  televideo-&gt;record();
}</pre>
<h4>※注意</h4>
<p><strong>dynamic_cast</strong>が適用できるのはポリモルフィック・クラス、 すなわち少なくとも一つのメンバ関数が仮想関数でなくてはなりません。</p>
<pre>struct non_polymorpic { ... }; // 仮想関数が存在しない
struct something : non_polumorphic { ... };
non_polymorphic* p;
something* q = <strong>dynamic_cast</strong>&lt;someting&gt;(p); // error!</pre>
<p>新しいキャストは従来のキャストよりはるかに安全です。<br />
また､grepなどで&#8221;_cast&#8221;をキーにすればキャストした個所を簡単に検索できるという副次的な効果もあります。</p>
<p><!--more--></p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/newcast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>sizeofの不思議</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/sizeof/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/sizeof/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=458</guid>
		<description><![CDATA[はじめに Cではsizeofによって構造体がメモリ上で占める大きさ(バイト数)を知ることができます。 struct s{ int x; int y; }; ... cout &#60;&#60; sizeof(s) &#60;&#60; endl; 僕の愛用する処理系、Visual C++ 6.0では 8が得られました。intひとつにつき4byteを消費するからでしょう。 それではC++でのclassの大きさはどうでしょう。内包するメンバ変数それぞれの占めるバイト数の総和になるのでしょうか。 class c { int x; int y; }; ... cout &#60;&#60; sizeof(c) &#60;&#60; endl; 答は&#8230;やはり 8 です。でもね、C++ではいつもこうなるとは限らないのですよ。 「C++はCよりデカい」と言われることがあります。その理由のひとつがここに明らかになります。 仮想関数 上記class cにメンバ関数を追加します。 class c { int x; int y; public: int sum(); }; ... cout &#60;&#60; [...]]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>Cではsizeofによって構造体がメモリ上で占める大きさ(バイト数)を知ることができます。</p>
<pre>
struct s{
  int x;
  int y;
};
...
cout &lt;&lt; <strong>sizeof</strong>(s) &lt;&lt; endl;
</pre>
<p>僕の愛用する処理系、Visual C++ 6.0では <strong>8</strong>が得られました。intひとつにつき4byteを消費するからでしょう。</p>
<p>それではC++でのclassの大きさはどうでしょう。内包するメンバ変数それぞれの占めるバイト数の総和になるのでしょうか。</p>
<p><span id="more-458"></span></p>
<pre>
class c {
  int x;
  int y;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>答は&#8230;やはり <strong>8</strong> です。でもね、C++ではいつもこうなるとは限らないのですよ。</p>
<p>「C++はCよりデカい」と言われることがあります。その理由のひとつがここに明らかになります。</p>
<h3>仮想関数</h3>
<p>上記class cにメンバ関数を追加します。</p>
<pre>
class c {
  int x;
  int y;
public:
  int sum();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>結果は <strong>8</strong> でした。メンバ関数を追加してもサイズに変化はないようです。</p>
<p>ところが、仮想関数を追加すると&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>結果は<strong>12</strong>。4バイト増加しています。まるでメンバ関数へのポインタがクラス内にこっそり追加されたかのようです。<br />
ならば仮想関数が2つあったら8バイト増えるのかな&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum();
  virtual int sub();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>依然として <strong>12</strong> のままです。実体を伴わない純粋仮想関数を追加したら&#8230;</p>
<pre>
class c {
  int x;
  int y;
public:
  virtual int sum() =0;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c) &lt;&lt; endl;
</pre>
<p>&#8230;ふむ、純粋仮想関数を追加してもクラスの大きさは <strong>12</strong> ですね。</p>
<p>これらのことから、クラスに仮想関数がひとつ以上あるとき、クラスの大きさは <strong>4</strong> バイト増加すると思われます。</p>
<p><strong>何故</strong>でしょう。その理由は仮想関数が実行されるときのからくりにあります。</p>
<p>仮想関数を含むクラスを宣言すると、そのクラスの中メンバ変数&apos;vptr&apos;がひとつ、こっそり追加されます。これは&quot;仮想関数へのポインタ配列&apos;vtbl&apos;へのポインタ&quot;です。<br />
vtblはクラス内にある仮想関数のポインタを配列内に納めたもので、仮想関数を含むクラス毎にひとつ作られます。仮想関数がコールされるとき、vptrの指す配列vtblの特定の位置から仮想関数へのポインタを取り出し、そこをコールします。</p>
<p>このようなからくりのために、仮想関数を含むクラスは、仮想関数のないクラスより<strong>少し大きい</strong>のです。</p>
<h3>継承</h3>
<p>継承したときのクラスの大きさはどうでしょうか。</p>
<pre>
class c0 {
  int x;
  int y;
};

class c1 : public c0 {
  int a;
  int b;
};
...
cout &lt;&lt; <strong>sizeof</strong>(c0) &lt;&lt; endl;
cout &lt;&lt; <strong>sizeof</strong>(c1) &lt;&lt; endl;
</pre>
<p>結果はそれぞれ <strong>8</strong>,<strong>16</strong> でした、予想通りの結果です。</p>
<p>仮想関数を含むとどうなるでしょう。</p>
<pre>
class c0 {
public:
  int x;
  int y;
  virtual int sum();
};

class c1 : public c0 {
public:
  int a;
  int b;
  virtual int diff();
};
...
cout &lt;&lt; <strong>sizeof</strong>(c0) &lt;&lt; endl;
cout &lt;&lt; <strong>sizeof</strong>(c1) &lt;&lt; endl;
</pre>
<p>sizeof(c0)が <strong>12</strong>だから、c1はそれにint2個分(8byte)と&apos;vptr&apos;(4byte)を加えて 24 &#8230;ではなくて <strong>20</strong> となりました。仮想関数テーブルポインタ&apos;vptr&apos;はクラス内にひとつあればいいからです。</p>
<pre>
cout &lt;&lt; <strong>offsetof</strong>(c1,x) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,y) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,a) &lt;&lt; endl;
cout &lt;&lt; <strong>offsetof</strong>(c1,b) &lt;&lt; endl;
</pre>
<p>の結果はそれぞれ、4, 8, 12, 16 となりました、このことから、class c1のメモリ上でのレイアウトは、</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>c1::vptr</td>
</tr>
<tr>
<td>+4</td>
<td>c0::x</td>
</tr>
<tr>
<td>+8</td>
<td>c0::y</td>
</tr>
<tr>
<td>+12</td>
<td>c1::a</td>
</tr>
<tr>
<td>+16</td>
<td>c1::b</td>
</tr>
</tbody>
</table>
<p>となっていると考えられます。</p>
<h3>多重継承</h3>
<p>多重継承の場合、ちょっとばかしややこしくなります。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
  virtual void f0() {}
};

class c1 {
public:
  int x1;
  int y1;
  virtual void f1() {}
};

class c2 : <strong>public c0, public c1</strong> {
public:
  int x2;
  int y2;
  virtual void f2() {}
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  :        &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0, c1 &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
</pre>
<p>&apos;仮想関数ポインタテーブル(vptr)はクラスにひとつ&apos;であるならば、sizeof(c2)は、10,c1,c2それぞれのメンバ変数領域の総和(8&#215;3=24byte)にvptr(4byte)を加えた28byteとなりそうです。</p>
<p>ところが結果は予想に反して <strong>32</strong>となりました。vptrが2つ含まれているようです。</p>
<pre>
cout &lt;&lt; offsetof(c2, x0) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y0) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, x1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, x2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c2, y2) &lt;&lt; endl;
</pre>
<p>の結果: 4, 8, 16, 20, 24, 28 から察するに、c2のメモリ・レイアウトは:</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>c2::vptr</td>
</tr>
<tr>
<td>+4</td>
<td>c0::x0</td>
</tr>
<tr>
<td>+8</td>
<td>c0::y0</td>
</tr>
<tr>
<td>+12</td>
<td>c1::vptr</td>
</tr>
<tr>
<td>+16</td>
<td>c1::x1</td>
</tr>
<tr>
<td>+20</td>
<td>c1::y1</td>
</tr>
<tr>
<td>+24</td>
<td>c1::x2</td>
</tr>
<tr>
<td>+28</td>
<td>c1::y2</td>
</tr>
</tbody>
</table>
<p>であろうと考えられます。</p>
<h3>仮想継承</h3>
<p>さらに仮想継承を考えると、話はさらにややこしくなります。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
  virtual void f0() {}
};

class c1 : <strong>virtual public c0</strong> {
public:
  int x1;
  int y1;
  virtual void f1() {}
};

class c2 : <strong>virtual public c0</strong> {
public:
  int x2;
  int y2;
  virtual void f2() {}
};

class c3 : <strong>public c1, public c2</strong> {
public:
  int x3;
  int y3;
  virtual void f3() {}
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  : c0     &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0     &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
cout &lt;&lt; &quot;c3  : c1, c2 &quot; &lt;&lt; sizeof(c3)  &lt;&lt; endl;
</pre>
<p>sizeof(c3)は <strong>52</strong> となりました。なんとも不思議な結果です。上記の各クラスから仮想関数を抜き取ってみましょう。</p>
<pre>
class c0 {
public:
  int x0;
  int y0;
};

class c1 : virtual public c0 {
public:
  int x1;
  int y1;
};

class c2 : virtual public c0 {
public:
  int x2;
  int y2;
};

class c3 : public c1, public c2 {
public:
  int x3;
  int y3;
};
...
cout &lt;&lt; &quot;c0  :        &quot; &lt;&lt; sizeof(c0)  &lt;&lt; endl;
cout &lt;&lt; &quot;c1  : c0     &quot; &lt;&lt; sizeof(c1)  &lt;&lt; endl;
cout &lt;&lt; &quot;c2  : c0     &quot; &lt;&lt; sizeof(c2)  &lt;&lt; endl;
cout &lt;&lt; &quot;c3  : c1, c2 &quot; &lt;&lt; sizeof(c3)  &lt;&lt; endl;
</pre>
<p>このとき、sizeof(c3)は <strong>40</strong>となりました。仮想継承したベースクラスc0の各メンバは、c3内にはひとつしかないはずなので、予想されるサイズは (8&#215;4=)32<br />
であるはずなのに、8byte余計な領域を必要としています。仮想関数を元に戻し、</p>
<pre>
cout &lt;&lt; offsetof(c3, x1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y1) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, x2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y2) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, x3) &lt;&lt; endl;
cout &lt;&lt; offsetof(c3, y3) &lt;&lt; endl;
</pre>
<p>を実行すると、それぞれ 8, 12, 24, 28, 32, 36<br />
となりました。この結果から得られるメモリ・レイアウトは:</p>
<table summary="レイアウト">
<tbody>
<tr>
<th>offset</th>
<th>member</th>
</tr>
<tr>
<td>+0</td>
<td>???</td>
</tr>
<tr>
<td>+4</td>
<td>???</td>
</tr>
<tr>
<td>+8</td>
<td>c1::x1</td>
</tr>
<tr>
<td>+12</td>
<td>c1::y1</td>
</tr>
<tr>
<td>+16</td>
<td>???</td>
</tr>
<tr>
<td>+20</td>
<td>???</td>
</tr>
<tr>
<td>+24</td>
<td>c2::x2</td>
</tr>
<tr>
<td>+28</td>
<td>c2::y2</td>
</tr>
<tr>
<td>+32</td>
<td>c3::x3</td>
</tr>
<tr>
<td>+36</td>
<td>c3::y3</td>
</tr>
<tr>
<td>+40</td>
<td>???</td>
</tr>
<tr>
<td>+44</td>
<td>???</td>
</tr>
<tr>
<td>+48</td>
<td>???</td>
</tr>
</tbody>
</table>
<p>&apos;???&apos;の領域のうち、少なくとも2つはc0::x0とc0::y0でしょう。</p>
<p><strong>「C++はCよりデカい」</strong>か?</p>
<p>結論から言うと、仮想関数、多重継承、仮想継承などなどを駆使したC++コードは確かにデカくなることがわかりました。最後の例など、Cでなら32byteで済むところが、20byteも余計にメモリを消費します。インスタンスひとつにつき20byteのメモリを余計に消費するのですからアプリケーション全体では無視できないでしょうね。</p>
<p>しかし、だからといってC++コードから仮想関数を排除するのは決して得策とは思えません。仮想関数と同じふるまいを仮想関数なしで実現するには、オブジェクトの本来の型を識別するためのIDをメンバとして用意しなければなりませんし、関数呼び出しのたびに、IDによる処理の振り分けを行なうことになります。結局仮想関数を用いた場合と同等のサイズになることが予想されるうえ、コードのメンテナンス性が著しく低下するでしょう。</p>
<p class="note">※注意:<br />
これはMicrosoft Visual C++ v6.0での実験結果です。<br />
これ以外のOSや処理系では異なる結果が得られることでしょう。</p>
<h3>おまけ : やりがちな誤り</h3>
<p>仮想関数のお話ついでに、うっかりやってしまいそうな誤りをひとつ紹介しておきます。あなたはいくつかのクラスを設計/実装することになり、それぞれのコンストラクタ/デストラクタが確実に行われているか、画面に表示して確かめたくなりました。そこであなたはこんなコードを書きました。</p>
<pre>
class base {
public:
  base();
  virtual ‾base();
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

base::base() {
  on_ctor();
}

base::‾base() {
  on_dtor();
}

void base::on_ctor() {
  cout &lt;&lt; &quot;base::ctor¥n&quot;;
}

void base::on_dtor() {
  cout &lt;&lt; &quot;base::dtor¥n&quot;;
}
</pre>
<p>class baseはそのコンストラクタ/デストラクタの中からそれぞれ仮想関数on_ctor()/on_dtor()を呼んでいます。ですからbaseの派生クラス側でon_ctor()/on_dtor()を再定義すればいいはずです。</p>
<pre>
class c0 : public base {
public:
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

void c0::<strong>on_ctor</strong>() {
  cout &lt;&lt; &quot;c0::ctor¥n&quot;;
}

void c0::<strong>on_dtor</strong>() {
  cout &lt;&lt; &quot;c0::dtor¥n&quot;;
}

class c1 : public base {
public:
  <strong>virtual void on_ctor</strong>();
  <strong>virtual void on_dtor</strong>();
};

void c1::<strong>on_ctor</strong>() {
  cout &lt;&lt; &quot;c1::ctor¥n&quot;;
}

void c1::<strong>on_dtor</strong>() {
  cout &lt;&lt; &quot;c1::dtor¥n&quot;;
}

int main() {
  c0 x;
  c1 y;
  return 0;
}
</pre>
<p>さて、結果はどうなったでしょう&#8230;</p>
<pre>
base::ctor
base::ctor
base::dtor
base::dtor
</pre>
<p>惨澹たる結果となりました。</p>
<p>そう、C++ではコンストラクタ/デストラクタの中から仮想関数を呼んでも、導出クラスで再定義された関数には飛んでこないんです。</p>
<p>この例のように、コンストラクタ/デストラクタから直接仮想関数を呼んでいるのであればそれに気づくのにそう時間はかからないのですが、コンストラクタ/デストラクタが呼んでいる関数の中、あるいはさらにそこから呼ばれている関数&#8230;<br />
コンストラクタ/デストラクタが終了するまでに直接的あるいは間接的に呼ばれている一連の関数の中に仮想関数が含まれていたとき、そのふるまいは意図しているものとは異なることになるのです。</p>
<p>コンストラクタ/デストラクタ内から仮想関数を呼び出すことはできませんから、仕方なしにコンストラクト/デストラクトと初期化/後始末を分離しなければなりません。</p>
<pre>
// ----- これならちゃんと動く,,, -----
class base {
public:
  base() {}
  virtual ‾base() {}
  void    <strong>start</strong>();
  void    <strong>stop</strong>();
  virtual void on_ctor();
  virtual void on_dtor();
};

void base::<strong>start</strong>() {
  <strong>on_ctor</strong>();
}

void base::<strong>stop</strong>() {
  <strong>on_dtor</strong>();
}

void base::on_ctor() {
  cout &lt;&lt; &quot;base::ctor¥n&quot;;
}

void base::on_dtor() {
  cout &lt;&lt; &quot;base::dtor¥n&quot;;
}

/*
 * c0, c1 はそのまま...
 */

int main() {
  c0 x; x.<strong>start</strong>();
  c1 y; y.<strong>start</strong>();
  y.<strong>stop</strong>();
  x.<strong>stop</strong>();
  return 0;
}
</pre>
<p>これがJavaだと思った通りに動いてくれます。ま、Javaにはデストラクタがありませんけど&#8230;</p>
<pre>
// ----- Trial.java -----
class base {
  base() {
    on_ctor();
  }
  void on_ctor() {
    System.out.println(&quot;base::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;base::dtor&quot;);
  }
}

class c0 extends base {
  void on_ctor() {
    System.out.println(&quot;c0::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;c0::dtor&quot;);
  }
}

class c1 extends base {
  void on_ctor() {
    System.out.println(&quot;c1::ctor&quot;);
  }
  protected void finalize() {
    System.out.println(&quot;c1::dtor&quot;);
  }
}

public class Trial {
  public static void main(String[] arg) throws Exception {
    c0 x = new c0();
    c1 y = new c1();
  }
}
</pre>
<p>実行結果:</p>
<pre>
c0::ctor
c1::ctor
</pre>
<p>&#8230;ほらね。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/sizeof/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>抽象データ型と Java/C++ そして COM/CORBA</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/adt/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/adt/#comments</comments>
		<pubDate>Mon, 02 Apr 2007 05:59:48 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=3439</guid>
		<description><![CDATA[抽象データ型とは&#8230; 抽象データ型 継承 多態 をオブジェクト指向の三本柱などと称しています。その中でも抽象データ型(あるいはデータの抽象化)はオブジェクト指向の最も基本的で重要な概念ではないかと考えます。 &#34;データを抽象化する&#34;とは、データをそれに対して適用できる操作の集合で定義することです。 簡単な例として&#34;カウンタ&#34;を考えてみましょう。カウンタには3つの操作: +1する (increment) -1する (decrement) 現在値を取得する を提供させることにします。 さて、このカウンタをCで実現するとどうなるでしょうか&#8230; typedef struct { long value_; } Counter; Counter* counter_create(); void counter_incr(Counter*); void counter_decr(Counter*); long counter_value(const Counter*); void counter_release(Counter*); /* ---- ここから実装部。Counterの利用者には見せない ---- */ Counter* counter_create() { Counter* c = (Counter*)malloc(sizeof(Counter)); c-&#62;value_ = 0; return c; } void counter_incr(Counter* c) { ++c-&#62;value_; [...]]]></description>
			<content:encoded><![CDATA[<h3>抽象データ型とは&#8230;</h3>
<ul>
<li>抽象データ型</li>
<li>継承</li>
<li>多態</li>
</ul>
<p>
をオブジェクト指向の三本柱などと称しています。その中でも抽象データ型(あるいはデータの抽象化)はオブジェクト指向の最も基本的で重要な概念ではないかと考えます。
</p>
<p>
&quot;<strong>データを抽象化する</strong>&quot;とは、データを<strong>それに対して適用できる操作の集合で定義する</strong>ことです。
</p>
<p>
簡単な例として&quot;カウンタ&quot;を考えてみましょう。カウンタには3つの操作:
</p>
<ul>
<li>+1する (increment)</li>
<li>-1する (decrement)</li>
<li>現在値を取得する</li>
</ul>
<p>を提供させることにします。</p>
<p>さて、このカウンタをCで実現するとどうなるでしょうか&#8230;</p>
<p><span id="more-3439"></span></p>
<pre>
typedef struct {
  long value_;
} Counter;

Counter* counter_create();
void     counter_incr(Counter*);
void     counter_decr(Counter*);
long     counter_value(const Counter*);
void     counter_release(Counter*);

/* ---- ここから実装部。Counterの利用者には見せない ---- */

Counter* counter_create() {
  Counter* c = (Counter*)malloc(sizeof(Counter));
  c-&gt;value_ = 0;
  return c;
}

void     counter_incr(Counter* c) { ++c-&gt;value_; }

void     counter_decr(Counter* c) { --c-&gt;value_; }

long     counter_value(const Counter* c) { return c-&gt;value_; }

void     counter_release(Counter* c) { free(c); }
</pre>
<p>&#8230;こんな感じになりますか。C++の入門書の中には、</p>
<p><strong>データ(構造体) + アルゴリズム(関数) = オブジェクト(クラス)</strong></p>
<p>と説明されたものもあります。つまり、構造体に、それに適用される操作を付け加えたものがクラスだ、というのです。</p>
<pre>
class Counter {
private:
  long value_;
public:
  Counter();
  ~Counter();
  void incr();
  void decr();
  long value() const;
};

/* --- ここから実装部。Counterの利用者には見せない --- */

Counte::Counter() : value_(0) {}

Counte::~Counter()  {}

void Counter::incr() { ++value_; }

void Counter::decr() { --value_; }

long Counter::value() const { return value_; }
</pre>
<p>Counterのメンバ変数value_は、利用者に勝手にアクセスさせないようprivate部に置きました。</p>
<p>カウンタの利用者にしてみればprivateメンバに何が定義されているかなんて(どうせアクセスできないのだから)知る必要もありません。+1できて、-1できて、値を取得できるということだけで十分です。</p>
<p>カウンタの実装者にしても同様です。+1する、-1する、値を返す、の3つの機能をきちんと実装できるのなら、privateメンバはその実現のためになら何を定義しようがかまわない。メソッドが呼ばれるたびに電話回線を通してはるか遠くにあるデータベースを更新・参照したっていいでしょうよ。</p>
<p>結局カウンタについて利用者と実装者との間できめておかなければならないのは、カウンタに対する操作だけなんです。</p>
<p><strong>&quot;Xに対して何ができるか&quot;で定義したX</strong>、それが<strong>抽象データ型</strong>なんですね。</p>
<h4>Javaにおける抽象データ型</h4>
<p>Javaは、抽象データ型を直接サポートするキーワード<strong>interface</strong>を持っています。</p>
<pre>
public interface Counter {
  void incr();
  void decr();
  int  value();
}
</pre>
<p>interfaceはclassではありません。実体が存在しません。Counterの実装者はinterfaceを実装(implements)するclassを作ります。</p>
<pre>
class CounterImpl implements Counter {
  private int value_;
  public CounterImpl() { value_ = 0; }
  // interface Counter を実装する
  public void incr() { ++value_; }
  public void decr() { --value_; }
  public int value() { return value_; }
}
</pre>
<p>利用者にCounterImplの存在を気づかせないために、Counterを生成するクラスCounterFactoryを用意しましょう。</p>
<pre>
public class CounterFactory {
  public static Counter create() { return new CounterImpl(); }
}
</pre>
<p>CounterFactory.create()はCounterImplをnewし、それをCounterとして返します。利用者にはCounterFactory.create()で手に入れたオブジェクトが実際にはCounterImplだということを隠しています。それでいいんです。利用者が欲しがっているのはCounterImplではなく、Counterに対する操作なのですから。</p>
<pre>
public class Client {
  public static void main(String[] arg) {
    Counter c = CounterFactory.create();
    for ( int i = 0; i &lt; 3; ++i ) {
      c.incr();
      System.out.println("value= "+c.value());
    }
  }
}
</pre>
<p>Clientをアプレット化するとこんな感じになります。<a id="reference_1" href="#footnote_1" class="reference">[1]</a></p>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/java.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h4>C++における抽象データ型</h4>
<p>残念ながらC++ではJavaのinterfaceのような抽象データ型を直接にはサポートしていません。が、それと同等のことなら可能です。一切のメンバ変数を持たず、そして全メンバ関数を純粋仮想関数とするのです。</p>
<pre>
class Counter {
public:
  virtual ~Counter() {}
  virtual void incr() =0;
  virtual void decr() =0;
  virtual long value() const =0;
};
</pre>
<p>そしてこのCounterから派生したCounterImpl、およびCounterFactoryを作りましょう。</p>
<pre>
class CounterFactory {
public:
  static Counter* create();
  static void     release(Counter*);
};

/* --- ここから実装部。Counterの利用者には見せない --- */

class CounterImpl : virtual public Counter {
private:
  long value_;
public:
   CounterImpl() : value_(0) {}
   virtual void incr() { ++value_; }
   virtual void decr() { --value_; }
   virtual long value() const { return value_; }
};

Counter* CounterFactory::create() {
  return new CounterImpl;
}

void CounterFactory::release(Counter* c) {
   delete c;
}
</pre>
<p>利用者(Client)は以下のようになります。</p>
<pre>
using namespace std;

int main() {
  adt::Counter* c = adt::CounterFactory::create();
  for ( int i = 0; i &lt; 3; ++i ) {
    c-&gt;incr();
    cout &lt;&lt; "value= " &lt;&lt; c-&gt;value() &lt;&lt; endl;
  }
  adt::CounterFactory::release(c);
  return 0;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/cpp.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h3>言語を越えて&#8230;</h3>
<p>抽象データ型はその利用者と実装者との間の申し合わせです。これを発展させると、抽象データ型を利用者と実装者双方が認識できる、すなわち実装者はそのインタフェースをサポートするオブジェクトを生成し、利用者はそのインタフェースを提供するオブジェクトを手に入れて利用することができるなら、利用者と実装者が互いに異なる言語で書かれていてもいいはずです。それを実現したのがCOM、そしてCORBAです。</p>
<h4>COM (Component Object Model)</h4>
<p>Visual C++ 6.0 ATLを使ってCOMサーバ(実装側)を作ってみましょう。</p>
<p>Visual C++の&#8221;ATL COMAppWizard&#8221;を使えばCOMサーバをとっても簡単に作れます。</p>
<p>CounterのインタフェースICounterを作ってできたファイルadt.idlを以下に示します。</p>
<pre>
...
  interface <strong>ICounter</strong> : IDispatch {
    [id(1), helpstring("+1する")] HRESULT <strong>incr</strong>();
    [id(2), helpstring("-1する")] HRESULT <strong>decr</strong>();
    [propget, id(3), helpstring("値を返す")] HRESULT <strong>value</strong>([out, retval] long *pVal);
  };
...
  coclass <strong>Counter</strong> {
    [default] interface <strong>ICounter</strong>;
  };
...
</pre>
<p>adt.idlの中に、incr,decr,valueをサポートするインタフェースICounterと、ICounterをインタフェースとして持つクラスCounterがあることがわかるでしょう。</p>
<p>以下に示すのは最終的にできあがったCounterの実装部CCounterです。</p>
<pre>
class ATL_NO_VTABLE <strong>CCounter</strong> :
  public CComObjectRootEx&lt;CComSingleThreadModel&gt;,
  public CComCoClass&lt;CCounter, &amp;CLSID_Counter&gt;,
  public IDispatchImpl&lt;<strong>ICounter</strong>, &amp;IID_ICounter, &amp;LIBID_ADTLib&gt; {
public:
  CCounter() { value_ = 0; }

DECLARE_REGISTRY_RESOURCEID(IDR_COUNTER)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CCounter)
  COM_INTERFACE_ENTRY(ICounter)
  COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

public:
  STDMETHOD(<strong>get_value</strong>)(long *pVal);
  STDMETHOD(<strong>decr</strong>)();
  STDMETHOD(<strong>incr</strong>)();
private:
  long value_;
};
/* --- implementation --- */

STDMETHODIMP CCounter::<strong>incr</strong>() {
  ++value_;
  return S_OK;
}

STDMETHODIMP CCounter::<strong>decr</strong>() {
  --value_;
  return S_OK;
}

STDMETHODIMP CCounter::<strong>get_value</strong>(long *pVal) {
  *pVal = value_;
  return S_OK;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/com.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<p>これをコンパイルすればadt.dllができあがり、レジストリに登録されます。これでCOMをサポートする言語ならなんであれ、利用者はCounterを使うことができるはずです。</p>
<p>Visual BasicからCounterを使ってみましょうか。VisualBasicのメニューから&#8221;プロジェクト|参照設定&#8221;ることで、VisualBasicがCounterのインタフェースを認識します。</p>
<p>フォーム上にテキストボックス(txtValue)とボタンをふたつ(btnIncr,btnDecr)配置して:</p>
<pre>
Option Explicit

Private c As Counter

Private Sub btnDecr_Click()
  c.decr
  txtValue = c.Value
End Sub

Private Sub btnIncr_Click()
  c.incr
  txtValue = c.Value
End Sub

Private Sub Form_Load()
  Set c = New Counter
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Set c = Nothing
End Sub
</pre>
<p>なんとまぁ、たったこれだけで動いてしまうんです。</p>
<h4>CORBA (Common Object Request Broker Architecture</h4>
<p>CORBAはインタフェースを利用者と実装者が共有するという考えをもう一歩すすめ、異なるマシン/異なるOS/異なるプロセス/異なる言語による利用者/実装者を可能にしてくれます。</p>
<p>Orbixを使ってCounterを作ります。まずはインタフェースの定義から。</p>
<pre>
module adt {
  interface <strong>Counter</strong> {
    void <strong>incr</strong>();
    void <strong>decr</strong>();
    readonly attribute long <strong>value</strong>;
  };
};
</pre>
<p>avaのinterfaceとそっくりです。このインタフェース定義ファイルadt.idlをIDLコンパイラに食わせると、利用者/実装者それぞれのためにスタブ/スケルトンを吐いてくれます。実装者はスケルトンを基にCounterを実装し、利用者はスタブを使ってCounterにアクセスします。</p>
<pre>
/*
 * interface Counter の実装
 */
#include "Counter.hh"

namespace adt {

  class CounterImpl : public virtual CounterBOAImpl {
  private:
    CORBA::Long value_;

  public:
    CounterImpl() : value_(0) {}

    virtual void <strong>incr</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
    virtual void <strong>decr</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
    virtual CORBA::Long <strong>value</strong>(CORBA::Environment &amp;IT_env=CORBA::default_environment) ;
  };

}

namespace adt {

  void CounterImpl::incr(CORBA::Environment &amp;IT_env) {
    ++value_;
  }

  void CounterImpl::<strong>decr</strong>(CORBA::Environment &amp;IT_env) {
    --value_;
  }

  CORBA::Long CounterImpl::<strong>value</strong>(CORBA::Environment &amp;IT_env)  {
    return value_;
  }

}
</pre>
<p>実装者(サーバ)は(COMをレジストリに登録するように)あらかじめOrbixデーモンに登録しておきます。利用者はデーモンの仲介でCounterを手に入れ、利用します。</p>
<pre>
/*
 * クライアント
 */
#include "Counter.hh"
#include &lt;iostream.h&gt;

int main() {
  adt::Counter_var c;
  c = adt::Counter::_bind(":adt");
  for ( int i = 0; i &lt; 3; ++i ) {
    c-&gt;incr();
    cout &lt;&lt; "value= " &lt;&lt; c-&gt;value() &lt;&lt; endl;
  }
  return 0;
}
</pre>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/corba.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="src" title="src" width="58" height="13" class="alignnone size-full wp-image-2351" /></a></p>
<h5>注釈</h5>
<ol>
<li id="footnote_1"><a href="#reference_1" class="footnote">^</a>弊社のWebサイト公開ポリシーによりアプレットは削除しております。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/adt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMLを用いた状態遷移 part-2</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/xml-fsmgen/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/xml-fsmgen/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 06:17:13 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=4165</guid>
		<description><![CDATA[はじめに 前作XMLを用いた状態遷移では状態遷移表をXMLで記述し、それをXMLパーサで解析して状態遷移表のふるまいを動的に再現すると言う試みを紹介しました。 今回はここからさらに発展させ、XMLで記述された状態遷移表に基づいて、そこに表現されたとおりに振る舞うC++/Javaのソースコードを生成することを考えます。つまりはSMCのXML版です。SMCでは構文解析/字句解析にyacc/lexを用いていましたが、XMLで記述することで、yacc/lexから解放されました。 前回用いた例&#8221;(門番のいる)ゲート&#8221;の状態遷移表を再度示します。 現在の状態 イベント 次の状態 アクション Locked Coin Unlocked Unlock Pass Locked Alarm Unlocked Coin Unlocked ThankYou Pass Locked Lock ゲートがLocked状態のとき: Coinイベントが発生したら、Unlocked状態に遷移してUnlockアクションを起こす。 Passイベントが発生したら、閉じているゲートを無理矢理誰かが通過したことに対しAlarmアクションを起こす ゲートがUnlocked状態のとき: Passイベントが発生したら、Locked状態に戻してLockアクションを起こす Coinイベントが発生したら、余分なお金を頂いた事に礼を言う(ThankYouアクションを起こす)。 状態遷移表のXMLによる表現 この状態遷移表のXMLによる記述は前作から少しばかり拡張されています。 文法 まず、状態遷移表をXMLで表現するための文法をDTD(Document Type Definition)で定義します。 これは今後いかなる状態遷移表をXMLで記述する場合でも再利用できます。 fsm.dtd : 状態遷移表の文法 &#60;!-- Document Type Definition for 'Finite State Machine' --&#62; &#60;!ELEMENT FSM (Note?, State*) &#62; &#60;!ATTLIST FSM name [...]]]></description>
			<content:encoded><![CDATA[<h2 id="intro">はじめに</h2>
<p>前作<a href="/cpptechdoc/article/xml-fsm/">XMLを用いた状態遷移</a>では状態遷移表をXMLで記述し、それをXMLパーサで解析して状態遷移表のふるまいを動的に再現すると言う試みを紹介しました。</p>
<p>
  今回はここからさらに発展させ、XMLで記述された状態遷移表に基づいて、そこに表現されたとおりに振る舞うC++/Javaのソースコードを生成することを考えます。つまりは<a href="/cpptechdoc/misc/smc_j/">SMC</a>のXML版です。SMCでは構文解析/字句解析にyacc/lexを用いていましたが、XMLで記述することで、yacc/lexから解放されました。</p>
<p>前回用いた例&#8221;(門番のいる)ゲート&#8221;の状態遷移表を再度示します。</p>
<p><span id="more-4165"></span></p>
<table>
<tr>
<th>現在の状態</th>
<th>イベント</th>
<th>次の状態</th>
<th>アクション</th>
</tr>
<tr>
<td >Locked</td>
<td>Coin</td>
<td>Unlocked</td>
<td>Unlock</td>
</tr>
<tr>
<td>Pass</td>
<td>Locked</td>
<td>Alarm</td>
</tr>
<tr>
<td >Unlocked</td>
<td>Coin</td>
<td>Unlocked</td>
<td>ThankYou</td>
</tr>
<tr>
<td>Pass</td>
<td>Locked</td>
<td>Lock</td>
</tr>
</table>
<ul>
<li>ゲートがLocked状態のとき:
<ul>
<li>Coinイベントが発生したら、Unlocked状態に遷移してUnlockアクションを起こす。</li>
<li>Passイベントが発生したら、閉じているゲートを無理矢理誰かが通過したことに対しAlarmアクションを起こす</li>
</ul>
</li>
<li>ゲートがUnlocked状態のとき:
<ul>
<li>Passイベントが発生したら、Locked状態に戻してLockアクションを起こす</li>
<li>Coinイベントが発生したら、余分なお金を頂いた事に礼を言う(ThankYouアクションを起こす)。</li>
</ul>
</li>
</ul>
<hr />
<h2 id="xml">状態遷移表のXMLによる表現</h2>
<p>この状態遷移表のXMLによる記述は前作から少しばかり拡張されています。</p>
<h3>文法</h3>
<p>
  まず、状態遷移表をXMLで表現するための文法をDTD(Document Type Definition)で定義します。<br />
  これは今後いかなる状態遷移表をXMLで記述する場合でも再利用できます。
</p>
<dl>
<dt><a href="/wp-content/uploads/2006/04/fsm.dtd">fsm.dtd</a> : 状態遷移表の文法</dt>
<dd>
<pre>
&lt;!-- Document Type Definition for 'Finite State Machine' --&gt;

  &lt;!ELEMENT FSM   (Note?, State*) &gt;
  &lt;!ATTLIST FSM   name     NMTOKEN #REQUIRED&gt;
  &lt;!ATTLIST FSM   initial  NMTOKEN #REQUIRED&gt;
  &lt;!ATTLIST FSM   terminal NMTOKEN #IMPLIED&gt;
  &lt;!ATTLIST FSM   package  NMTOKEN #IMPLIED&gt;
  &lt;!ATTLIST FSM   header   NMTOKEN #IMPLIED&gt;

  &lt;!ELEMENT State (Note?, Event*) &gt;
  &lt;!ATTLIST State name  NMTOKEN  #REQUIRED&gt;
  &lt;!ATTLIST State enter NMTOKEN  #IMPLIED&gt;
  &lt;!ATTLIST State exit  NMTOKEN  #IMPLIED&gt;

  &lt;!ELEMENT Event (Note?) &gt;
  &lt;!ATTLIST Event name       NMTOKEN  #REQUIRED&gt;
  &lt;!ATTLIST Event transition NMTOKEN  #IMPLIED&gt;
  &lt;!ATTLIST Event action     NMTOKEN  #IMPLIED&gt;

  &lt;!ELEMENT Note (#PCDATA)&gt;
  </pre>
</dd>
</dl>
<ul>
<li>
<pre>
&lt;!ELEMENT FSM   (Note?, State*) &gt;
</pre>
<p>
&lt;FSM&gt;タグはその要素として0または1個の&lt;Note&gt;タグに続いて&lt;State&gt;タグを0個以上含みます。&lt;Note&gt;タグには適当なコメントを記述できます。
</p>
</li>
<li>
<pre>
&lt;!ATTLIST FSM   name     NMTOKEN #REQUIRED&gt;
&lt;!ATTLIST FSM   initial  NMTOKEN #REQUIRED&gt;
&lt;!ATTLIST FSM   terminal NMTOKEN #IMPLIED&gt;
&lt;!ATTLIST FSM   package  NMTOKEN #IMPLIED&gt;
&lt;!ATTLIST FSM   header   NMTOKEN #IMPLIED&gt;
</pre>
<p>
&lt;FSM&gt;タグはname(名前),initial(初期状態),terminal(停止状態)、そしてpackage(パッケージ名)とheader(ヘッダディレクトリ)を属性に持ちます。name,initialは必須属性です。packageは生成するJava-packageを指定します。たとえば<br />
name=&#8221;Gate&#8221; package=&#8221;jp.co.s34.fsm&#8221; とすると、生成されるコードはpackage<br />
jp.co.s34.fsm.Gate に置かれます。なお、生成されるJavaコードは Context.java,<br />
State.java, Fsm.java の3本です。また、C++コードはFSM名.h および FSM名.cpp<br />
です。宣言/実装はすべて namespace FSM名 {&#8230;} で囲まれます。<br />
headerは生成するC++コードの宣言部(ヘッダ)のディレクトリを指定します。たとえば name=&#8221;Gate&#8221;<br />
header=&#8221;fsm&#8221; とすると、生成される実装部 fsm.cpp は #include &#8220;fsm/Gate.h&#8221;<br />
します。</p>
</li>
<li>
<pre>
&lt;!ELEMENT State (Note?, Event*) &gt;
</pre>
<p>
&lt;State&gt;タグはその要素として0または1個の&lt;Note&gt;タグに続いて&lt;Event&gt;タグを0個以上含みます。&lt;Note&gt;タグには適当なコメントを記述できます。</p>
</li>
<li>
<pre>
&lt;!ATTLIST State name  NMTOKEN  #REQUIRED&gt;
&lt;!ATTLIST State enter NMTOKEN  #IMPLIED&gt;
&lt;!ATTLIST State exit  NMTOKEN  #IMPLIED&gt;
</pre>
<p>
&lt;State&gt;タグはname(名前),enter(入場動作)およびexit(退場動作)を属性に持ちます。nameは必須属性です。</p>
</li>
<li>
<pre>
&lt;!ELEMENT Event (Note?) &gt;
  </pre>
<p>&lt;Event&gt;タグはその要素として0または1個の&lt;Note&gt;タグを含みます。</p>
</li>
<li>
<pre>
&lt;!ATTLIST Event name       NMTOKEN  #REQUIRED&gt;
&lt;!ATTLIST Event transition NMTOKEN  "none"&gt;
&lt;!ATTLIST Event action     NMTOKEN  "none"&gt;
  </pre>
<p>
    &lt;EventM&gt;タグはname(名前),transition(次の状態),action(アクション)を属性に持ちます。nameは必須属性です。transitionを省略すると、そのイベントは状態の遷移を起こさず、入場動作/退場動作も行ないません。</p>
</li>
</ul>
<h3>文法に基づいた状態遷移表</h3>
<p>では、この文法に基づいてゲートの状態遷移表をXMLで記述します。</p>
<dl>
<dt><a href="/wp-content/uploads/2006/04/gate.xml">gate.xml</a> : ゲートの状態遷移表</dt>
<dd>
<pre>
&lt;?xml version="1.0" encoding="Shift_JIS"?&gt;
&lt;?xml-stylesheet type="text/xsl" href="fsm.xsl"?&gt;

&lt;!DOCTYPE FSM SYSTEM "fsm.dtd"&gt;

&lt;FSM name="Gate" initial="Locked" package="jp.co.s34.fsm" header="fsm"&gt;
  &lt;Note&gt;門番のいるゲート&lt;/Note&gt;
  &lt;State name="Locked" enter="EnterLocked" exit="ExitLocked"&gt;
    &lt;Note&gt;ゲートが閉じている&lt;/Note&gt;
    &lt;Event name="Coin" action="Unlock" transition="Unlocked"&gt;
      &lt;Note&gt;ゲートを開ける&lt;/Note&gt;
    &lt;/Event&gt;
    &lt;Event name="Pass" action="Alarm"&gt;
      &lt;Note&gt;不法侵入&lt;/Note&gt;
    &lt;/Event&gt;
  &lt;/State&gt;

  &lt;State name="Unocked" enter="EnterUnlocked" exit="ExitUnlocked"&gt;
    &lt;Note&gt;ゲートが開いている&lt;/Note&gt;
    &lt;Event name="Coin" action="ThankYou"&gt;
      &lt;Note&gt;チップとして受け取る&lt;/Note&gt;
    &lt;/Event&gt;
    &lt;Event name="Pass" action="Lock" transition="Locked"&gt;
      &lt;Note&gt;ゲートを閉じる&lt;/Note&gt;
    &lt;/Event&gt;
  &lt;/State&gt;

&lt;/FSM&gt;
  </pre>
</dd>
</dl>
<p>XSLスタイルシートを用意しました。</p>
<dl>
<dt><a href="/wp-content/uploads/2006/04/fsm.xsl">fsm.xsl</a> : スタイルシート</dt>
<dd>
<pre>
&lt;?xml version="1.0" encoding="Shift_JIS"?&gt;
&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" &gt;
  &lt;xsl:template match="/"&gt;
    &lt;html&gt;
      &lt;header&gt;
        &lt;TITLE&gt;XMLによる状態遷移表&lt;/TITLE&gt;
      &lt;/header&gt;
      &lt;body&gt;
        &lt;hr/&gt;
        &lt;xsl:for-each select="FSM"&gt;
          &lt;h1&gt;&lt;xsl:value-of select="@name"/&gt; の状態遷移&lt;/h1&gt;
          &lt;p&gt;&lt;i&gt;&lt;xsl:value-of select="Note"/&gt;&lt;/i&gt;&lt;/p&gt;
          &lt;table&gt;
            &lt;tr&gt;
              &lt;th align="left"&gt;初期状態&lt;/th&gt;
              &lt;td&gt;&lt;font color="blue"&gt;&lt;xsl:value-of select="@initial"/&gt;&lt;/font&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
              &lt;th align="left"&gt;停止状態&lt;/th&gt;
              &lt;td&gt;&lt;font color="blue"&gt;&lt;xsl:value-of select="@terminal"/&gt;&lt;/font&gt;&lt;/td&gt;
            &lt;/tr&gt;
          &lt;/table&gt;
          &lt;ul&gt;Java source&lt;p/&gt;
            &lt;font color="gray"&gt;
            &lt;li&gt;&lt;xsl:value-of select="@package"/&gt;.&lt;xsl:value-of select="@name"/&gt;.Context.java&lt;/li&gt;
            &lt;li&gt;&lt;xsl:value-of select="@package"/&gt;.&lt;xsl:value-of select="@name"/&gt;.State.java&lt;/li&gt;
            &lt;li&gt;&lt;xsl:value-of select="@package"/&gt;.&lt;xsl:value-of select="@name"/&gt;.FSM.java&lt;/li&gt;
            &lt;/font&gt;
          &lt;/ul&gt;
          &lt;ul&gt;C++ source&lt;p/&gt;
            &lt;font color="gray"&gt;
            &lt;li&gt;&lt;xsl:value-of select="@name"/&gt;.h&lt;/li&gt;
            &lt;li&gt;&lt;xsl:value-of select="@name"/&gt;.cpp&lt;/li&gt;
            &lt;/font&gt;
          &lt;/ul&gt;

          &lt;ol&gt;
          &lt;xsl:for-each select="State"&gt;
            &lt;li&gt;状態 : &lt;font color="blue"&gt;&lt;xsl:value-of select="@name"/&gt;&lt;/font&gt;
            &lt;p&gt;&lt;i&gt;&lt;xsl:value-of select="Note"/&gt;&lt;/i&gt;&lt;/p&gt;
            &lt;p&gt;
            &lt;ul&gt;
              &lt;li&gt;入場動作 : &lt;font color="red"&gt;&lt;xsl:value-of select="@enter"/&gt;&lt;/font&gt;&lt;/li&gt;
              &lt;li&gt;退場動作 : &lt;font color="red"&gt;&lt;xsl:value-of select="@exit"/&gt;&lt;/font&gt;&lt;/li&gt;
            &lt;/ul&gt;
            &lt;/p&gt;
            &lt;p&gt;
            &lt;table border="2"&gt;
            &lt;tr&gt;
              &lt;th&gt;イベント&lt;/th&gt;
              &lt;th&gt;アクション&lt;/th&gt;
              &lt;th&gt;遷移先&lt;/th&gt;
              &lt;th&gt;説明&lt;/th&gt;
            &lt;/tr&gt;
            &lt;xsl:for-each select="Event" &gt;
              &lt;tr&gt;
                &lt;td&gt;&lt;font color="green"&gt;&lt;xsl:value-of select="@name"/&gt;&lt;/font&gt;&lt;/td&gt;
                &lt;td&gt;&lt;font color="red"&gt;&lt;xsl:value-of select="@action"/&gt;&lt;/font&gt;&lt;/td&gt;
                &lt;td&gt;&lt;font color="blue"&gt;&lt;xsl:value-of select="@transition"/&gt;&lt;/font&gt;&lt;/td&gt;
                &lt;td&gt;&lt;i&gt;&lt;xsl:value-of select="Note"/&gt;&lt;/i&gt;&lt;/td&gt;
              &lt;/tr&gt;
            &lt;/xsl:for-each&gt;
            &lt;/table&gt;
            &lt;/p&gt;
            &lt;/li&gt;
          &lt;/xsl:for-each&gt;
          &lt;/ol&gt;
          &lt;p/&gt;
          &lt;p&gt;※ 遷移先が空欄である場合、状態の変化がなく、入場/退場動作も行わないことを意味します&lt;/p&gt;
          &lt;hr/&gt;
        &lt;/xsl:for-each&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;
  </pre>
</dd>
</dl>
<p>XML/XSLに対応したブラウザであれば、次のように整形されたドキュメントが表示されます。</p>
<dl>
<dt>スタイルシートによる整形(一部)</dt>
<dd><img src="/wp-content/uploads/2006/04/gate.gif"  alt="gate.gif" /></dd>
</dl>
<h2 id="machanism">生成コードの駆動メカニズム</h2>
<p>
  状態遷移表のとおりに振る舞うコードの生成に、&#8221;STATEパターン&#8221;と呼ばれるデザイン・パターンを用います。STATEパターンによる状態遷移表のからくりは以下のとおりです。</p>
<ol>
<li>Context
<p>まず、状態遷移表に現われるすべての<strong><br />
	&#8220;red&#8221;>アクション</strong>(入場動作/退場動作を含む)を抽象メソッド(純粋仮想関数)として定義したクラスContextを用意します。(引数<br />
      FSM については後述)</p>
<pre>
class Context {
public:
  virtual void <strong>Lock</strong>(FSM*) =0;
  virutal void <strong>Unlock</strong>(FSM*) =0;
  ...
};
    </pre>
<p>
Contextに定義された<strong>アクション</strong>が実際のコードの中でどうふるまうかは状態遷移表には書かれておらず、利用者がこのContextから導出した派生クラスで再定義します。</p>
</li>
<li>State
<p>状態遷移表に現われるすべての<strong>イベント</strong>をメソッドとするクラスStateを定義します。</p>
<pre>
class State {
public:
  virtual void <strong>Coin</strong>(FSM*);
  virtual void <strong>Pass</strong>(FSM*);
  ...
};
  </pre>
<p>そして状態遷移表に現われる<strong>状態</strong>ひとつひとつをStateから導出します</p>
<pre>
class <strong>Locked</strong>State : public State {
public:
  virtual void <strong>Coin</strong>(FSM*);
  virtual void <strong>Pass</strong>(FSM*);
  ...
};

class <strong>Unocked</strong>State : public State {
public:
  virtual void <strong>Coin</strong>(FSM*);
  virtual void <strong>Pass</strong>(FSM*);
  ...
};
  </pre>
<p>
このように定義すれば、たとえばメソッド<strong>Locked</strong>State::<strong>Coin</strong>()は<strong>Locked</strong>状態において<strong>Coin</strong>というイベントが発生したときの振る舞いに対応づける事ができます。メソッドの引数FSMにコンテキストを取得するメソッド<br />
Context* getContext()、そして次の状態を設定するメソッド void setState(State*)<br />
を用意しておけば、<strong>状態</strong>と<strong>イベント</strong>の組に対応する<strong>アクション</strong>の実行と<strong>次の状態</strong>への遷移をコード化できます。</p>
<pre>
void <strong>状態</strong>State::<strong>イベント</strong>(FSM* fsm) {
  fsm-&gt;getContext()-&gt;<strong>アクション</strong>(fsm);
  fsm-&gt;setState(<strong>次の状態</strong>);
}
</pre>
</li>
<li>FSM
<p>コンテキストおよび現在の状態を内包するくらすFSMを定義します。FSMには状態遷移表に現われる全<strong>イベント</strong>をメソッドとして定義し、また、全<strong>状態</strong>をstaticメンバとして内包します。</p>
<pre>
class FSM {
  Context* context_; // コンテキスト
  State*   state_;   // 現在の<strong>状態</strong>
  static   <strong>Locked</strong>State <strong>Locked</strong>_;
  static   <strong>Unocked</strong>State <strong>Unocked</strong>_;
public:
  void <strong>Coin</strong>();
  void <strong>Pass</strong>();
  ...
};
</pre>
<p>状態遷移表の各<strong>イベント</strong>に対応したメソッドでは、現在の<strong>状態</strong>を表す変数 state_ の同名のメソッドを呼び出します。<strong>状態</strong>と<strong>イベント</strong>の組に対応した振る舞いのため、<strong>状態</strong>State::<strong>イベント</strong>()に委譲するわけです。</p>
<pre>
void FSM::<strong>イベント</strong>() {
  state_-&gt;<strong>イベント</strong>(this);
}
</pre>
</li>
</ol>
<h2 id="cplusplus">C++</h2>
<h3 id="cppgen">生成されたコード</h3>
<dl>
<dt><a href="/wp-content/uploads/2006/04/Gate.h">Gate.h</a> 宣言部</dt>
<dd>
<pre>
#ifndef __fsm_Gate_h__
#define __fsm_Gate_h__

namespace Gate {

class FSM;

class Context {
public:
  virtual void error(const char* state_name, const char* event_name) =0;
  virtual void <strong>Alarm</strong>(FSM* fsm) =0;
  virtual void <strong>EnterLocked</strong>(FSM* fsm) =0;
  virtual void <strong>EnterUnlocked</strong>(FSM* fsm) =0;
  virtual void <strong>ExitLocked</strong>(FSM* fsm) =0;
  virtual void <strong>ExitUnlocked</strong>(FSM* fsm) =0;
  virtual void <strong>Lock</strong>(FSM* fsm) =0;
  virtual void <strong>ThankYou</strong>(FSM* fsm) =0;
  virtual void <strong>Unlock</strong>(FSM* fsm) =0;
};

class State {
public:
  virtual const char* getName() const =0;
  virtual void enter(FSM* fsm);
  virtual void exit(FSM* fsm);
  virtual void <strong>Coin</strong>(FSM* fsm);
  virtual void <strong>Pass</strong>(FSM* fsm);
};

class <strong>Locked</strong>State : public State {
public:
  virtual const char* getName() const;
  virtual void enter(FSM* fsm);
  virtual void exit(FSM* fsm);
  virtual void <strong>Coin</strong>(FSM* fsm);
  virtual void <strong>Pass</strong>(FSM* fsm);
};

class <strong>Unlocked</strong>State : public State {
public:
  virtual const char* getName() const;
  virtual void enter(FSM* fsm);
  virtual void exit(FSM* fsm);
  virtual void <strong>Coin</strong>(FSM* fsm);
  virtual void <strong>Pass</strong>(FSM* fsm);
};

class FSM  {
  Context* context_;
  State*   state_;
  State*   newstate_;
  State*   terminal_;
  static <strong>Locked</strong>State <strong>Locked</strong>_;
  static <strong>Unlocked</strong>State <strong>Unlocked</strong>_;
public:
  FSM() { state_ = &amp;Locked_; terminal_ = 0; }
  bool isTerminated() const { return state_ == terminal_; }

  void setContext(Context* context) { context_ = context; }
  Context* getContext() const { return context_; }

  void setNewState(State* state) { newstate_ = state; }
  State* getNewState() { return newstate_; }

  void setState(State* state) { state_ = state; }
  void setState() { state_ = newstate_; }
  State* getState() { return state_; }

  static State* <strong>Locked</strong>() { return &amp;<strong>Locked</strong>_; }
  static State* <strong>Unlocked</strong>() { return &amp;<strong>Unlocked</strong>_; }

  void <strong>Coin</strong>() { state_-&gt;<strong>Coin</strong>(this); }
  void <strong>Pass</strong>() { state_-&gt;<strong>Pass</strong>(this); }
};

}

#endif
</pre>
</dd>
<dt><a href="/wp-content/uploads/2006/04/Gate.cpp">Gate.cpp</a> 実装部</dt>
<dd>
<pre>
#include "fsm/Gate.h"

namespace Gate {

void State::enter(FSM* fsm) {}
void State::exit(FSM* fsm) {}
void State::<strong>Coin</strong>(FSM* fsm) { fsm-&gt;getContext()-&gt;error(getName(), "<strong>Coin</strong>"); }
void State::<strong>Pass</strong>(FSM* fsm) { fsm-&gt;getContext()-&gt;error(getName(), "<strong>Pass</strong>"); }

const char* <strong>Locked</strong>State::getName() const {  return "<strong>Locked</strong>"; }
void <strong>Locked</strong>State::enter(FSM* fsm) { fsm-&gt;getContext()-&gt;<strong>EnterLocked</strong>(fsm); }
void <strong>Locked</strong>State::exit(FSM* fsm) { fsm-&gt;getContext()-&gt;<strong>ExitLocked</strong>(fsm); }

void <strong>Locked</strong>State::<strong>Coin</strong>(FSM* fsm) {
  Context* context = fsm-&gt;getContext();
  fsm-&gt;setNewState(fsm-&gt;<strong>Unlocked</strong>());
  context-&gt;<strong>Unlock</strong>(fsm);
  exit(fsm);
  fsm-&gt;setState();
  fsm-&gt;getState()-&gt;enter(fsm);
}

void <strong>Locked</strong>State::<strong>Pass</strong>(FSM* fsm) {
  Context* context = fsm-&gt;getContext();
  fsm-&gt;setNewState(this);
  context-&gt;<strong>Alarm</strong>(fsm);
  if ( fsm-&gt;getNewState() != this ) {
    exit(fsm);
    fsm-&gt;setState();
    fsm-&gt;getState()-&gt;enter(fsm);
  } else {
    fsm-&gt;setState();
  }
}

<strong>Locked</strong>State FSM::<strong>Locked</strong>_;

const char* <strong>Unlocked</strong>State::getName() const {  return "<strong>Unlocked</strong>"; }
void <strong>Unlocked</strong>State::enter(FSM* fsm) { fsm-&gt;getContext()-&gt;<strong>EnterUnlocked</strong>(fsm); }
void <strong>Unlocked</strong>State::exit(FSM* fsm) { fsm-&gt;getContext()-&gt;<strong>ExitUnlocked</strong>(fsm); }

void <strong>Unlocked</strong>State::<strong>Coin</strong>(FSM* fsm) {
  Context* context = fsm-&gt;getContext();
  fsm-&gt;setNewState(this);
  context-&gt;<strong>ThankYou</strong>(fsm);
  if ( fsm-&gt;getNewState() != this ) {
    exit(fsm);
    fsm-&gt;setState();
    fsm-&gt;getState()-&gt;enter(fsm);
  } else {
    fsm-&gt;setState();
  }
}

void <strong>Unlocked</strong>State::<strong>Pass</strong>(FSM* fsm) {
  Context* context = fsm-&gt;getContext();
  fsm-&gt;setNewState(fsm-&gt;<strong>Locked</strong>());
  context-&gt;<strong>Lock</strong>(fsm);
  exit(fsm);
  fsm-&gt;setState();
  fsm-&gt;getState()-&gt;enter(fsm);
}

<strong>Unlocked</strong>State FSM::<strong>Unlocked</strong>_;

}
</pre>
</dd>
</dl>
<h3 id="cppusr">利用者コード</h3>
<dl>
<dt><a href="/wp-content/uploads/2006/04/main.cpp">main.cpp</a></dt>
<dd>
<pre>
#include &lt;iostream&gt;
#include &lt;string&gt;

#include "fsm/Gate.h"

using namespace std;

class GateContext : public Gate::Context {
public:
  virtual void error(const char* state_name, const char* event_name) {
    cout &lt;&lt; "error state=" &lt;&lt; state_name &lt;&lt; " event=" &lt;&lt; event_name &lt;&lt; endl;
  }
  virtual void <strong>Alarm</strong>(Gate::FSM* fsm)     { cout &lt;&lt; "金払え!!" &lt;&lt; endl; }
  virtual void <strong>EnterLocked</strong>(Gate::FSM*)   { cout &lt;&lt; "ゲートが閉まりました" &lt;&lt; endl; }
  virtual void <strong>EnterUnlocked</strong>(Gate::FSM*) { cout &lt;&lt; "ゲートが開きました" &lt;&lt; endl; }
  virtual void <strong>ExitLocked</strong>(Gate::FSM*)    { cout &lt;&lt; "閉じた状態を終わります" &lt;&lt; endl; }
  virtual void <strong>ExitUnlocked</strong>(Gate::FSM*)  { cout &lt;&lt; "開いた状態を終わります" &lt;&lt; endl; }
  virtual void <strong>Lock</strong>(Gate::FSM*)          { cout &lt;&lt; "ゲートを閉めます" &lt;&lt; endl; }
  virtual void <strong>ThankYou</strong>(Gate::FSM*)      { cout &lt;&lt; "ありがとうございます" &lt;&lt; endl; }
  virtual void <strong>Unlock</strong>(Gate::FSM*)        { cout &lt;&lt; "ゲートを開けます" &lt;&lt; endl; }
};

int main() {
  GateContext context;
  Gate::FSM fsm;
  fsm.setContext(&amp;context);

  while ( true ) {
    cout &lt;&lt; fsm.getState()-&gt;getName() &lt;&lt; endl;
    cout &lt;&lt; "  enter an event :";
    string event_name;
    cin &gt;&gt; event_name;
    if ( event_name == "exit" ) break;
    else
    if ( event_name == "Coin" ) fsm.<strong>Coin</strong>();
    else
    if ( event_name == "Pass" ) fsm.<strong>Pass</strong>();
    else continue;

  }
  return 0;
}
</pre>
</dd>
</dl>
<h2 id="java">Java</h2>
<h3 id="javagen">生成されたコード</h3>
<dl>
<dt><a href="/wp-content/uploads/2006/04/Context.java">Context.java</a></dt>
<dd>
<pre>
package jp.co.s34.fsm.Gate;

public interface Context {
  void error(String state_name, String event_name);
  void <strong>Alarm</strong>(FSM fsm);
  void <strong>EnterLocked</strong>(FSM fsm);
  void <strong>EnterUnlocked</strong>(FSM fsm);
  void <strong>ExitLocked</strong>(FSM fsm);
  void <strong>ExitUnlocked</strong>(FSM fsm);
  void <strong>Lock</strong>(FSM fsm);
  void <strong>ThankYou</strong>(FSM fsm);
  void <strong>Unlock</strong>(FSM fsm);
}
</pre>
</dd>
<dt><a href="/wp-content/uploads/2006/04/State.java">State.java</a></dt>
<dd>
<pre>
package jp.co.s34.fsm.Gate;

public abstract class State {
  abstract public String getName();
  public void enter(FSM fsm) {}
  public void exit(FSM fsm) {}
  public void <strong>Coin</strong>(FSM fsm) { fsm.getContext().error(getName(), "<strong>Coin</strong>"); }
  public void <strong>Pass</strong>(FSM fsm) { fsm.getContext().error(getName(), "<strong>Pass</strong>"); }
}
</pre>
</dd>
<dt><a href="/wp-content/uploads/2006/04/Fsm.java">Fsm.java</a></dt>
<dd>
<pre>
package jp.co.s34.fsm.Gate;

class <strong>Locked</strong>State extends State {
  public String getName() { return "<strong>Locked</strong>"; }
  public void enter(FSM fsm) { fsm.getContext().<strong>EnterLocked</strong>(fsm); }
  public void exit(FSM fsm) { fsm.getContext().<strong>ExitLocked</strong>(fsm); }
  public void <strong>Coin</strong>(FSM fsm) {
    Context context = fsm.getContext();
    fsm.setNewState(fsm.<strong>Unlocked</strong>());
    context.<strong>Unlock</strong>(fsm);
    exit(fsm);
    fsm.setState();
    fsm.getState().enter(fsm);
  }
  public void <strong>Pass</strong>(FSM fsm) {
    Context context = fsm.getContext();
    fsm.setNewState(this);
    context.<strong>Alarm</strong>(fsm);
    if ( fsm.getNewState() != this ) {
      exit(fsm);
      fsm.setState();
      fsm.getState().enter(fsm);
    } else {
      fsm.setState();
    }
  }
}

class <strong>Unlocked</strong>State extends State {
  public String getName() { return "<strong>Unlocked</strong>"; }
  public void enter(FSM fsm) { fsm.getContext().<strong>EnterUnlocked</strong>(fsm); }
  public void exit(FSM fsm) { fsm.getContext().<strong>ExitUnlocked</strong>(fsm); }
  public void <strong>Coin</strong>(FSM fsm) {
    Context context = fsm.getContext();
    fsm.setNewState(this);
    context.<strong>ThankYou</strong>(fsm);
    if ( fsm.getNewState() != this ) {
      exit(fsm);
      fsm.setState();
      fsm.getState().enter(fsm);
    } else {
      fsm.setState();
    }
  }
  public void <strong>Pass</strong>(FSM fsm) {
    Context context = fsm.getContext();
    fsm.setNewState(fsm.<strong>Locked</strong>());
    context.<strong>Lock</strong>(fsm);
    exit(fsm);
    fsm.setState();
    fsm.getState().enter(fsm);
  }
}

public class FSM  {
  Context context_;
  State   state_;
  State   terminal_;
  State   newstate_;
  private final static <strong>Locked</strong>State <strong>Locked</strong>_ = new <strong>Locked</strong>State();
  private final static <strong>Unlocked</strong>State <strong>Unlocked</strong>_ = new <strong>Unlocked</strong>State();

  public FSM() { state_ = <strong>Locked</strong>_; terminal_ = null; }
  public boolean isTerminated() { return state_ == terminal_; }

  public void setContext(Context context) { context_ = context; }
  public Context getContext() { return context_; }

  public void setNewState(State state) { newstate_ = state; }
  public State getNewState() { return newstate_; }

  public void setState(State state) { state_ = state; }
  public void setState() { state_ = newstate_; }
  public State getState() { return state_; }
  public static State <strong>Locked</strong>() { return <strong>Locked</strong>_; }
  public static State <strong>Unlocked</strong>() { return <strong>Unlocked</strong>_; }

  public void <strong>Coin</strong>() { state_.<strong>Coin</strong>(this); }
  public void <strong>Pass</strong>() { state_.<strong>Pass</strong>(this); }
}
</pre>
</dd>
</dl>
<h3 id="javausr">利用者コード</h3>
<dl>
<dt><a href="/wp-content/uploads/2006/04/Gate.java">Gate.java</a></dt>
<dd>
<pre>
import jp.co.s34.fsm.Gate.*;
import java.io.*;

public class Gate implements Context {

  public void error(String state_name, String event_name) {
    System.out.println("error state=" + state_name +  " event=" + event_name);
  }
  public void <strong>Alarm</strong>(FSM fsm)         { System.out.println("金払え!!"); }
  public void <strong>EnterLocked</strong>(FSM fsm)   { System.out.println("ゲートが閉まりました"); }
  public void <strong>EnterUnlocked</strong>(FSM fsm) { System.out.println("ゲートが開きました"); }
  public void <strong>ExitLocked</strong>(FSM fsm)    { System.out.println("閉じた状態を終わります"); }
  public void <strong>ExitUnlocked</strong>(FSM fsm)  { System.out.println("開いた状態を終わります"); }
  public void <strong>Lock</strong>(FSM fsm)          { System.out.println("ゲートを閉めます"); }
  public void <strong>ThankYou</strong>(FSM fsm)      { System.out.println("ありがとうございます"); }
  public void <strong>Unlock</strong>(FSM fsm)        { System.out.println("ゲートを開けます"); }

  public static void main(String[] arg) throws Exception {
    Context context = new Gate();
    FSM     fsm = new FSM();
    fsm.setContext(context);

    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));    while ( true ) {
      System.out.println(fsm.getState().getName());
      System.out.print("enter an event :");
      String event_name = input.readLine();
      if ( event_name.equals("exit") ) break;
      else
      if ( event_name.equals("Coin") ) fsm.<strong>Coin</strong>();
      else
      if ( event_name.equals("Pass") ) fsm.<strong>Pass</strong>();
      else continue;
    }

  }
}
</pre>
</dd>
</dl>
<h2 id="codegen">イベント駆動型XMLパーサによるコード生成</h2>
<p>
アプリケーションの実装にはIBM alphaWorks XML4C(XML Paeser for C++) 3.0.1<br />
が提供するイベント駆動型パーサを使いました。
</p>
<p>
XML上のエレメント&lt;Event&gt;/&lt;State&gt;/&lt;FSM&gt;それぞれが持つ属性およびそれらの子エレメントを内包する3種の<br />
struct を定義します。</p>
<pre>
struct <strong>Event</strong> {
  std::string <strong>name</strong>;        // 名前
  std::string <strong>action</strong>;      // アクション
  std::string <strong>transition</strong>;  // 次の状態
  ...
};

typedef std::set&lt;<strong>Event</strong>&gt; <strong>Events</strong>;

struct <strong>State</strong> {
  std::string <strong>name</strong>;  // 名前
  std::string <strong>enter</strong>; // 入場動作
  std::string <strong>exit</strong>;  // 退場動作
  <strong>Events</strong> events;     // Eventの集合
  ...
};

typedef std::set&lt;<strong>State</strong>&gt; <strong>States</strong>;

struct FSM {
  std::string name;     // 名前
  std::string <strong>initial</strong>;  // 初期状態
  std::string <strong>terminal</strong>; // 停止状態
  std::string package;  // package名(Java)
  std::string header;   // ヘッダディレクトリ(C++)
  <strong>States</strong> states;        // Stateの集合
};
</pre>
<p>
パーサがタグの開始/終了を検出した時点でハンドラのメソッドstartElement()/endElement()が呼び出されますから、そこでタグ名に応じて上記<br />
struct を組み立てます。
</p>
<p>このようにして構築された struct は状態遷移表のすべての情報を包含していますから、<a href="#machanism">&#8220;生成コードの駆動メカニズム&#8221;</a>で解説したとおりのコードをファイルに書き出せばできあがりです。</p>
<dl>
<dt><a href="/wp-content/uploads/2006/04/fsm.cpp">fsm.cpp</a></dt>
<dd>
<pre>
/*
 * Finite State Machine code generator
 *  using XML4C/SAXParser (IBM XML Parser for C++ 3.0.1)
 */

#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;set&gt;
#include &lt;string&gt;

#include &lt;util/PlatformUtils.hpp&gt;    // PlatformUtils
#include &lt;parsers/SAXParser.hpp&gt;     // SAXParser
#include &lt;sax/HandlerBase.hpp&gt;       // HandlerBase
#include &lt;sax/SaxParseException.hpp&gt; // SaxParseException

#define LN "\n"

/*
 * UNICODEからstringへの変換
 */
inline std::string transcode(const XMLCh* xmlch) {
  return xmlch ? XMLString::transcode(xmlch) : "";
}

typedef std::set&lt;std::string&gt; Names;

/*
 * Event
 */
struct Event {
  std::string <strong>name</strong>;        // 名前
  std::string <strong>action</strong>;      // アクション
  std::string <strong>transition</strong>;  // 次の状態
  Event() {}
  Event(const std::string&amp; n) : <strong>name</strong>(n) {}
  Event(const std::string&amp; n, const std::string&amp; a, const std::string&amp; t)
    : <strong>name</strong>(n), <strong>action</strong>(a), <strong>transition</strong>(t) {}
};

inline bool operator&lt;(const Event&amp; x, const Event&amp; y) {
  return x.name &lt; y.name;
}

typedef std::set&lt;Event&gt; Events;

/*
 * State
 */
struct State {
  std::string <strong>name</strong>;  // 名前
  std::string <strong>enter</strong>; // 入場動作
  std::string <strong>exit</strong>;  // 退場動作
  Events events;     // Eventの集合
  State() {}
  State(const std::string&amp; n) : <strong>name</strong>(n) {}
};

inline bool operator&lt;(const State&amp; x, const State&amp; y) {
  return x.name &lt; y.name;
}

typedef std::set&lt;State&gt; States;

/*
 * FSM
 */
struct FSM {
  std::string name;     // 名前
  std::string <strong>initial</strong>;  // 初期状態
  std::string <strong>terminal</strong>; // 停止状態
  std::string package;  // package名(Java)
  std::string header;   // ヘッダディレクトリ(C++)
  States states;        // Stateの集合
  Names <strong>state_names</strong>;
  Names <strong>event_names</strong>;
  Names <strong>action_names</strong>;
  Names <strong>transition_names</strong>;
};

/*
 * エラーメッセージの出力
 */
std::ostream&amp; operator&lt;&lt;(std::ostream&amp; stream, const SAXParseException&amp; exception) {
  return stream &lt;&lt; "public-id=" &lt;&lt; transcode(exception.getPublicId())
                &lt;&lt; " system-id=" &lt;&lt; transcode(exception.getSystemId())
                &lt;&lt; " line=" &lt;&lt; exception.getLineNumber()
                &lt;&lt; " column=" &lt;&lt; exception.getColumnNumber()
                &lt;&lt; " : " &lt;&lt; transcode(exception.getMessage());
}

using namespace std;

/*
 * XMLからFSMを構築するためのハンドラ
 */
class Handler : public HandlerBase {
  State current_state;
  FSM&amp;  fsm;
public:

  Handler(FSM&amp; f) : fsm(f) {}

  // DocumentHandler

  // エレメント(タグ)の開始
  virtual void startElement(const XMLCh* const tag_name, AttributeList&amp; attrs) {
    std::string tag = transcode(tag_name);
    std::string tmp;
    // &lt;FSM ...&gt;
    if ( tag == "FSM" ) {
      fsm.name     = transcode(attrs.getValue(L"name"));
      fsm.<strong>initial</strong>  = transcode(attrs.getValue(L"initial"));
      fsm.<strong>terminal</strong> = transcode(attrs.getValue(L"terminal"));
      fsm.package  = transcode(attrs.getValue(L"package"));
      fsm.header   = transcode(attrs.getValue(L"header"));
    } else
    // &lt;State ...&gt;
    if ( tag == "State" ) {
      current_state.<strong>name</strong>  = tmp = transcode(attrs.getValue(L"name"));
      if ( !tmp.empty() ) fsm.<strong>state_names</strong>.insert(tmp);
      current_state.<strong>enter</strong> = tmp = transcode(attrs.getValue(L"enter"));
      if ( !tmp.empty() ) fsm.<strong>action_names</strong>.insert(tmp);
      current_state.<strong>exit</strong>  = tmp = transcode(attrs.getValue(L"exit"));
      if ( !tmp.empty() ) fsm.<strong>action_names</strong>.insert(tmp);
      current_state.events.clear();
    } else
    // &lt;Event ...&gt;
    if ( tag == "Event" ) {
      Event event;
      event.<strong>name</strong> = tmp = transcode(attrs.getValue(L"name"));
      if ( !tmp.empty() ) fsm.<strong>event_names</strong>.insert(tmp);
      event.<strong>action</strong> = tmp = transcode(attrs.getValue(L"action"));
      if ( !tmp.empty() ) fsm.<strong>action_names</strong>.insert(tmp);
      event.<strong>transition</strong> = tmp = transcode(attrs.getValue(L"transition"));
      if ( !tmp.empty() ) fsm.<strong>transition_names</strong>.insert(tmp);
      current_state.events.insert(event);
    }
  }

  // エレメント(タグ)の終了
  virtual void endElement(const XMLCh* const tag_name) {
    std::string tag = transcode(tag_name);
    // &lt;/State&gt;
    if ( tag == "State" ) {
      fsm.states.insert(current_state);
    }
  }

  // ErrorHandler

  virtual void warning(const SAXParseException&amp; exception)
    { cout &lt;&lt; "warning: " &lt;&lt; exception &lt;&lt; endl; }

  virtual void error(const SAXParseException&amp; exception)
    { cout &lt;&lt; "error:   " &lt;&lt; exception &lt;&lt; endl; }

  virtual void fatalError(const SAXParseException&amp; exception)
    { cout &lt;&lt; "fatal:   " &lt;&lt; exception &lt;&lt; endl; }

};

/*
 * ----- main -----
 */
int main(int argc, char* argv[]) {

  FSM fsm;
  Events::const_iterator eit;
  States::const_iterator sit;
  Names::const_iterator  nit;

  /*
   * 初期化とFSMの構築
   */
  try {
    XMLPlatformUtils::Initialize();
    SAXParser parser;
    Handler   handler(fsm);

    parser.setDoValidation(true);        // DTDによる検証を行う
    parser.setDocumentHandler(&amp;handler); // ハンドラ設定
    parser.setErrorHandler(&amp;handler);    // 同上(エラー用)

    parser.parse(argv[1]);

  } catch ( const XMLException&amp; er ) {
    cout &lt;&lt; er.getMessage() &lt;&lt; endl;
  }

  /*
   * 初期状態は定義されているか?
   */
  if ( fsm.<strong>state_names</strong>.find(fsm.<strong>initial</strong>) == fsm.<strong>state_names</strong>.end() ) {
    cerr &lt;&lt; "invalid initial : " &lt;&lt; fsm.<strong>initial</strong> &lt;&lt; endl;
    return 1;
  }

  /*
   * 停止状態は定義されているか?
   */
  if ( !fsm.<strong>terminal</strong>.empty() &amp;&amp; fsm.<strong>state_names</strong>.find(fsm.<strong>terminal</strong>) == fsm.<strong>state_names</strong>.end() ) {
    cerr &lt;&lt; "invalid terminal : " &lt;&lt; fsm.<strong>terminal</strong> &lt;&lt; endl;
    return 1;
  }

  /*
   * 遷移先は定義されているか?
   */
  for ( nit = fsm.<strong>transition_names</strong>.begin(); nit != fsm.<strong>transition_names</strong>.end(); ++nit ) {
    if ( fsm.<strong>state_names</strong>.find(*nit) == fsm.<strong>state_names</strong>.end() ) {
      cerr &lt;&lt; "invalid transition : " &lt;&lt; <strong>*nit</strong> &lt;&lt; endl;
      return 1;
    }
  }

  ofstream fout;

#include "cppgen.inc"  /* C++  コード生成部 */
#include "javagen.inc" /* Java コード生成部 */

  return 0;
}
</pre>
</dd>
<dt><a href="/wp-content/uploads/2006/04/cppgen.inc">cppgen.inc</a> C++ コード生成部</dt>
<dd>
<pre>
  /*
   * C++ コードの生成
   */

  fout.open((fsm.name + ".h").c_str());

  /*
   * 宣言部 (ヘッダ)
   */
  if ( fsm.header.empty() ) {
    fout &lt;&lt; "#ifndef __" &lt;&lt; fsm.name &lt;&lt; "_h__" LN
         &lt;&lt; "#define __" &lt;&lt; fsm.name &lt;&lt; "_h__" LN LN;
  } else {
    fout &lt;&lt; "#ifndef __" &lt;&lt; fsm.header &lt;&lt; '_' &lt;&lt; fsm.name &lt;&lt; "_h__" LN
         &lt;&lt; "#define __" &lt;&lt; fsm.header &lt;&lt; '_' &lt;&lt; fsm.name &lt;&lt; "_h__" LN LN;
  }

  fout &lt;&lt; "namespace " &lt;&lt; fsm.name &lt;&lt; " {" LN LN;

  /*
   * Context 宣言
   */
  fout &lt;&lt; "class FSM;" LN LN
          "class Context {" LN "public:" LN
          "  virtual void error(const char* state_name, const char* event_name) =0;" LN;
  for ( nit = fsm.<strong>action_names</strong>.begin(); nit != fsm.<strong>action_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  virtual void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "(FSM* fsm) =0;" LN;
  }
  fout &lt;&lt; "};" LN LN;

  /*
   * State(base) 宣言
   */
  fout &lt;&lt; "class State {" LN "public:" LN
       &lt;&lt; "  virtual const char* getName() const =0;" LN
       &lt;&lt; "  virtual void enter(FSM* fsm);" LN
       &lt;&lt; "  virtual void exit(FSM* fsm);" LN;
  for ( nit = fsm.<strong>event_names</strong>.begin(); nit != fsm.<strong>event_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  virtual void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "(FSM* fsm);" LN;
  }
  fout &lt;&lt; "};" LN LN;

  /*
   * State 宣言
   */
  for ( sit = fsm.<strong>states</strong>.begin(); sit != fsm.<strong>states</strong>.end(); ++sit) {
    fout &lt;&lt; "class " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State : public State {" LN "public:" LN
         &lt;&lt; "  virtual const char* getName() const;" LN;
    if ( !<strong>sit-&gt;enter</strong>.empty() ) {
      fout &lt;&lt; "  virtual void enter(FSM* fsm);" LN;
    }
    if ( !<strong>sit-&gt;exit</strong>.empty() ) {
      fout &lt;&lt; "  virtual void exit(FSM* fsm);" LN;
    }
    for ( eit = <strong>sit-&gt;events</strong>.begin(); eit != <strong>sit-&gt;events</strong>.end(); ++eit ) {
      fout &lt;&lt; "  virtual void " &lt;&lt; <strong>eit-&gt;name</strong> &lt;&lt; "(FSM* fsm);" LN;
    }
    fout &lt;&lt; "};" LN LN;
  }

  /*
   * FSM 宣言
   */
  fout &lt;&lt; "class FSM  {" LN
          "  Context* context_;" LN
          "  State*   <strong>state_</strong>;" LN
          "  State*   <strong>newstate_</strong>;" LN
          "  State*   <strong>terminal_</strong>;" LN;
  for ( nit = fsm.<strong>state_names</strong>.begin(); nit != fsm.<strong>state_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  static " &lt;&lt; <strong>*nit</strong> &lt;&lt; "State " &lt;&lt; <strong>*nit</strong> &lt;&lt; "_;" LN;
  }
  fout &lt;&lt; "public:" LN
          "  FSM() { <strong>state_</strong> = &amp;" &lt;&lt; fsm.<strong>initial</strong> &lt;&lt; "_; ";
  if ( fsm.<strong>terminal</strong>.empty() ) {
    fout &lt;&lt; "<strong>terminal_</strong> = 0; }" LN;
  } else {
    fout &lt;&lt; "<strong>terminal_</strong> = &amp;" &lt;&lt; fsm.<strong>terminal</strong> &lt;&lt; "_; }" LN;
  }
  fout &lt;&lt; "  bool isTerminated() const { return <strong>state_</strong> == <strong>terminal_</strong>; }" LN LN
       &lt;&lt; "  void setContext(Context* context) { context_ = context; }" LN
       &lt;&lt; "  Context* getContext() const { return context_; }" LN LN
       &lt;&lt; "  void setNewState(State* <strong>state</strong>) { <strong>newstate_</strong> = <strong>state</strong>; }" LN
       &lt;&lt; "  State* getNewState() { return <strong>newstate_</strong>; }" LN LN
       &lt;&lt; "  void setState(State* state) { <strong>state_</strong> = state; }" LN
       &lt;&lt; "  void setState() { <strong>state_</strong> = <strong>newstate_</strong>; }" LN
       &lt;&lt; "  State* getState() { return <strong>state_</strong>; }" LN LN;
  for ( nit = fsm.<strong>state_names</strong>.begin(); nit != fsm.<strong>state_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  static State* " &lt;&lt; <strong>*nit</strong> &lt;&lt; "() { return &amp;" &lt;&lt; <strong>*nit</strong> &lt;&lt; "_; }" LN;
  }
  fout &lt;&lt; LN;
  for ( nit = fsm.<strong>event_names</strong>.begin(); nit != fsm.<strong>event_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "() { state_-&gt;" &lt;&lt; <strong>*nit</strong> &lt;&lt; "(this); }" LN;
  }
  fout &lt;&lt; "};" LN LN;

  fout &lt;&lt; "}" LN LN;
  fout &lt;&lt; "#endif" LN;
  fout.close();

  /*
   * 実装部
   */
  fout.open((fsm.name + ".cpp").c_str());

  if ( fsm.header.empty() ) {
    fout &lt;&lt; "#include \"" &lt;&lt; fsm.name &lt;&lt; ".h\"" LN LN;
  } else {
    fout &lt;&lt; "#include \"" &lt;&lt; fsm.header &lt;&lt; '/' &lt;&lt; fsm.name &lt;&lt; ".h\"" LN LN;
  }

  fout &lt;&lt; "namespace " &lt;&lt; fsm.name &lt;&lt; " {" LN LN;

  /*
   * Context 実装
   */
  // 実装すべきものはなにもない。

  /*
   * State(base) 実装
   */
  fout &lt;&lt; "void State::enter(FSM* fsm) {}" LN
          "void State::exit(FSM* fsm) {}" LN;
  for ( nit = fsm.<strong>event_names</strong>.begin(); nit != fsm.<strong>event_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "void State::" &lt;&lt; <strong>*nit</strong>
           &lt;&lt; "(FSM* fsm) { fsm-&gt;getContext()-&gt;error(getName(), \"" &lt;&lt; <strong>*nit</strong> &lt;&lt; "\"); }" LN;
  }
  fout &lt;&lt; LN;

  /*
   * State 実装
   */
  for ( sit = fsm.<strong>states</strong>.begin(); sit != fsm.<strong>states</strong>.end(); ++sit) {
    fout &lt;&lt; "const char* " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State::getName() const {"
            "  return \"" &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "\"; }" LN;
    if ( !<strong>sit-&gt;enter</strong>.empty() ) {
      fout &lt;&lt; "void " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State::enter(FSM* fsm) { fsm-&gt;getContext()-&gt;"
           &lt;&lt; <strong>sit-&gt;enter</strong> &lt;&lt; "(fsm); }" LN;
    }
    if ( !<strong>sit-&gt;exit</strong>.empty() ) {
      fout &lt;&lt; "void " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State::exit(FSM* fsm) { fsm-&gt;getContext()-&gt;"
           &lt;&lt; <strong>sit-&gt;exit</strong> &lt;&lt; "(fsm); }" LN;
    }
    fout &lt;&lt; LN;

    for ( eit = <strong>sit-&gt;events</strong>.begin(); eit != <strong>sit-&gt;events</strong>.end(); ++eit ) {
      fout &lt;&lt; "void " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State::" &lt;&lt; <strong>eit-&gt;name</strong> &lt;&lt; "(FSM* fsm) {" LN
              "  Context* context = fsm-&gt;getContext();" LN;
      if ( <strong>eit-&gt;transition</strong>.empty() ) {
        fout &lt;&lt; "  fsm-&gt;setNewState(this);" LN;
      } else {
        fout &lt;&lt; "  fsm-&gt;setNewState(fsm-&gt;" &lt;&lt; <strong>eit-&gt;transition</strong> &lt;&lt; "());" LN;
      }
      if ( !<strong>eit-&gt;action</strong>.empty() ) {
        fout &lt;&lt; "  context-&gt;" &lt;&lt; <strong>eit-&gt;action</strong> &lt;&lt; "(fsm);" LN;
      }
      States::const_iterator tit = <strong>eit-&gt;transition</strong>.empty() ? sit : fsm.<strong>states</strong>.find(State(<strong>eit-&gt;transition</strong>));

      if ( <strong>eit-&gt;transition</strong>.empty() ) {
        fout &lt;&lt; "  if ( fsm-&gt;getNewState() != this ) {" LN
                "    exit(fsm);" LN
                "    fsm-&gt;setState();" LN
                "    fsm-&gt;getState()-&gt;enter(fsm);" LN
                "  } else {" LN
                "    fsm-&gt;setState();" LN
                "  }" LN;
      } else {
        if ( !<strong>sit-&gt;exit</strong>.empty() ) {
          fout &lt;&lt; "  exit(fsm);" LN;
        }
        fout &lt;&lt; "  fsm-&gt;setState();" LN;
        if ( !<strong>tit-&gt;enter</strong>.empty() ) {
          fout &lt;&lt; "  fsm-&gt;getState()-&gt;enter(fsm);" LN;
        }
      }
      fout &lt;&lt; "}" LN LN;
    }
    fout &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State FSM::" &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "_;" LN LN;

  }

  fout &lt;&lt; "}" LN;
  fout.close();
</pre>
</dd>
<dt><a href="/wp-content/uploads/2006/04/javagen.inc">javagen.inc</a> Java コード生成部</dt>
<dd>
<pre>
  /*
   * Java コードの生成
   */

  /*
   * Context.java
   */
  fout.open("Context.java");
  if ( fsm.package.empty() ) {
    fout &lt;&lt; "package " &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  } else {
    fout &lt;&lt; "package " &lt;&lt; fsm.package &lt;&lt; '.' &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  }

  fout &lt;&lt; "public interface Context {" LN
       &lt;&lt; "  void error(String state_name, String event_name);" LN ;
  for ( nit = fsm.<strong>action_names</strong>.begin(); nit != fsm.<strong>action_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "(FSM fsm);" LN;
  }
  fout &lt;&lt; "}" LN LN;
  fout.close();

  /*
   * State.java
   */
  fout.open("State.java");
  if ( fsm.package.empty() ) {
    fout &lt;&lt; "package " &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  } else {
    fout &lt;&lt; "package " &lt;&lt; fsm.package &lt;&lt; '.' &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  }

  /*
   * State(base)
   */
  fout &lt;&lt; "public abstract class State {" LN
          "  abstract public String getName();" LN
          "  public void enter(FSM fsm) {}" LN
          "  public void exit(FSM fsm) {}" LN;
  for ( nit = fsm.<strong>event_names</strong>.begin(); nit != fsm.<strong>event_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  public void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "(FSM fsm) { fsm.getContext().error(getName(), \""
           &lt;&lt; <strong>*nit</strong> &lt;&lt; "\"); }" LN;
  }
  fout &lt;&lt; "}" LN LN;
  fout.close();

  /*
   * FSM.java
   */
  fout.open("FSM.java");
  if ( fsm.package.empty() ) {
    fout &lt;&lt; "package " &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  } else {
    fout &lt;&lt; "package " &lt;&lt; fsm.package &lt;&lt; '.' &lt;&lt; fsm.name &lt;&lt; ";" LN LN;
  }

  /*
   * State
   */
  for ( sit = fsm.<strong>states</strong>.begin(); sit != fsm.<strong>states</strong>.end(); ++sit) {
    fout &lt;&lt; "class " &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "State extends State {" LN
            "  public String getName() { return \"" &lt;&lt; <strong>sit-&gt;name</strong> &lt;&lt; "\"; }" LN;
    if ( !<strong>sit-&gt;enter</strong>.empty() ) {
      fout &lt;&lt; "  public void enter(FSM fsm) { fsm.getContext()." &lt;&lt; <strong>sit-&gt;enter</strong> &lt;&lt; "(fsm); }" LN;
    }
    if ( !<strong>sit-&gt;exit</strong>.empty() ) {
      fout &lt;&lt; "  public void exit(FSM fsm) { fsm.getContext()." &lt;&lt; <strong>sit-&gt;exit</strong> &lt;&lt; "(fsm); }" LN;
    }

    for ( eit = <strong>sit-&gt;events</strong>.begin(); eit != <strong>sit-&gt;events</strong>.end(); ++eit ) {
      fout &lt;&lt; "  public void " &lt;&lt; <strong>eit-&gt;name</strong> &lt;&lt; "(FSM fsm) {" LN
              "    Context context = fsm.getContext();" LN;
      if ( <strong>eit-&gt;transition</strong>.empty() ) {
        fout &lt;&lt; "    fsm.setNewState(this);" LN;
      } else {
        fout &lt;&lt; "    fsm.setNewState(fsm." &lt;&lt; <strong>eit-&gt;transition</strong> &lt;&lt; "());" LN;
      }
      if ( !<strong>eit-&gt;action</strong>.empty() ) {
        fout &lt;&lt; "    context." &lt;&lt; <strong>eit-&gt;action</strong> &lt;&lt; "(fsm);" LN;
      }
      States::const_iterator tit = <strong>eit-&gt;transition</strong>.empty() ? sit : fsm.<strong>states</strong>.find(State(<strong>eit-&gt;transition</strong>));

      if ( <strong>eit-&gt;transition</strong>.empty() ) {
        fout &lt;&lt; "    if ( fsm.getNewState() != this ) {" LN
                "      exit(fsm);" LN
                "      fsm.setState();" LN
                "      fsm.getState().enter(fsm);" LN
                "    } else {" LN
                "      fsm.setState();" LN
                "    }" LN;
      } else {
        if ( !<strong>sit-&gt;exit</strong>.empty() ) {
          fout &lt;&lt; "    exit(fsm);" LN;
        }
        fout &lt;&lt; "    fsm.setState();" LN;
        if ( !<strong>tit-&gt;enter</strong>.empty() ) {
          fout &lt;&lt; "    fsm.getState().enter(fsm);" LN;
        }
      }

      fout &lt;&lt; "  }" LN;

    }
    fout &lt;&lt; "}" LN LN;
  }

  /*
   * FSM
   */
  fout &lt;&lt; "public class FSM  {" LN
          "  Context context_;" LN
          "  State   <strong>state_</strong>;" LN
          "  State   <strong>terminal_</strong>;" LN
          "  State   <strong>newstate_</strong>;" LN;
  for ( nit = fsm.<strong>state_names</strong>.begin(); nit != fsm.<strong>state_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  private final static " &lt;&lt; <strong>*nit</strong>
            &lt;&lt; "State " &lt;&lt; <strong>*nit</strong> &lt;&lt; "_ = new " &lt;&lt; <strong>*nit</strong> &lt;&lt; "State();" LN;
  }
  fout &lt;&lt; LN;
  fout &lt;&lt; "  public FSM() { <strong>state_</strong> = " &lt;&lt; fsm.<strong>initial</strong> &lt;&lt; "_; ";
  if ( fsm.<strong>terminal</strong>.empty() ) {
    fout &lt;&lt; "<strong>terminal_</strong> = null; }" LN;
  } else {
    fout &lt;&lt; "<strong>terminal_</strong> = " &lt;&lt; fsm.<strong>terminal</strong> &lt;&lt; "State_; }" LN;
  }
  fout &lt;&lt; "  public boolean isTerminated() { return <strong>state_</strong> == <strong>terminal_</strong>; }" LN LN
          "  public void setContext(Context context) { context_ = context; }" LN
          "  public Context getContext() { return context_; }" LN LN
          "  public void setNewState(State state) { <strong>newstate_</strong> = state; }" LN
          "  public State getNewState() { return <strong>newstate_</strong>; }" LN LN
          "  public void setState(State state) { <strong>state_</strong> = state; }" LN
          "  public void setState() { <strong>state_</strong> = <strong>newstate_</strong>; }" LN
          "  public State getState() { return <strong>state_</strong>; }" LN;
  for ( nit = fsm.<strong>state_names</strong>.begin(); nit != fsm.<strong>state_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  public static State " &lt;&lt; <strong>*nit</strong> &lt;&lt; "() { return " &lt;&lt; <strong>*nit</strong> &lt;&lt; "_; }" LN;
  }
  fout &lt;&lt; LN;
  for ( nit = fsm.<strong>event_names</strong>.begin(); nit != fsm.<strong>event_names</strong>.end(); ++nit ) {
    fout &lt;&lt; "  public void " &lt;&lt; <strong>*nit</strong> &lt;&lt; "() { state_." &lt;&lt; <strong>*nit</strong> &lt;&lt; "(this); }" LN;
  }
  fout &lt;&lt; "}" LN LN;

  fout.close();
</pre>
</dd>
</dl>
<p>
※サンプルを含む全ソースコードは<a href="/wp-content/uploads/2006/04/sources.zip">ここ</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/xml-fsmgen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Handle-Bodyイディオム : 参照カウントによる実装</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/handlebody/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/handlebody/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 06:09:40 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=1176</guid>
		<description><![CDATA[オブジェクト(クラス)のメンバ変数や関数の引数に値を用いるか、あるいは参照(ポインタ)を用いるかはプログラマの頭を悩ませます。 一般には値の方が良い、とされています。なぜなら値は参照(ポインタ)よりシンプルだからです。 ポインタが一方から他方へ引き渡されたとき、一つの実体を複数の場所で共有することになります。このときプログラマは実体のライフタイム(その実体をどこで/いつdeleteするか)を厳密に管理しなければなりません。複雑に絡み合ったポインタを矛盾なく解きほぐすのは相当に神経を使う作業となります。 しかしながら一方で値には問題があります。ひとつには値のコピーはコスト高となることがあるのです。オブジェクト内部に数多くのメンバを内在するときなど、そのコピーに必要な時間と手間はポインタに較べて非常に大きくなります。また、値渡しとはコピーを作ってそれを他方に引き渡すことになります。引き渡された側ではその本体(コピー元)にアクセスすることができず、値渡しそのこと自体が誤りである場合も少なくありません。 Handle-Bodyは値と参照(ポインタ)の双方のメリットを活かすイディオム(定石)です。 Handle-Bodyイディオムによって、ポインタを値のように扱うことが可能となり、オブジェクトの共有やライフタイム管理、そしてコストのかからないコピーといったことが実現できます。 Handle-Bodyイディオム(Bridgeパターン)とは、インタフェースと実装とを分離するからくりです。 Handle(インタフェース)がBody(実装)をその内部に持ち、Handleのメソッド呼び出しはそのまま内包するBodyに送られます。 そしてこのとき、HandleがBodyをポインタとして内包するなら、Handleのメンバ変数はポインタひとつですから非常にコンパクトでコピーを非常に短く/速く行えます。 しかし、HandleがBodyの&apos;単なるポインタ&apos;をメンバとして持った場合、Handle間でのコピーはひとつの実体を複数のHandleが共有することとなり、冒頭で述べた共有された実体のライフタイム管理が複雑になります。 共有された実体のライフタイム管理問題の解決のため、&apos;参照カウント&apos;を導入しましょう。参照カウントとは、ひとつの実体に対していくつのオブジェクトが参照しているかを表す数です。参照カウントは、あるオブジェクトがその実体への参照を開始したとき+1され、その実体への参照の必要がなくなるとき(参照しているオブジェクトがデストラクトされるとき)に-1されます。 そして参照カウントが0となったとき、実体そのものをデストラクトするからくりを用意するのです。 参照カウントを介して実体にアクセスすれば、プログラマはもはや実体の破壊タイミングを気にする必要はなくなります。複数のオブジェクトがひとつの実体を参照しているとき、最後のひとつのオブジェクトが参照を必要としなくなった瞬間、実体が自動的に破壊されます。 参照カウントは極めてにシンプルなガベジ・コレクタとして機能します。 HandleがBodyの参照カウントを保持することで値と参照(ポインタ)の双方のメリットを利用できるわけです。 参照カウントによる共有ポインタの実装のひとつがBoostのshared_ptrです。 shared_ptrはクラス・テンプレートであり、たとえば boost::shared_ptrはXへの共有ポインタとなります。 shared_ptr間のコピーは内包するポインタへの参照カウントを+1し、shared_ptrのデストラクタは参照カウント-1します。そして参照カウントが0となったとき、ポインタがdeleteされます。 { boost::shared_ptr&#60;int&#62; p0(new int(123)); // [*] 参照数:1 { boost::shared_ptr&#60;int&#62; p1 = p0; // 参照数:2 std::cout &#60;&#60; *p1 &#60;&#60; std::endl; // ここでp1が破壊され、参照数:1 } // ここでp0が破壊され、参照数:0 -&#62; [*] で作られたintが解放される。 } boost::shared_ptrを利用してHandle-Bodyイディオムの単純な実装を試みました。 HandleとBodyの基底クラス BodyBase.h #ifndef BODYBASE_H #define BODYBASE_H #include [...]]]></description>
			<content:encoded><![CDATA[<p>オブジェクト(クラス)のメンバ変数や関数の引数に値を用いるか、あるいは参照(ポインタ)を用いるかはプログラマの頭を悩ませます。</p>
<p>一般には値の方が良い、とされています。なぜなら値は参照(ポインタ)よりシンプルだからです。</p>
<p>ポインタが一方から他方へ引き渡されたとき、一つの実体を複数の場所で共有することになります。このときプログラマは実体のライフタイム(その実体をどこで/いつdeleteするか)を厳密に管理しなければなりません。複雑に絡み合ったポインタを矛盾なく解きほぐすのは相当に神経を使う作業となります。</p>
<p>しかしながら一方で値には問題があります。ひとつには値のコピーはコスト高となることがあるのです。オブジェクト内部に数多くのメンバを内在するときなど、そのコピーに必要な時間と手間はポインタに較べて非常に大きくなります。また、値渡しとはコピーを作ってそれを他方に引き渡すことになります。引き渡された側ではその本体(コピー元)にアクセスすることができず、値渡しそのこと自体が誤りである場合も少なくありません。</p>
<p>Handle-Bodyは値と参照(ポインタ)の双方のメリットを活かすイディオム(定石)です。<br />
Handle-Bodyイディオムによって、ポインタを値のように扱うことが可能となり、オブジェクトの共有やライフタイム管理、そしてコストのかからないコピーといったことが実現できます。</p>
<p><span id="more-1176"></span></p>
<p>Handle-Bodyイディオム(Bridgeパターン)とは、インタフェースと実装とを分離するからくりです。</p>
<p>Handle(インタフェース)がBody(実装)をその内部に持ち、Handleのメソッド呼び出しはそのまま内包するBodyに送られます。<br />
そしてこのとき、HandleがBodyをポインタとして内包するなら、Handleのメンバ変数はポインタひとつですから非常にコンパクトでコピーを非常に短く/速く行えます。</p>
<p>しかし、HandleがBodyの&apos;単なるポインタ&apos;をメンバとして持った場合、Handle間でのコピーはひとつの実体を複数のHandleが共有することとなり、冒頭で述べた共有された実体のライフタイム管理が複雑になります。</p>
<p>共有された実体のライフタイム管理問題の解決のため、&apos;参照カウント&apos;を導入しましょう。参照カウントとは、ひとつの実体に対していくつのオブジェクトが参照しているかを表す数です。参照カウントは、あるオブジェクトがその実体への参照を開始したとき<code>+1</code>され、その実体への参照の必要がなくなるとき(参照しているオブジェクトがデストラクトされるとき)に<code>-1</code>されます。<br />
そして参照カウントが0となったとき、実体そのものをデストラクトするからくりを用意するのです。</p>
<p>参照カウントを介して実体にアクセスすれば、プログラマはもはや実体の破壊タイミングを気にする必要はなくなります。複数のオブジェクトがひとつの実体を参照しているとき、最後のひとつのオブジェクトが参照を必要としなくなった瞬間、実体が自動的に破壊されます。<br />
参照カウントは極めてにシンプルなガベジ・コレクタとして機能します。</p>
<p>HandleがBodyの参照カウントを保持することで値と参照(ポインタ)の双方のメリットを利用できるわけです。</p>
<p>参照カウントによる共有ポインタの実装のひとつが<a href="http://www.boost.org/">Boost</a>の<strong>shared_ptr</strong>です。</p>
<p><strong>shared_ptr</strong>はクラス・テンプレートであり、たとえば boost::shared_ptrはXへの共有ポインタとなります。</p>
<p><strong>shared_ptr</strong>間のコピーは内包するポインタへの参照カウントを<code>+1</code>し、<strong>shared_ptr</strong>のデストラクタは参照カウント<code>-1</code>します。そして参照カウントが0となったとき、ポインタがdeleteされます。</p>
<pre>
{
  boost::<strong>shared_ptr</strong>&lt;int&gt; p0(new int(123)); // [*] 参照数:1
  {
    boost::<strong>shared_ptr</strong>&lt;int&gt; p1 = p0; // 参照数:2
    std::cout &lt;&lt; *p1 &lt;&lt; std::endl;
    // ここでp1が破壊され、参照数:1
  }
  // ここでp0が破壊され、参照数:0 -&gt; [*] で作られたintが解放される。
}
</pre>
<p>boost::<strong>shared_ptr</strong>を利用してHandle-Bodyイディオムの単純な実装を試みました。</p>
<h3>HandleとBodyの基底クラス</h3>
<h4 id="handlebody_body_base_h">BodyBase.h</h4>
<pre>
#ifndef BODYBASE_H
#define BODYBASE_H

#include &lt;boost/utility.hpp&gt; // noncopyable

/*
 * BodyBase
 */
class BodyBase : public boost::noncopyable {
public:
  virtual ‾BodyBase() {}
};

#endif
</pre>
<h4 id="handlebody_handle_base_h">HandleBase.h</h4>
<pre>
#ifndef HANDLEBASE_H
#define HANDLEBASE_H

#include &lt;boost/shared_ptr.hpp&gt; // shared_ptr
#include &quot;<a href="#handlebody_body_base_h">BodyBase.h</a>&quot;

/*
 * HandleBase
 */
class HandleBase {
  boost::shared_ptr&lt;BodyBase&gt; body_;
protected:
  HandleBase() {}
  HandleBase(BodyBase* p) : body_(p) {}
  BodyBase&amp; body() const { return *body_; }
};

#endif
</pre>
<p>実装したHandle-Bodyを利用したサンプルを示します</p>
<h3>サンプル</h3>
<h4 id="handlebody_greeter_impl_h">GreeterImpl.h</h4>
<pre>
#ifndef GREETERIMPL_H
#define GREETERIMPL_H

#include &lt;iostream&gt;

#include &quot;<a href="#handlebody_greeter_h">Greeter.h</a>&quot;

// abstract
class GreeterImpl : public BodyBase {
public:
  virtual void greet() const =0;
};

// concrete
class GreeterJa : public GreeterImpl {
  virtual void greet() const;
  GreeterJa();
  ‾GreeterJa();
public:
  static Greeter make();
};

// concrete
class GreeterEn : public GreeterImpl {
  virtual void greet() const;
  GreeterEn();
  ‾GreeterEn();
public:
  static Greeter make();
};

#endif
</pre>
<h4 id="handlebody_greeter_h">Greeter.h</h4>
<pre>
#ifndef GREETER_H
#define GREETER_H

#include &quot;<a href="#handlebody_handle_base_h">HandleBase.h</a>&quot;

class GreeterImpl;

class Greeter : public HandleBase {
  GreeterImpl&amp; body() const;
public:
  Greeter() {}
  Greeter(const Greeter&amp; other) : HandleBase(other) {}
  Greeter(GreeterImpl* impl);
  void greet();
};

#endif
</pre>
<h4 id="handlebody_greeter_impl_cpp">GreeterImpl.cpp</h4>
<pre>
#include &quot;<a href="#handlebody_greeter_impl_h">GreeterImpl.h</a>&quot;

void GreeterJa::greet() const
{ std::cout &lt;&lt; &quot;こんにちは&quot; &lt;&lt; std::endl; }

GreeterJa::GreeterJa()
{ std::cout &lt;&lt; &quot;おはようございます&quot; &lt;&lt; std::endl; }

GreeterJa::‾GreeterJa()
{ std::cout &lt;&lt; &quot;おやすみなさい&quot; &lt;&lt; std::endl; }

Greeter GreeterJa::make()
{ return Greeter(new GreeterJa); }

void GreeterEn::greet() const
{ std::cout &lt;&lt; &quot;Hello!&quot; &lt;&lt; std::endl; }

GreeterEn::GreeterEn()
{ std::cout &lt;&lt; &quot;Good morning.&quot; &lt;&lt; std::endl; }

GreeterEn::‾GreeterEn()
{ std::cout &lt;&lt; &quot;Good night.&quot; &lt;&lt; std::endl; }

Greeter GreeterEn::make()
{ return Greeter(new GreeterEn); }
</pre>
<h4 id="handlebody_greeter_cpp">Greeter.cpp</h4>
<pre>
#include &quot;<a href="#handlebody_greeter_h">Greeter.h</a>&quot;
#include &quot;<a href="#handlebody_greeter_impl_h">GreeterImpl.h</a>&quot;

Greeter::Greeter(GreeterImpl* impl) : HandleBase(impl) {}

GreeterImpl&amp; Greeter::body() const {
  return static_cast&lt;GreeterImpl&amp;&gt;(HandleBase::body());
}

void Greeter::greet() {
  body().greet();
}
</pre>
<h4 id="handlebody_trial_cpp">trial.cpp</h4>
<pre>
#include &lt;iostream&gt;

#include &quot;<a href="#handlebody_greeter_impl_h">GreeterImpl.h</a>&quot;

/*
 * TRIAL
 */
int main() {
  Greeter g;

  {
  std::cout &lt;&lt; &quot;--- create GreeterJa&quot; &lt;&lt; std::endl;
  Greeter j = GreeterJa::make();
  g = j;
  }
  g.greet();

  {
  std::cout &lt;&lt; &quot;--- create GreeterEn&quot; &lt;&lt; std::endl;
  Greeter e = GreeterEn::make();
  g = e;
  }
  g.greet();

  return 0;
}
</pre>
<h4>実行結果</h4>
<pre>
--- create GreeterJa
おはようございます
こんにちは
--- create GreeterEn
Good morning.
おやすみなさい
Hello!
Good night.
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/handlebody/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>はじめてのDBTools.h++ (part-2)</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/dbtools2/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/dbtools2/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 05:39:58 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=1220</guid>
		<description><![CDATA[結合された複数テーブルからの読み出し [part-1]に引き続き、DBTools.h++を使ったRDBの操作法に関する解説を続けます。 [part-1]ではひとつのテーブルに対するレコードの追加/変更/削除/検索を行ないました。では、結合された複数のテーブルから検索条件を指定して読み出してみる事にしましょう。 デモンストレーションのために、次のようなデータを用意しました。 NAME KIND HEADER accumulate function numeric adjacext_difference function numeric adjacext_find function algorithm &#8230; &#8230; &#8230; これはStandard Template Libraryが提供する関数/構造体/クラスの NAME : 名前 KIND : 種別 (関数/構造体/クラス) HEADER : 宣言されているヘッダ を一覧にした表です。まず各フィールドを以下のような3つのテーブルに分割しました。 T_STL フィールド名 型 サイズ NAME CHAR 32 KIND_ID LONG 4 HEAD_ID LONG 4 T_KIND フィールド名 型 サイズ ID LONG 4 NAME CHAR [...]]]></description>
			<content:encoded><![CDATA[<h3>結合された複数テーブルからの読み出し</h3>
<p>[part-1]に引き続き、DBTools.h++を使ったRDBの操作法に関する解説を続けます。</p>
<p>[part-1]ではひとつのテーブルに対するレコードの追加/変更/削除/検索を行ないました。では、結合された複数のテーブルから検索条件を指定して読み出してみる事にしましょう。</p>
<p><span id="more-1220"></span></p>
<p>デモンストレーションのために、次のようなデータを用意しました。</p>
<table summary="Standard Template Library関数/構造体/クラス一覧表">
<tbody>
<tr>
<th>NAME</th>
<th>KIND</th>
<th>HEADER</th>
</tr>
<tr>
<td>accumulate</td>
<td>function</td>
<td>numeric</td>
</tr>
<tr>
<td>adjacext_difference</td>
<td>function</td>
<td>numeric</td>
</tr>
<tr>
<td>adjacext_find</td>
<td>function</td>
<td>algorithm</td>
</tr>
<tr>
<td>&#8230;</td>
<td>&#8230;</td>
<td>&#8230;</td>
</tr>
</tbody>
</table>
<p>これはStandard Template Libraryが提供する関数/構造体/クラスの</p>
<ul>
<li>NAME : 名前</li>
<li>KIND : 種別 (関数/構造体/クラス)</li>
<li>HEADER : 宣言されているヘッダ</li>
</ul>
<p>を一覧にした表です。まず各フィールドを以下のような3つのテーブルに分割しました。</p>
<table summary="">
<tbody>
<tr>
<td>
<table summary="">
<tbody>
<tr>
<th colspan="3">T_STL</th>
</tr>
<tr>
<th>フィールド名</th>
<th>型</th>
<th>サイズ</th>
</tr>
<tr>
<td>NAME</td>
<td>CHAR</td>
<td>32</td>
</tr>
<tr>
<td>KIND_ID</td>
<td>LONG</td>
<td>4</td>
</tr>
<tr>
<td>HEAD_ID</td>
<td>LONG</td>
<td>4</td>
</tr>
</tbody>
</table>
</td>
<td>
<table summary="">
<tbody>
<tr>
<th colspan="3">T_KIND</th>
</tr>
<tr>
<th>フィールド名</th>
<th>型</th>
<th>サイズ</th>
</tr>
<tr>
<td>ID</td>
<td>LONG</td>
<td>4</td>
</tr>
<tr>
<td>NAME</td>
<td>CHAR</td>
<td>8</td>
</tr>
</tbody>
</table>
</td>
<td>
<table summary="">
<tbody>
<tr>
<th colspan="3">T_HEAD</th>
</tr>
<tr>
<th>フィールド名</th>
<th>型</th>
<th>サイズ</th>
</tr>
<tr>
<td>ID</td>
<td>LONG</td>
<td>4</td>
</tr>
<tr>
<td>NAME</td>
<td>CHAR</td>
<td>16</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>T_STLには関数/構造体/クラスの名前と、種別/ヘッダのIDが格納されます。種別/ヘッダ名はそれぞれT_KIND/T_HEADのIDに対応するNAMEフィールドに格納します。</p>
<p>これらのテーブルから、元の形式のデータを手に入れるには、</p>
<p>T_STL.KIND_ID と T_KIND.ID が等しく</p>
<p>かつ</p>
<p>T_STL.HEAD_ID と T_HEAD.ID が等しい</p>
<p>であるようなレコードの組み合わせから、</p>
<p>T_STL.NAME と T_KIND.NAME と T_HEAD.NAME を抽出すればいいわけです。</p>
<p>SQLで表現すると:</p>
<pre>
SELECT T_STL,NAME, T_KIND.NAME, T_HEAD.NAME
FROM   T_STL, T_KIND, T_HEAD
WHERE  T_STL.KIND_ID = T_KIND.ID AND T_STL.HEAD_ID = T_HEAD.ID
</pre>
<p>となります。</p>
<p>これをDBTools.h++で実現してみましょう。DBTools.h++による複数テーブルからの検索は、単一テーブルからの検索と何ら変わるところがありません。</p>
<p>まずデータベースをオープンし、テーブルを用意します。</p>
<pre>
  <strong>RWDBDatabase</strong> db_;
  <strong>RWDBTable</strong>    stl_;
  <strong>RWDBTable</strong>    kind_;
  <strong>RWDBTable</strong>    head_;

  db_ = <strong>RWDBManager</strong>::<strong>database</strong>(
     // serverType, serverName,  userName, password, databaseName
        SERVER,     "MASTER",    "FRUIT",  "APPLE",  "LIBRARIES"
        );

  stl_  = db_.<strong>table</strong>("T_STL");
  kind_ = db_.<strong>table</strong>("T_KIND");
  head_ = db_.<strong>table</strong>("T_HEAD");
</pre>
<p>つぎにデータベースからselectorを手に入れ、抽出フィールドと検索条件を設定します。</p>
<pre>
  <strong>RWDBSelector</strong> selector_;

  selector_ = db_.<strong>selector</strong>();
  selector_ &lt;&lt; stl_["NAME"] &lt;&lt; kind_["NAME"] &lt;&lt; head_["NAME"];
  selector_.<strong>where</strong>(stl_["KIND_ID"] == kind_["ID"] &amp;&amp;
                  stl_["HEAD_ID"] == head_["ID"])
</pre>
<p>あとはselectorから得られたreaderを使ってフィールドを取り出します。</p>
<pre>
  <strong>RWDBReader</strong> reader_;
  reader_ = selector_.<strong>reader</strong>();
  while ( reader_<strong>()</strong> ) {
    RWCString name, kind, header;
    reader_ &gt;&gt; name &gt;&gt; kind &gt;&gt; header;
    ....
  }
</pre>
<h3>自己結合</h3>
<p>上の例は、複数のテーブル上の特定のフィールドを他のテーブルのフィールドと結合させています(内部結合:Inner Join)。</p>
<p>次の例では、あるフィールドを&quot;同じテーブル&quot;のフィールドと結合させます(自己結合:Self Join)。</p>
<table summary="">
<tbody>
<tr>
<th colspan="3">T_FAMILY</th>
</tr>
<tr>
<th>フィールド名</th>
<th>型</th>
<th>サイズ</th>
</tr>
<tr>
<td>ID</td>
<td>LONG</td>
<td>4</td>
</tr>
<tr>
<td>NAME</td>
<td>CHAR</td>
<td>8</td>
</tr>
<tr>
<td>FATHER_ID</td>
<td>LONG</td>
<td>4</td>
</tr>
</tbody>
</table>
<p>このテーブルには、ある人のIDと名前、そしてその人の父親のIDが定義されています。そして父親のレコードもまた、同じテーブル上に格納するものとします。</p>
<p>このとき、親子関係にある二人の名前の組を検索してみましょう。</p>
<p>SQLでは以下のようになります:</p>
<pre>
SELECT first.NAME, second.NAME
FROM   T_FAMILY first, T_FAMILY second
WHERE  first.ID = second.FATHER_ID
</pre>
<p>DBTools.h++では、ひとつのテーブルから複数のRWDBTableを作ることで実現します:</p>
<pre>
<strong>RWDBTable</strong> first  = db.table("T_FAMILY");
<strong>RWDBTable</strong> second = db.table("T_FAMILY");
<strong>RWDBSelector</strong> select = db.selector();
select &lt;&lt; first["NAME"] &lt;&lt; second["NAME"];
select.where(first["ID"] == second["FATHER_ID"]);
</pre>
<h3>カーソルによるナビゲーション</h3>
<p>前の例ではselectorからreaderを取り出していましたが、これでは最初のレコードから順に取り出す事しかできません。</p>
<p>カーソルを使えば、上のようにして得られたレコードの集合に対し、</p>
<ul>
<li>最初のレコード</li>
<li>最後のレコード</li>
<li>次のレコード</li>
<li>前のレコード</li>
</ul>
<p>を取り出すことができます。</p>
<p>まず、selectorからcursorを手に入れます。</p>
<pre>
  <strong>RWDBCursor</strong> cursor_;
  cursor_ = selector_.<strong>cursor</strong>(RWDBCursor::Scrolling);
</pre>
<p>selectorのメソッドcursorの引数としてRWDBCursor::Scrollingを与えています。これを省略するとRWDBCursor::Sequentialとみなされ、順次読み出ししかできません。</p>
<p>次にカーソルから読み出されるデータを変数にアタッチします。</p>
<pre>
  RWCString name, kind, header;
  cursor &lt;&lt; &amp;name &lt;&lt; &amp;kind &lt;&lt; &amp;header;
</pre>
<p>これによって、カーソルが移動すると同時にカーソルがポイントするレコードの各フィールドの内容が変数にセットされます。</p>
<p>カーソルの移動はfetchRow()メソッドで行ないます。</p>
<ul>
<li>cursor_.<strong>fetchRow</strong>(RWDBCursor::First); //<br />
  先頭へ</li>
<li>cursor_.<strong>fetchRow</strong>(RWDBCursor::Last); //<br />
  末尾へ</li>
<li>cursor_.<strong>fetchRow</strong>(RWDBCursor::Next); //<br />
  次へ</li>
<li>cursor_.<strong>fetchRow</strong>(RWDBCursor::Previous);<br />
  // 前へ</li>
</ul>
<p>下図のようなアプリケーションを作ってみました。</p>
<p><img src="http://www.s34.co.jp/wp-content/uploads/2009/12/fig01.gif" alt="アプリケーション" width="452" height="409" class="alignnone size-full wp-image-1231" /></p>
<p>ダイアログの中ほどにある4つのボタンで先頭/前/次/末尾に移動します。</p>
<p>ソースコードは以下のようになります。</p>
<pre>
// ------------- definition ---
#ifndef _DBTOOLSDLG_H_
#define _DBTOOLSDLG_H_

class DBToolsDlg : public CDialog {
private:
    <strong>RWDBDatabase</strong> db_;
    <strong>RWDBTable</strong>    stl_;
    <strong>RWDBTable</strong>    kind_;
    <strong>RWDBTable</strong>    head_;
    <strong>RWDBSelector</strong> selector_;
    <strong>RWDBCursor</strong>   cursor_;

    static CDialog* self_;

private:
    static void onError(const <strong>RWDBStatus</strong>&amp; status);
    void initDB();  // データベースとの接続
    void display(); // 検索結果の表示

public:
    DBToolsDlg(CWnd* pParent = NULL);

    //{{AFX_DATA(DBToolsDlg)
    enum { IDD = IDD_DBTOOLS_4_DIALOG };
    //}}AFX_DATA

    //{{AFX_VIRTUAL(DBToolsDlg)
    //}}AFX_VIRTUAL

protected:
    HICON m_hIcon;

    //{{AFX_MSG(DBToolsDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void OnFirst();  // <strong>先頭レコードへ</strong>
    afx_msg void OnLast();   // <strong>末尾レコードへ</strong>
    afx_msg void OnNext();   // <strong>次のレコードへ</strong>
    afx_msg void OnPrev();   // <strong>前のレコードへ</strong>
    afx_msg void OnSearch(); // 検索開始
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

#endif

// ------------- implementation ---
#include "stdafx.h"
#include "DBTools_4.h"
#include "DBToolsDlg.h"

DBToolsDlg::DBToolsDlg(CWnd* pParent /*=NULL*/)
  : CDialog(DBToolsDlg::IDD, pParent) {
    //{{AFX_DATA_INIT(DBToolsDlg)
    //}}AFX_DATA_INIT
    m_hIcon = AfxGetApp()-&gt;LoadIcon(IDR_MAINFRAME);
    self_ = this;
}

BEGIN_MESSAGE_MAP(DBToolsDlg, CDialog)
    //{{AFX_MSG_MAP(DBToolsDlg)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_FIRST, OnFirst)
    ON_BN_CLICKED(IDC_LAST, OnLast)
    ON_BN_CLICKED(IDC_NEXT, OnNext)
    ON_BN_CLICKED(IDC_PREV, OnPrev)
    ON_BN_CLICKED(IDC_SEARCH, OnSearch)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void DBToolsDlg::OnSysCommand(UINT nID, LPARAM lParam) {
  if ((nID &amp; 0xFFF0) == IDM_ABOUTBOX) {
    CAboutDlg dlgAbout;
    dlgAbout.DoModal();
  } else {
    CDialog::OnSysCommand(nID, lParam);
  }
}

void DBToolsDlg::OnPaint() {
  if (IsIconic()) {
    CPaintDC dc(this); // 描画用のデバイス コンテキスト
    SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
    CRect rect;
    GetClientRect(&amp;rect);
    int x = (rect.Width() - GetSystemMetrics(SM_CXICON) + 1) / 2;
    int y = (rect.Height() - GetSystemMetrics(SM_CYICON) + 1) / 2;
    dc.DrawIcon(x, y, m_hIcon);
  } else {
    CDialog::OnPaint();
  }
}

HCURSOR DBToolsDlg::OnQueryDragIcon() {
    return (HCURSOR) m_hIcon;
}

/*
 * サーバ・タイプ: Debug mode   - msq15d.dll
 *                 Release mode - msq12d.dll
 */
#ifdef RWDEBUG
#define SERVER "msq15d.dll"
#else
#define SERVER "msq12d.dll"
#endif

CDialog* DBToolsDlg::self_ = 0;

namespace {

  void none(const <strong>RWDBStatus</strong>&amp;) {
    // nothing to do...
  }

  inline void set_bitmap(CWnd* wnd, UINT n) {
    HBITMAP bmp = LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(n));
    static_cast&lt;CButton*&gt;(wnd)-&gt;SetBitmap(bmp);
  }

  struct record {
    RWCString name;
    RWCString kind;
    RWCString head;
    void clear() { name = ""; kind = ""; head = ""; }
  } rec;
}

BOOL DBToolsDlg::OnInitDialog() {
  CDialog::OnInitDialog();

  ASSERT((IDM_ABOUTBOX &amp; 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX &lt; 0xF000);
  CMenu* pSysMenu = GetSystemMenu(FALSE);
  if (pSysMenu != NULL) {
    CString strAboutMenu;
    strAboutMenu.LoadString(IDS_ABOUTBOX);
    if (!strAboutMenu.IsEmpty()) {
      pSysMenu-&gt;AppendMenu(MF_SEPARATOR);
      pSysMenu-&gt;AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
  }

  SetIcon(m_hIcon, TRUE);
  SetIcon(m_hIcon, FALSE);

  set_bitmap(GetDlgItem(IDC_FIRST),IDB_FIRST);
  set_bitmap(GetDlgItem(IDC_PREV), IDB_PREV);
  set_bitmap(GetDlgItem(IDC_NEXT), IDB_NEXT);
  set_bitmap(GetDlgItem(IDC_LAST), IDB_LAST);

  initDB();
  SetDlgItemText(IDC_PATTERN, "%");
  static_cast&lt;CButton*&gt;(GetDlgItem(IDC_RNAME))-&gt;SetCheck(1);
  OnSearch();
  return TRUE;
}

// データベースとの接続とテーブルの取得
void DBToolsDlg::initDB() {
  <strong>RWDBManager</strong>::<strong>setErrorHandler</strong>(onError);

  db_ = <strong>RWDBManager</strong>::<strong>database</strong>(
     // serverType, serverName,   userName, password, databaseName
        SERVER,     "GATEWAY_FF", "sa",     "",       "TRIAL"
        );

  stl_  = db_.<strong>table</strong>("T_STL");
  kind_ = db_.<strong>table</strong>("T_KIND");
  head_ = db_.<strong>table</strong>("T_HEAD");
}

// 先頭レコードへ
void DBToolsDlg::OnFirst() {
  cursor_.<strong>fetchRow</strong>(<strong>RWDBCursor</strong>::First);
  display();
}

// 末尾レコードへ
void DBToolsDlg::OnLast() {
  cursor_.<strong>fetchRow</strong>(<strong>RWDBCursor</strong>::Last);
  display();
}

// 次のレコードへ
void DBToolsDlg::OnNext() {
  if ( cursor_.<strong>fetchRow</strong>(<strong>RWDBCursor</strong>::Next).<strong>isValid</strong>() )
    display();
  else {
    MessageBeep(0);
    OnLast();
  }
}

// 前のレコードへ
void DBToolsDlg::OnPrev() {
  if ( cursor_.<strong>fetchRow</strong>(<strong>RWDBCursor</strong>::Previous).<strong>isValid</strong>() )
    display();
  else {
    MessageBeep(0);
    OnFirst();
  }
}

// レコードの表示
void DBToolsDlg::display() {
  SetDlgItemText(IDC_NAME, rec.name.data());
  SetDlgItemText(IDC_KIND, rec.kind.data());
  SetDlgItemText(IDC_HEAD, rec.head.data());
}

// エラーハンドラ : エラーメッセージを出力
void DBToolsDlg::onError(const <strong>RWDBStatus</strong>&amp; aStatus) {
  if ( aStatus.<strong>errorCode</strong>() != <strong>RWDBStatus</strong>::ok ) {
    AfxMessageBox(aStatus.<strong>message</strong>().data());
    self_-&gt;EndDialog(IDCANCEL);
  }
}

// 検索
void DBToolsDlg::OnSearch() {

  <strong>RWDBColumn</strong> column;
  switch ( GetCheckedRadioButton(IDC_RNAME,IDC_RHEAD) ) {
  case IDC_RHEAD :
    column = head_<strong>[</strong>"NAME"<strong>]</strong>; break;
  case IDC_RKIND :
    column = kind_<strong>[</strong>"NAME"<strong>]</strong>; break;
  case IDC_RNAME : // no break, fall through...
  default:
    column = stl_<strong>[</strong>"NAME"<strong>]</strong>; break;
  }

  /* 生成されるSQL文は:
   *
   * SELECT
   *   T_STL,NAME,
   *   T_KIND.NAME,
   *   T_HEAD.NAME
   * FROM
   *   T_STL,
   *   T_KIND,
   *   T_HEAD
   * WHERE
   *   T_STL.KIND_ID = T_KIND.ID
   *     AND
   *   T_STL.HEAD_ID = T_HEAD.ID
   *     AND
   *   COLUMN LIKE 'STRING'
   * ORDER BY
   *   COLUMN ASC
   *
   * と等価である。
   * なお 上記SQLにおいて COLUMN, 'STRING' はユーザが設定する
   */

  CString key;
  GetDlgItemText(IDC_PATTERN, key);
  <strong>RWDBCriterion</strong> join = stl_<strong>[</strong>"KIND_ID"] == kind_<strong>[</strong>"ID"<strong>]</strong> &amp;&amp;
                       stl_<strong>[</strong>"HEAD_ID"<strong>]</strong> == head_<strong>[</strong>"ID"<strong>]</strong>;
  selector_ = db_.<strong>selector</strong>();
  selector_ &lt;&lt; stl_<strong>[</strong>"NAME"<strong>]</strong> &lt;&lt; kind_<strong>[</strong>"NAME"<strong>]</strong> &lt;&lt; head_<strong>[</strong>"NAME"<strong>]</strong>;
  selector_.<strong>where</strong>(join &amp;&amp; column.<strong>like</strong>(static_cast&lt;const char*&gt;(key)));
  selector_.<strong>orderBy</strong>(column);

  // 生成されたSQL文を表示
  SetDlgItemText(IDC_SQL, selector_.<strong>asString</strong>());
  /*
   * 次のようなSQL文が生成される:
   *
   * SELECT
   *   t3.NAME,
   *   t4.NAME,
   *   t5.NAME
   * FROM
   *   T_STL t3,
   *   T_KIND t4,
   *   T_HEAD t5
   * WHERE
   *   t3.KIND_ID = t4.ID
   *     AND
   *   t3.HEAD_ID = t5.ID
   *     AND
   *   t3.NAME LIKE '%'
   * ORDER BY
   *   t3.NAME ASC
   */

  // カーソルを取得
  cursor_ = selector_.<strong>cursor</strong>(<strong>RWDBCursor</strong>::Scrolling);
  cursor_ &lt;&lt; &amp;rec.name &lt;&lt; &amp;rec.kind &lt;&lt; &amp;rec.head;
  cursor_.<strong>setErrorHandler</strong>(none);
  rec.clear();
  // 先頭レコードへ
  OnFirst();
}
</pre>
<h3>トランザクション</h3>
<p>複数のテーブルでひとつのデータの組を表現しているとき、データの更新(追加/削除/変更)を行なうには、当然ながら複数のテーブルに対して追加/削除/変更しなければなりません。</p>
<p>このとき、いずれかのテーブルの更新に失敗したとき、他のテーブルに対して行なった更新をキャンセルしてあげないと、データの整合が狂ってしまいます。すなわち、対象となる全テーブルの更新が成功しなかったときは、更新前の状態に戻してあげなければなりません。</p>
<p>DBTools.h++ではconnectionを使って変更の確定とキャンセルを行ないます。</p>
<p>データベースからconnectionを手に入れます。</p>
<pre>
  <strong>RWDBConnection</strong> con;
  con = db_.<strong>connection</strong>();
  con.<strong>autoCommit</strong>(false);
</pre>
<p>テーブルの更新に先立って、トランザクションを開始します。</p>
<pre>
  con.<strong>beginTransaction</strong>();
</pre>
<p>以後、テーブルに対する操作(追加/削除/変更)は、connectionを引数に渡します。</p>
<p>たとえばレコードの追加であれば:</p>
<pre>
  <strong>RWDBInserter</strong> inserter = stl_.<strong>inserter</strong>();
  //          NAME , KIND_ID, HEAD_ID
  inserter &lt;&lt; "some_function" &lt;&lt; 10 &lt;&lt; 4;
  inserter.<strong>execute</strong>(<strong>con</strong>);
</pre>
<p>もし一連のテーブルの更新の途中でエラーが発生したとき、</p>
<pre>
  con.<strong>rollbackTransaction</strong>();
</pre>
<p>すれば、全テーブルの内容は con.beginTransaction() を行なった時点の状態まで巻き戻されます。</p>
<p>全テーブルの更新が成功したときは</p>
<pre>
  con.<strong>commitTransaction</strong>();
</pre>
<p>することで、全テーブルの内容が&quot;確定&quot;します。</p>
<p>エラーハンドラと例外を使えば、一連のトランザクション処理を以下のように書く事ができます:</p>
<pre>
void onError(const <strong>RWDBStatus</strong>&amp; aStatus) {
  // エラーが発生したら例外をthrowする
  if ( aStatus.<strong>errorCode</strong>() != <strong>RWDBStatus</strong>::ok )
    aStatus.<strong>raise</strong>();
}

...
// エラーハンドラを設定する
<strong>RWDBManager</strong>::<strong>setErrorHandler</strong>(onError);

<strong>RWDBConnection</strong> con = db_.<strong>connection</strong>();
con.<strong>autoCommit</strong>(false);
try {
  con.<strong>beginTransaction</strong>();
  // テーブル更新処理
  // ...
  con.<strong>commitTransaction</strong>();
} // エラーが発生したらロールバック(巻き戻し)する
catch ( RWExternalErr&amp;) {
  con.<strong>rollbackTransaction</strong>();
}
</pre>
<h3>一括読み出し/書き込み</h3>
<p>テーブルに対するレコードの読み出し/追加にはRWDBReader/REDBInserterを使います。が、RWDBReader/RWDBInserterは読み出し/書き込みごとにRDBへのアクセスを行ないます。したがって、大量のレコードを読み出す/書き込むときにはRDBへのアクセス回数が非常に多いため、パフォーマンスに問題があります。RWDBBulkReader/RWBulkInserterを使えば、一回のRDBアクセスで複数のレコードを一気に読み込む/書き込むことができます。</p>
<pre>
<strong>RWDBConnection</strong> con = db_.<strong>connection</strong>();

// 10レコードづつ一括読み込み
<strong>RWDBBulkReader</strong> bulk_r = stl_.<strong>bulkReader</strong>(con);
<strong>RWDBStringVector</strong> names(10, 32); // 32文字の文字列を10個分
<strong>RWDBVector</strong>&lt;long&gt; kinds(10);
<strong>RWDBVector</strong>&lt;long&gt; heads(10);
bulk_r &lt;&lt; names &lt;&lt; kinds &lt;&lt; heads;

int numsRead = 0;
while ( (numsRead = bulk_r<strong>()</strong>) != 0 ) {
  for ( int i = 0; i &lt;&lt; numsRead; ++i ) {
    cout &lt;&lt; names[i] &lt;&lt; ' '
         &lt;&lt; kinds[i] &lt;&lt; ' '
         &lt;&lt; heads[i]
         &lt;&lt; endl;
  }
}

// 10レコード一括書き込み
<strong>RWDBBulkInserter</strong> bulk_i = stl_.<strong>bulkInserter</strong>(con);
bulk_i &lt;&lt; names &lt;&lt; kinds &lt;&lt; heads;
for ( int i = 0; i &lt; 10; ) {
  names[i] = ...
  kinds[i] = ...
  heads[i] = ...
}
bulk_i.<strong>execute</strong>();
</pre>
<h3>プライマリ・キー(PrimaryKey)と外部キー(Foreign Key)</h3>
<p>テーブルの作成時、フィールドにプライマリ・キー(Primary Key)を設定することにより、データベースのパフォーマンスを向上させることができます。</p>
<p>また、そのプライマリ・キーを別のテーブルのフィールドの外部キーに設定しておけば、テーブル間の整合を保つことができます。</p>
<pre>
  // T_KIND のスキーマ定義
  <strong>RWDBSchema</strong>  kindSchema;

  <strong>RWDBColumn</strong> kind_id;
  kind_id.<strong>name</strong>("ID").type(<strong>RWDBValue</strong>::Long).<strong>nullAllowed</strong>(FALSE);
  kindSchema.<strong>appendColumn</strong>(kind_id);

  <strong>RWDBColumn</strong> kind_name;
  kind_name.<strong>name</strong>("NAME").type(<strong>RWDBValue</strong>::String).nullAllowed(FALSE).<strong>storageLength</strong>(8);
  kindSchema.<strong>appendColumn</strong>(kind_name);

  // ID を T_KIND のプライマリ・キーに設定する
  <strong>RWDBSchema</strong>  primeKey;
  primeKey.<strong>appendColumn</strong>(kindSchema["ID"]);
  kindSchema.<strong>primaryKey</strong>(primeKey);

  // テーブル T_KIND を作成
  db_.<strong>createTable</strong>("T_KIND", kindSchema);

  // T_STL のスキーマ定義
  <strong>RWDBSchema</strong> stlSchema;

  <strong>RWDBColumn</strong> stl_name;
  stl_name.<strong>name</strong>("NAME").type(<strong>RWDBValue</strong>::String).nullAllowed(FALSE).<strong>storageLength</strong>(50);
  stlSchema.<strong>appendColumn</strong>(stl_name);

  <strong>RWDBColumn</strong> stl_kind_id;
  stl_kind_id.<strong>name</strong>("KIND_ID").type(<strong>RWDBValue</strong>::Long).<strong>nullAllowed</strong>(FALSE);
  stlSchema.<strong>appendColumn</strong>(stl_kind_id);

  <strong>RWDBColumn</strong> stl_head_id;
  stl_head_id.<strong>name</strong>("HEAD_ID").type(<strong>RWDBValue</strong>::Long).<strong>nullAllowed</strong>(FALSE);
  stlSchema.<strong>appendColumn</strong>(stl_head_id);

  // T_KIND.ID を T_STL.KIND_ID の外部キーに設定する
  <strong>RWDBForeignKey</strong> keyToKind("KIND_ID");
  keyToKind.<strong>appendColumn</strong>(kindSchema["ID"]);
  stlSchema.<strong>foreignKey</strong>(keyToKind);

  // テーブル T_STL を作成
  db_.<strong>createTable</strong>("T_STL", stlSchema);
</pre>
<h3>マルチスレッド環境下での排他制御</h3>
<p>複数のクライアントからの要求に応じてデータベースの操作を行なうアプリケーションを想定してみてください。クライアントからのいくつもの要求に迅速に対応するには、マルチスレッド化が不可欠となります。しかし、その場合同一のテーブルに対するレコードの追加/削除/変更および検索要求がいくつも飛び込んできます。このとき、それぞれの要求に対してきちんと排他制御を行なわないとテーブルの不整合が発生したり、誤った検索結果を返すことになるでしょう。</p>
<p>DBTools.h++はスレッド間の競合を防ぐため、基本的な排他制御メカニズムを提供しています。</p>
<p>DBTools.h++の提供する多くのクラスには、メソッド<strong>acquire</strong>()と<strong>release</strong>()が定義されています。</p>
<p>acquire()によってそのオブジェクトの使用権を取得します。このとき、他のスレッドによって使用中であった場合、そのオブジェクトが使用可能となるまでブロックされます(acquire()から戻ってこない)。</p>
<p>release()によってそのオブジェクトの使用権を放棄します。acquire()でブロックしているスレッドがあれば、そのスレッドが動き始めます。</p>
<p>このメカニズムを利用すれば、ある瞬間においてオブジェクトを使用しているスレッドをひとつだけに制限する、すなわち排他制御が可能となります。</p>
<pre>
<strong>RWDBConnection</strong> sharedConn;

void foo(const RWDBDatabase&amp; database) {
  sharedConn.<strong>acquire</strong>();
  {
    RWDBStoredProc sp = database::storedProc("myProc", sharedConn);
    sp.execute(sharedConn);
  }
  sharedConn.<strong>release</strong>();
  return;
}
</pre>
<p>上記の例は、ストアド・プロシージャを実行するものです。ストアド・プロシージャの実行に先立ちsharedConnに対してacquire()することで、sharedConnの使用権を取得しています。その後saredConn.release()されるまでは、他のスレッドがsharedConnを使うことができません。こうすることで、ストアド・プロシージャに対し、それを実行するスレッドはただ一つであることを保証します。</p>
<p>ただし、このコードには小さな穴があいています。acuire()からrelease()までの間に例外が発生した場合です。例外が発生すると、この関数のreturnに到達することなく、catchブロックに飛び込みます。したがってこの関数のreturnの直前にあるrelease()が呼ばれることはなく、結果的にsharedConnは使用権が解放されないままになってしまい、その後いかなるスレッドもこの関数をコールしたとたんブロックされることでしょう。</p>
<p>このようなデッドロックを避けるには、この関数の中で例外を捕まえ、そこでrelease()することになるでしょう。</p>
<pre>
RWDBConnection sharedConn;

void foo(const RWDBDatabase&amp; database) {
  sharedConn.<strong>acquire</strong>();
  <strong>try</strong> {
    RWDBStoredProc sp = database::storedProc("myProc", sharedConn);
    sp.execute(sharedConn);
  } <strong>catch</strong> ( RWExternalErr&amp;) {
    sharedConn.<strong>release</strong>();
    <strong>throw</strong>; // 再throw
  }
  sharedConn.<strong>release</strong>();
  return;
}
</pre>
<p>Guardクラスを用意し、そのコンストラクタでacquire(),デストラクタでrelease()すると便利です。</p>
<pre>
<strong>RWDBConnection</strong> sharedConn;

template&lt;class T&gt;
class <strong>Guard</strong> {
  T&amp; object_;
public:
  Guard(const T&amp; t) : object_(t) { object_.acquire(); }
 ~Guard() { object_.release(); }
};

void foo(const RWDBDatabase&amp; database) {
  <strong>Guard</strong>&lt;RWDBConnection&gt; guard(sharedConn);
  {
    RWDBStoredProc sp = database::storedProc("myProc", sharedConn);
    sp.execute(sharedConn);
  }
  return;
}
</pre>
<p>こうしておけば、例外が発生したときGuardのデストラクタがrelease()してくれるのでデッドロックを回避できます。</p>
<h3>SQLの直接実行</h3>
<p>このように、DBTools.h++はSQLをまったく記述せずにRDBの様々な操作を可能にしてくれます。</p>
<p>しかしながら、特定のRDBだけがサポートするサービスの利用など、場合によってはどうしてもSQLでないと実行できないものもあります。そんなときのために、DBTools.h++はSQLの直接実行を許しています(マニュアルには&quot;お勧めしない&quot;と明記されていますが&#8230;)。</p>
<p>以下はRogue Waveのサポートチームが提供してくれた、SQL直接実行のサンプルです。コメントは適宜和訳しました。</p>
<pre>
/*
** 以下のSQL-SELECT文を実行するプログラム
** (外部結合の入れ子はDBTools.h++では未サポート)
**
**  SELECT t0.NAME, t1.NAME
**  FROM T_JOB t1 RIGHT JOIN
**   (T_EMP t0 INNER JOIN T_MAP t2
**      on t0.ID = t2.EMP_ID)
**   on t1.ID = t2.JOB_ID
*/

#include &lt;rw/rstream.h&gt;
#include &lt;rw/db/db.h&gt;

void errorHandler (const RWDBStatus&amp; aStatus) {
  // エラーメッセージを出力
  if (((int)aStatus.errorCode()) != 0) {
    cout &lt;&lt; "Error code:       " &lt;&lt; (int) aStatus.errorCode() &lt;&lt; endl
           &lt;&lt; "Error message     " &lt;&lt; aStatus.message() &lt;&lt; endl
           &lt;&lt; "Is terminal:      " &lt;&lt; (aStatus.isTerminal() ? "Yes" : "No") &lt;&lt; endl
           &lt;&lt; "Vendor error 1:   " &lt;&lt; aStatus.vendorError1() &lt;&lt; endl
           &lt;&lt; "Vendor error 2:   " &lt;&lt; aStatus.vendorError2() &lt;&lt; endl
           &lt;&lt; "Vendor message 1: " &lt;&lt; aStatus.vendorMessage1() &lt;&lt; endl
           &lt;&lt; "Vendor message 2: " &lt;&lt; aStatus.vendorMessage2() &lt;&lt; endl;
  }
}

int main()  {

  // エラーハンドラをセット
  RWDBManager::setErrorHandler(errorHandler);

  // データベースとの接続
  // (接続パラメータは環境に合わせて適宜変更すること)
  RWDBDatabase dataBase =
    RWDBManager::database("library","server","user","password","");

  // コネクションの生成
  RWDBConnection conn = dataBase.connection();

  // SQL文のトレース
  RWDBTracer&amp; tracer = dataBase.tracer();
  tracer.setOn(RWDBTracer::SQL);
  tracer.stream(cout);

  // 3つのテーブル 'T_EMP', 'T_JOB', 'T_EMP' を作成
  RWDBTable tab1 = dataBase.table("T_EMP");
  RWDBTable tab2 = dataBase.table("T_JOB");
  RWDBTable tab3 = dataBase.table("T_MAP");
  if(!tab1.exists(conn, TRUE)) {
    conn.<strong>executeSql</strong>("create table T_EMP(ID int, NAME char(10))");
    conn.<strong>executeSql</strong>("insert into T_EMP values(1, 'bill')");
    conn.<strong>executeSql</strong>("insert into T_EMP values(2, 'sally')");
    conn.<strong>executeSql</strong>("insert into T_EMP values(3, 'dave')");
    conn.<strong>executeSql</strong>("insert into T_EMP values(4, 'ted')");
    conn.<strong>executeSql</strong>("insert into T_EMP values(5, 'george')");
  }

  if(!tab2.exists(conn, TRUE)) {
    conn.<strong>executeSql</strong>("create table T_JOB(ID int, NAME char(10))");
    conn.<strong>executeSql</strong>("insert into T_JOB values(1, 'porter')");
    conn.<strong>executeSql</strong>("insert into T_JOB values(2, 'smelter')");
    conn.<strong>executeSql</strong>("insert into T_JOB values(3, 'fisher')");
    conn.<strong>executeSql</strong>("insert into T_JOB values(4, 'bell hop')");
    conn.<strong>executeSql</strong>("insert into T_JOB values(5, 'cook')");
  }

  if(!tab3.exists(conn, TRUE)) {
    conn.<strong>executeSql</strong>("create table T_MAP(EMP_ID int, JOB_ID int)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(1, 1)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(2, 2)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(3, 3)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(4, 4)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(5, 5)");
    conn.<strong>executeSql</strong>("insert into T_MAP values(1, 6)");  // T_MAP.EMP_ID は必ず
    conn.<strong>executeSql</strong>("insert into T_MAP values(2, 7)");  // T_EMP.ID に存在するが、
    conn.<strong>executeSql</strong>("insert into T_MAP values(3, 8)");  // T_MAP.JOB_ID は
    conn.<strong>executeSql</strong>("insert into T_MAP values(4, 9)");  // T_JOB.ID になくてもかまわない
    conn.<strong>executeSql</strong>("insert into T_MAP values(5, 10)");

  }
  //  後に続くcleanupのためにselector/readerを解放すべく、{}で囲んだ
  {
    RWDBSelector select = dataBase.selector();
    select &lt;&lt; tab1["NAME"] &lt;&lt; tab2["NAME"];

    // 冒頭のコメントに書かれたSQLと等価な条件式を作る
    RWDBPhraseBook phrase = dataBase.phraseBook();
    RWDBExprFormDefinition on("on %0 %1 %2");
    RWDBExprFormDefinition RJOneArg = ("%0 RIGHT JOIN ");
    RWDBExprFormDefinition IJTwoArg = ("%0 INNER JOIN %1 ");
    RWDBExpr eq("=", FALSE);
    RWCString cs =  RJOneArg(tab2).asString(phrase) + "("
            + IJTwoArg(tab1,tab3).asString(phrase)
            + on(tab1["ID"], eq,tab3["EMP_ID"]).asString(phrase) + ") "
            + on(tab2["ID"], eq,tab3["JOB_ID"]).asString(phrase);

    select.from(cs);

    //  RDBに送られるSQLを出力
    cout &lt;&lt; select.asString() &lt;&lt; endl;
    // SELECT 実行!
    RWDBResult restab = select.execute();

    // 結果を出力
    RWDBReader rdr = restab.table().reader();

    RWDBValue in1;
    RWDBValue in2;
    while(rdr())
    {
      rdr &gt;&gt; in1 &gt;&gt; in2;
      cout &lt;&lt; in1.asString() &lt;&lt; "\t" &lt;&lt; in2.asString() &lt;&lt; endl;
    }

  }
  // テーブルを削除
  tab1.drop();
  tab2.drop();
  tab3.drop();
  return 0;
}
</pre>
<h3>DBTools.h++ vs Oracle-OCI/ODBC</h3>
<table summary="">
<tbody>
<tr>
<th colspan="2">TABLE1</th>
</tr>
<tr>
<th>フィールド名</th>
<th>型</th>
</tr>
<tr>
<td>ID</td>
<td>Integer</td>
</tr>
<tr>
<td>Name</td>
<td>String</td>
</tr>
<tr>
<td>Percent</td>
<td>Float</td>
</tr>
</tbody>
</table>
<p>上のようなテーブルを読み出すコードをDBTools.h++ と Oracle-OCI,そしてODBCで書かれたコードを示します。DBTools.h++がどれだけ簡単でパワpフルかがわかります。</p>
<p>/* * <strong>DBTools.h++</strong> による * &apos;SELECT * FROM TABLE1&apos; */</p>
<pre>
#include &lt;iostream&gt;
#include &lt;rw/db/db.h&gt;

using namespace std;

int       anInt;
RWCString aString;
float     aFloat;

void main () {
  // データベースをオープン
  <strong>RWDBDatabase</strong> aDB =
    <strong>RWDBManager</strong>::<strong>database</strong>("ODBC", "Q+E_paradox", "", "", "c:\\paradat");
  // テーブルを取得
  <strong>RWDBTable</strong> aTable = aDB.<strong>table</strong>("TABLE1");
  // selectorを取得
  <strong>RWDBSelector</strong> aSelector = aDB.<strong>selector</strong>();
  // SELECT * FROM
  aSelector &lt;&lt; aTable;
  // readerを取得
  <strong>RWDBReader</strong> aReader = aSelector.<strong>reader</strong>();
  // 全レコードを出力
  while (aReader<strong>()</strong>) {
    aReader &gt;&gt; anInt &gt;&gt; aString &gt;&gt; aFloat;
    cout &lt;&lt; anInt &lt;&lt; '\t' &lt;&lt; aString &lt;&lt; '\t' &lt;&lt; aFloat &lt;&lt; endl;
  }
}
</pre>
<pre>
/* <strong>Oracle OCI</strong> による
 * 'SELECT * FROM TABLE1'
 */
#include &lt;stdio.h&gt;
#include &lt;ctype.h&gt;
#include &lt;malloc.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;oci.h&gt;
#include &lt;iostream.h&gt;

/* カーソル */

struct <strong>cda_def</strong> cursorStruct;
struct <strong>cda_def</strong> *cursor = &amp;cursorStruct;

#define ASTRING_LEN 31

int   anInt;
char  aString[ASTRING_LEN];
float aFloat;

char *selectString = "SELECT * FROM TABLE1";

<strong>Lda_Def</strong> lda;
text hda[256];

int getOff() {
  <strong>ologof</strong>(&amp;lda);
  return -1;
}

int main(int argc, char **argv) {
  int ret;
  char *uid = "SCOTT/TIGER";

  /* Oracle にログイン */
  if (<strong>orlon</strong>(&amp;lda, hda, (text *) uid, -1, 0, -1, -1))
    return getOff();

  /* カーソルオープン */
  if (<strong>oopen</strong>(cursor, &amp;lda, (text *) 0, -1, -1, (text *) 0, -1))
    return getOff();

  /* SQLを実行 */
  if (<strong>osql3</strong>(cursor, (text *) selectString, -1))
    return getOff();

  /* 出力バッファにバインド */
  if (<strong>odefin</strong>(cursor, 1, (ub1 *) &amp;anInt, sizeof(int), OCI_EINT,
             -1, (sb2 *) 0, (text *) 0, -1, -1, (ub2 *) 0, (ub2 *) 0)
      || <strong>odefin</strong>(cursor, 2, (ub1 *) aString, ASTRING_LEN, OCI_ESTR,
             -1, (sb2 *) 0, (text *) 0, -1, -1, (ub2 *) 0, (ub2 *) 0)
      || <strong>odefin</strong>(cursor, 3,(ub1 *) &amp;aFloat, sizeof(float), OCI_EFLT,
             -1, (sb2 *) 0, (text *) 0, -1, -1, (ub2 *) 0, (ub2 *) 0))
    return getOff();

  if (<strong>oexec</strong>(cursor))
    return getOff();

  /* カーソルを移動しながら出力 */
  while (!<strong>ofetch</strong>(cursor))
    cout &lt;&lt; anInt &lt;&lt; '\t' &lt;&lt; aString &lt;&lt; '\t' &lt;&lt; aFloat &lt;&lt; endl;

  <strong>ologof</strong>(&amp;lda);
  return 0;
}
</pre>
<pre>
/*
 * <strong>ODBC</strong> による
 * 'SELECT * FROM TABLE1'
 */
#include &lt;windows.h&gt;
#include &lt;odbcinst.h&gt;
#include &lt;w16macro.h&gt;
#include &lt;sql.h&gt;
#include &lt;sqlext.h&gt;
#include &lt;iostream.h&gt;

#define STRING_LEN 30

<strong>HENV</strong>    henv;
<strong>HDBC</strong>    hdbc;
<strong>HSTMT</strong>   hstmt;
<strong>RETCODE</strong> retcode;

int   anInt;
UCHAR aString[STRING_LEN];
float aFloat;
<strong>SDWORD</strong> cbInt, cbString, cbFloat;

void main() {
  /* 環境ハンドルを取得 */
  retcode = <strong>SQLAllocEnv</strong>(&amp;henv);

  if (retcode != SQL_SUCCESS)
     return; /* Error */

  /* 接続ハンドルを取得 */
  retcode = <strong>SQLAllocConnect</strong>(henv, &amp;hdbc);

  if (retcode != SQL_SUCCESS)
    return; /* Error */

  /* ログイン・タイムアウトを5秒に設定 */
  <strong>SQLSetConnectOption</strong>(hdbc, SQL_LOGIN_TIMEOUT, 5);
  /* データソースに接続 */
  retcode = <strong>SQLConnect</strong>(hdbc, "Q+E_paradox", SQL_NTS, "", SQL_NTS, "", SQL_NTS);

  if (retcode != SQL_SUCCESS &amp;&amp; retcode != SQL_SUCCESS_WITH_INFO)
    return; /* Error */

  /* ステートメントハンドルを取得 */
  retcode = <strong>SQLAllocStmt</strong>(hdbc, &amp;hstmt);
  /* SQL文の実行 */
  retcode = <strong>SQLExecDirect</strong>(hstmt, "SELECT * FROM TABLE1", SQL_NTS);

  if (retcode == SQL_SUCCESS) {
    while (TRUE) {
      /* フェッチ */
      retcode = <strong>SQLFetch</strong>(hstmt);

      if (retcode == SQL_ERROR)
        return; /* Error condition */

      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
        /* レコードの出力 */
        <strong>SQLGetData</strong>(hstmt, 1, SQL_C_DEFAULT, &amp;anInt, 0, &amp;cbInt);
        <strong>SQLGetData</strong>(hstmt, 2, SQL_C_CHAR, aString, STRING_LEN, &amp;cbString);
        <strong>SQLGetData</strong>(hstmt, 3, SQL_C_FLOAT, &amp;aFloat, 0, &amp;cbFloat);
        cout &lt;&lt; anInt &lt;&lt; '\t' &lt;&lt; aString &lt;&lt; '\t' &lt;&lt; aFloat &lt;&lt; endl;
      }
      else
        break;
    }
  }

  <strong>SQLFreeStmt</strong>(hstmt, SQL_DROP);
  <strong>SQLDisconnect</strong>(hdbc);
  <strong>SQLFreeConnect</strong>(hdbc);
  <strong>SQLFreeEnv</strong>(henv);
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/dbtools2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>7つの電話番号簿</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/phonebooks/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/phonebooks/#comments</comments>
		<pubDate>Thu, 06 Apr 2006 05:56:06 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=1049</guid>
		<description><![CDATA[電話番号簿を作る&#8230; 僕のSTL本&#34;Standard Template Library プログラミング&#34;では、STLコンテナのサンプルとして、簡単な電話番号簿を紹介しています。この本の中ではひとつのアプリケーション(電話番号簿)を、STLが提供する様々なコンテナを使って実装してみました。 このアーティクルでは趣向を変えて、電話番号簿アプリケーションをSTL,MFCコレクション,そしてTools.h++を使った実装を試み、それぞれの違いをご覧にいれましょう。 電話番号簿は&#34;電話番号(Key)と名前(Value)の対応表(Map)&#34;です。電話番号簿の機能は: 電話番号と名前の組を登録する 電話番号から名前を検索する 電話番号(と名前の組)を削除する すべての電話番号(と名前の組)を削除する 登録された電話番号と名前の組をファイルに書き込む/読み出す の5つです。 Standard C++ : STL ANSI/ISO standard C++(標準C++)のライブラリの一部として組み入れられたSTL。STLは数多くのライブラリの中でも最もポータビリティに優れたライブラリといえるでしょう。何といっても標準ですからね。 map&#60;Key,T&#62; STLが提供するmap&#60;Key,T&#62;は、電話番号簿を実装するのに最適のコンテナで、2つのデータの1対1の対応表を実現します。 mapは電話番号簿の実装に必要な機能のほとんどを満足しているのですが、ただひとつ、ファイルに対する書き込み/読み込み部分はプログラマが用意しなくてはなりません。 mapに限らずSTLは基本的な最小限の機能をきっちりとサポートするというのがコンセプトであり、ファイルに対する入出力については&#34;道具は用意してやるから、必要ならばあんたが作んな&#34;というスタンスに立っているのです。 /* * phonebook.cpp * standard C++ STL * map&#60;Key,T&#62; を使った簡単な電話番号簿 */ #include &#60;iostream&#62; // cin, cout #include &#60;fstream&#62; // ifstream, ofstream #include &#60;string&#62; // string #include &#60;map&#62; // map #include &#60;iterator&#62; [...]]]></description>
			<content:encoded><![CDATA[<h3>電話番号簿を作る&#8230;</h3>
<p>僕のSTL本&quot;Standard Template Library プログラミング&quot;では、STLコンテナのサンプルとして、簡単な電話番号簿を紹介しています。この本の中ではひとつのアプリケーション(電話番号簿)を、STLが提供する様々なコンテナを使って実装してみました。</p>
<p>このアーティクルでは趣向を変えて、電話番号簿アプリケーションをSTL,MFCコレクション,そしてTools.h++を使った実装を試み、それぞれの違いをご覧にいれましょう。</p>
<p>電話番号簿は&quot;電話番号(Key)と名前(Value)の対応表(Map)&quot;です。電話番号簿の機能は:</p>
<ol>
<li>電話番号と名前の組を登録する</li>
<li>電話番号から名前を検索する</li>
<li>電話番号(と名前の組)を削除する</li>
<li>すべての電話番号(と名前の組)を削除する</li>
<li>登録された電話番号と名前の組をファイルに書き込む/読み出す</li>
</ol>
<p>の5つです。</p>
<h3>Standard C++ : STL</h3>
<p>ANSI/ISO standard C++(標準C++)のライブラリの一部として組み入れられたSTL。STLは数多くのライブラリの中でも最もポータビリティに優れたライブラリといえるでしょう。何といっても<strong>標準</strong>ですからね。</p>
<h4>map&lt;Key,T&gt;</h4>
<p>STLが提供するmap&lt;Key,T&gt;は、電話番号簿を実装するのに最適のコンテナで、2つのデータの1対1の対応表を実現します。</p>
<p>mapは電話番号簿の実装に必要な機能のほとんどを満足しているのですが、ただひとつ、ファイルに対する書き込み/読み込み部分はプログラマが用意しなくてはなりません。</p>
<p>mapに限らずSTLは基本的な最小限の機能をきっちりとサポートするというのがコンセプトであり、ファイルに対する入出力については<strong>&quot;道具は用意してやるから、必要ならばあんたが作んな&quot;</strong>というスタンスに立っているのです。</p>
<pre>
/*
 * phonebook.cpp
 *   standard C++ STL
 *   map&lt;Key,T&gt; を使った簡単な電話番号簿
 */

#include &lt;iostream&gt;   // cin, cout
#include &lt;fstream&gt;    // ifstream, ofstream
#include &lt;string&gt;     // string
#include &lt;map&gt;        // map
#include &lt;iterator&gt;   // istream_iterator, ostream_iterator
#include &lt;algorithm&gt;  // copy

using namespace std;

// 名前(string)をKey, 電話番号(string)をValueとする辞書
typedef <strong>map&lt;string,string&gt;</strong>    book_type;

/* namespace std { ... } の必要はないのだが、VC++ではこうしないと
 * compile errorとなる。 */
namespace std {

  // レコード(名前と電話番号の組)を出力する
  ostream&amp; operator&lt;&lt;(ostream&amp; strm, const book_type::value_type&amp; v) {
    return strm &lt;&lt; v.first
           &lt;&lt; &quot; : &quot;
           &lt;&lt; v.second;
  }

  // レコード(名前と電話番号の組)を入力する
  istream&amp; operator&gt;&gt;(istream&amp; strm, book_type::value_type&amp; v) {
    // 名前と電話番号の間にある&apos;:&apos;を抜くためにdummyを用意した
    char dummy;
    return strm &gt;&gt; const_cast&lt;book_type::key_type&amp;&gt;(v.first) // <a href="#stl_1">[1]</a>
                &gt;&gt; dummy
                &gt;&gt; v.second;
  }

}

// プロンプト
char prompt() {
  string command;
  cout &lt;&lt; &quot;add/delete/find/list/clear/quit [a,d,f,l,c,q] ?&quot; &lt;&lt; flush;
  cin &gt;&gt; command;
  return command[0];
}

int main() {

  book_type book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  ifstream in(&quot;phonebook.txt&quot;);
  if ( in.is_open() ) {
    copy(istream_iterator&lt;book_type::value_type&gt;(in), // <a href="#stl_2">[2]</a>
         istream_iterator&lt;book_type::value_type&gt;(),
         inserter(book, book.<strong>begin</strong>()));
    in.close();
  }

  bool quit = false;
  do {
    string name;
    string phone;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>find</strong>(name) != book.<strong>end</strong>() ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book<strong>[</strong>name<strong>]</strong> = phone;
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      if ( book.<strong>find</strong>(name) == book.<strong>end</strong>() ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        book.<strong>erase</strong>(name); // <a href="#stl_3">[3]</a>
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      book_type::iterator i = book.<strong>find</strong>(name);
      if ( i == book.<strong>end</strong>() ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; *i &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.<strong>size</strong>() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      copy(book.<strong>begin</strong>(),
           book.<strong>end</strong>(),
           ostream_iterator&lt;book_type::value_type&gt;(cout,&quot;¥n&quot;));
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>clear</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  ofstream out(&quot;phonebook.txt&quot;);
  if ( out.is_open() ) {
    copy(book.<strong>begin</strong>(),
         book.<strong>end</strong>(),
         ostream_iterator&lt;book_type::value_type&gt;(out,&quot;¥n&quot;));
    out.close();
  }

  return 0;
}
</pre>
<dl>
<dt><a name="stl_1">1:</a></dt>
<dd>
<p>map&lt;Key,T&gt;::value_type は pair&lt;<strong>const</strong> Key, T&gt;であるため、ストリームから読み出したKeyを代入できません。そこでconst_castによってconst Key&amp; をKey&amp;にキャストしています。 </p>
</dd>
<dt><a name="stl_2">2:</a></dt>
<dd>
<p>アルゴリズム copy と istream_iterator, insert_iterator を使ってストリームから読み出し、bookに登録しています。</p>
</dd>
<dt><a name="stl_3">3:</a></dt>
<dd>
<p>直前のbook.find(name)で得られたiteratorを流用する、すなわち: </p>
<pre>
book_type::iterator it = book.find(name);
if ( it == book.end() ) {
  cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
} else {
  book.erase(it);
}
</pre>
<p>としたほうがbetterでしょう。このままでは検索が2回行われることになりますから。 </p>
</dd>
</dl>
<h3>Microsoft : MFC</h3>
<p>Microsoft Visual C++(VC++)はWindows環境下でのC++アプリケーション開発環境として不動の地位を保っています。VC++がこれだけポピュラーな開発環境となった要因としてMFC(Microsoft Foundation Classes)の存在は外せません。MFCがなかったらVC++がこれほどまでに多くのプログラマに支持されることはなかったでしょう。</p>
<p>MFCの提供するコレクション(コンテナ)には様々なものがありますが、電話番号簿に適したクラスといえばCMapStringToStringおよびCMapでしょうか。 </p>
<h4>CMapStringToString</h4>
<p>CMapStringToStringは同じくMFCが提供する文字列クラスCStringをKey/Valueとする対応表です。MFCはその昔、Windows3.xの頃から使われていました。当時のVC++(v1.x)はまだtemplateをサポートしていなかったため、template を使わないコンテナがいくつか提供されており、現在もMFCﾉ中に生き残っています。CMapStringToStringはその旧き善き(?)コンテナクラスのひとつです。</p>
<p>CMapStringToStringはハッシュ表で実装されています。ですから検索は非常に高速ですが、コンテナの内容を順次取り出したときKeyの大小関係とは全く関係の無い順序となっています。 </p>
<pre>
/*
 * phonebook.cpp
 *   Microsoft MFC
 *   CMapStringToString を使った簡単な電話番号簿
 */

#include &lt;afx.h&gt;     // CMapStringToString, CFile, CArchive, CString
#include &lt;iostream&gt;  // cin, cout

using namespace std;

// プロンプト
char prompt() {
  char command[256];
  cout &lt;&lt; &quot;add/delete/find/list/clear/quit [a,d,f,l,c,q] ?&quot; &lt;&lt; flush;
  cin &gt;&gt; command;
  return command[0];
}

int main() {

  <strong>CMapStringToString</strong> book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    CFile in;
    if ( in.Open(&quot;phonebook.dat&quot;,CFile::modeRead) ) {
      CArchive ar(&#038;in,CArchive::load);
      book.<strong>Serialize</strong>(ar);
    }
  }

  bool quit = false;
  do {
    char name[64];
    char phone[64];
    CString key;
    CString value;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>Lookup</strong>(name,value) ) { // <a href="#mfc_1_1">[1]</a>
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt;quot;phone:&quot; &lt;&lt; flush;
        cin >> phone;
        // レコードを追加する
        book.<strong>SetAt</strong>(name,phone);
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      if ( !book.<strong>Lookup</strong>(name,value) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        book.<strong>RemoveKey</strong>(name);
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      if ( !book.<strong>Lookup</strong>(name,value) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; name
             &lt;l&lt; &quot; : &quot;
             &lt;&lt; static_cast&lt;const char*&gt;(value)// <a href="#mfc_1_2">[2]</a>
             &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.GetCount() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      POSITION pos = book.<strong>GetStartPosition</strong>(); // <a href="#mfc_1_3">[3]</a>
      while ( pos ) {
        book.<strong>GetNextAssoc</strong>(pos,key,value);
        cout &lt;&lt; static_cast&lt;const char*&gt;(key)
             &lt;&lt; &quot; : &quot;
             &lt;&lt; static_cast&lt;const char*&gt;(value)
             &lt;&lt; endl;
      }
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>RemoveAll</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &#038;ltl&#038;ltl endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &gt;&gt; endl;
  {
    CFile out;
    if ( out.Open(&quot;phonebook.dat&quot;,CFile::modeWrite|CFile::modeCreate) ) {
      CArchive ar(&amp;out,CArchive::store);
      book.<strong>Serialize</strong>(ar);
    }
  }

  return 0;
}
</pre>
<dl>
<dt><a name="mfc_1_1">1:</a></dt>
<dd>
<p>CMapStringToString::Lookup(LPCTSTR k, CString&amp; v): 検索を行ないます。keyを探し、見つかったら対応するvalueをvにセットしてくれます。</p>
</dd>
<dt><a name="mfc_1_2">2:</a></dt>
<dd>
<p>CStringはストリームに対して&lt;&lt;できません。const char*にキャストします。</p>
</dd>
<dt><a name="mfc_1_3">3:</a></dt>
<dd>
<p>MFCコレクションの要素を順に取り出すには、このような奇怪なやり方になるんですよね。</p>
<pre>
POSITION pos = book.GetStartPosition(); // 先頭位置を取得する
while ( pos ) {
  book.GetNextAssoc(pos,key,value); // keyとvalueを手に入れ、posをひとつ進める
  ...
}
</pre>
</dd>
</dl>
<h3>CMap&lt;K,AK,V,AV&gt;</h3>
<p>VC++がtemplateをサポートするようになって、MFCにもtemplateを用いたコンテナが提供されるようになりました。CMap&lt;K,AK,V,AV&gt;はtemplateによるコンテナのひとつです。</p>
<p>CMap&lt;K,AK,V,AV&gt;もCMapStringToStringと同様、ハッシュ表による対応表です。ハッシュ表を実現するにはデータからハッシュ値を返すハッシュ関数、そして2つのデータが同じであるかを判断する関数が必要ですが、MFCではプログラマがこれらヘルパ関数を定義することで与えます。電話番号簿の例ではハッシュ関数ヘルパを明示的に定義してはいません。これはKeyであるCStringのハッシュ関数はあらかじめMFCが用意してくれているからです。</p>
<p>このようにあらかじめ与えておいたひとつのハッシュ関数/比較関数をすべてのCMap&lt;K,AK,V,AV&gt;から呼び出す構造であり、用途に応じてハッシュ/比較関数をとりかえることができません。たとえば</p>
<pre>
struct person {
  CString name;
  CString id;
  ...
}
</pre>
<p>があったとき、nameで検索するCMapとidで検索するCMapをひとつのアプリケーションの中で実装するのが非常に困難です(無理をすればできないこともないのですが&#8230;)。</p>
<pre>
/*
 * phonebook.cpp
 *   Microsoft MFC
 *   CMap&lt;K,AK,V,AV&gt; を使った簡単な電話番号簿
 */

#include &lt;afx.h&gt;      // CFile, CArchive, CString
#include &lt;afxtempl.h&gt; // CMap
#include &lt;iostream&gt;   // cin, cout

using namespace std;

// プロンプト: 省略

int main() {

  <strong>CMap&lt;CString,const char*,CString,const char*&gt;</strong> book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    CFile in;
    if ( in.Open(&quot;phonebook.dat&quot;,CFile::modeRead) ) {
      CArchive ar(&amp;in,CArchive::load);
      book.<strong>Serialize</strong>(ar);
    }
  }

  bool quit = false;
  do {
    char name[64];
    char phone[64];
    CString key;
    CString value;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>Lookup</strong>(name,value) ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book.<strong>SetAt(</strong>name,phone);
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      if ( !book.<strong>Lookup</strong>(name,value) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        book.<strong>RemoveKey</strong>(name);
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      if ( !book.<strong>Lookup</strong>(name,value) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; name
             &lt;&lt; &quot; : &quot;

             &lt;&lt; static_cast&lt;const char*&gt;(value)
             &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.<strong>GetCount</strong>() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      POSITION pos = book.<strong>GetStartPosition</strong>();
      while ( pos ) {
        book.<strong>GetNextAssoc</strong>(pos,key,value);
        cout &lt;&lt; static_cast&lt;const char*&gt;(key)
             &lt;&lt; &quot; : &quot;

             &lt;&lt; static_cast&lt;const char*&gt;(value)
             &lt;&lt; endl;
      }
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>RemoveAll</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    CFile out;
    if ( out.Open(&quot;phonebook.dat&quot;,CFile::modeWrite|CFile::modeCreate) ) {
      CArchive ar(&amp;out,CArchive::store);
      book.<strong>Serialize</strong>(ar);
    }
  }

  return 0;
}
</pre>
<h3>Rogue Wave : Tools.h++</h3>
<p>Rogue Wave Software社のクラスライブラリTools.h++は様々なOS/コンパイラに対応した、優れたライブラリです。</p>
<p>STLが提供するコンテナは以下の7種:</p>
<pre>
vector ------ 可変長配列
list -------- 双方向リスト
deque ------- 両端キュー
set --------- 重複を許さない集合
multiset ---- 重複を許す集合
map --------- 重複を許さない辞書
multimap ---- 重複を許す辞書
</pre>
<p>MFCでは</p>
<pre>
CArray ---- 可変長配列
CList ----- 双方向リスト
CMap ------ 重複を許さない辞書
</pre>
<p>の、たったの3種です。</p>
<p>これに対しTools.h++は、</p>
<pre>
RWBitVec ---------------------------- ビットベクタ
RWBTreeOnDisk ----------------------- ディスク上のB木
    RWBag --------------------------- バッグ
    RWBinaryTree -------------------- 2進木
    RWBTree ------------------------- B木
      RWBTreeDictionary ------------- B木による辞書
      RWDlistCollectables ----------- 双方向リスト
      RWOrdered --------------------- 可変長配列
        RWSortedVector -------------- ソートされた配列
      RWSlistCollectables ----------- 単方向リスト
        RWSlistCollectablesQueue ---- キュー
        RWSlistCollectablesStack ---- スタック
    RWHashTable --------------------- ハッシュ表
      RWSet ------------------------- セット
        RWHashDictionary ------------ 辞書
          RWIdentityDictionary ------ 同一性辞書
        RWIdentitySet --------------- 同一性セット

RWGBitVec(size) --------------------- ビット配列
RWGDlist(type) ---------------------- 双方向リスト
RWGOrderedVector(val) --------------- 可変長配列
RWGQueue(type) ---------------------- キュー
RWGSlist(type) ---------------------- 単方向リスト
RWGSortedVector(val) ---------------- ソートされた配列
RWGStack(type) ---------------------- スタック
RWGVector(val) ---------------------- 配列

RWTBitVec&lt;size&gt; --------------------- ビットベクタ
RWTIsvDlist&lt;T&gt; ---------------------- 挿入的双方向リスト
RWTIsvSlist&lt;T&gt; ---------------------- 挿入的単方向リスト
RWTPtrDlist&lt;T&gt; ---------------------- 双方向リスト
RWTPtrHashTable&lt;T&gt; ------------------ ハッシュ表
  RWTPtrHashSet&lt;T&gt; ------------------ セット
RWTPtrHashDictionary&lt;K,V&gt; ----------- 辞書
RWTPtrOrderedVector&lt;T&gt; -------------- 配列
RWTPtrSlist&lt;T&gt; ---------------------- 単方向リスト
RWTPtrSortedVector&lt;T&gt; --------------- ソートされた配列
RWTPtrVector&lt;T&gt; --------------------- 配列
RWTQueue&lt;T,C&gt; ----------------------- キュー
RWTStack&lt;T,C&gt; ----------------------- スタック
RWTValDlist&lt;T&gt; ---------------------- 双方向リスト
RWTValHashTable&lt;T&gt; ------------------ ハッシュ表
  RWTValHashSet&lt;T&gt; ------------------ セット
RWTValHashDictionary&lt;K,V&gt; ----------- 辞書
RWTValOrderedVector&lt;T&gt; -------------- 配列
  RWTValSortedVector&lt;T&gt; ------------- ソートされた配列
RWTValSlist&lt;T&gt; ---------------------- 単方向リスト
RWTValVector&lt;T&gt; --------------------- 配列
</pre>
<p>これだけのコンテナを用意してくれています。</p>
<h4>RWBTreeDictionay</h4>
<p>RWBTreeDictionaryはその名の通り、B-treeで実装された対応表です。KeyとValueはどちらもRWCollectableの派生クラスでなくてはなりません。Tools.h++の文字列クラスRWCollectableStringはRWCollectableの派生クラスであるため、RWBTreeDictionaryの要素となれます。</p>
<pre>
/*
 * phonebook.cpp
 *   Rogue Wave Tools.h++
 *   RWBTreeDictionary を使った簡単な電話番号簿
 */

#include &lt;iostream&gt;     // cin, cout
#include &lt;rw/btrdict.h&gt; // RWBTreeDictionary
#include &lt;rw/collstr.h&gt; // RWCollectableString

using namespace std;

void print_record(RWCollectable* key, RWCollectable* value, void* s) {
  ostream&amp; strm = *static_cast&lt;ostream*&gt;(s);
  strm &lt;&lt; *static_cast&lt;RWCollectableString*&gt;(key)
       &lt;&lt; &quot; : &quot;

       &lt;&lt; *static_cast&lt;RWCollectableString*&gt;(value)
       &lt;&lt; endl;
}

// プロンプト
char prompt() {
  RWCString command;
  cout &lt;&lt; &quot;add/delete/find/list/clear/quit [a,d,f,l,c,q] ?&quot; &lt;&lt; flush;
  cin &gt;&gt; command;
  return command[0U];
}

int main() {

  <strong>RWBTreeDictionary</strong> book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    RWFile in(&quot;phonebook.dat&quot;,&quot;rb&quot;);
    if ( in.isValid() ) {
      in &gt;&gt; book;
    }
  }

  bool quit = false;
  do {
    RWCollectableString name;
    RWCollectableString phone;
    RWCollectable* key;
    RWCollectable* value;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>contains</strong>(&amp;name) ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book.<strong>insertKeyAndValue</strong>(new RWCollectableString(name), // <a href="#tls_1_1">[1]</a>

                               new RWCollectableString(phone));
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      key = book.<strong>removeKeyAndValue</strong>(&amp;name,value);
      if ( !key ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        delete key; // <a href="#tls_1_2">[2]</a>

        delete value;
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      value = book.<strong>findValue</strong>(&amp;name);
      if ( !value ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        print_record(&amp;name,value,&amp;cout);
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.<strong>entries</strong>() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      book.<strong>applyToKeyAndValue</strong>(&amp;print_record,&amp;cout); // <a href="#tls_1_3">[3]</a>

      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>clearAndDestroy</strong>(); // <a href="#tls_1_4">[4]</a>
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    RWFile out(&quot;phonebook.dat&quot;,&quot;wb&quot;);
    if ( out.isValid() ) {
      out &lt;&lt; book;
    }
  }

  // <a href="#tls_1_5">[5]</a>

  return 0;
}
</pre>
<dl>
<dt><a name="tls_1_1">1:</a></dt>
<dd>
<p>RWBTreeDictionaryは参照ベースコンテナ、すなわち要素のポインタをコンテナ内に格納します。ですから、operator new() によってインスタンスをヒープから生成しなければなりません。</p>
</dd>
<dt><a name="tls_1_2">2:</a></dt>
<dd>
<p>メモリ･リークを起こさぬよう、確実にdeleteしましょう。</p>
</dd>
<dt><a name="tls_1_3">3:</a></dt>
<dd>
<p>残念なことにRWBTreeDictionaryはイテレータによる反復をサポートしていません。メソッドapplyToKeyAndValue()はコンテナ内の全要素に対し指定した関数を適用します。</p>
</dd>
<dt><a name="tls_1_4">4:</a></dt>
<dd>
<p>メソッドclearAndDestroy()はコンテナ内の全要素をdeleteします。</p>
</dd>
<dt><a name="tls_1_5">5:</a></dt>
<dd>
<p>おっと、最後に book.clearAndDestroy() するのを忘れてますね^^;</p>
</dd>
</dl>
<h4>RWHashDictionary</h4>
<p>RWHashDictionaryはハッシュ表による辞書です。要素の基底クラスRWCollectableに定義されたハッシュ関数/比較関数を派生クラスで再定義します。RWCollectableStringはハッシュ関数/比較関数があらかじめ再定義されているので、以下のサンプルのようにそのまま用いることができます。MFCのCMapと同じく、ハッシュ関数/比較関数を任意に設定できないのが欠点です。</p>
<pre>
/*
 * phonebook.cpp
 *   Rogue Wave Tools.h++
 *   RWHashDictionary を使った簡単な電話番号簿
 */

#include &lt;iostream&gt;      // cin, cout
#include &lt;rw/hashdict.h&gt; // RWHashDictionary
#include &lt;rw/collstr.h&gt;  // RWCollectableString

using namespace std;

// プロンプト: 省略

int main() {

  <strong>RWHashDictionary</strong> book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    RWFile in(&quot;phonebook.dat&quot;,&quot;rb&quot;);
    if ( in.isValid() ) {
      in &gt;&gt; book;
    }
  }

  bool quit = false;
  do {
    RWCollectableString name;
    RWCollectableString phone;
    RWCollectable* key;
    RWCollectable* value;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>contains</strong>(&amp;name) ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book.<strong>insertKeyAndValue</strong>(new RWCollectableString(name),
                               new RWCollectableString(phone));
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      key = book.<strong>removeKeyAndValue</strong>(&amp;name,value);
      if ( !key ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        delete key;
        delete value;
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      value = book.<strong>findValue</strong>(&amp;name);
      if ( !value ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; name
             &lt;&lt; &quot; : &quot;

             &lt;&lt; *static_cast&lt;RWCollectableString*&gt;(value)
             &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.<strong>entries</strong>() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      RWHashDictionaryIterator it(book);
      while ( it() ) {
        cout &lt;&lt; *static_cast&lt;RWCollectableString*&gt;(it.key())
             &lt;&lt; &quot; : &quot;

             &lt;&lt; *static_cast&lt;RWCollectableString*&gt;(it.value())
             &lt;&lt; endl;
      }
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>clearAndDestroy</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    RWFile out(&quot;phonebook.dat&quot;,&quot;wb&quot;);
    if ( out.isValid() ) {
      out &lt;&lt; book;
    }
  }

  return 0;
}
</pre>
<h4>RWTValMap&lt;K,T,C&gt;</h4>
<p>Tools.h++のtemplate版辞書がRWTValMap&lt;K,T,C&gt;です。RWTValMap&lt;K,T,C&gt;はSTLのmapと同様、要素の大小関係に基づいた2進木で実装されています。大小関係を判断する関数オブジェクトを第3template引数に与える構造であるため、比較関数を取り替えることで任意のKey照合が使えます。</p>
<pre>
/*
 * phonebook.cpp
 *   Rogue Wave Tools.h++
 *   RWTValMap&lt;K,V,C&gt; を使った簡単な電話番号簿
 */

#include &lt;iostream&gt;      // cin, cout
#include &lt;functional&gt;    // less
#include &lt;rw/tvmap.h&gt;    // RWTValMap
#include &lt;rw/cstring.h&gt;  // RWCString

using namespace std;

// プロンプト: 省略

int main() {

  <strong>RWTValMap&lt; RWCString,RWCString,less&lt;RWCString&gt; &gt;</strong> book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    RWFile in(&quot;phonebook.dat&quot;,&quot;rb&quot;);
    if ( in.isValid() ) {
      in &gt;&gt; book;
    }
  }

  bool quit = false;
  do {
    RWCString name;
    RWCString phone;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>contains</strong>(name) ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book<strong>[</strong>name<strong>]</strong> = phone;
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      if ( !book.<strong>remove</strong>(name) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      if ( !book.<strong>findValue</strong>(name,phone) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; name &lt;&lt; &quot; : &quot; &lt;&lt; phone &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.entries() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      RWTValMapIterator&lt; RWCString,RWCString,less&lt;RWCString&gt; &gt; it(book);
      while ( it() ) {
        cout &lt;&lt; it.key() &lt;&lt; &quot; : &quot; &lt;&lt; it.value() &lt;&lt; endl;
      }
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>clear</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    RWFile out(&quot;phonebook.dat&quot;,&quot;wb&quot;);
    if ( out.isValid() ) {
      out &lt;&lt; book;
    }
  }

  return 0;
}</pre>
<h4>RWTValHashMap&lt;K,T,H,EQ&gt;</h4>
<p>RWTValHashMap&lt;K,T,H,EQ&gt;はハッシュ表によるtemplate版辞書です。第3/第4template引数にハッシュ関数/比較関数を与えるので、任意のKey照合が使えます。</p>
<pre>
/*
 * phonebook.cpp
 *   Rogue Wave Tools.h++
 *   RWTValHashMap&lt;K,T,H,EQ&gt; を使った簡単な電話番号簿
 */

#include &lt;iostream&gt;     // cin, cout
#include &lt;functional&gt;   // unary_function, equal_to
#include &lt;rw/tvhmap.h&gt;  // RWTValHashMap
#include &lt;rw/cstring.h&gt; // RWCString

using namespace std;

// プロンプト: 省略

struct hash_str : std::unary_function&lt;RWCString,unsigned long&gt; {
  unsigned long operator()(const RWCString&amp; x) const {
    return x.hash();
  }
};

int main() {

  <strong>RWTValHashMap&lt; RWCString,RWCString,hash_str,equal_to&lt;RWCString&gt; &gt;</strong> book; // <a href="#tls_4_1">[1]</a>

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    RWFile in(&quot;phonebook.dat&quot;,&quot;rb&quot;);
    if ( in.isValid() ) {
      in &gt;&gt; book;
    }
  }

  bool quit = false;
  do {
    RWCString name;
    RWCString phone;
    switch ( prompt() ) {
    // 追加
    case &apos;a&apos; : case &apos;A&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていないことを確認する
      if ( book.<strong>contains</strong>(name) ) {
        cout &lt;&lt; &quot;already exists.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; &quot;phone:&quot; &lt;&lt; flush;
        cin &gt;&gt; phone;
        // レコードを追加する
        book<strong>[</strong>name<strong>]</strong> = phone;
      }
      break;
    }
    // 削除
    case &apos;d&apos; : case &apos;D&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそれを削除する
      if ( !book.<strong>remove</strong>(name) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      }
      break;
    }
    // 検索
    case &apos;f&apos; : case &apos;F&apos; : {
      cout &lt;&lt; &quot;name:&quot; &lt;&lt; flush;
      cin &gt;&gt; name;
      // 登録されていればそのレコードを出力する
      if ( !book.<strong>findValue</strong>(name,phone) ) {
        cout &lt;&lt; &quot;not found.&quot; &lt;&lt; endl;
      } else {
        cout &lt;&lt; name &lt;&lt; &quot; : &quot; &lt;&lt; phone &lt;&lt; endl;
      }
      break;
    }
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.<strong>entries</strong>() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      RWTValHashMapIterator&lt; RWCString,RWCString,hash_str,equal_to&lt;RWCString&gt; &gt; it(book);
      while ( it() ) {
        cout &lt;&lt; it.key() &lt;&lt; &quot; : &quot; &lt;&lt; it.value() &lt;&lt; endl;
      }
      break;
    }
    // クリア
    case &apos;c&apos; : case &apos;C&apos; : {
      book.<strong>clear</strong>();
      break;
    }
    // 終了
    case &apos;q&apos; : case &apos;Q&apos; : {
      quit = true;
      break;
    }
    default :
      cout &lt;&lt; &apos;?&apos; &lt;&lt; endl;
    }
  } while ( !quit );

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    RWFile out(&quot;phonebook.dat&quot;,&quot;wb&quot;);
    if ( out.isValid() ) {
      out &lt;&lt; book;
    }
  }

  return 0;
}
</pre>
<dl>
<dt><a name="tls_4_1">1:</a></dt>
<dd>
<p>RWTValHashMap&lt;K,V,H,EQ&gt;の第3/4templateにはハッシュ関数と比較関数を与えます。</p>
</dd>
</dl>
<h4>STLとの親和性</h4>
<p>Tools.h++のtemplateコンテナは</p>
<ul>
<li>STL依存モード</li>
<li>STL独立モード</li>
</ul>
<p>2種類の実装モードが選択できます。STL依存モードでは、templateコンテナの実装に、可能な限りSTLを使います。たとえばさきほどのRWTValMap&lt;K,V,C&gt;の場合、クラス内部にSTLコンテナであるstd::map&lt;K,V,C&gt;のインスタンスを内包し、各メソッドはSTLコンテナに委譲します。</p>
<p>STL依存モードではbegin(),end()などのSTLコンパチなメソッドが公開されるので、STLアルゴリズムにそのまま引き渡すことができます。</p>
<p>このメカニズムによって、通常はインタフェースの簡単なTools.h++コンテナを使い、必要に応じてSTLを利用することができ、STLとの親和性は<strong>抜群</strong>です。</p>
<pre>
    // こう書いてもいいわけだ
    RWTValMap&lt; RWCString,RWCString,less&lt;RWCString&gt; &gt; book;
    ...
    // リスト
    case &apos;l&apos; : case &apos;L&apos; : {
      cout &lt;&lt; book.entries() &lt;&lt; &quot;entries:&quot; &lt;&lt; endl;
      // 全レコードを出力する
      RWTValMap&lt; RWCString,RWCString,less&lt;RWCString&gt; &gt;::iterator it = book.begin();
      while ( it != it.end() ) {
        cout &lt;&lt; it->first &lt;&lt; &quot; : &quot; &lt;&lt; it->second &lt;&lt; endl;
        ++it;
      }
      break;
    }
</pre>
<p>電話番号簿の7つの実装、あなたはどれがお好みですか?</p>
<p>僕は&#8230;そうねぇ、RWTValMapあたりが涼しげで好きですね。</p>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2009/12/sources.zip'><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/src.gif" alt="source archive" title="src" width="58" height="13" class="size-full wp-image-548" /></a></p>
<h3>おまけ: Java</h3>
<p>Java(JDK 1.2.x)でも書いてみました。ObjectSpace社のライブラリJGL(Generic Collection Library for Java)のcom.objectspace.jgl.HashMap、そしてJDK1.2のサポートするjava.util.Hashtableによる実装です。</p>
<h4>JGL3.1 com.objectspace.jgl.HashMap</h4>
<p>JGLはSTLのJavaによる迫真の実装ともいえるライブラリです。HashMapに登録された全レコードを出力している部分にご注目ください。JGLのコンテナもSTLと同様、イテレータを返すメソッドbegin()/end()を持っていることがわかるでしょう。</p>
<pre>
/*
 * phonebook.java
 *   Sun Microsystem Java2
 *   com.objectspace.jgl.HashMap を使った簡単な電話番号簿
 */

import com.objectspace.jgl.*;
import java.io.*;

public class phonebook {

  static BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
  static PrintStream    cout = System.out;

  static char prompt() {
    String command = &quot;?&quot;;
    cout.print(&quot;add/delete/find/list/clear/quit [a,d,f,l,c,q] ?&quot;);
    cout.flush();
    try {
      command = cin.readLine();
    } catch ( IOException e ) {}
    return command.charAt(0);
  }

  public static void main(String[] arg) {

    <strong>HashMap</strong> book = null;

    // 電話番号簿をファイルから読み出す
    cout.println(&quot;loading file...&quot;);
    try {
      FileInputStream in = new FileInputStream(&quot;phonebook.dat&quot;);
      ObjectInputStream strm = new ObjectInputStream(in);
      book = (HashMap)strm.readObject();
    } catch ( Exception e ) {}

    if ( book == null )
      book = new <strong>HashMap</strong>();

    try {
    boolean quit = false;
    do {
      String name;
      String phone;
      Object value;
      switch ( prompt() ) {
      // 追加
      case &apos;a&apos; : case &apos;A&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていないことを確認する
        if ( book.<strong>get</strong>(name) != null ) {
          cout.println(&quot;already exists.&quot;);
        } else {
          cout.print(&quot;phone:&quot;);
          phone = cin.readLine();
          // レコードを追加する
          book.<strong>add</strong>(name,phone);
        }
        break;
      }
      // 削除
      case &apos;d&apos; : case &apos;D&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていればそれを削除する
        if ( book.<strong>remove</strong>(name) == null ) {
          cout.println(&quot;not found.&quot;);
        }
        break;
      }
      // 検索
      case &apos;f&apos; : case &apos;F&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていればそのレコードを出力する
        value = book.<strong>get</strong>(name);
        if ( value == null ) {
          cout.println(&quot;not found.&quot;);
        } else {
          cout.println(name + &quot; : &quot; + value);
        }
        break;
      }
      // リスト
      case &apos;l&apos; : case &apos;L&apos; : {
        cout.println(book.size() + &quot;entries:&quot;);
        // 全レコードを出力する
        HashMapIterator it = book.<strong>begin</strong>();
        while ( !it.equals(book.<strong>end</strong>()) ) {
          cout.println(it.key() + &quot; : &quot; + it.value());
          it.advance();
        }
        break;
      }
      // クリア
      case &apos;c&apos; : case &apos;C&apos; : {
        book.<strong>clear</strong>();
        break;
      }
      // 終了
      case &apos;q&apos; : case &apos;Q&apos; : {
        quit = true;
        break;
      }
      default :
        cout.println(&quot;?&quot;);
      }
    } while ( !quit );
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    // 電話番号簿をファイルに書き込む
    cout.println(&quot;saving file...&quot;);
    try {
      FileOutputStream out = new FileOutputStream(&quot;phonebook.dat&quot;);
      ObjectOutputStream strm = new ObjectOutputStream(out);
      strm.writeObject(book);
    } catch ( Exception e ) {}
  }

}
</pre>
<h4>JDK1.2 java.util.Hashtable</h4>
<pre>
/*
 * phonebook.java
 *   Sun Microsystem Java2
 *   java.util.Hashtable を使った簡単な電話番号簿
 */

import java.util.*;
import java.io.*;

public class phonebook {

  static BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
  static PrintStream    cout = System.out;

  // プロンプト: 省略

  public static void main(String[] arg) {

    <strong>Hashtable</strong> book = null;

    // 電話番号簿をファイルから読み出す
    cout.println(&quot;loading file...&quot;);
    try {
      FileInputStream in = new FileInputStream(&quot;phonebook.dat&quot;);
      ObjectInputStream strm = new ObjectInputStream(in);
      book = (Hashtable)strm.readObject();
    } catch ( Exception e ) {}

    if ( book == null )
      book = new Hashtable();

    try {
    boolean quit = false;
    do {
      String name;
      String phone;
      Object value;
      switch ( prompt() ) {
      // 追加
      case &apos;a&apos; : case &apos;A&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていないことを確認する
        if ( book.<strong>containsKey</strong>(name) ) {
          cout.println(&quot;already exists.&quot;);
        } else {
          cout.print(&quot;phone:&quot;);
          phone = cin.readLine();
          // レコードを追加する
          book.<strong>put</strong>(name,phone);
        }
        break;
      }
      // 削除
      case &apos;d&apos; : case &apos;D&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていればそれを削除する
        if ( book.<strong>remove</strong>(name) == null ) {
          cout.println(&quot;not found.&quot;);
        }
        break;
      }
      // 検索
      case &apos;f&apos; : case &apos;F&apos; : {
        cout.print(&quot;name:&quot;);
        cout.flush();
        name = cin.readLine();
        // 登録されていればそのレコードを出力する
        value = book.<strong>get</strong>(name);
        if ( value == null ) {
          cout.println(&quot;not found.&quot;);
        } else {
          cout.println(name + &quot; : &quot; + value);
        }
        break;
      }
      // リスト
      case &apos;l&apos; : case &apos;L&apos; : {
        cout.println(book.size() + &quot;entries:&quot;);
        // 全レコードを出力する
        Iterator it = book.<strong>entrySet</strong>().iterator();
        while ( it.hasNext() ) {
          Map.Entry entry = (Map.Entry)it.next();
          cout.println(entry.getKey() + &quot; : &quot; + entry.getValue());
        }
        break;
      }
      // クリア
      case &apos;c&apos; : case &apos;C&apos; : {
        book.<strong>clear</strong>();
        break;
      }
      // 終了
      case &apos;q&apos; : case &apos;Q&apos; : {
        quit = true;
        break;
      }
      default :
        cout.println(&quot;?&quot;);
      }
    } while ( !quit );
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    // 電話番号簿をファイルに書き込む
    cout.println(&quot;saving file...&quot;);
    try {
      FileOutputStream out = new FileOutputStream(&quot;phonebook.dat&quot;);
      ObjectOutputStream strm = new ObjectOutputStream(out);
      strm.writeObject(book);
    } catch ( Exception e ) {}
  }

}
</pre>
<h4>Tools.h++ ProfessionalによるJava/C++相互運用</h4>
<p>おまけついでに、Rogue WaveのTools.h++ Professionalが提供する&quot;Java互換シリアライズ&quot;を紹介します。</p>
<p>Javaのシリアライズ機構によってシリアライズされたファイルをC++から読み書きしてしまうという、なんともマジカルな機能です。これによってJava/C++双方からアクセスできる共通のオブジェクトファイルが実現します。</p>
<p>以下のC++コードで作られるデータファイル&quot;phonebook.dat&quot;はJavaストリーム互換であり、Java/C++双方からアクセスできます。</p>
<pre>
/*
 * phonebook.cpp
 *   Rogue Wave Tools.h++ Professinal
 *   RWTValHashMap&lt;K,T,H,EQ&gt; を使った簡単な電話番号簿
 *   java:ObjectStream 互換
 */

#include &lt;iostream&gt;      // cin, cout
#include &lt;fstream&gt;       // ifstream, ofstream
#include &lt;functional&gt;   // unary_function, equal_to
#include &lt;rw/tvhmap.h&gt;  // RWTValHashMap
#include &lt;rw/cstring.h&gt; // RWCString
#include &lt;rw/toolpro/jtoolsmap.h&gt; // Java/C++ mappings

using namespace std;

// プロンプト: 省略

struct hash_str : std::unary_function&lt;RWCString,unsigned long&gt; {
  unsigned long operator()(const RWCString&amp; x) const {
    return x.hash();
  }
};

int main() {

  typedef RWTValHashMap&lt; RWCString,RWCString,hash_str,std::equal_to&lt;RWCString&gt; &gt; book_type;
  book_type* book;

  // 電話番号簿をファイルから読み出す
  cout &lt;&lt; &quot;loading file...&quot; &lt;&lt; endl;
  {
    ifstream in(&quot;phonebook.dat&quot;,ios_base::binary | ios_base::in);
    if ( in.is_open() ) {
      // java.io.ObjectInputStream 互換の入力ストリーム
      <strong>RWJObjectInputStream</strong> strm(in);
      // RWTValHashMapをjava.util.Hashtableへマップする
      <strong>RWJDTemplateToolsMap</strong>&lt;RWCString,RWCString,hash_str,equal_to&lt;RWCString&gt; &gt;

        ::<strong>registerHashtableToRWTValHashMap</strong>(strm);
      strm &gt;&gt; book;
    } else {
      book = new book_type;
    }
  }

  // book.function() を book->function() に置き換えるだけなので、さっくり省略
  ...

  // 電話番号簿をファイルに書き込む
  cout &lt;&lt; &quot;saving file...&quot; &lt;&lt; endl;
  {
    ofstream out(&quot;phonebook.dat&quot;,ios_base::binary | ios_base::out | ios_base::trunc);
    if ( out.is_open() ) {
      // java.io.ObjectOutputStream 互換の出力ストリーム
      <strong>RWJObjectOutputStream</strong> strm(out);
      // RWTValHashMapをjava.util.Hashtableへマップする
      <strong>RWJDTemplateToolsMap</strong>&lt;RWCString,RWCString,hash_str,equal_to&lt;RWCString&gt; &gt;

        ::<strong>registerHashtableToRWTValHashMap</strong>(strm);
      strm &lt;&lt; book;
    }
  }

  delete book;

  return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/phonebooks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Xerces-CにICUを使わせるには</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/xml-xerces/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/xml-xerces/#comments</comments>
		<pubDate>Thu, 06 Apr 2006 05:56:06 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=4159</guid>
		<description><![CDATA[使えない文字コード Apache Xerces-Cは最も広く用いられているC++版XML-parserのひとつです。 Apache XMLのwebサイトhttp:://xml.apache.org/からリンクをたどればXerces-CのWin32バイナリ版が入手でき、Visual C++ 6.0で利用できます。 ですがこのバイナリ版にはひとつだけ困った問題があります。 Win32バイナリ版Xerces-Cをインストールし、その中のサンプル&#8217;DOMPrint&#8217;を使ってXMLをプリントしてみます。このときプリント対象となるXMLがshift_jisあるいはUNICODE(UTF-16/UTF-8)であれば何の問題もないのですが、たとえばeuc-jpやiso-2022-jpで書かれたDOMを正しくparseすることができません。 様々なencodingで書かれたXMLをプリントすると&#8230; shift-jis domprint -x=shift_jis sjis.xml &#60;?xml version="1.0" encoding="shift_jis"?&#62; &#60;message&#62;この文字列はshift_jisです&#60;/message&#62; utf-16 domprint -x=shift_jis utf.xml &#60;?xml version="1.0" encoding="shift_jis"?&#62; &#60;message&#62;この文字列はUTF-16です&#60;/message&#62; utf-8 domprint -x=shift_jis utf8.xml &#60;?xml version="1.0" encoding="shift_jis"?&#62; &#60;message&#62;この文字列はUTF-8です&#60;/message&#62; euc-jp domprint -x=shift_jis euc.xml &#60;?xml version="1.0" encoding="shift_jis"?&#62; &#60;message&#62;､ｳ､ﾎﾊｸｻﾎ､ﾏeuc-jp､ﾇ､ｹ&#60;/message&#62;[1] iso-2022-jp(JIS) domprint -x=shift_jis jis.xml Fatal Error at file "jis.xml", line 2, column 11 [...]]]></description>
			<content:encoded><![CDATA[<h3>使えない文字コード</h3>
<p>Apache Xerces-Cは最も広く用いられているC++版XML-parserのひとつです。 Apache XMLのwebサイトhttp:://xml.apache.org/からリンクをたどればXerces-CのWin32バイナリ版が入手でき、Visual C++ 6.0で利用できます。 ですがこのバイナリ版にはひとつだけ困った問題があります。</p>
<p>Win32バイナリ版Xerces-Cをインストールし、その中のサンプル&#8217;DOMPrint&#8217;を使ってXMLをプリントしてみます。このときプリント対象となるXMLがshift_jisあるいはUNICODE(UTF-16/UTF-8)であれば何の問題もないのですが、たとえばeuc-jpやiso-2022-jpで書かれたDOMを正しくparseすることができません。</p>
<h4>様々なencodingで書かれたXMLをプリントすると&#8230;</h4>
<dl>
<dt>shift-jis</dt>
<dd>
<pre>domprint -x=shift_jis sjis.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;この文字列はshift_jisです&lt;/message&gt;</pre>
</dd>
<dt>utf-16</dt>
<dd>
<pre>domprint -x=shift_jis utf.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;この文字列はUTF-16です&lt;/message&gt;</pre>
</dd>
<dt>utf-8</dt>
<dd>
<pre>domprint -x=shift_jis utf8.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;この文字列はUTF-8です&lt;/message&gt;</pre>
</dd>
<dt>euc-jp</dt>
<dd>
<pre>domprint -x=shift_jis euc.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;､ｳ､ﾎﾊｸｻﾎ､ﾏeuc-jp､ﾇ､ｹ&lt;/message&gt;<a id="reference_1" href="#footnote_1" class="reference">[1]</a></pre>
</dd>
<dt>iso-2022-jp(JIS)</dt>
<dd>
<pre>domprint -x=shift_jis jis.xml
Fatal Error at file "jis.xml", line 2, column 11
   Message: Invalid character (Unicode: 0x1B)
An error occured during parsing</pre>
</dd>
</dl>
<p>入力文字列をUNICODEに変換する際、XML-parserが内部で利用しているWindows-APIがiso-2022-jpやeuc-jpに対応していないからです。</p>
<p>Xerces-Cに添付されているHTMLドキュメントには文字コード変換にIBMのICUを利用するようにライブラリを修正する手順が明記されていますが、それによるとperlおよびUNIX-コンパチブルなshellを必要とします。ここではperlやshellを使わずにVisual C++ IDEでXerces-CをICU対応させる手順を紹介します。</p>
<div class="note">
<p>※ 以下に示す手順は、2001年5月現在入手可能な最新版:</p>
<ul>
<li>Xerces-C 1.4.0</li>
<li>ICU 1.8</li>
</ul>
<p>に対応しています。</p>
</div>
<h3>作業手順</h3>
<ol>
<li>ICUのインストールとビルド
<p>IBM-ICUのサイトからICU1.8をダウンロードし、適当なディレクトリに展開します。以降、ICUを展開したディレクトリを&lt;ICU&gt;と表します(ここでは m:\icu とします)。ICU-distributionに含まれる&lt;ICU&gt;\source\allinone\allinone.dswをVC++IDEで開き、プロジェクト&#8217;all&#8217;のRelease/Debugライブラリ(DLL)をビルドします。</p>
<p>ビルドが完了したら&lt;ICU&gt;\bin\icuuc.dll, &lt;ICU&gt;\bin\icuucd.dllおよび&lt;ICU&gt;\source\data\icudt18l.dllをPATHの通ったディレクトリにコピーしておきます。</p>
</li>
<li>Xerces-C Win32バイナリ版のインストール
<p>Apache XMLのサイトからXerces-C 1.4.0 Win32バイナリ版をダウンロードし、適当なディレクトリに展開します。以降、Xerces-C Win32バイナリ版を展開したディレクトリを&lt;XERCES&gt;と表します。</p>
</li>
<li>Xerces-C Win32ソースコード版のインストール
<p>Apache XMLのサイトからXerces-C 1.4.0 ソースコード版をダウンロードし、適当なディレクトリに展開します。以降、Xerces-C ソースコード版を展開したディレクトリを&lt;XERCESSRC&gt;と表します。</p>
</li>
<li>Xerces-C プロジェクト設定の変更
<p>&lt;XERCESSRC&gt;\D:\Projects\Win32\VC6\xerces-all\xerces-all.dswをVC++IDEで開き、プロジェクト設定を以下のとおりに変更します。</p>
<ul>
<li>インクルード・パス <code>&lt;ICU&gt;\include</code> を追加します。</li>
<li>プリプロセッサ・マクロ <code>XML_USE_WIN32_TRANSCODER</code> を <code>XML_USE_ICU_TRANSCODER</code> に書き換えます。</li>
<li>
<div><img src="http://www.s34.co.jp/wp-content/uploads/2006/04/compile.gif" alt="プロジェクトの設定、C/C++" width="677" height="421"/></div>
</li>
<li>ライブラリ・パス <code>&lt;ICU&gt;\lib</code> および <code>&lt;ICU&gt;\source\data</code> を追加します。</li>
<li>ライブラリ <code>icuuc.lib</code>(Debug版は<code>icuucd.lib</code>) および <code>icudata.lib</code> を追加します。</li>
<li>
<div><img src="http://www.s34.co.jp/wp-content/uploads/2006/04/link.gif" width="677" height="421" alt="プロジェクトの設定、リンク" /></div>
</li>
<li>ワークスペースから <code>Win32TransService.hpp</code>, <code>Win32TransService.cpp</code> を削除し、<code>&lt;XERCESSRC&gt;\src\util\Transcoders\ICU\ICUTransService.hpp</code>, <code>&lt;XERCESSRC&gt;\src\util\Transcoders\ICU\ICUTransService.cpp</code> を追加します。</li>
<li>
<div><img src="http://www.s34.co.jp/wp-content/uploads/2006/04/workspace.gif" width="365" height="339" alt="ワークスペース" /></div>
</li>
</ul>
</li>
<li>プロジェクト &#8216;XercesLib&#8217; のRelease/Debug版をビルドします。
<p>ビルドが完了すれば <code>&lt;XERCESSRC&gt;\Build\Win32\Vc6\Release</code> および <code>&lt;XERCESSRC&gt;\Build\Win32\Vc6\Debug</code> に</p>
<ul>
<li><code>xerces-c_1.lib</code>, <code>xerces-c_1_4.dll</code> (Release版)</li>
<li><code>xerces-c_1D.lib</code>, <code>xerces-c_1_4D.dll</code> (Debug版)</li>
</ul>
<p>が生成されています。これらを <code>&lt;XERCES&gt;\lib</code>, <code>&lt;XERCES&gt;\bin</code> にコピーします。</p>
</li>
</ol>
<h3>確認</h3>
<p>さて、ICU-enabled な Xerces-C の構築に成功したか、さきほどうまくいかなかったencodingによるXMLを再度 DOMPrint に食わせてみましょう。</p>
<h4>euc-jp</h4>
<pre>domprint -x=shift_jis euc.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;この文字列はeuc-jpです&lt;/message&gt;</pre>
<h4>iso-2022-jp(JIS)</h4>
<pre>domprint -x=shift_jis jis.xml
&lt;?xml version="1.0" encoding="shift_jis"?&gt;

&lt;message&gt;この文字列はiso2022-jpです&lt;/message&gt;</pre>
<p>ちょっとした応用を考えてみましょう。XML-parserがiso-2022-jpを受け付けてくれるので、XMLドキュメントをメール(SMTP)に投げ、POP3で受け取ってparseすることができるはずです。</p>
<p>適当なメールアカウントに対し</p>
<pre>&lt;?xml version='1.0'
encoding='iso-2022-jp' ?&gt;
&lt;message&gt;このメッセージは電子メールに投げられたXMLから取り出されました&lt;/message&gt;</pre>
<p>を送信し、以下のプログラムを起動すると、&lt;message&gt;タグに囲まれたテキスト</p>
<p>このメッセージは電子メールに投げられたXMLから取り出されました</p>
<p>がコンソールに出力されます</p>
<pre>// --- StdLib. (SourcePro Core)
#include &lt;iostream&gt;
#include &lt;sstream&gt;
#include &lt;locale&gt;

// --- SourcePro Network
#include &lt;rw/network/RWSocketPortal.h&gt;       // RWSocketPortal
#include &lt;rw/network/RWPortalIStream.h&gt;      // RWPortalIStream
#include &lt;rw/network/RWWinSockInfo.h&gt;        // RWWinSockInfo
#include &lt;rw/internet/util.h&gt;                // rwNormalizeLine()
#include &lt;rw/internet/RWStreamCoupler.h&gt;     // RWStreamCoupler
#include &lt;rw/pop3/RWPop3Agent.h&gt;             // RWPop3Agent

// --- Xerces-C
#include &lt;util/PlatformUtils.hpp&gt;            // PlatformUtils
#include &lt;parsers/DOMParser.hpp&gt;             // DOMParser
#include &lt;sax/ErrorHandler.hpp&gt;              // ErrorHandler
#include &lt;sax/SAXParseException.hpp&gt;         // SAXParseException
#include &lt;dom/DOM.hpp&gt;                       // DOM_xxx
#include &lt;framework/MemBufInputSource.hpp&gt;   // MemBufInputSource

/*
 * definitions
 */

#define POP3_SVR "POP3サーバアドレス"
#define POP3_USR "ユーザ名"
#define POP3_PWD "パスワード"

/*
 * namespace
 */
using namespace std;

/*
 * ErrorHandler for DOMParser
 */
class ErrorReporter : public ErrorHandler {
  bool ok_;
public:
  ErrorReporter() : ok_(true) {}
  ~ErrorReporter() {}
  virtual void warning(const SAXParseException&amp; ex)
    { }
  virtual void error(const SAXParseException&amp; ex)
    { ok_ = false; }
  virtual void fatalError(const SAXParseException&amp; ex)
    { ok_ = false; }
  virtual void resetErrors() { ok_ = true; }
  bool ok() const { return ok_; }
};

/*
 * XML-documentをparseし、
 * ドキュメント中の 'message' タグの内容を表示する
 */
int parse_message(const string&amp; message) {
  DOMParser parser;
  ErrorReporter errReporter;
  parser.setErrorHandler(&amp;errReporter);
  parser.setExitOnFirstFatalError(true);
  MemBufInputSource source(reinterpret_cast&lt;const unsigned char*&gt;(message.data()),
                           message.length(),
                           L"application/xml");
  parser.parse(source);
  if ( !errReporter.ok() ) {
    return 1;
  }
  DOM_Document document = parser.getDocument();
  DOM_NodeList nodes = document.getElementsByTagName("message");
  for ( unsigned i = 0; i &lt; nodes.getLength(); ++i ) {
    DOM_Node node = nodes.item(i);
    if ( node.getNodeType() != DOM_Node::ELEMENT_NODE ) {
      continue;
    }
    char* message = node.getFirstChild().getNodeValue().transcode();
    cout &lt;&lt; message &lt;&lt; endl;
    delete[] message;
  }
  return 0;
}

/*
 * POP3からメールを取り出し、メッセージをparse_message()へ
 */
int get_mail() {
  RWPop3Agent agent(POP3_SVR, POP3_USR, POP3_PWD);
  int n = agent.messages();
  for ( int i = 1; i &lt;= n; ++i ) {
    RWSocketPortal portal = agent.get(i);
    RWPortalIStream istrm(portal);
    RWCString text;
    bool body = false;
    do {
      text.readLine(istrm, false);
      text = rwNormalizeLine(text);
      if ( text.isNull() ) {
        body = true;
        break;
      }
    } while ( text != "." );
    ostringstream ostrm;
    if ( body ) {
      RWStreamCoupler couple;
      couple(istrm, ostrm, pop3StreamFilter);
      agent.removeMessage(i);
      parse_message(ostrm.str());
    }
  }
  return 0;
}

/*
 * メイン
 */
int main(int argc, char* argv[]) {
  locale::global(locale("japanese"));
  try {
    RWWinSockInfo info;
    XMLPlatformUtils::Initialize();
    get_mail();
  } catch ( RWxmsg&amp; rwer) {
    cerr &lt;&lt; "Exception : " &lt;&lt; rwer.why() &lt;&lt; endl;
  } catch ( std::exception&amp; stder ) {
    cerr &lt;&lt; stder.what() &lt;&lt; endl;
  }
  XMLPlatformUtils::Terminate();
  return 0;
}</pre>
<h4>編集部注</h4>
<ol>
<li id="footnote_1"><a href="#reference_1" class="footnote">^</a> 文字化けした結果をWebサイト上で表現するために、実際の出力を一部加工しています。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/xml-xerces/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMLを用いた永続オブジェクトの試み</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/xml-persist/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/xml-persist/#comments</comments>
		<pubDate>Thu, 06 Apr 2006 05:56:06 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=4161</guid>
		<description><![CDATA[永続オブジェクトとXML 近頃XMLがやたらとクローズアップされてきています。サーバ/サーバ間あるいはクライアント/サーバ間のインタフェースとしてXMLを用いることで様々な接続に対応できるってことで、e-commerceをはじめとしたITの基礎技術として脚光を浴びてきたということでしょう。(e-commerceについては僕には良くわからないし、そんなbigなシステムを構築するつもりはありません^^;) XMLで遊んでいると様々な使いみちを思い付きます。アプリケーションの設定情報なんかをXMLで書くとか、状態遷移表をXMLで表現するとか&#8230; XMLは階層構造を持ったデータを表現する汎用のデータ・フォーマットです。階層構造を持ったデータを表現できるのなら、オブジェクトをXMLで表現できるだろうと考えました。たとえば、 class person { string name_; // 名前 int age_; // 年齢 public: person(string n, int a) : name_(n), age_(a) {} ... }; のようなオブジェクトがあったとき、person(&#8220;adam&#8221;,24) を &#60;person&#62; &#60;name&#62;adam&#60;/name&#62; &#60;age&#62;24&#60;/age&#62; &#60;/person&#62; のようなXMLに変換すること、逆にXMLからpersonを生成することができるんじゃないか&#8230;というわけ。 これができればアプリケーションが扱うオブジェクトの集合をXML化でき、XMLによるアプリケーション間でのオブジェクトのやりとりが可能になります。 &#8230;というわけで、XMLによるオブジェクトの永続化の試みです。 ※このアーティクルでは、Microsoft XML Parser (MSXML.dll) を用いますが、XML4C や Xerces などXML Parserでも考え方は同じです。 書き込みメカニズム 例として class person { string name_; // 名前 int age_; [...]]]></description>
			<content:encoded><![CDATA[<h3>永続オブジェクトとXML</h3>
<p>近頃XMLがやたらとクローズアップされてきています。サーバ/サーバ間あるいはクライアント/サーバ間のインタフェースとしてXMLを用いることで様々な接続に対応できるってことで、e-commerceをはじめとしたITの基礎技術として脚光を浴びてきたということでしょう。(e-commerceについては僕には良くわからないし、そんなbigなシステムを構築するつもりはありません^^;)</p>
<p>XMLで遊んでいると様々な使いみちを思い付きます。アプリケーションの設定情報なんかをXMLで書くとか、<a href="/cpptechdoc/article/xml-fsmgen/">状態遷移表</a>をXMLで表現するとか&#8230;</p>
<p>XMLは階層構造を持ったデータを表現する汎用のデータ・フォーマットです。階層構造を持ったデータを表現できるのなら、オブジェクトをXMLで表現できるだろうと考えました。たとえば、</p>
<pre>class <strong>person</strong> {
  string <strong>name</strong>_; // 名前
  int    <strong>age</strong>_;  // 年齢
public:
  person(string n, int a) : name_(n), age_(a) {}
  ...
};</pre>
<p>のようなオブジェクトがあったとき、person(&#8220;adam&#8221;,24) を</p>
<pre>&lt;<strong>person</strong>&gt;
  &lt;<strong>name</strong>&gt;adam&lt;/<strong>name</strong>&gt;
  &lt;<strong>age</strong>&gt;24&lt;/<strong>age</strong>&gt;
&lt;/<strong>person</strong>&gt;</pre>
<p>のようなXMLに変換すること、逆にXMLからpersonを生成することができるんじゃないか&#8230;というわけ。</p>
<p>これができればアプリケーションが扱うオブジェクトの集合をXML化でき、XMLによるアプリケーション間でのオブジェクトのやりとりが可能になります。</p>
<p>&#8230;というわけで、XMLによるオブジェクトの永続化の試みです。</p>
<p class="note">※このアーティクルでは、Microsoft XML Parser (MSXML.dll) を用いますが、XML4C や Xerces などXML Parserでも考え方は同じです。</p>
<h3>書き込みメカニズム</h3>
<p>例として</p>
<pre>class <strong>person</strong> {
  string <strong>name</strong>_; // 名前
  int    <strong>age</strong>_;  // 年齢
public:
  person(string n, int a) : name_(n), age_(a) {}
  ...
};</pre>
<p>をXML化することを考えます。そのために新たなメソッド<code>virtual void save(IXMLDOMNodePtr node) const</code>を追加します。</p>
<p class="note">※IXMLDOMNodePtrはMSXMLにおけるDOMNodeのスマート・ポインタです。</p>
<p>メソッド save は、自分自身のメンバに基づいて DOMNode を生成します。たとえば <code>name_ == "adam" , age_ == 24</code> であるpersonオブジェクトからは</p>
<pre>&lt;<strong>person</strong>&gt;
  &lt;<strong>name</strong>&gt;adam&lt;/<strong>name</strong>&gt;
  &lt;<strong>age</strong>&gt;24&lt;/<strong>age</strong>&gt;
&lt;/<strong>person</strong>&gt;</pre>
<p>なるDOMNodeを生成します。</p>
<p>次に、生成された DOMNode を引数 node の子エレメントとして追加します。たとえば node が <code>&lt;root/&gt;</code> (あるいは<code>&lt;root&gt;</code><code>&lt;/root&gt;</code>) であるとすると、</p>
<pre>&lt;<strong>root</strong>&gt;
  &lt;<strong>person</strong>&gt;
    &lt;<strong>name</strong>&gt;adam&lt;/<strong>name</strong>&gt;
    &lt;<strong>age</strong>&gt;24&lt;/<strong>age</strong>&gt;
  &lt;/<strong>person</strong>&gt;
&lt;/<strong>root</strong>&gt;</pre>
<p>となります。node が既に子エレメントを持っていた場合、その末っ子として追加します。</p>
<p>まず、メソッド save の冒頭では、</p>
<ul>
<li>[1] &#8220;person&#8221; をタグ名とする DOMElement を作る</li>
</ul>
<p>を行ないます。</p>
<p>次にメンバ変数name_から<code>&lt;name&gt;adam&lt;/name&gt;</code> なるDOMNodeを作るには､</p>
<ul>
<li>[2] name_の値を文字列化し、それを値とする DOMText を作る</li>
<li>[3] &#8220;age&#8221; をタグ名とする DOMElement を作る</li>
<li>[4] [3]の子ノードとして[2]を追加する</li>
</ul>
<p>というステップを踏みます。</p>
<p>これでふたつの DOMNode :</p>
<ul>
<li>[1]<code>&lt;person&gt;&lt;/person&gt;</code></li>
<li>[4] <code>&lt;name&gt;adam&lt;/name&gt;</code></li>
</ul>
<p>ができたので、</p>
<ul>
<li>[5] [1]の子ノードとして[4]を追加する</li>
</ul>
<p>を行ないます。</p>
<p>全く同様に age_ から<code>&lt;age&gt;24&lt;/age&gt;</code> なるDOMNode を作り、[1]の子ノードとして追加します。</p>
<p>最後に、save の引数として与えられた node の子ノードとして[1]を追加します。ここまでを実装してみましょう:</p>
<pre>    void person::save(IXMLDOMNodePtr node) const {
      assert(node != 0);

      IXMLDOMDocumentPtr doc  = node-&gt;ownerDocument;
      IXMLDOMNodePtr     elem = doc-&gt;createElement("person");
      IXMLDOMNodePtr     item;
      IXMLDOMTextPtr     text;

      item = doc-&gt;createElement("name");
      text = doc-&gt;createTextNode(name_.c_str());
      item-&gt;appendChild(text);
      elem-&gt;appendChild(item);

      item = doc-&gt;createElement("age");
      char buff[16];
      _itoa(age_,buff,10);
      text = doc-&gt;createTextNode(buff);
      item-&gt;appendChild(text);
      elem-&gt;appendChild(item);

      node-&gt;appendChild(elem);
    }</pre>
<p>personの派生クラス:</p>
<pre>class <strong>programmer</strong> : public person {
  string <strong>lang</strong>_; // 言語
  ...
};</pre>
<p>のメソッド<code>save(IXMLDOMNode node) const</code>では、ベースクラスのメソッドsaveを再帰的に呼び出します。すなわち、</p>
<ul>
<li>[6] &#8220;programmer&#8221; をタグ名とする DOMElement を作る</li>
<li>[7] [6]を引数にベースクラス(person)のsaveを呼び出しことで、[6]の子ノード<code>&lt;person&gt;...&lt;/person&gt;</code> を追加する</li>
<li>[8] メンバ変数 lang_ に基づいて<code>&lt;language&gt;...&lt;/language&gt;</code>を作る</li>
<li>[9] [6]の子ノードとして[8]を追加する</li>
</ul>
<p>実装は以下のようになります:</p>
<pre>    void programmer::save(IXMLDOMNodePtr node) const {
      assert(node != 0);

      IXMLDOMDocumentPtr doc = node-&gt;ownerDocument;
      IXMLDOMNodePtr elem  = doc-&gt;createElement("programmer");
      IXMLDOMNodePtr item;
      IXMLDOMTextPtr text;

      person::save(elem);

      item = doc-&gt;createElement("language");
      text = doc-&gt;createTextNode(lang_.c_str());
      item-&gt;appendChild(text);
      elem-&gt;appendChild(item);

      node-&gt;appendChild(elem);
    }</pre>
<p>これによって、programmerからは以下のような DOMNode が生成されることになります:</p>
<pre>&lt;<strong>programmer</strong>&gt;
  &lt;<strong>person</strong>&gt;
    &lt;<strong>name</strong>&gt;bill&lt;/<strong>name</strong>&gt;
    &lt;<strong>age</strong>&gt;31&lt;/<strong>age</strong>&gt;
  &lt;/<strong>person</strong>&gt;
  &lt;<strong>language</strong>&gt;BASIC&lt;/<strong>language</strong>&gt;
&lt;/<strong>programmer</strong>&gt;</pre>
<p>最後に、XMLとしての体裁を整える、すなわち、</p>
<ul>
<li>[10] DOMDocument を生成する</li>
<li>[11] rootエレメント を作る</li>
<li>[12] [11]で作ったrootエレメントに対し、オブジェクト(person or programmer)をsaveする</li>
</ul>
<p>という処理を行なうことでXMLによる永続データの完成です。</p>
<pre>  int save(string file) {

    // 空のドキュメントを生成する
    IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
    doc-&gt;appendChild(
      doc-&gt;createProcessingInstruction(

    // ルートエレメントを生成する。
    IXMLDOMNodePtr root = doc-&gt;createElement("root");
    doc-&gt;appendChild(root);

    // [1] personとprogrammerをrootにsave
    cout &lt;&lt; "save person &amp; programmer¥n";
    person("adam",24).save(root);
    programmer("bill",20,"BASIC").save(root);

    // [2] vector&lt;programmer&gt;を生成
    vector&lt;programmer&gt; pv;
    pv.push_back(programmer("bjarne",28,"C++"));
    pv.push_back(programmer("wirth",30,"Modula-2"));

    // [3] rootの子"programmers"を作り、
    // その中にvector&lt;programmer&gt;をsave
    cout &lt;&lt; "save programmers¥n";
    IXMLDOMNodePtr pvelm = doc-&gt;createElement("programmers");
    for ( int i = 0; i &lt; pv.size(); ++i )
      pv[i].save(pvelm);
    root-&gt;appendChild(pvelm);

    // XMLファイルを出力
    doc-&gt;save(file.c_str());
    return 0;
  }</pre>
<p>上記のサンプルコードによって生成された<a href="http://www.s34.co.jp/wp-content/uploads/2006/04/persist.xml">persist.xml</a>をIE5でブラウズしたイメージを以下に示します:</p>
<p><img src="http://www.s34.co.jp/wp-content/uploads/2006/04/persist.gif" width="344" height="520" alt="persist.xml"/></p>
<h3>読み込みメカニズム</h3>
<p>読み込みは前述の書き込みと同じ順序で DOMNode をほどいてゆきます。</p>
<p>すなわち personのメソッド <code>void load(IXMLDOMNodePtr node)</code>では、</p>
<ul>
<li>[1] 最初の子ノード(<code>&lt;name&gt;...&lt;/name&gt;</code>)を取得する</li>
<li>[2] [1]の子ノード(DOMText)を取得する</li>
<li>[3] [2]から得られる文字列を基にメンバ変数 name_ をセットする</li>
<li>[4] 次の子ノード(<code>&lt;age&gt;...&lt;/age&gt;</code>)を取得する</li>
<li>[5] [4]の子ノード(DOMText)を取得する</li>
<li>[6] [5]から得られる文字列を基にメンバ変数 age_ をセットする</li>
</ul>
<pre>    void person::load(IXMLDOMNodePtr node) {
      assert(node != 0);
      assert(node-&gt;nodeName == _bstr_t("person"));

      IXMLDOMNodePtr item;
      IXMLDOMNodePtr text;

      item = node-&gt;firstChild;
      text = item-&gt;firstChild;
      name_ = static_cast&lt;const char*&gt;(_bstr_t(text-&gt;nodeValue));

      item = item-&gt;nextSibling;
      text = item-&gt;firstChild;
      age_ = atoi(static_cast&lt;const char*&gt;(_bstr_t(text-&gt;nodeValue)));
    }</pre>
<p>同様に <code>programmer::load(IXMLDOMNodePtr node)</code> では:</p>
<ul>
<li>[7] 最初の子ノード(<code>&lt;person&gt;...&lt;/person&gt;</code>)を取得する</li>
<li>[8] [7]を引数にベースクラス(person)のメソッドloadを呼び出し、ベースクラスのメンバ変数をセットする</li>
<li>[9] 次の子ノード(<code>&lt;language&gt;...&lt;/language&gt;</code>)を取得する</li>
<li>[10] [9]の子ノード(DOMText)を取得する</li>
<li>[11] [10]から得られる文字列を基にメンバ変数 lang_ をセットする</li>
</ul>
<pre>    void programmer::load(IXMLDOMNodePtr node) {
      assert(node != 0);
      assert(node-&gt;nodeName == _bstr_t("programmer"));

      IXMLDOMNodePtr item;
      IXMLDOMNodePtr text;

      item = node-&gt;firstChild;
      person::load(item);

      item = item-&gt;nextSibling;
      text = item-&gt;firstChild;
      lang_ = static_cast&lt;const char*&gt;(_bstr_t(text-&gt;nodeValue));
    }</pre>
<p>永続データ(すなわちXML)からオブジェクトを復元するには:</p>
<ul>
<li>[12] XMLをパースし、DOMDocument を取得する</li>
<li>[11] [12]からrootエレメントを取得する</li>
<li>[12] [11]で作ったrootエレメントに対し、オブジェクト(person or programmer)をloadする</li>
</ul>
<p>という処理を行なうことになります。</p>
<pre>  int load(string file) {

    IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
    doc-&gt;validateOnParse = VARIANT_FALSE;
    doc-&gt;load(file.c_str());

    IXMLDOMNodePtr root = doc-&gt;documentElement;
    IXMLDOMNodePtr node;

    // save:[1] でsaveした person/programmer を読み出す
    cout &lt;&lt; "¥nload person &amp; programmer¥n";
    int i;
    person* p;
    node = root-&gt;firstChild;
    p = node-&gt;nodeName == _bstr_t("person") ? new person : new programmer;
    p-&gt;load(node);
    p-&gt;print(cout);
    cout &lt;&lt; endl;
    delete p;

    node = node-&gt;nextSibling;
    p = node-&gt;nodeName == _bstr_t("person") ? new person : new programmer;
    p-&gt;load(node);
    p-&gt;print(cout);
    cout &lt;&lt; endl;
    delete p;

    // save:[3] でsaveしたvector&lt;programmer&gt;を読み出す
    cout &lt;&lt; "¥nload programmers¥n";
    vector&lt;programmer&gt; pv;
    node = node-&gt;nextSibling;
    for ( node = node-&gt;firstChild; node != 0; node = node-&gt;nextSibling ) {
      programmer p;
      p.load(node);
      pv.push_back(p);
    }

    for ( i = 0; i &lt; pv.size(); ++i ) {
      pv[i].print(cout);
      cout &lt;&lt; endl;
    }

    // saveされたpersonを出力
    // このとき、programmerが内包するpersonも含む
    cout &lt;&lt; "¥nload persons (including programmers)¥n";
    IXMLDOMNodeListPtr nodes = doc-&gt;getElementsByTagName("person");
    for ( i = 0; i &lt; nodes-&gt;length; ++i ) {
      person p;
      p.load(nodes-&gt;item[i]);
      p.print(cout);
      cout &lt;&lt; endl;
    }

    return 0;
  }</pre>
<p>上記コードの実行結果は以下のようになります:</p>
<p><img src="http://www.s34.co.jp/wp-content/uploads/2006/04/result.gif" width="304" height="233" alt="実行結果"/></p>
<h3>サンプルコード</h3>
<pre>#include &lt;iostream&gt; // ostream
#include &lt;string&gt;   // string
#include &lt;vector&gt;   // vector
#include &lt;cassert&gt;  // assert
#include &lt;cstdlib&gt;  // atoi, _itoa

#import "msxml.dll" rename_namespace("msxml")

namespace msxml {

  /*
   * テストのための2つのクラス : person と programmer
   */
  class <strong>person</strong> {

    std::string name_;
    int         age_;

  public:

    person(std::string name, int age)
      : name_(name), age_(age) {}
    person() {}

    virtual void <strong>save</strong>(<strong>IXMLDOMNodePtr</strong> node) const {
      assert(node != 0);

      <strong>IXMLDOMDocumentPtr</strong> doc  = node-&gt;<strong>ownerDocument</strong>;
      <strong>IXMLDOMNodePtr</strong>     elem = doc-&gt;<strong>createElement</strong>("person");
      <strong>IXMLDOMNodePtr</strong>     item;
      <strong>IXMLDOMTextPtr</strong>     text;

      item = doc-&gt;<strong>createElement</strong>("name");
      text = doc-&gt;<strong>createTextNode</strong>(name_.c_str());
      item-&gt;<strong>appendChild</strong>(text);
      elem-&gt;<strong>appendChild</strong>(item);

      item = doc-&gt;<strong>createElement</strong>("age");
      char buff[16];
      _itoa(age_,buff,10);
      text = doc-&gt;<strong>createTextNode</strong>(buff);
      item-&gt;<strong>appendChild</strong>(text);
      elem-&gt;<strong>appendChild</strong>(item);

      node-&gt;<strong>appendChild</strong>(elem);
    }

    virtual void <strong>load</strong>(<strong>IXMLDOMNodePtr</strong> node) {
      assert(node != 0);
      assert(node-&gt;<strong>nodeName</strong> == _bstr_t("person"));

      <strong>IXMLDOMNodePtr</strong> item;
      <strong>IXMLDOMNodePtr</strong> text;

      item = node-&gt;<strong>firstChild</strong>;
      text = item-&gt;<strong>firstChild</strong>;
      name_ = static_cast&lt;const char*&gt;(_bstr_t(text-&gt;<strong>nodeValue</strong>));

      item = item-&gt;<strong>nextSibling</strong>;
      text = item-&gt;<strong>firstChild</strong>;
      age_ = atoi(static_cast&lt;const char*&gt;(_bstr_t(text-&gt;<strong>nodeValue</strong>)));
    }

    virtual void print(std::ostream&amp; strm) const {
      strm &lt;&lt; name_ &lt;&lt; '(' &lt;&lt; age_ &lt;&lt; ')';
    }

  };

  class <strong>programmer</strong> : public <strong>person</strong> {

    std::string lang_;

  public:

    programmer(std::string name, int age, std::string lang)
      : person(name,age), lang_(lang) {}
    programmer() {}

    virtual void <strong>save</strong>(<strong>IXMLDOMNodePtr</strong> node) const {
      assert(node != 0);

      <strong>IXMLDOMDocumentPtr</strong> doc = node-&gt;<strong>ownerDocument</strong>;
      <strong>IXMLDOMNodePtr</strong> elem  = doc-&gt;<strong>createElement</strong>("programmer");
      <strong>IXMLDOMNodePtr</strong> item;
      <strong>IXMLDOMTextPtr</strong> text;

      <strong>person</strong>::<strong>save</strong>(elem);

      item = doc-&gt;<strong>createElement</strong>("language");
      text = doc-&gt;<strong>createTextNode</strong>(lang_.c_str());
      item-&gt;<strong>appendChild</strong>(text);
      elem-&gt;<strong>appendChild</strong>(item);

      node-&gt;<strong>appendChild</strong>(elem);
    }

    virtual void <strong>load</strong>(<strong>IXMLDOMNodePtr</strong> node) {
      assert(node != 0);
      assert(node-&gt;<strong>nodeName</strong> == _bstr_t("programmer"));

      <strong>IXMLDOMNodePtr</strong> item;
      <strong>IXMLDOMNodePtr</strong> text;

      item = node-&gt;<strong>firstChild</strong>;
      <strong>person</strong>::<strong>load</strong>(item);

      item = item-&gt;<strong>nextSibling</strong>;
      text = item-&gt;<strong>firstChild</strong>;
      lang_ = static_cast&lt;const char*&gt;(_bstr_t(text-&gt;<strong>nodeValue</strong>));
    }

    virtual void print(std::ostream&amp; strm) const {
      person::print(strm);
      strm &lt;&lt; ':' &lt;&lt; lang_;
    }

  };

  /*
   * お試し
   */
  using namespace std;

  int <strong>save</strong>(string file) {

    // 空のドキュメントを生成する
    <strong>IXMLDOMDocumentPtr</strong> doc("MSXML.DOMDocument");
    doc-&gt;<strong>appendChild</strong>(
      doc-&gt;<strong>createProcessingInstruction</strong>(

    // ルートエレメントを生成する。
    <strong>IXMLDOMNodePtr</strong> root = doc-&gt;<strong>createElement</strong>("root");
    doc-&gt;<strong>appendChild</strong>(root);

    // [1] personとprogrammerをrootにsave
    cout &lt;&lt; "save person &amp; programmer¥n";
    person("adam",24).<strong>save</strong>(root);
    programmer("bill",20,"BASIC").<strong>save</strong>(root);

    // [2] vector&lt;programmer&gt;を生成
    vector&lt;programmer&gt; pv;
    pv.push_back(programmer("bjarne",28,"C++"));
    pv.push_back(programmer("wirth",30,"Modula-2"));

    // [3] rootの子"programmers"を作り、
    // その中にvector&lt;programmer&gt;をsave
    cout &lt;&lt; "save programmers¥n";
    <strong>IXMLDOMNodePtr</strong> pvelm = doc-&gt;<strong>createElement</strong>("programmers");
    for ( int i = 0; i &lt; pv.size(); ++i )
      pv[i].<strong>save</strong>(pvelm);
    root-&gt;<strong>appendChild</strong>(pvelm);

    // XMLファイルを出力
    doc-&gt;<strong>save</strong>(file.c_str());
    return 0;
  }

  int <strong>load</strong>(string file) {

    <strong>IXMLDOMDocumentPtr</strong> doc("MSXML.DOMDocument");
    doc-&gt;<strong>validateOnParse</strong> = VARIANT_FALSE;
    doc-&gt;<strong>load</strong>(file.c_str());

    <strong>IXMLDOMNodePtr</strong> root = doc-&gt;<strong>documentElement</strong>;
    <strong>IXMLDOMNodePtr</strong> node;

    // [1] でsaveした person/programmer を読み出す
    cout &lt;&lt; "¥nload person &amp; programmer¥n";
    int i;
    <strong>person</strong>* p;
    node = root-&gt;<strong>firstChild</strong>;
    p = node-&gt;<strong>nodeName</strong> == _bstr_t("person") ? new <strong>person</strong> : new <strong>programmer</strong>;
    p-&gt;<strong>load</strong>(node);
    p-&gt;print(cout);
    cout &lt;&lt; endl;
    delete p;

    node = node-&gt;<strong>nextSibling</strong>;
    p = node-&gt;<strong>nodeName</strong> == _bstr_t("person") ? new <strong>person</strong> : new <strong>programmer</strong>;
    p-&gt;<strong>load</strong>(node);
    p-&gt;print(cout);
    cout &lt;&lt; endl;
    delete p;

    // [3] でsaveしたvector&lt;programmer&gt;を読み出す
    cout &lt;&lt; "¥nload programmers¥n";
    vector&lt;programmer&gt; pv;
    node = node-&gt;<strong>nextSibling</strong>;
    for ( node = node-&gt;<strong>firstChild</strong>; node != 0; node = node-&gt;<strong>nextSibling</strong> ) {
      <strong>programmer</strong> p;
      p.<strong>load</strong>(node);
      pv.push_back(p);
    }

    for ( i = 0; i &lt; pv.size(); ++i ) {
      pv[i].print(cout);
      cout &lt;&lt; endl;
    }

    // saveされたpersonを出力
    // このとき、programmerが内包するpersonも含む
    cout &lt;&lt; "¥nload persons (including programmers)¥n";
    <strong>IXMLDOMNodeListPtr</strong> nodes = doc-&gt;<strong>getElementsByTagName</strong>("person");
    for ( i = 0; i &lt; nodes-&gt;<strong>length</strong>; ++i ) {
      <strong>person</strong> p;
      p.load(nodes-&gt;<strong>item</strong>[i]);
      p.print(cout);
      cout &lt;&lt; endl;
    }

    return 0;
  }

  int entry(string file) {
    int result;
    if ( result = save(file) ) return result;
    if ( result = load(file) ) return result;
    return result;
  }

}

using namespace msxml;

int main(int argc, char* argv[]) {
  if ( FAILED(CoInitialize(0)) ) return 1;
  int result = entry("persist.xml");
  CoUninitialize();
  return result;
}</pre>
<p>　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/xml-persist/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>オブジェクト指向とはなにか</title>
		<link>http://www.s34.co.jp/cpptechdoc/article/oo/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/article/oo/#comments</comments>
		<pubDate>Thu, 06 Apr 2006 05:43:34 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[article]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=632</guid>
		<description><![CDATA[日常生活の中のオブジェクト 私たちの身の回りには、自分で直接できることと自分では直接できないこととがあります。朝おきて何気なく読む新聞も、新聞社という組織が作り、近くの販売所が毎日届けてくれます。私たちが新聞を読むときには、誰がどのようにして作っているかを知る必要はありません。 また、あなたがエレベータに乗るときも、あなたは自分のいる階でボタンを押せばいずれエレベータがやってくると期待しているのです。それは、世の中にあるエレベータという乗り物がおおむねその様な動きをしていることを知っているからです。 駅まで行き、電車に乗る場合でも同じです。電車は、どこかの鉄道会社が作ったダイヤに従って動いているのです。あなたが、どの電車はこのように動きなさい、と規定するわけではありません。 通りでタクシーを拾う場合もそうです。あるタクシーは客待ちをしており、また、あるタクシーは街の中を流して客を探しているでしょう。しかし、タクシーを利用するときに、すべてのタクシーがどのような動きをしているかを知らなくてもよいのです。しかも、たいていは、目的地までの道順をあなたが知らなくても、目的地にたどり着くことができます。 私たちひとりひとりが個人で知り得る知識や個人で直接行える事柄には限りがありますが、それにもかかわらず私たちがさまざまなことをなし得るのは、社会のしくみそのものが、私たちの意志とは無関係に、ある一定のルールにしたがって自立的に動いているからなのです。 エレベータも電車もタクシーもその使い方さえ知っていれば使える便利な「もの」なのです。これをオブジェクトと呼ぶことにしましょう。 さて、今度はソフトウェアの世界のことを考えてみましょう。普通のソフトウェアのシステムの世界で何かやろうとするときに、ひとつの仕事をサブルーチン（手続きの流れを記述したソフトウェアの構成要素）という形で記述することがあります。しかし、このサブルーチンは私たちの世界の中のエレベータや電車のようなシステムにはなっていません。なぜならサブルーチンは、それを使うときになって初めて生成され、それ以外のときには存在しないからです。 ソフトウェアの世界でエレベータに乗るということを考えた場合、いったいどのようなことになるのでしょうか。従来のソフトウェアの世界では、まずエレベータのサブルーチンが初期化されます。初期化に従って1階にいるとか10階にいるとかが決まり、サブルーチンはその状態に従わなければなりません。つまり、エレベータそのものの動きを使う人が知っていなければ、ソフトウェアの世界ではエレベータを使うことができないのです。また、既存のサブルーチンを寄せ集めて何かをするということもできず、何かをしたいときは新しくサブルーチンを作って初期化して使わなければならないのです。 果たしてこのやり方は本当に便利なのでしょうか。ソフトウェアの世界も大きくなってくると、プログラムを細部まで把握して管理することが困難になってきます。もし各サブルーチンが、現実の世界の中のエレベータや電車のように、自分自身を管理しているオブジェクトであったらどうでしょうか。 この場合には、各オブジェクトの使い方さえ把握していれば、それらの動き（規定されたルール）について細かく知らなくても各オブジェクトを使うことができます。ソフトウェアの世界でもオブジェクトが使えれば私たちの日常生活のようにシステムが構築でき、私たちにとっては非常に使いやすいシステムにすることができるのです。 新しいパラダイム 人間の知的能力にはさまざまな側面があります。新しいものを作り出す創造的な能力、機械には不得手な言語理解やパターン認識の能力、数学などで要求される論理思考能力などさまざまです。日常生活の中では、私たちの思考や発言はいつも論理的であるとは言えません。むしろ情緒的で概念的でかなりあいまいなものです。実は論理的思考を長く続けることはかなり骨の折れることと言えるのです。 いままでのプログラミング言語はコンピュータの動作ルールに依存した特殊ともいえる論理一辺倒のものが大部分で、プログラムを組む際にも特殊な論理的思考能力が要求されています。人間がプログラムを組むときにエラーが発生し、デバッグが余儀なくされるのは、人間がこのような論理的思考を長く持続していくことを得手としていないことに起因しているといえるでしょう。 では、ソフトウェアの世界をもっと、人間の側に近づけることはできないでしょうか。コンピュータに何かをさせようとするとき、従来のプログラミング言語では、コンピュータの動きにそって個別の事象を記述しなければなりませんでした。つまり、先ほどの例を用いれば、エレベータに乗りたいときエレベータの設計・製造から起動方法に関する記述まですべてを行わなければ、エレベータに乗ることはできませんでした。 しかし、本来の目的はただエレベータに乗りたいわけですから、私たちの世界のように、エレベータに関する設計や製造などは考えずに、誰かが作っておいてくれているものを利用すればそれを呼ぶだけでエレベータに乗ることはできるはずです。また、こうしてエレベータに関して記述されたものは他のところでも利用できるのです。 このようなソフトウェアの世界が実現できたらどんなに素晴らしいことでしょうか。実はこれがオブジェクト指向が登場した理由の1つといえるのです。 では、もう少し具体的にソフトウェアの世界での「オブジェクト」はどのような存在であればよいのかを考えてみることにしましょう。それは、端的に言うならば、データの部分と手続きの部分が切り離されずにひとつになっているものと言えます。 実は現実の世界ではこの方がずっと自然な考え方です。これだけではわかりにくいので先程の例で考えてみましょう。 エレベータの場合、エレベータの箱（人が乗る部分）や各フロア、各フロアにあるエレベータの扉がデータとなり、モーターを回すとか、扉を開閉するという操作が手続きになります。データと手続きが一緒になっていると自己管理能力が出てきます。ソフトウェアについても、もっと人間に近いレベルで考えたほうが便利です。そのアプローチのひとつがオブジェクト指向なのです。これは、いつも人間が物を見ている見方と適合するものなのです。 なぜオブジェクトか オブジェクトという形にしておけば、ソフトウェアのモジュラリティが非常に良くなります。このモジュラリティが良いと言うことは、たとえて言うと、1つのシステムが積み木のように1つ1つを組み合わせてできているということです。モジュラリティが良くなると、メンテナンスがしやすくなります。古くなって調子の悪い積み木は、その外形が同じであれば、取り換えることができます。 このことはソフトウェアを財産化する道につながるのです。なぜなら、誰かが作った積み木をそのまま、他の人が使ってシステムを作り上げることができるからです。最高の品質を持つ積み木は、いろいろな人がシステムを作るときに利用することになります。これが財産になるのです。ここでは、このことについて、もう少し詳しく見ていきましょう。 プログラムの部品化 これまでのプログラミング言語では、あるシステムを記述していく場合、いわば何もない状態から始めなければなりませんでした。プログラムを作るたびに、同じような目的のサブルーチンを作らなければならなかったのです。 しかし、オブジェクト指向型言語では、それまでにプログラムされたことがすべて蓄積として残されます。オブジェクトというデータ構造（データ+手続き）のために、部品として何度でも利用できるのです。 つまり、オブジェクト指向型言語におけるプログラミングでは、それまで培われてきたプログラムを部品として付け加えたり取り除いたり、また改良を加えたりすることが自由にできるのです。 この部品は、ハードウェアの部品と違って、部品と部品の結合がロジカルなものであるので、1つの部品が同時にいくつもの部品と結合することができるわけです。部品化が可能であると以下のようにいくつもの利点が生まれます。 メンテナンスの容易性 よいソフトウェアは長く使われます。しかし、長く使っている間には手直しが必要になります。手直しの仕方もこれまでのやり方は、パッチワーク的なやり方で、つぎあてをしていました。 しかし、どんなによいソフトウェアでも、このような直し方をしているとしだいに複雑になってしまい、最後には手直しをするくらいなら新たにプログラム全体を作り上げるほうが楽である、というようなことになってしまいます。そんなことをしていては効率が悪いので、もっと本質的に異なるプログラムを作っておくことが必要なわけです。 たとえばデータベースの場合、最初にデータのフォーマットを決めてしまったとします。そのデータ構造にアクセスするような仕組みを今までのソフトウェア・システムで書いたとすると、そのカラム数を変えたくなったときに、その部分を変更すると、そのデータをアクセスしているソフトウェアをすべて書き換えなければならなくなりました。しかし、部品化しておけば、その部分だけを取り換えるだけでよいのです。 それでは、このような部品化はBASICやFORTRANのような手続き型言語ではできないのでしょうか。ソフトウェアを大きな部品から小さな部品へと順次細分化しながら作ってゆくトップダウン設計の手法を使えば、部品化はできます。オブジェクト指向ではなくてもモジュール化しておけば、上のルーチンとは関係なしに必要な部分だけ変えればよいようにできるのです。 しかし、手続き型言語には問題があります。では、どのような問題があるのでしょうか。 手続き型言語でソフトウェア・システムを作るやり方としては、モジュール化するやり方とモジュール化しないやり方の2通りがあります。モジュール化すると速度が遅くなるので、ある部分はモジュール化しないでおきましょうということもできます。そして、一度このようにモジュール化されていない部分がソフトウェア・システムの中に組み込まれてしまうと、あるサブルーチンが勝手気ままにあるデータを使用するという現象を起こし、そのシステムは全体としてみた場合、モジュール構造を持つことについての保証がまったくなくなってしまうのです。 ハードウェアの能力が低く、ソフトウェア・システムの処理速度が問題にされた時代には、モジュール化されずに作られたものが多かったようです。考えようによっては、手続き型言語には柔軟性があるということにもなりますが、実はこれがシステムに悪影響を与えることがあるのです。 オブジェクト指向言語の場合はどうでしょう。オブジェクト指向型言語では、必ずモジュール化しないとプログラムが書けないのです。そこがオブジェクト指向型言語と手続き型言語の大きな違いです。 もちろん、質の高いプログラマーはどのような言語を使用しても、ソフトウェアをモジュール化して、後の変更に対して強いシステムを書くでしょうが、入門したばかりのプログラマーではそういう書き方が必ずしもできるわけではありません。ここに落とし穴があるわけです。たとえば、作業を分担してプログラムを作った場合、ある部分はモジュール化されているのに、別の部分はモジュール化されていないということが起こります。 オブジェクト指向型言語では、モジュール化しないとプログラムが書けないということが、メンテナンスのしやすさを保証しているのです。メンテナンスしやすいということは、ソフトウェア・システムの作り方にも大きな変革をもたらしました。これが次に述べるプロトタイピングです。 プロトタイピングという手法により、これまでの要求仕様定義のように固定的な方法から、もっとダイナミックに仕様を変更していける道が開けたのです。そして、これを実現したのがオブジェクト指向型言語なのです。 オブジェクト指向分析/設計 「ソフトウェアについても、もっと人間に近いレベルで考えたほうが便利です。」と述べました。 これは実装（製造）だけにとどまらず、前工程である分析/設計においても同じです。ソフトウェアの開発とは、現実世界をモデル化し、さらにそのモデルを計算機世界にマッピングする作業といえます。 現実世界をモデル化する（何を作るかを定義する）作業がすなわち分析、モデルの計算機世界へのマッピング（どう作るかを決定する）作業が設計および製造です。であれば、現実世界から計算機世界までの一連の変換作業を「人間が物を理解するやり方」に近い、オブジェクト指向で通すのが自然です。 従来の設計手法では、機能モジュールに分割し、そしてそのそれぞれをブレークダウンしていました。この手法は機能を骨組みとしたやり方です。作成された設計書あるいはコードは機能を柱とした構造を持っています。ソフトウェアの変更や改造はほとんどの場合機能の変更/改造ですから、場合によってはソフトウェアの構造を根幹から揺るがすことになりまねません。 オブジェクト指向分析/設計では、そのシステムを構成する「もの」とその関連が骨組みとなります。機能的な変更/改造が「もの」そのもの、あるいは「もの」の間にある関連を大きく揺さぶることはまれです。オブジェクト指向によるソフトウェア開発が変更/改造に強いといわれるのは、機能を骨組としていないからです。 ソフトウェア開発の前工程、すなわち分析や設計におけるオブジェクト指向はどんな意味を持っているのでしょうか。 もちろん実装の過程でオブジェクト指向型言語を使用する場合においては、前段階からオブジェクト指向に基づいた分析/設計を行っておけばアイデアと実装とのギャップを小さくできる、言い換えれば「アイデアがそのままコードになる」境地に近づくことができるのはいうまでもないことですが、従来の手続き型言語で実装するにしてもオブジェクト指向分析/設計のメリットはあると考えます。 それは、オブジェクト指向型言語が実装面での再利用性を高めると同様に、オブジェクト指向分析/設計が分析/設計フェーズでの再利用に有効なのです。 たとえば「時計」を設計したとしましょう。時計には「時刻合せ」と「時を刻む」という大きな機能があります。時計の満たすべき基本的な条件と機能とをオブジェクト指向分析/設計できちんと定義しておけば、その後「タイムレコーダ」を設計する際に、時計の設計書が再利用できます。すなわち、「タイムレコーダは時計の一種である。基本的な条件/機能は時計の設計書を参照のこと。タイムレコーダを実現するにあたり、時計とは異なる項目、および追加すべき項目を以下に述べる」などと設計書の冒頭に書き、時計との差分/追加分のみを記述すればタイムレコーダ設計書のできあがりとなるならなんて楽でしょう。 いわば設計書のライブラリ化が可能となります。 Ｃ＋＋学習のために オブジェクト指向と言う言葉がパソコン世界のいたる所に浸透してきておりその概念を知っていないと置いていかれると言う状況になってきました。 多くの資料がありプログラミング上での勉強の資料に事欠きませんがプログラミング上での資料ですからパソコンの世界でのオブジェクトとなると対象が広いだけに曖昧模糊としてつかみどころのないものになっています。 特定の言語に依存してしまう「オブジェクト指向言語」の資料ではその言語自体をマスターしていなくては理解できず言語を理解するにはオブジェクト指向の概念を理解していなければならないというパラドックスで初心者にはお手上げ状態になっています。 [...]]]></description>
			<content:encoded><![CDATA[<h3>日常生活の中のオブジェクト</h3>
<p>私たちの身の回りには、自分で直接できることと自分では直接できないこととがあります。朝おきて何気なく読む新聞も、新聞社という組織が作り、近くの販売所が毎日届けてくれます。私たちが新聞を読むときには、誰がどのようにして作っているかを知る必要はありません。</p>
<p>また、あなたがエレベータに乗るときも、あなたは自分のいる階でボタンを押せばいずれエレベータがやってくると期待しているのです。それは、世の中にあるエレベータという乗り物がおおむねその様な動きをしていることを知っているからです。</p>
<p>駅まで行き、電車に乗る場合でも同じです。電車は、どこかの鉄道会社が作ったダイヤに従って動いているのです。あなたが、どの電車はこのように動きなさい、と規定するわけではありません。</p>
<p>通りでタクシーを拾う場合もそうです。あるタクシーは客待ちをしており、また、あるタクシーは街の中を流して客を探しているでしょう。しかし、タクシーを利用するときに、すべてのタクシーがどのような動きをしているかを知らなくてもよいのです。しかも、たいていは、目的地までの道順をあなたが知らなくても、目的地にたどり着くことができます。</p>
<p>私たちひとりひとりが個人で知り得る知識や個人で直接行える事柄には限りがありますが、それにもかかわらず私たちがさまざまなことをなし得るのは、社会のしくみそのものが、私たちの意志とは無関係に、ある一定のルールにしたがって自立的に動いているからなのです。</p>
<p>エレベータも電車もタクシーもその使い方さえ知っていれば使える便利な「もの」なのです。これをオブジェクトと呼ぶことにしましょう。</p>
<p>さて、今度はソフトウェアの世界のことを考えてみましょう。普通のソフトウェアのシステムの世界で何かやろうとするときに、ひとつの仕事をサブルーチン（手続きの流れを記述したソフトウェアの構成要素）という形で記述することがあります。しかし、このサブルーチンは私たちの世界の中のエレベータや電車のようなシステムにはなっていません。なぜならサブルーチンは、それを使うときになって初めて生成され、それ以外のときには存在しないからです。</p>
<p>ソフトウェアの世界でエレベータに乗るということを考えた場合、いったいどのようなことになるのでしょうか。従来のソフトウェアの世界では、まずエレベータのサブルーチンが初期化されます。初期化に従って1階にいるとか10階にいるとかが決まり、サブルーチンはその状態に従わなければなりません。つまり、エレベータそのものの動きを使う人が知っていなければ、ソフトウェアの世界ではエレベータを使うことができないのです。また、既存のサブルーチンを寄せ集めて何かをするということもできず、何かをしたいときは新しくサブルーチンを作って初期化して使わなければならないのです。</p>
<p>果たしてこのやり方は本当に便利なのでしょうか。ソフトウェアの世界も大きくなってくると、プログラムを細部まで把握して管理することが困難になってきます。もし各サブルーチンが、現実の世界の中のエレベータや電車のように、自分自身を管理しているオブジェクトであったらどうでしょうか。</p>
<p>この場合には、各オブジェクトの使い方さえ把握していれば、それらの動き（規定されたルール）について細かく知らなくても各オブジェクトを使うことができます。ソフトウェアの世界でもオブジェクトが使えれば私たちの日常生活のようにシステムが構築でき、私たちにとっては非常に使いやすいシステムにすることができるのです。</p>
<h2>新しいパラダイム</h2>
<p>人間の知的能力にはさまざまな側面があります。新しいものを作り出す創造的な能力、機械には不得手な言語理解やパターン認識の能力、数学などで要求される論理思考能力などさまざまです。日常生活の中では、私たちの思考や発言はいつも論理的であるとは言えません。むしろ情緒的で概念的でかなりあいまいなものです。実は論理的思考を長く続けることはかなり骨の折れることと言えるのです。</p>
<p>いままでのプログラミング言語はコンピュータの動作ルールに依存した特殊ともいえる論理一辺倒のものが大部分で、プログラムを組む際にも特殊な論理的思考能力が要求されています。人間がプログラムを組むときにエラーが発生し、デバッグが余儀なくされるのは、人間がこのような論理的思考を長く持続していくことを得手としていないことに起因しているといえるでしょう。</p>
<p>では、ソフトウェアの世界をもっと、人間の側に近づけることはできないでしょうか。コンピュータに何かをさせようとするとき、従来のプログラミング言語では、コンピュータの動きにそって個別の事象を記述しなければなりませんでした。つまり、先ほどの例を用いれば、エレベータに乗りたいときエレベータの設計・製造から起動方法に関する記述まですべてを行わなければ、エレベータに乗ることはできませんでした。</p>
<p>しかし、本来の目的はただエレベータに乗りたいわけですから、私たちの世界のように、エレベータに関する設計や製造などは考えずに、誰かが作っておいてくれているものを利用すればそれを呼ぶだけでエレベータに乗ることはできるはずです。また、こうしてエレベータに関して記述されたものは他のところでも利用できるのです。</p>
<p>このようなソフトウェアの世界が実現できたらどんなに素晴らしいことでしょうか。実はこれがオブジェクト指向が登場した理由の1つといえるのです。</p>
<p>では、もう少し具体的にソフトウェアの世界での「オブジェクト」はどのような存在であればよいのかを考えてみることにしましょう。それは、端的に言うならば、データの部分と手続きの部分が切り離されずにひとつになっているものと言えます。</p>
<p>実は現実の世界ではこの方がずっと自然な考え方です。これだけではわかりにくいので先程の例で考えてみましょう。</p>
<p>エレベータの場合、エレベータの箱（人が乗る部分）や各フロア、各フロアにあるエレベータの扉がデータとなり、モーターを回すとか、扉を開閉するという操作が手続きになります。データと手続きが一緒になっていると自己管理能力が出てきます。ソフトウェアについても、もっと人間に近いレベルで考えたほうが便利です。そのアプローチのひとつがオブジェクト指向なのです。これは、いつも人間が物を見ている見方と適合するものなのです。</p>
<h3>なぜオブジェクトか</h3>
<p>オブジェクトという形にしておけば、ソフトウェアのモジュラリティが非常に良くなります。このモジュラリティが良いと言うことは、たとえて言うと、1つのシステムが積み木のように1つ1つを組み合わせてできているということです。モジュラリティが良くなると、メンテナンスがしやすくなります。古くなって調子の悪い積み木は、その外形が同じであれば、取り換えることができます。</p>
<p>このことはソフトウェアを財産化する道につながるのです。なぜなら、誰かが作った積み木をそのまま、他の人が使ってシステムを作り上げることができるからです。最高の品質を持つ積み木は、いろいろな人がシステムを作るときに利用することになります。これが財産になるのです。ここでは、このことについて、もう少し詳しく見ていきましょう。</p>
<h4>プログラムの部品化</h4>
<p>これまでのプログラミング言語では、あるシステムを記述していく場合、いわば何もない状態から始めなければなりませんでした。プログラムを作るたびに、同じような目的のサブルーチンを作らなければならなかったのです。</p>
<p>しかし、オブジェクト指向型言語では、それまでにプログラムされたことがすべて蓄積として残されます。オブジェクトというデータ構造（データ+手続き）のために、部品として何度でも利用できるのです。</p>
<p>つまり、オブジェクト指向型言語におけるプログラミングでは、それまで培われてきたプログラムを部品として付け加えたり取り除いたり、また改良を加えたりすることが自由にできるのです。</p>
<p>この部品は、ハードウェアの部品と違って、部品と部品の結合がロジカルなものであるので、1つの部品が同時にいくつもの部品と結合することができるわけです。部品化が可能であると以下のようにいくつもの利点が生まれます。</p>
<h4>メンテナンスの容易性</h4>
<p>よいソフトウェアは長く使われます。しかし、長く使っている間には手直しが必要になります。手直しの仕方もこれまでのやり方は、パッチワーク的なやり方で、つぎあてをしていました。</p>
<p>しかし、どんなによいソフトウェアでも、このような直し方をしているとしだいに複雑になってしまい、最後には手直しをするくらいなら新たにプログラム全体を作り上げるほうが楽である、というようなことになってしまいます。そんなことをしていては効率が悪いので、もっと本質的に異なるプログラムを作っておくことが必要なわけです。</p>
<p>たとえばデータベースの場合、最初にデータのフォーマットを決めてしまったとします。そのデータ構造にアクセスするような仕組みを今までのソフトウェア・システムで書いたとすると、そのカラム数を変えたくなったときに、その部分を変更すると、そのデータをアクセスしているソフトウェアをすべて書き換えなければならなくなりました。しかし、部品化しておけば、その部分だけを取り換えるだけでよいのです。</p>
<p>それでは、このような部品化はBASICやFORTRANのような手続き型言語ではできないのでしょうか。ソフトウェアを大きな部品から小さな部品へと順次細分化しながら作ってゆくトップダウン設計の手法を使えば、部品化はできます。オブジェクト指向ではなくてもモジュール化しておけば、上のルーチンとは関係なしに必要な部分だけ変えればよいようにできるのです。</p>
<p>しかし、手続き型言語には問題があります。では、どのような問題があるのでしょうか。</p>
<p>手続き型言語でソフトウェア・システムを作るやり方としては、モジュール化するやり方とモジュール化しないやり方の2通りがあります。モジュール化すると速度が遅くなるので、ある部分はモジュール化しないでおきましょうということもできます。そして、一度このようにモジュール化されていない部分がソフトウェア・システムの中に組み込まれてしまうと、あるサブルーチンが勝手気ままにあるデータを使用するという現象を起こし、そのシステムは全体としてみた場合、モジュール構造を持つことについての保証がまったくなくなってしまうのです。</p>
<p>ハードウェアの能力が低く、ソフトウェア・システムの処理速度が問題にされた時代には、モジュール化されずに作られたものが多かったようです。考えようによっては、手続き型言語には柔軟性があるということにもなりますが、実はこれがシステムに悪影響を与えることがあるのです。</p>
<p>オブジェクト指向言語の場合はどうでしょう。オブジェクト指向型言語では、必ずモジュール化しないとプログラムが書けないのです。そこがオブジェクト指向型言語と手続き型言語の大きな違いです。</p>
<p>もちろん、質の高いプログラマーはどのような言語を使用しても、ソフトウェアをモジュール化して、後の変更に対して強いシステムを書くでしょうが、入門したばかりのプログラマーではそういう書き方が必ずしもできるわけではありません。ここに落とし穴があるわけです。たとえば、作業を分担してプログラムを作った場合、ある部分はモジュール化されているのに、別の部分はモジュール化されていないということが起こります。</p>
<p>オブジェクト指向型言語では、モジュール化しないとプログラムが書けないということが、メンテナンスのしやすさを保証しているのです。メンテナンスしやすいということは、ソフトウェア・システムの作り方にも大きな変革をもたらしました。これが次に述べるプロトタイピングです。</p>
<p>プロトタイピングという手法により、これまでの要求仕様定義のように固定的な方法から、もっとダイナミックに仕様を変更していける道が開けたのです。そして、これを実現したのがオブジェクト指向型言語なのです。</p>
<h3>オブジェクト指向分析/設計</h3>
<p>「ソフトウェアについても、もっと人間に近いレベルで考えたほうが便利です。」と述べました。</p>
<p>これは実装（製造）だけにとどまらず、前工程である分析/設計においても同じです。ソフトウェアの開発とは、現実世界をモデル化し、さらにそのモデルを計算機世界にマッピングする作業といえます。</p>
<p>現実世界をモデル化する（何を作るかを定義する）作業がすなわち分析、モデルの計算機世界へのマッピング（どう作るかを決定する）作業が設計および製造です。であれば、現実世界から計算機世界までの一連の変換作業を「人間が物を理解するやり方」に近い、オブジェクト指向で通すのが自然です。</p>
<p>従来の設計手法では、機能モジュールに分割し、そしてそのそれぞれをブレークダウンしていました。この手法は機能を骨組みとしたやり方です。作成された設計書あるいはコードは機能を柱とした構造を持っています。ソフトウェアの変更や改造はほとんどの場合機能の変更/改造ですから、場合によってはソフトウェアの構造を根幹から揺るがすことになりまねません。</p>
<p>オブジェクト指向分析/設計では、そのシステムを構成する「もの」とその関連が骨組みとなります。機能的な変更/改造が「もの」そのもの、あるいは「もの」の間にある関連を大きく揺さぶることはまれです。オブジェクト指向によるソフトウェア開発が変更/改造に強いといわれるのは、機能を骨組としていないからです。</p>
<p>ソフトウェア開発の前工程、すなわち分析や設計におけるオブジェクト指向はどんな意味を持っているのでしょうか。</p>
<p>もちろん実装の過程でオブジェクト指向型言語を使用する場合においては、前段階からオブジェクト指向に基づいた分析/設計を行っておけばアイデアと実装とのギャップを小さくできる、言い換えれば「アイデアがそのままコードになる」境地に近づくことができるのはいうまでもないことですが、従来の手続き型言語で実装するにしてもオブジェクト指向分析/設計のメリットはあると考えます。</p>
<p>それは、オブジェクト指向型言語が実装面での再利用性を高めると同様に、オブジェクト指向分析/設計が分析/設計フェーズでの再利用に有効なのです。</p>
<p>たとえば「時計」を設計したとしましょう。時計には「時刻合せ」と「時を刻む」という大きな機能があります。時計の満たすべき基本的な条件と機能とをオブジェクト指向分析/設計できちんと定義しておけば、その後「タイムレコーダ」を設計する際に、時計の設計書が再利用できます。すなわち、「タイムレコーダは時計の一種である。基本的な条件/機能は時計の設計書を参照のこと。タイムレコーダを実現するにあたり、時計とは異なる項目、および追加すべき項目を以下に述べる」などと設計書の冒頭に書き、時計との差分/追加分のみを記述すればタイムレコーダ設計書のできあがりとなるならなんて楽でしょう。</p>
<p>いわば設計書のライブラリ化が可能となります。</p>
<h3>Ｃ＋＋学習のために</h3>
<p>オブジェクト指向と言う言葉がパソコン世界のいたる所に浸透してきておりその概念を知っていないと置いていかれると言う状況になってきました。</p>
<p>多くの資料がありプログラミング上での勉強の資料に事欠きませんがプログラミング上での資料ですからパソコンの世界でのオブジェクトとなると対象が広いだけに曖昧模糊としてつかみどころのないものになっています。</p>
<p>特定の言語に依存してしまう「オブジェクト指向言語」の資料ではその言語自体をマスターしていなくては理解できず言語を理解するにはオブジェクト指向の概念を理解していなければならないというパラドックスで初心者にはお手上げ状態になっています。</p>
<p>さて、プログラミング用語であると思われるオブジェクト指向をアプリケーションを使う側で知るべきかという点でオブジェクト指向の何たるかを知っているのと知らないのとでは天国と地獄の差が出てきます。</p>
<p>ですのでこの原稿では本来のプログラミングと言う観点からはピントのずれている箇所もありますし、オブジェクト指向を取り入れているアプリケーション、言語は列挙するだけで膨大な数になりその中で使用される用語も大同小異と言ったものから小同大異と言えるものまでありますのでこの原稿では広く使用されている用法を表記解説しますことをまずお断りします。</p>
<p>そしてオブジェクト指向の概念を解説するという大それた考えはまったくなく、おじさんたちへの理解の一歩を踏み出すためのきっかけとなれば幸いとの考えで書き連ねますからこの原稿の後それなりの本格的オブジェクト指向の書籍を読む事をお薦めいたします。</p>
<p>ではさっそくオブジェクト指向の勉強における最低限の知識習得にはいります。</p>
<p>まずオブジェクト指向全体に触れなくてはなりません、結論から言うと「オブジェクトを中心にする考え方」で今までの操作手順を中心にする考え方は手続き指向と言います。</p>
<p>オブジェクトと言う意味は「(知覚できる)物,又は物体」と言うことですが、使用方法から言うと広義の意味と狭義とがあります。</p>
<p>このことをわかり易く解説しますと</p>
<p>ディスプレイの画面上にある広さを持つ枠（土台）がありその中にボタンが一つ配置されボタンをマウスカーソルなどで押すと簡単なメッセージを表示すると言う簡単なプログラムがあるとします。</p>
<p>この例でボタンが表示されている土台部分をフォームと言います、そしてフォームもボタンもオブジェクトで、「ボタンが」押されるなどの行為に反応するプログラムがボタンの部分に関連して書き込まれていない限り「単にボタンを表示する」だけのものです（ボタンなどのパーツと言って良いものを狭義の意味でのオブジェクトと言うことがおわかりだと思います)</p>
<p>ボタンが押された時に実行されるものをメソッドと言いますがプログラムにおける「手続き」と言う意味です、このメソッドがボタンと言うオブジェクトに書き加えられことでプログラムが完成されます。</p>
<p>Visual BasicCやExcelなどを学んでいる方には周知のことですが、これらの言語やアプリケーションでは基本になるフォームもボタンも最初から用意されていますのでフォーム上にボタンを配置してボタンを押したら表示する文章をボタンに書き加えるだけで簡単にプログラムが完成します。</p>
<p>ぐだくだと頭からフォームやボタンの形態の記述はいりません、なぜこんな簡単なことでプログラムが完成するのでしょうか、それはオブジェクト(各パーツ)が全て独立しているからにほかなりません、「知覚される物」がオブジェクト指向の基本概念です。</p>
<p>先の「物又は物体」を広義でオブジェクトと言うのがおわかりと思います。</p>
<p>独立したオブジェクトがただ単に独立して動作するだけではなんのメリットもありませんし単純作業しかこなせないものでしかありません。</p>
<p>独立しつつも複雑な連携を実行するが、それをユーザーに感じさせない。これが求められているのです。</p>
<p>そのための考えが</p>
<ul>
<li><a href="#capsule">カプセル化</a> : Encapsulation</li>
<li><a href="#inheritance">継承</a> : Inheritance</li>
<li><a href="#polymorphism">多態性</a> : Polymorphism</li>
</ul>
<p>という３つの概念です。</p>
<p><a name="capsule" /></p>
<h4>カプセル化</h4>
<p>カプセル化とは「データとルーチンが一体化されている」ことを指します。プログラム言語ではレコード型（Ｃ言語などでは構造体となります）の定義は「異なったデータをひとかたまりとして扱う」と言う点でした、この異なったデータと言う部分を「データの代わりにメソッドや関数をも記述できる」としていただければ理解できると思います。</p>
<p>データとメソッドが単に一緒になっていればよいのではなくデータに対する操作方法がすべて記述されていなくてはなりません、ということはデータはメソッドで保護されメソッドを通してのみ操作できるということが必要になります。</p>
<p>データとメソッドを一つのものとして扱いさらにはデータにアクセスするにはデータと一体化されているメソッドを通してしか接することができません、これによってデータの内容を知らなくてもメソッドの使い方さえ分かればデータの操作ができます。</p>
<p>データの安全性が高まるのです、実にシンプルな考え方で、これ以上何物でもありません。</p>
<p>カプセル化と言う言葉に惑わされてはいけません基本理念は「データの保護」と言うことです、データの保護のためにメソッドが一緒にされ、さらに保護のためにメソッドを通さなければならないのです。</p>
<p><a name="inheritance" /></p>
<h4>継承</h4>
<p>カプセル化の次は「継承」です、継承とは読んで字のごとく「継承＝引き継ぐ」と言うことになります、なにがなにを引き継ぐかと言いますと今あるオブジェクトのデータとメソッドを別のオブジェクトが引き継ぎオブジェクト間で上位、下位と言う関係を作ることを言います。</p>
<p>元になるクラスを「BaseClass」と言いそこから派生したクラスを「DerivedClass」と言います。</p>
<p>ここで言うクラスはプログラム言語によって若干の違いがあります、カプセル化された一単位のデータとメソッドを指す考え方、そしていくつかの似通ったオブジェクトをまとめてクラスという表現をする考え方で通常こちらの考え方を「クラス」として使います。</p>
<p>似通ったオブジェクトをまとめて表現することをオブジェクト指向用語風に書くと</p>
<p>「機能や性質に共通点があるオブジェクトの総称」となります。</p>
<p>例えとして「乗り物−自動車−乗用車−『の中の大衆車クラス』にはサニー、カローラ、シビック等がある」と言う言い回しでこの場合のクラスと言う表現がわかると思います。</p>
<p>そしてこのクラスは階層構造を作る事が多く、オブジェクト指向の参考書などでは「系図」などとして取り上げられているますので簡単に理解できると思います。</p>
<p>さて継承は上位の特徴と言えるものを下位が引継ぐと言うことですが逆に言うと既にあるクラスを利用して新しいクラスを作ることができると言う事になります。</p>
<p>新たなクラスを作る場合、既に記述してあるクラスの機能を一々新たなクラス内で記述しなおすことは非能率でしかありません。</p>
<p>既にある機能を継承できれば新しい機能だけを継ぎ足すだけでいとも簡単に新たなクラスを作ることができることになります。</p>
<p>通常継承をインヘリタンスと表記しますが、この言葉自体はなんら意味を持ちません、単に継承を読み換えているだけと理解して下さい、ただし言葉上では単なる継承ですが機能上、自身のクラスに存在しないメソッドの実行を求められた時、上位クラスに定義されているメソッドの実行で代用する事を含んでいることに注意して下さい。</p>
<p>最初から設計をしっかりしておけばインヘリタンスを無理して取り入れる事もないのではと言う疑問も出てきますがインヘリタンスはオブジェクト指向における重要な要素となっていると言うことはそれなりの理由があっての事です。</p>
<p>ポットにミルと言う機能付け加えるだけでいとも簡単にミル付きコーヒーポットができます（物としては簡単ではないでしょうがが）ビデオカメラを継承してテレビと言う機能を付け加えれば新たな機種が出来上がります。</p>
<p>これらは物としての考えですがこれをプログラムなどに導入したと考えてください、オブジェクトで言うインヘリタンスの意味するところがおぼろげながら見えてきたはずです。</p>
<p>さらに便利なことに上位のオブジェクトで定義された機能を下位のオブジェクトで書き直すことができます、全て継承することもなく又継承したくない部分を書き換えてしまうことが許されておりそれをオーバーライド（override）と呼びます。</p>
<p>ここで継承＝インヘリタンスをまとめますと</p>
<ol>
<li>既にあるクラスを利用して新しいクラスを作る。</li>
<li>新しいデータやメソッドを追加できる。</li>
<li>上位のデータやメソッドを定義し直せる。</li>
</ol>
<p>ということになります。</p>
<p>１と２は表裏一体の関係と言えます、新しいデータやメソッドを追加せずただ継承だけではなんら必要性が認められないからです。</p>
<p>３のオーバーライドもこの後で軽く触れる「多態性」では意味を失いますが現在は素直に受け取っていただいて結構です。</p>
<p>この便利なオブジェクト指向の特徴であるインヘリタンスも上位のクラスを一つしかもてないシングルインヘリタンスですのでマルチインヘリタンスに比べると不便と言えば不便です。</p>
<p>シングルインヘリタンスと聞きますと上位から下位まで一本の線でしかつながっていない直線構造の様に考えますが、シングルと言っても直系の上位クラスが一つと言う事で直系の下位クラスは任意の個数を持つことができます。</p>
<p>しかしインスタンスのラインが１本と考えるとわかりやすいので系図をもって説明していきます。</p>
<pre>
　　 車
　　　｜
　　　｜　←トランクリッドを取る
　　　↓
　　ピックアップ・トラック
　　　｜
　　　｜　←荷台にルーフをつける
　　　↓
　　バン・ワゴン
　　　｜
　　　｜　←四輪駆動にする
　　　↓
　　ＲＶ
　　　｜
　　　｜　←ボディ鋼板を厚くする
　　　↓
　　軽装甲車
</pre>
<p>と、強引に現実の製品系列を無視していますがその意図は理解していただけると思います、すべての製品が自動車と言う機能を継承していますし途中で追加された機能も下位の製品では継承しています。</p>
<p>先のオーバーライドをこの図式で説明しますと「荷台にルーフをつける」で金属製ではなくキャンバス地にすると言った感じで機能を付け替える（オーバーライド）ことで感じがつかめるはずです。</p>
<p>インヘリタンスにおける直系上位は一つだけですが直系下位は任意の個数定義できますのでこの自動車系図を当てはめますと</p>
<pre>
上位
　　　　　　　　　　　　車
　　　　　　　　┌───┤
　ルーフを取る→｜　　　｜←トランクリッドを取る
　　　　　　　　↓　　　↓
　　　オープンカー　　ピックアップ・トラック
　　　　　　　　│　　　│
　　キャンバス→｜　　　｜←荷台にルーフをつける
　　　　ルーフ　｜　　　↓
　　　　　　　　｜　　バン・ワゴン
　　　四輪駆動→｜　　　｜
　　　　　　　　↓　　　｜←四輪駆動にする
　　　　　　ジープ　　　↓
　　　　　　　　｜　　ＲＶ
　スクリューを→｜　　　｜←ボディ鋼板を厚くする
　　　　つける　｜　　　├−−−┐
　　　　　　　　↓　　　｜　　　｜←砲をつける
　　　水陸両用車　　　　↓　　　｜←キャタピラに変える
　　　　　　　　　　　軽装甲車　｜
　　　　　　　　　　　　　　　　｜
　　　　　　　　　　　　　　　　↓
ジープは登録商標です　　　　　　戦車
</pre>
<p>と言ったとこでしょうか、各名称は適当につけていることと下位の枝分かれが２つだけですが単なる紙面の都合だけですとお断りしておきます、しかし下へいくほど軍用の形態になっていくと言うのは不思議ですね車の機能から言ってスポーツカーと軍用車が両極の形と言う事なんでしょうか。</p>
<h5>閑話休題</h5>
<p>この例でも全ての下位は車と言う機能をインヘリタンスしていますことがおわかりと思います。しかしこの個々の製品は外見上見分けがつきますがオブジェクトの概念から言えばその以前の機能は表面的にまったくわからないと言うことに注意してください。（もっとも最近の車は外見だけでは区別がつかないものが多くなっていますけど）</p>
<p>さらに多重継承としての概念が有ります、いままではすべて上からの継承は一つだけてしたが、例えばラジオカセットは別の観点から見るとラジオにカセットテープという二つの機能が継承され合体してできたものでラジオにテープレコーダーが付属したのではなく両方の機能が融和してできたものとみなす事ができます。</p>
<p>この例からも分かるとおり複数のクラスからデータやメソッド等を継承していることを「多重継承」と言いますが単なる言葉としておぼえておいてくださるだけでそれ以上は初級入門者には必要ないでしょう。</p>
<p>なぜなら多重継承を系図化すると</p>
<pre>
　Ｎｏ１−−−−−−−
　　｜　　　　　　　　｜
　　｜　　　　　　　　｜
　Ｎｏ２　　　　　　Ｎｏ３−−−−−−−
　　｜　　　　　　　　｜　　　　　　　　｜
　　｜　　　　　　　　｜　　　　　　　　｜
　Ｎｏ４　　　　　　Ｎｏ５　　　　　　Ｎｏ６
　　｜　　　　　　　　　　　　　　　　　｜
　　｜　　　　　　　　　　　　　　　　　｜
　　　−−−−−−−Ｎｏ７ーーーーーーＮｏ８
　　　　　　　　　　　｜
　　　　　　　　　　　｜
</pre>
<p>と言う感じになります、この系図でNo8に対する直接の親（Base）はNo7とNo6になり両方のオブジェクトやメソッドなどがぶつかる場合がでてきます。</p>
<p>この様な場合に「なにをさせようとしているのか」「両方をさせようとしているのか」「両方の時どちらを優先させるか」などと言う問題が出てきます。</p>
<p>これらは非常に高度のテクニックと細心の注意が必要ですから初級入門者には難題となりますので言葉としてとどのようなものかだけを覚えておけばよいでしょう。</p>
<p><a name="polymorphism" /></p>
<h4>多態性</h4>
<p>オブジェクト指向プログラムにおける最大の難所である多態性ですが、この拙文はオブジェクト指向プログラムの勉強ではなく「オブジェクト全体」の入門解説ですから深くは触れませんがある程度の事を知っていても損はないので軽く触れます。</p>
<p>多態性というまったくもって意味の通らない日本語のもとはポリモルフィズムと言う言葉でポリモルフィズムとはPolymorphismと書きギリシャ語のpoly＝多くの、とplymorphism＝意味の要素、が合わさってできた言葉で「同名異型」という意味です。</p>
<p>そして導きだされることは「異なる型に対して同名の操作を行う事ができる」となり逆の書き方をすると「異なる機能を持つ物が同じ名前をもつことができる」と言う事になります。</p>
<p>先のインヘリタンスでもメソッドで同じ名前を定義するオーバーライドということができましたしオーバーライドと同一の様ですがまったく違っております。</p>
<p>さきでのオーバーライドでは同じ名前を使うことにより上位クラスの機能は下位クラスの機能に書き換えられてしまいます、しかしポリモルフィズムにおいては同一の名前をつけても機能が書き換えられません。</p>
<p>　「同一の名前でも機能が違う」</p>
<p>と言うことです</p>
<p>このことがどんなメリットがあるかと言うことは初級程度のプログラムではなかなか難しいところがありますが、構造化プログラムの特質である、ルーチンの部品化を突き詰めていく結果に出てきたのでは思われます。</p>
<p>すなわちプログラム上では必要となる知識ではあるが、単純にオブジェクト指向の入門と言う立場では特に必要がある言葉だろうか、と言う結論がでてしまう身も蓋もないことになります。</p>
<p>初級におけるポリモルフィズムの意味は</p>
<p>上位のクラスから下位のクラスまで一つの動作に同じ名前をつけてそれぞれのクラス内では別々の動作をさせる、そしてポリモルフィズムを実現するのがバーチャルメソッドで、仮想的に記述することによりそれらのメソッドの参照先をコンパイル時に決めず実行時に決めるとなります。</p>
<p>簡潔に言えばポリモルフィズムが「戦略」でバーチャルメソッドが「戦術」と言えます。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/article/oo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

