<?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>株式会社エス・スリー・フォー</title>
	<atom:link href="http://www.s34.co.jp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.s34.co.jp</link>
	<description>株式会社エス・スリー・フォーは、一人一人のスキルを重視したシステム・ネットワーク構築のプロフェッショナル集団です。蓄積されたノウハウと最新技術により、お客様が考えられているシステムを明確にし、ご提案させて頂きます。</description>
	<lastBuildDate>Wed, 31 Mar 2010 05:35:31 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>WebkitとPythonを使ったアプリケーション開発</title>
		<link>http://www.s34.co.jp/s34lab/webkit_python/</link>
		<comments>http://www.s34.co.jp/s34lab/webkit_python/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 07:05:57 +0000</pubDate>
		<dc:creator>taka</dc:creator>
				<category><![CDATA[S34 Lab]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=3749</guid>
		<description><![CDATA[はじめに

組み込みアプリケーション開発、特にセットトップボックス(STB)やデジタル家電、携帯電話などそれなりのUIを要求される製品の開発では、Webブラウザを搭載しユーザーインターフェース(UI)をHTML+JavaScriptで構築することがあります。この手法のメリットとして以下のようなことが挙げられます。

UIとロジックの分離
家電製品に搭載するような画面の動きをプログラマが全てコードで記述しようとすると大変です。UIをHTML(+CSS)で記述できれば画面作成はデザイナーにお願いし、プログラマーはロジックに集中できます。最後のUIとロジックの結合作業はプログラマー側に残りますが&#8230;

開発工数の削減
UIをHTMLで記述できれば、C/C++やJavaで記述するのに比べて開発効率の向上が見込まれます。それに伴い、工数削減、開発期間短縮が期待出来ます。

豊かな表現力、操作性の実現
ブラウザがFlashなどのプラグインを使用できれば高度な視覚効果を比較的簡単に実現出来ます。



本記事では、これらのメリットをPC上のアプリケーションにも適用することを目指し、サンプルアプリケーションの作成を行います。


システム構成
UIにWebブラウザを利用する場合の典型的なシステム構成は次のようになります。この場合、UIと処理エンジンはブラウザを介して双方向に通信し、連携して動作します。

図１：組み込みブラウザを使った一般的な構成図

&#8220;組み込み ブラウザ&#8221; をキーワードにネットで検索すれば、多くの組込み用ブラウザ製品がヒットし、この図と同じような資料や説明が見つかると思います。

今回はブラウザ機能にWebKit、アプリケーションロジックにPythonを使い、上の図と似たような構成のアプリケーションを作っていきます。
WebKitとは

オープンソースのHTMLレンダリングエンジンの一つで、Appleの「Safari」や、Googleの「Google Chrome」などのWebブラウザに使われています。
Webブラウザ以外では、Appleの「iTunes」、Googleの「Android」などに使われています。

Pythonとは
プログラミング言語の一つです。Perl、PHP、Rubyと比較されるスクリプト言語です。
なぜPythonなのか
Pythonネタを探してて、このネタに辿り着いたというのが本音ですが&#8230;.それはおいといて、

Androidであれば、WebKit + Java の開発環境をGoogleが用意してくれています。また、Mac OS 上ならWebKit + Objective-C が使えます。これらの組み合せであればネット上で豊富な資料が入手可能ですが、それなりの開発環境構築の手間がかかります。

WebKit自体は、各種バインディングを利用すれば、特定の言語に縛られず様々な言語から利用出来ます。今回は言語の一例としてPythonを取り上げ、お手軽なアプリケーション開発を目指します。
準備するもの
今回のアプリケーション開発・実行には、次のソフトウェアが必要になります。(必要な順に挙げています)
OS &#8211; Linux
記事の作成にはUbuntu9.10と、9.04を使用しました。
Ubuntu以外のディストビリューションでも動作可能と思います。(勿論、BSD系でも)
WebKit
WebKit公式ページ

私の環境では、Google Chrome (Linux版) をインストールした時に、同時にインストールされました。
Ubuntuの場合、後述するpython-webkitをインストールすると一緒に入るかも？（未確認です）

Python
Python公式ページ

UbuntuでX Window Systemを使っている場合、デフォルトでインストールされます。
他のディストリビューションでもデフォルトでインストールされているか、簡単にインストール出来ると思います。

python-webkit
http://code.google.com/p/pywebkitgtk/
正式名称は、WebKit/Gtk Python bindings で、Python+GTK の環境からWebKitを利用できるようにします。
Ubuntuの場合、apt-getでインストール出来ます。
$ sudo apt-get install python-webkit
python-jswebkit
PythonとJavaScriptエンジンの連携を実現します。
公式HPは、上述のpython-webkitと同じようですが、何故かこちら(gwrite)にソースコードが置いてあります。
http://code.google.com/p/gwrite/downloads/list
Ubuntu9.10の場合、上のURIに登録されているパッケージを使えば、簡単にインストール出来ます。

32bit版パッケージ
64bit版パッケージ

それ以外の方は、
http://gwrite.googlecode.com/files/python-jswebkit-0.0.1.tar.gz
をダウンロードして解凍します。
解凍後、インストールスクリプトを実行します。

$ sudo python setup.py install

インストールの際、libwebkit-dev が必要です。/usr/include/webkit-1.0/以下のファイルを要求された場合は、libwebkit-devをインストールして下さい。

WebKitとPythonでどんなことが出来るか？
python-webkitについてくるサンプルアプリケーションを実行してみましょう。
Ubuntu9.10 (python-webkit 1.1.5-1) の場合、
$ /usr/share/doc/python-webkit/examples/tabbed_browser.py
Ubuntu9.04 (python-webkit 1.0.2-1) の場合、
$ /usr/share/doc/python-webkit/examples/webbrowser.py
問題がなければ、次のようなアプリが起動するはずです。

図２：python-webkit付属のサンプル・アプリケーション
&#8230;立派なWebブラウザが実行されましたね。
ということで、Webブラウザは既にあるので、今回はWebブラウザ以外を目指すことにします。
クリアすべき課題
単にダイアログにWebKitをはめ込んでHTMLのページを表示だけでは面白くありません。(単なるブラウザです)
UI(HTML+JavaScript)とアプリケーションロジック(Python)が連携してアプリケーションらしい動きを目指します。
そのためには、

PythonからJavaScriptの呼び出し
JavaSctiptからPythonの呼び出し

を行う必要があります。
その上で、JavaScriptだけでは実現できないような機能をPython側で行います。
PythonからJavaScriptの呼び出し
python-jswebkit を利用します。
JavaScriptCoreのインスタンスを取得し、EvaluateScript()メソッドを使って呼び出します。以下にサンプルを示します。
記述例

インポート

import webkit
import jswebkit

&#8216;load-finished&#8217;シグナルのハンドラで、JavaScriptCoreを取得

def _cb_load_finished(self, view, frame):
    gc = frame.get_global_context()
    [...]]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>
組み込みアプリケーション開発、特にセットトップボックス(STB)やデジタル家電、携帯電話などそれなりのUIを要求される製品の開発では、Webブラウザを搭載しユーザーインターフェース(UI)をHTML+JavaScriptで構築することがあります。この手法のメリットとして以下のようなことが挙げられます。</p>
<ol>
<li><strong>UIとロジックの分離</strong>
<p>家電製品に搭載するような画面の動きをプログラマが全てコードで記述しようとすると大変です。UIをHTML(+CSS)で記述できれば画面作成はデザイナーにお願いし、プログラマーはロジックに集中できます。最後のUIとロジックの結合作業はプログラマー側に残りますが&#8230;</p>
</li>
<li><strong>開発工数の削減</strong>
<p>UIをHTMLで記述できれば、C/C++やJavaで記述するのに比べて開発効率の向上が見込まれます。それに伴い、工数削減、開発期間短縮が期待出来ます。</p>
</li>
<li><strong>豊かな表現力、操作性の実現</strong>
<p>ブラウザがFlashなどのプラグインを使用できれば高度な視覚効果を比較的簡単に実現出来ます。</p>
</li>
</ol>
<p>
本記事では、これらのメリットをPC上のアプリケーションにも適用することを目指し、サンプルアプリケーションの作成を行います。
</p>
<p><span id="more-3749"></span></p>
<h4>システム構成</h4>
<p>UIにWebブラウザを利用する場合の典型的なシステム構成は次のようになります。この場合、UIと処理エンジンはブラウザを介して双方向に通信し、連携して動作します。</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/Diagram1.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/Diagram1.png" alt="Diagram1" title="Diagram1" width="222" height="245" class="alignnone size-full wp-image-3950" /></a></p>
<p><strong>図１：組み込みブラウザを使った一般的な構成図</strong></p>
<p>
&#8220;組み込み ブラウザ&#8221; をキーワードにネットで検索すれば、多くの組込み用ブラウザ製品がヒットし、この図と同じような資料や説明が見つかると思います。
</p>
<p>今回はブラウザ機能に<strong>WebKit</strong>、アプリケーションロジックに<strong>Python</strong>を使い、上の図と似たような構成のアプリケーションを作っていきます。</p>
<h5>WebKitとは</h5>
<p>
オープンソースのHTMLレンダリングエンジンの一つで、Appleの「Safari」や、Googleの「Google Chrome」などのWebブラウザに使われています。<br />
Webブラウザ以外では、Appleの「iTunes」、Googleの「Android」などに使われています。
</p>
<h5>Pythonとは</h5>
<p>プログラミング言語の一つです。Perl、PHP、Rubyと比較されるスクリプト言語です。</p>
<h6>なぜPythonなのか</h6>
<p>Pythonネタを探してて、このネタに辿り着いたというのが本音ですが&#8230;.それはおいといて、</p>
<p>
Androidであれば、WebKit + Java の開発環境をGoogleが用意してくれています。また、Mac OS 上ならWebKit + Objective-C が使えます。これらの組み合せであればネット上で豊富な資料が入手可能ですが、それなりの開発環境構築の手間がかかります。
</p>
<p>WebKit自体は、各種バインディングを利用すれば、特定の言語に縛られず様々な言語から利用出来ます。今回は言語の一例としてPythonを取り上げ、<strong>お手軽なアプリケーション開発</strong>を目指します。</p>
<h3>準備するもの</h3>
<p>今回のアプリケーション開発・実行には、次のソフトウェアが必要になります。(必要な順に挙げています)</p>
<h4>OS &#8211; Linux</h4>
<p>記事の作成には<a href="http://www.ubuntu.com/">Ubuntu</a>9.10と、9.04を使用しました。</p>
<p>Ubuntu以外のディストビリューションでも動作可能と思います。(勿論、BSD系でも)</p>
<h4>WebKit</h4>
<p><a href="http://webkit.org/">WebKit公式ページ</a></p>
<p>
私の環境では、<a href="http://www.google.co.jp/chrome">Google Chrome (Linux版)</a> をインストールした時に、同時にインストールされました。<br />
Ubuntuの場合、後述するpython-webkitをインストールすると一緒に入るかも？（未確認です）
</p>
<h4>Python</h4>
<p><a href="http://www.python.org/">Python公式ページ</a></p>
<p>
UbuntuでX Window Systemを使っている場合、デフォルトでインストールされます。<br />
他のディストリビューションでもデフォルトでインストールされているか、簡単にインストール出来ると思います。
</p>
<h4>python-webkit</h4>
<p><a href="http://code.google.com/p/pywebkitgtk/">http://code.google.com/p/pywebkitgtk/</a></p>
<p>正式名称は、WebKit/Gtk Python bindings で、Python+GTK の環境からWebKitを利用できるようにします。</p>
<p>Ubuntuの場合、apt-getでインストール出来ます。</p>
<pre>$ sudo apt-get install python-webkit</pre>
<h4>python-jswebkit</h4>
<p>PythonとJavaScriptエンジンの連携を実現します。</p>
<p>公式HPは、上述のpython-webkitと同じようですが、何故かこちら(gwrite)にソースコードが置いてあります。</p>
<p><a href="http://code.google.com/p/gwrite/downloads/list">http://code.google.com/p/gwrite/downloads/list</a></p>
<p>Ubuntu9.10の場合、上のURIに登録されているパッケージを使えば、簡単にインストール出来ます。</p>
<ul>
<li><a href="http://gwrite.googlecode.com/files/python-jswebkit_0.0.1-1ppa1%7Ekarmic_i386.deb">32bit版パッケージ</a></li>
<li><a href="http://gwrite.googlecode.com/files/python-jswebkit_0.0.1-1ppa1%7Ekarmic_amd64.deb">64bit版パッケージ</a></li>
</ul>
<p>それ以外の方は、</p>
<p><a href="http://gwrite.googlecode.com/files/python-jswebkit-0.0.1.tar.gz   ">http://gwrite.googlecode.com/files/python-jswebkit-0.0.1.tar.gz</a></p>
<p>をダウンロードして解凍します。</p>
<p>解凍後、インストールスクリプトを実行します。</p>
<pre>
$ sudo python setup.py install
</pre>
<p>インストールの際、libwebkit-dev が必要です。<code>/usr/include/webkit-1.0/</code>以下のファイルを要求された場合は、libwebkit-devをインストールして下さい。
</p>
<h3>WebKitとPythonでどんなことが出来るか？</h3>
<p>python-webkitについてくるサンプルアプリケーションを実行してみましょう。</p>
<p>Ubuntu9.10 (python-webkit 1.1.5-1) の場合、</p>
<pre>$ /usr/share/doc/python-webkit/examples/tabbed_browser.py</pre>
<p>Ubuntu9.04 (python-webkit 1.0.2-1) の場合、</p>
<pre>$ /usr/share/doc/python-webkit/examples/webbrowser.py</pre>
<p>問題がなければ、次のようなアプリが起動するはずです。</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/Screenshot-PyWebKitGtk.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/Screenshot-PyWebKitGtk.png" alt="Screenshot-PyWebKitGtk" title="Screenshot-PyWebKitGtk" width="628" height="358" class="alignnone size-full wp-image-3953" /></a></p>
<div align="center"><strong>図２：python-webkit付属のサンプル・アプリケーション</strong></div>
<p>&#8230;立派なWebブラウザが実行されましたね。</p>
<p>ということで、Webブラウザは既にあるので、今回はWebブラウザ以外を目指すことにします。</p>
<h3>クリアすべき課題</h3>
<p>単にダイアログにWebKitをはめ込んでHTMLのページを表示だけでは面白くありません。(単なるブラウザです)</p>
<p>UI(HTML+JavaScript)とアプリケーションロジック(Python)が連携してアプリケーションらしい動きを目指します。</p>
<p>そのためには、</p>
<ol>
<li>PythonからJavaScriptの呼び出し</li>
<li>JavaSctiptからPythonの呼び出し</li>
</ol>
<p>を行う必要があります。</p>
<p>その上で、JavaScriptだけでは実現できないような機能をPython側で行います。</p>
<h4>PythonからJavaScriptの呼び出し</h4>
<p>python-jswebkit を利用します。</p>
<p>JavaScriptCoreのインスタンスを取得し、EvaluateScript()メソッドを使って呼び出します。以下にサンプルを示します。</p>
<h6>記述例</h6>
<ol>
<li>インポート
<pre>
import webkit
import jswebkit</pre>
</li>
<li>&#8216;load-finished&#8217;シグナルのハンドラで、JavaScriptCoreを取得
<pre>
def _cb_load_finished(self, view, frame):
    gc = frame.get_global_context()
    self._javascript = jswebkit.JSContext(gc)</pre>
</li>
<li><strong>JavaScriptコードを実行</strong>
<pre>result = self._javascript.EvaluateScript(<strong>'JavaScriptのコード'</strong>)</pre>
</li>
</ol>
<h6>JavaScriptの記述方法</h6>
<p>上の例の <strong>&#8216;JavaScriptのコード&#8217;</strong> の部分には、任意のJavaScriptのコードを記述できます。</p>
<ol>
<li>関数名()を書けば、JavaScriptの関数を実行出来ます。
<pre>ret = self._javascript.EvaluateScript('foo()')</pre>
<p>と実行すると、JavaScriptのfoo()関数が実行されます。</p>
</li>
<li>グローバル変数名を書けば、変数の値を取得出来ます。
<p>JavaScriptで</p>
<pre>var hoge="hogeの中身";     // これはグローバル変数</pre>
<p>と書いおいて、Pythonから</p>
<pre>result = self._javascript.EvaluateScript('hoge')</pre>
<p>と実行すると、result変数に Javasctript変数「hoge」の値 「hogeの中身」 が格納されます。</p>
</li>
</ol>
<h6>補足</h6>
<p>もっと簡単にJavaScriptを呼び出す方法として、python-webkit のwebkit.WebView.execute_script()メソッドを使うという方法もありますが、この方法ではJavaScriptの実行結果を取得出来ないため、今回は採用していません。</p>
<h4>JavaSctiptからPythonの呼び出し</h4>
<p>Android(WebKit+Java)であれば、addJavaScriptInterface()でJava側のインターフェースを登録しておけばJavaScriptから簡単に呼び出せるのですが&#8230;<br />
python-webkit, python-jswebkitの両方でいろいろ試しましたが、どちらを使っても JavaScript -> Pythonの呼び出しがうまく出来ませんでした。</p>
<p>そのため今回は、JavaSctiptからPythonの呼び出しはあきらめ、Python側からJavaScriptの関数を定期的に呼び出してメッセージを取得することにします。</p>
<h3>アプリケーションの仕様</h3>
<p>今回作成するアプリケーションの仕様を定義します。</p>
<h4>基本仕様</h4>
<p>選択された画像ファイルを表示するアプリケーション、ということにします。基本機能は次の通りです。</p>
<ol>
<li>ユーザーが指定したフォルダにあるファイルの一覧を表示する。</li>
<li>ユーザーが選択したファイルの画像を表示する。</li>
</ol>
<h5>この仕様に決めた理由</h5>
<p>次のような理由から、アプリケーションの仕様を決めました。</p>
<ol>
<li>JavaScriptのみで実現不可能
<p>JavaScriptからローカルリソースにアクセス出来ないため、JavaScriptのみでファイル一覧を表示することは出来ません。この部分をPythonと連携して実現することで単なるブラウザ以上のことを実現します。</p>
</li>
<li>UIにブラウザを使うメリットを享受出来る。
<p>さまざまな画像ファイルのフォーマットを知らなくても、ブラウザさえ対応していれば簡単に画像を表示できます。「ブラウザの機能が使えて良かった」と実感出来ると思います。</p>
</li>
</ol>
<h5>画面イメージ</h5>
<p>画面イメージは下の通りです。</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/Screenshot-image_viewer.py_.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/Screenshot-image_viewer.py_.png" alt="Screenshot-image_viewer.py" title="Screenshot-image_viewer.py" width="640" height="460" class="alignnone size-full wp-image-3954" /></a></p>
<div align="center"><strong>図３：実行画面</strong></div>
<h5>制限事項</h5>
<p>本記事のコンセプトに合わせて、アプリケーション作成に以下の制限を設けました。</p>
<ol>
<li>UIは全てHTML+JavaScript(+CSS) で作成する。
<p>
   Pythonにはダイアログ表示以外のUIの機能を持たせません。<br />
   ダイアログも全領域にWebKitを表示するだけです。
  </p>
</li>
<li> 表示ファイル(html)はローカルに持ち、ファイル名固定で良い。
<p>ブラウザではないので、手元にあるファイルの表示で十分です。</p>
</li>
</ol>
<h3>アプリケーションの使い方</h3>
<p>今回作成したサンプルアプリケーション一式はここにあります。</p>
<p><a href='http://www.s34.co.jp/wp-content/uploads/2010/01/python_webkit_application.tgz'>サンプルアプリケーション一式</a></p>
<p>ファイルをダウンロードして解凍すると次のファイルが出てきます。</p>
<pre>
menu.html
image_viewer.py
no_image.png
</pre>
<p>以下のように入力してアプリケーションを起動します。</p>
<pre>
$ python image_viewer.py
</pre>
<p>成功すれば、先に挙げた実行画面が現れると思います。</p>
<p>使い方は次の通りです。</p>
<ol>
<li>Directory: に画像ファイルのあるパスを指定。
<p>実行画面例では、GIMPを画像フォルダ <code>/usr/share/gimp/2.0/images</code>を指定しています。</p>
</li>
<li>[ファイル一覧更新]ボタンを押す。</li>
<li>リストボックスにファイル一覧が表示されるので、画像ファイルを選択。
<p>画像表示領域に、選択した画像ファイルを表示します。<br />
         画像以外のファイルを選択すると、画像表示領域に「NO IMAGE」と表示します。
  </p>
</li>
<li>1 に戻って、別のパスを指定することも可能。</li>
</ol>
<h3>アプリケーションの解説</h3>
<p>作成したアプリケーションについて説明します。</p>
<h4>シナリオ</h4>
<p>アプリケーションの利用シナリオを定義します。</p>
<p>登場するアクター、機能は次の通りです。</p>
<ol>
<li>ユーザー</li>
<li>UI (HTML+JavaScript)</li>
<li>Python</li>
</ol>
<p>今回は、次の３つの操作シナリオを想定しました。</p>
<h5>ファイル一覧表示</h5>
<ol>
<li>ユーザーが、パスを入力し[ファイル一覧更新]ボタンを押して確定する。</li>
<li>UIは、パス名をJavaScriptのグローバル変数に格納する。</li>
<li>Pythonは、
<ol>
<li>定期的にJavaScript関数を呼び出し、パス名を監視する。</li>
<li>パス名を取得できたら、指定パスにあるファイル名のリストを作成する。
<p>JavaScriptからファイル一覧を作成することは出来ません。そのためファイル一覧の作成をPython側で行います。</p>
</li>
<li>JavaScript関数を呼び出し、ファイルリストを渡す。</li>
</ol>
</li>
<li>UIは、リストボックスにファイルリストを登録する。(リストの内容は全更新)</li>
</ol>
<h5> 画像表示</h5>
<ol>
<li>ユーザーがリストボックスからファイルを一つ選択する。</li>
<li>UIは、
<ol>
<li>ファイル選択イベントを監視し、</li>
<li>選択されたファイルをimageエリアに画像を表示する。
<p>選択ファイルが画像以外の場合、あらかじめ決められた画像(&#8221;NO IMAGE&#8221;)を表示</p>
</li>
</ol>
</li>
</ol>
<h5>終了</h5>
<ol>
<li>ユーザーが、 [終了]ボタンを押す。</li>
<li>UIは、JavaScriptのグローバル変数に終了リクエストを格納する。</li>
<li>Pythonは、
<ol>
<li>定期的にJavaScript関数を呼び出し、終了リクエストを監視する</li>
<li>終了リクエスト：ONなら、プログラムを終了する</li>
</ol>
</li>
</ol>
<h4>ファイル構成</h4>
<p>本アプリケーションは、次の３つのファイルから構成されます。</p>
<ol>
<li>menu.html
<p>UIを実装したHTML(+JavaScript)ファイルです。</p>
</li>
<li>image_viewer.py
<p>WebKitの表示を行うPythonスクリプトです。</p>
</li>
<li>no_image.png
<p>画像以外のファイルを選択されたときに表示する画像ファイルです。</p>
</li>
</ol>
<h4>設計資料</h4>
<h5>内部構成</h5>
<p>アプリケーションの内部構成(WebKit, UI, Python ) は次の様になっています。<br />
<a href="http://www.s34.co.jp/wp-content/uploads/2010/01/component.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/component.png" alt="component" title="component" width="669" height="785" class="alignnone size-full wp-image-3955" /></a></p>
<div align="center"><strong>図４：内部構成図</strong></div>
<p>呼び出しは、Python -> JavaScript の一方向のみで、次の２つの呼び出しを行っています。</p>
<ul>
<li>イベント監視
<p>GUIのイベントが発生してもJavaScriptからPythonのコードを呼び出せないため、自発的にイベントを通知することが出来ません。そのため、JavaScript側ではイベントをキューにストアしておき、Python側からタイマーイベントで定期的にJavaScriptのget_event()関数を呼び出してイベントを取り出しています。</p>
</li>
<li>ファイル一覧通知
<p>ファイル一覧を更新するときに、JavaScriptのset_file_list()関数を呼び出します。</p>
</li>
</ul>
<p>それでは、実際のソースコードについて説明していきます。</p>
<h4>Python側のソースコード (image_viewer.py)</h4>
<p>まずは、Python側のソースから見ていきます。</p>
<pre title="code" class="python">
#!/usr/bin/env python
# coding: UTF-8
from gettext import gettext as _
import gtk
import webkit
import gobject
import jswebkit
import time
import sys
import dircache
import os

class WebBrowser(gtk.Window):

    def __init__(self):
        """
        (処理)
        1. WebKitのインスタンスを生成し、ダイアログへ追加
        2. シグナル(’load-finished’)に対するコールバック関数(_cb_load_finished)の登録
        3. ダイアログを表示
        4. UI定義ファイル(menu.html)を表示
        """
        gtk.Window.__init__(self)

        self._cmd = {'update':self._cmd_update, 'quit':self._cmd_quit }

        self._browser = webkit.WebView()
        self._browser.connect('load-finished', self._cb_load_finished)
        self.add(self._browser)

        self.set_size_request(640, 460)
        #self.fullscreen()

        self.connect('destroy', gtk.main_quit)
        self.show_all()
        self._browser.open("file://menu.html")

    def _cb_load_finished(self, view, frame):
        """
        シグナル(’load-finished’)に対するコールバック関数。
        HTMLコンテンツのロードが完了したときに、シグナルが通知(コールバック)される。
        (処理)
        1. JavaScriptCore(JavaScript実行エンジン)の取得(メンバー変数に登録)
        2. 監視タイマーの登録
        """
        print "=> load-finished"
        gc = frame.get_global_context()
        self._javascript = jswebkit.JSContext(gc)
        self._set_timer()

    def _set_timer(self):
        gobject.timeout_add(100, self._cb_timer, None)

    def _cb_timer(self, params) :
        """
        タイマーハンドラ
        (処理)
        1. JavaScriptの get_event()関数を呼び出してメッセージ取得
        2. メッセージ対応したハンドラ関数を実行
        """
        event = self._javascript.EvaluateScript('get_event()')
        if event != None:
            print "event[", event, "]"

            params = event.split()
            if self._cmd.has_key(params[0]):
                self._cmd[params[0]](params[1:])
            else:
                print "### err: event[", params[0], "] unknown ###"

        self._set_timer()

    def _cmd_update(self, params):
        """
        “update”メッセージハンドラ関数
        ファイル一覧を取得し、UIへ通知する。
        """
        resp = 'set_file_list("' + os.path.abspath(params[0]) + os.sep + '", ['
        for file in dircache.listdir(params[0]):
            resp += ('"' + file + '",')
        resp += '])'
        #print resp
        self._javascript.EvaluateScript(resp)

    def _cmd_quit(self, params):
        """
        “quit”メッセージハンドラ関数
        直ちにプログラムを終了する。
        """
        sys.exit()

if __name__ == "__main__":
   gobject.threads_init()
   webbrowser = WebBrowser()
   gtk.main()
</pre>
<h5>補足</h5>
<p>JavaScriptCoreの取得は、_cb_load_finished()メソッドで行っています。</p>
<pre>
gc = frame.get_global_context()
self._javascript = jswebkit.JSContext(gc)
</pre>
<p>JavaScriptの実行は以下の様に行っています。(_cmd_update()メソッド)</p>
<pre>event = self._javascript.EvaluateScript('get_event()')</pre>
<h4>UI側のソースコード  (menu.html)</h4>
<p>次にUI側(HTML+JavaScript)のコードです。</p>
<pre title="code" class="html">
&lt;html&gt;

&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;title&gt;Webkit+Python Sample Application&lt;/title&gt;

&lt;style type="text/css"&gt;
&lt;!--
div.left_block {float: left; width: 50%;}
div.right_block{float: right; width: 50%;}
--&gt;
&lt;/style&gt;

&lt;script type="text/javascript"&gt;
&lt;!--
/**
 *  Pyton側へ通知するイベントのキューイングに使用
 */
function Queue(){
	this._queue = new Array();
}

Queue.prototype.push = function(item){
	this._queue.push(item);
}

Queue.prototype.pop = function(){
	return this._queue.length ==0 ? null : this._queue.shift();
}

Queue.prototype.empty = function(){
	return this._queue.length == 0 ? true : false;
} 

var event = new Queue();
var workpath = ".";

/**
 *  [ファイル一覧更新]ボタンが押されたとき、キューにメッセージを格納
 */
function on_update(){
	var target_path = document.form.location.value;
	if(0 &lt; target_path.length){
		event.push("update " + document.form.location.value);
	}
}

/**
 *  [終了]ボタンが押されたとき、キューにメッセージを格納
 */
function on_exit(){
	event.push("quit");
}

/**
 * Python から定期的に呼び出される。
 * キューの先頭にあるメッセージを返す。（キューが空なら null を返す）
 */
function get_event(){
	return event.empty() ? null : event.pop();
}

/**
 * Python からファイル一覧の通知時に呼び出される。
 * ファイル一覧リストボックスの内容を更新する。
 */
function set_file_list(path, list){
	workpath = path;
	var file_list = document.result.file_list;

	while(0 &lt; file_list.length ){
		file_list.removeChild(le_list.options[0]);
	}

	for(var i=0; i&lt;list.length; i++ ){
		file_list.options[file_list.options.length] = new Option(list[i], list[i]);
	}
}

/**
 * ユーザーがファイルを選択した時のイベントハンドラ
 * ファイルを画像領域に表示する。
 */
function on_select(){
	var file_list = document.result.file_list;
	var index = file_list.selectedIndex;
	var uri = "file://" + workpath + file_list.options[index].text;

	document.image1.src = uri;
	//console.log(uri);
}

/**
 * ファイルを画像として表示出来なかったときのイベントハンドラ
 * 代わりにno_image.pngを表示する。
 */
function on_image_error(){
	document.image1.src = "file://no_image.png";
}

//--&gt;
&lt;/script&gt;
&lt;/head&gt;

&lt;body bgcolor='lavender'&gt;
  &lt;form name="form" &gt;
    Directory: &lt;input type="text" name="location" style="width:80%"/&gt;
  &lt;/form&gt;
  &lt;button style="width:100%" onClick='on_update()' &gt;ファイル一覧更新&lt;/button&gt;
  &lt;div class="left_block"&gt;
    &lt;form name="result"&gt;
      &lt;select name="file_list" style="width:90%" size=17 onChange='on_select()'&gt;&lt;/select&gt;
    &lt;/form&gt;
  &lt;/div&gt;
  &lt;div class="right_block"&gt;
    &lt;a id="image" width="320" height="320" &gt;
      &lt;img name="image1" src="file://no_image.png" alt="" onerror='on_image_error()'/&gt;
    &lt;/a&gt;
  &lt;/div&gt;

  &lt;button style="width:100%" onClick="on_exit()" &gt;終了&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<h5>補足</h5>
<p>Pythonへ渡すメッセージはQueueオブジェクトに格納します。</p>
<p>Python から呼び出される関数は、次の２つです。</p>
<ul>
<li>get_event()</li>
<li>set_file_list(path, list)</li>
</ul>
<h3>あとがき</h3>
<p>今回は組込みソフトウェアの手法をデスクトップアプリケーションに適用してみました。</p>
<p>
WebKitをUI構築ツールの一つと捉えると、非常に便利な開発用ライブラリと考えることが出来ると思います。(GoogleのAndroidもそれを狙っているのでしょうけど)
</p>
<p>
最初のほうにも述べましたが、WebKitは各種言語向けバインディングを利用することにより、いろいろな言語から利用出来ます。現時点(2010年2月)ではその辺りの情報が少ないかな？と思うので、本記事が参考になれば幸いです。
</p>
<p>
機会があれば、Pythonに限らず、Perl, Ruby, PHPなど、皆さんの慣れ親しんだ言語との組み合わせをぜひ試してみて下さい。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/s34lab/webkit_python/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Graphvizを使った有向グラフの自動生成</title>
		<link>http://www.s34.co.jp/s34lab/graphviz/</link>
		<comments>http://www.s34.co.jp/s34lab/graphviz/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 07:29:35 +0000</pubDate>
		<dc:creator>taka</dc:creator>
				<category><![CDATA[S34 Lab]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2685</guid>
		<description><![CDATA[Graphvizとは
AT&#38;T研究所が開発したグラフ描画ツールです。
ただし、折れ線グラフや棒グラフみたいなExcelで作成するようなグラフではなく、ダイヤグラムや有向グラフなどが対象となります。

Graphvizの公式サイト

Graphvizを使うことにより、高品質な有向グラフを作成することが出来ます。どんなグラフを作成できるかは、Graphviz公式サイト 生成したグラフのサンプルを参照して下さい。


ソフトウェア技術者の方にとっては、Graphivizの使い方はそれほど難しくありませんし、ネットで検索すれば多くの解説記事が見つかります。
そのため本記事ではGraphvizの説明は最低限に留め、Graphvizをどのように活用していくかに焦点を当てていきます。


基本的な使い方
まずは Graphvizを全く知らない人のために、簡単なグラフ作成手順を紹介しておきます。
作成手順

 DOT言語のスクリプトを作成し
 dotコマンドを使って、スクリプトからグラフを作成します。

使ってみよう
まずは簡単なdotスクリプトを作成してみます。
dottest.dot

digraph sample{
  graph [rankdir = LR];
  one -&#62; two;
  one -&#62; three;
  two -&#62; four;
  two -&#62; five;
  five -&#62; six -&#62; seven;
}

スクリプトの詳細は後回しにして、まずはdotコマンドを使ってグラフ画像を作ってみましょう。
dotコマンドの書式
dotコマンドの基本的な使い方は以下の通りです。
 dot -T[出力形式] [スクリプトファイル名] -o [出力ファイル名]

-Tで画像形式を指定します。
bmp, jpeg, png, tiff, その他が指定できます。どんな形式で出力可能かは、Graphviz公式サイト Output Formatsを参照して下さい。

-o で出力ファイル名を指定します。指定しない場合、標準出力に出力します。
その他、細かいオプションは、Graphviz公式サイト Command-line Invocationを参照して下さい。

実行例
以下のように実行します。
$ dot -Tpng dottest.dot -o dottest.png
成功すれば、次のような画像ファイルが作成されます。
変換結果：dottest.png

dotスクリプトの説明
上で使ったdotスクリプトについて簡単に説明します。

digraph sample{
  ...
}


スクリプト全体を、"digraph [...]]]></description>
			<content:encoded><![CDATA[<h3>Graphvizとは</h3>
<p>AT&amp;T研究所が開発したグラフ描画ツールです。<br />
ただし、折れ線グラフや棒グラフみたいなExcelで作成するようなグラフではなく、ダイヤグラムや有向グラフなどが対象となります。
</p>
<p><a href="http://graphviz.org/">Graphvizの公式サイト</a></p>
<p>
Graphvizを使うことにより、高品質な有向グラフを作成することが出来ます。どんなグラフを作成できるかは、<a href="http://www.graphviz.org/Gallery.php">Graphviz公式サイト 生成したグラフのサンプル</a>を参照して下さい。
</p>
<p>
ソフトウェア技術者の方にとっては、Graphivizの使い方はそれほど難しくありませんし、ネットで検索すれば多くの解説記事が見つかります。<br />
そのため本記事ではGraphvizの説明は最低限に留め、Graphvizをどのように活用していくかに焦点を当てていきます。
</p>
<p><span id="more-2685"></span></p>
<h3>基本的な使い方</h3>
<p>まずは Graphvizを全く知らない人のために、簡単なグラフ作成手順を紹介しておきます。</p>
<h4>作成手順</h4>
<ol>
<li> DOT言語のスクリプトを作成し</li>
<li> dotコマンドを使って、スクリプトからグラフを作成します。</li>
</ol>
<h4>使ってみよう</h4>
<p>まずは簡単なdotスクリプトを作成してみます。</p>
<h6>dottest.dot</h6>
<pre>
digraph sample{
  graph [rankdir = LR];
  one -&gt; two;
  one -&gt; three;
  two -&gt; four;
  two -&gt; five;
  five -&gt; six -&gt; seven;
}
</pre>
<p>スクリプトの詳細は後回しにして、まずはdotコマンドを使ってグラフ画像を作ってみましょう。</p>
<h5>dotコマンドの書式</h5>
<p>dotコマンドの基本的な使い方は以下の通りです。</p>
<pre> dot -T[出力形式] [スクリプトファイル名] -o [出力ファイル名]</pre>
<ul>
<li>-Tで画像形式を指定します。
<p>bmp, jpeg, png, tiff, その他が指定できます。どんな形式で出力可能かは、<a href="http://graphviz.org/doc/info/output.html">Graphviz公式サイト Output Formats</a>を参照して下さい。</p>
</li>
<li>-o で出力ファイル名を指定します。指定しない場合、標準出力に出力します。</li>
<li>その他、細かいオプションは、<a href="http://graphviz.org/doc/info/command.html">Graphviz公式サイト Command-line Invocation</a>を参照して下さい。</li>
</ul>
<h5>実行例</h5>
<p>以下のように実行します。</p>
<pre>$ dot -Tpng dottest.dot -o dottest.png</pre>
<p>成功すれば、次のような画像ファイルが作成されます。</p>
<h6>変換結果：dottest.png</h6>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/dottest.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/dottest.png" alt="dottest.png" title="dottest.png" width="640" height="172" class="alignnone size-full wp-image-3032" /></a></p>
<h4>dotスクリプトの説明</h4>
<p>上で使ったdotスクリプトについて簡単に説明します。</p>
<pre>
digraph sample{
  ...
}
</pre>
<p>
スクリプト全体を、<code>"digraph [ID]{"</code> ～ <code>"}"</code> で囲みます。<br />
IDには任意の名前をつけることが出来ます。ただし使用できる文字に制限があります。
</p>
<pre>  graph [rankdir = LR];</pre>
<p>
グラフを横方向に作成するオプションで、省略可能です。 省略した場合、縦方向にグラフが作成されます。
</p>
<pre>  one -&gt; two;</pre>
<p>ノードとノードをエッジ(<code>"-&gt;"</code>) で連結して記述します。</p>
<pre> five -&gt; six -&gt; seven;</pre>
<p>このように３つ以上を一度に記述することもできます。</p>
<h6>ノード名</h6>
<p>
ノード名にはアルファベット<code>[a-zA-Z]</code>、アンダースコア<code>('_')</code>、数字<code>[0-9]</code>が使えます。 ただし、以下の制限があります。
</p>
<ul>
<li>数字から始まってはいけません（C言語等の変数名の制限と同じですね)</li>
<li>ハイフン<code>('-')</code>, <code>&gt;</code> は使えません (エッジで使用します)</li>
<li>空白<code>(' ')</code>も使えません(単語の区切りになります)</li>
</ul>
<p>上記のルールに抵触するノード名を表示したい場合、ノード名は上記のルールに従った上で、</p>
<pre>ノード名 [label="表示名"]</pre>
<p>のように、label属性で表示文字列を指定します。</p>
<p>Graphvizの公式サイトには、もっと詳しい説明があります。</p>
<ul>
<li><a href="http://www.graphviz.org/Documentation.php">ドキュメントのTOPページ</a></li>
<li><a href="http://graphviz.org/doc/info/shapes.html">ノードの形の指定</a></li>
<li><a href="http://graphviz.org/doc/info/arrows.html">エッジの形の指定</a></li>
<li><a href="http://graphviz.org/doc/info/colors.html">色の指定</a></li>
<li><a href="http://graphviz.org/doc/info/attrs.html">属性一覧</a></li>
</ul>
<h4>少し凝ったスクリプト</h4>
<p>次にもう少し凝った、多少意味のあるグラフを作ってみましょう。</p>
<p>題材はBSDの系譜図です。</p>
<p><a href="http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/share/misc/bsd-family-tree?rev=1.135;content-type=text%2Fplain">The UNIX system family tree: Research and BSD</a> を参考にしました。ただし、全て再現すると大変なので、<strong>一部(?)</strong>省略しています。
</p>
<pre>
digraph BSD{
  graph [rankdir = LR];

  _43BSD_NET2 [label=&quot;4.3BSD NET/2&quot;, shape=box];
  _386BSD [label=&quot;386BSD&quot;];
  _43BSD_NET2 -&gt; _386BSD;

  _44BSD [label=&quot;4.4BSD&quot;];
  _43BSD_NET2 -&gt; _44BSD;

  BSD386 [label=&quot;BSD/386&quot;];
  _43BSD_NET2 -&gt; BSD386;
  _43BSD_NET2 -&gt; NetBSD;

  subgraph cluster0{
    label=&quot;(NetBSD)&quot;;
    color=lightgray;
    NetBSD [label=&quot;NetBSD&quot;];
    NetBSD_current [label=&quot;NetBSD -current&quot;, color=yellow, style=filled];
    NetBSD -&gt; NetBSD_current;
  }
  _386BSD -&gt; NetBSD;
  _44BSD -&gt; NetBSD;

  subgraph cluster1{
    label=&quot;(FreeBSD)&quot;;
    color=lightgray;
    FreeBSD [label=&quot;FreeBSD&quot;];
    FreeBSD_current [label=&quot;FreeBSD 9 -current&quot;, color=yellow, style=filled];
    FreeBSD -&gt; FreeBSD_current;
  }
  _386BSD -&gt; FreeBSD;
  _44BSD -&gt; FreeBSD;

  DragonFly [label=&quot;DragonFly&quot;, color=yellow, style=filled];
  FreeBSD -&gt; DragonFly  [label=&quot;FreeBSD 4.8&quot;, fontcolor=blue, fontsize=9];

  BSD_OS [label=&quot;BSD/OS&quot;];
  BSD386 -&gt; BSD_OS;
  _44BSD -&gt; BSD_OS;

  subgraph cluster2{
    label=&quot;(OpenBSD)&quot;;
    color=lightgray;
    OpenBSD [label=&quot;OpenBSD&quot;];
    OpenBSD_current [label=&quot;OpenBSD -current&quot;, color=yellow, style=filled];
    OpenBSD -&gt; OpenBSD_current;
  }
  NetBSD -&gt; OpenBSD;

  subgraph cluster3{
    label=&quot;(Mac OS X)&quot;;
    color=lightgray;

    Rhapsody [label=&quot;Rhapsody&quot;, color=lightblue, style=filled];
    Darwin [label=&quot;Darwin/Mac OS X&quot;, color=lightblue, style=filled];
    MacOSX [label=&quot;Mac OS X&quot;, color=lightblue, style=filled];
    Rhapsody -&gt; Darwin -&gt; MacOSX;
  }
  FreeBSD -&gt; Darwin [label=&quot;FreeBSD 3.2&quot;, fontcolor=blue, fontsize=9];
  NetBSD -&gt; Darwin [label=&quot;NetBSD 1.4 (?)&quot;, style=dotted, fontcolor=blue, fontsize=9];
  _44BSD -&gt; Rhapsody;
}
</pre>
<h5>コードの補足</h5>
<p>上のコードについて、少し説明しておきます。</p>
<h6>ノード</h6>
<p>今回の題材では、</p>
<ul>
<li><code>"4.4BSD"</code> (違反：数字で始まっている)</li>
<li><code>"FreeBSD 9 -current"</code> (違反：禁止文字(空白,ハイフン)を含む)</li>
</ul>
<p>
のように、そのままではノード名に使えない名詞が多いため、アンダースコア<code>('_')</code>に置き換えたりして制限を回避しています。全く違う名称に置き換えても良いのですが、自分で書いてて混乱しそうなので元の名称を参考にしました。
</p>
<p>グラフに表示するテキストはlabel属性で指定しています。</p>
<pre>_43BSD_NET2 [label="4.3BSD NET/2", shape=box];</pre>
<p>ノードの属性を指定する時は、上記のように先にノード情報のみを定義しておきます。</p>
<p>ノードとノードの連結は、別の行で定義します。</p>
<pre>_43BSD_NET2 -> _44BSD;</pre>
<h6>エッジ</h6>
<p>エッジも修飾可能です。</p>
<pre>FreeBSD -> DragonFly  [label="FreeBSD 4.8", fontcolor=blue, fontsize=9];</pre>
<p>デフォルトではフォントサイズ：14ですが、ここでは少し小さめのサイズ：9を指定しています。またフォントカラーに青色を指定しています。</p>
<h6>サブグラフ</h6>
<p>
また、<strong>subgraph</strong> 機能を使って、いくつかのサブグラフを指定しています。<br />
またサブグラフのIDを<strong>&#8220;cluster&#8221;</strong>で始めると、枠を描画してくれます。
</p>
<pre>
  subgraph cluster0{
    ...
  }
</pre>
<h5>変換結果</h5>
<p>このスクリプトをdotコマンドで変換すると、以下のような結果を得られます。</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/bsdtree.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/bsdtree.png" alt="bsdtree.png" title="bsdtree.png" width="640" height="303" class="alignnone size-full wp-image-3037" /></a></p>
<h3>正しい使い方</h3>
<p>
ここまでの説明で、<strong>がんばれば凝ったグラフを作成できる</strong>ことは分かって頂けたと思いますが、 <span style="color: #ff0000;"><strong>VISIOとかで書いた方が楽じゃん</strong></span>と思った人もいるかもしれません &#8230;それは正しいです。
</p>
<p>
人がスクリプトをごりごり書くのではなく、下の図のように<strong>プログラムで自動生成したdotスクリプトからグラフを自動生成</strong>というのがGraphvizの正しい使い方と思います。(たぶん)
</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/dot_process.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/dot_process.png" alt="dot_process.png" title="dot_process.png" width="640" height="57" class="alignnone size-full wp-image-3040" /></a></p>
<p>
この図もGraphvizを使って作成しています。<a href='http://www.s34.co.jp/wp-content/uploads/2010/01/dot_process.dot'>ソースコード</a></p>
<p>日本語も無事通ってますね。ドキュメントによるとUTF-8をサポートしています。</p>
<h4>どうやって使うの？</h4>
<p>
普通はユーザーが知らない間に<strong>ソフトウェアが裏でこっそりGraphvizを使ってグラフを自動生成</strong> &#8230;というパターンになります。
</p>
<p>私がお世話になっている以下のソフトウェアでも Graphvizを利用しています。</p>
<ul>
<li><a href="http://www.stack.nl/~dimitri/doxygen/">doxygen</a>
<p>ソースコードからドキュメントを自動生成するツールです。<br />
UML図は、関数呼び出し図などの作成に利用しています。</p>
</li>
<li><a href="http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindIndex">KCachegrind</a>
<p>Valgrind/Callgrind(プログラムのプロファイル用ツール)をアシストするデータ視覚化ツールです。<br />
プロファイル情報を視覚化して表示してくれます。</p>
</li>
<li><a href="http://www.hping.org/visitors/">visitors</a>
<p>WEBサーバのアクセスログの解析ツールです。<br />
ユーザのページ遷移をグラフ化してくれます。</p>
</li>
</ul>
<p>
上で挙げたツールは全て技術者向けなので「裏でこっそり」とはいかず、ユーザーはGraphvizの存在を意識することになりますが&#8230;<br />
(さすがに、自動生成するdotスクリプトの中身までは読む必要はありませんが)
</p>
<h3>正しく使ってみよう</h3>
<p>
<strong>裏でこっそりやってくれるんなら関係ないや</strong>&#8230;という結論だと後が続かないので、ここは一つ<span style="color: #ff0000;"><strong>裏でこっそりやってくれるプログラムを作ってみましょう。</strong></span>
</p>
<h4>プログラムの仕様</h4>
<p>今回はXMLファイルを題材としたサンプルを作ってみます。</p>
<h5>仕様</h5>
<p>今回作成するプログラムの仕様は次の通りです。</p>
<p class='note'>
XMLファイルを読み込んで、要素(エレメント)をdotのノードに変換して、dotスクリプトを出力する。<br />
ただし、全てのエレメントの親子関係を線で結ぶこと。
</p>
<p>
親子関係を表現するには、Rootエレメントを起点に、全エレメントを <strong>&#8220;親エレメント <code>-&gt;</code> 子エレメント;&#8221;</strong> と継いで出力すれば可能です。
</p>
<p>このプログラムを使ったグラフ作成の流れは次の様になります。赤い部分が今回の作成対象です。</p>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/xml2dot_process.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/xml2dot_process.png" alt="xml2dot_process.png" title="xml2dot_process.png" width="640" height="56" class="alignnone size-full wp-image-3045" /></a></p>
<h4>期待する動作</h4>
<h5>入力</h5>
<p>例えば、次のようなXMLファイルを受けとると、</p>
<h6>addressbook.xml</h6>
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;addressbook&gt;
    アドレス帳
    &lt;person&gt;
      &lt;name&gt;foo&lt;/name&gt;
      &lt;address&gt;osaka&lt;/address&gt;
      &lt;phone&gt;0120-xxx-xxx&lt;/phone&gt;
    &lt;/person&gt;
    &lt;person&gt;
      &lt;name&gt;hoge hoge&lt;/name&gt;
      &lt;address&gt;kobe&lt;/address&gt;
      &lt;phone&gt;0120-yyy-yyy&lt;/phone&gt;
    &lt;/person&gt;
&lt;/addressbook&gt;
</pre>
<h5>結果</h5>
<p>dotスクリプトを出力し、それをdotコマンドで変換すると次のような結果が得られます。</p>
<h6>addressbook.png</h6>
<p><a href="http://www.s34.co.jp/wp-content/uploads/2010/01/addressbook.png"><img src="http://www.s34.co.jp/wp-content/uploads/2010/01/addressbook.png" alt="addressbook.png" title="addressbook.png" width="640" height="373" class="alignnone size-full wp-image-3136" /></a></p>
<h5>注意事項</h5>
<ol>
<li>dotでは、同じノード名は同じノードと扱うので、同じエレメント名が複数回出現したとき、それぞれを違うノード名を割り当てる必要があります。</li>
<li>XMLのエレメントで使えるが、dotのノード名で使えない文字があります。<br />
      例えば、dotではノード名にハイフン<code>('-')</code>は使えません。出現したら何らかの対処が必要です。</li>
</ol>
<p>
今回はプログラムで自動生成する(=人が介在しない)ので、ノード名にはXMLエレメント名と関係ないユニークなIDをつける事にします。表示するXMLエレメント名はlabel属性で指定します。
</p>
<h4>サンプルプログラム</h4>
<p>せっかく入力がXMLなので、XSLTスタイルシートで作成しました。</p>
<h6>xml2dot.xml</h6>
<pre>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0"&gt;

  &lt;xsl:strip-space elements="*" /&gt;
  &lt;xsl:output method="text" encoding="UTF-8"&gt;&lt;/xsl:output&gt;

  &lt;xsl:template match="/"&gt;
    &lt;xsl:text&gt;digraph sample{ graph [rankdir = LR];
&lt;/xsl:text&gt;
    &lt;xsl:apply-templates&gt;&lt;/xsl:apply-templates&gt;
    &lt;xsl:text&gt;}&lt;/xsl:text&gt;
  &lt;/xsl:template&gt;

  &lt;xsl:template match="*"&gt;
    &lt;xsl:param name="id"&gt;&lt;xsl:value-of select="generate-id()"&gt;&lt;/xsl:value-of&gt;&lt;/xsl:param&gt;
    &lt;xsl:param name="parent-id"&gt;&lt;/xsl:param&gt;

    &lt;xsl:value-of select="$id"&gt;&lt;/xsl:value-of&gt;&lt;xsl:text&gt; [label = &quot;&lt;/xsl:text&gt;
    &lt;xsl:value-of select="name()"&gt;&lt;/xsl:value-of&gt;
    &lt;xsl:text&gt;&quot;, color=cyan, style=filled]; &lt;/xsl:text&gt;
    &lt;xsl:if test="../.."&gt;
      &lt;xsl:value-of select="$parent-id"&gt;&lt;/xsl:value-of&gt;
      &lt;xsl:text&gt; -&gt; &lt;/xsl:text&gt;
      &lt;xsl:value-of select="$id"&gt;&lt;/xsl:value-of&gt;
      &lt;xsl:text&gt;; &lt;/xsl:text&gt;
    &lt;/xsl:if&gt;
    &lt;xsl:text&gt;
&lt;/xsl:text&gt;
    &lt;xsl:apply-templates&gt;
      &lt;xsl:with-param name="parent-id"&gt;&lt;xsl:value-of select="$id"&gt;&lt;/xsl:value-of&gt;&lt;/xsl:with-param&gt;
    &lt;/xsl:apply-templates&gt;

  &lt;/xsl:template&gt;

  &lt;xsl:template match="text()"&gt;
    &lt;xsl:param name="id"&gt;&lt;xsl:value-of select="generate-id()"&gt;&lt;/xsl:value-of&gt;&lt;/xsl:param&gt;
    &lt;xsl:param name="parent-id"&gt;&lt;/xsl:param&gt;

    &lt;xsl:value-of select="$id"&gt;&lt;/xsl:value-of&gt;
    &lt;xsl:text&gt;_text [label = &quot;&lt;/xsl:text&gt;
    &lt;xsl:value-of select="normalize-space()"&gt;&lt;/xsl:value-of&gt;
    &lt;xsl:text&gt;&quot;, shape=box, color=yellow, style=filled]; &lt;/xsl:text&gt;
    &lt;xsl:value-of select="$parent-id"&gt;&lt;/xsl:value-of&gt;
    &lt;xsl:text&gt; -&gt; &lt;/xsl:text&gt;
    &lt;xsl:value-of select="$id"&gt;&lt;/xsl:value-of&gt;
    &lt;xsl:text&gt;_text; &lt;/xsl:text&gt;
    &lt;xsl:text&gt;
&lt;/xsl:text&gt;
  &lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;
</pre>
<h4>使い方</h4>
<p>
実行にはXSLTプロセッサが必要です。ここでは2種類(libxml, MSXSL) のXSLTプロセッサでの実行方法について説明します。
</p>
<h5>libxml</h5>
<p>Unix系, Windowsの両方で使用可能です。</p>
<pre>$ xsltproc  -o [出力ファイル名]  [XSLTスタイルシート]  [入力XMLファイル]</pre>
<h6>引数説明</h6>
<ul>
<li>-o 出力ファイル名<br />
      生成したdotファイル名の保存先を指定します。<br />
      -o オプションを省略すると標準出力に出力します。
  </li>
<li>XSLTスタイルシート<br />
      xml2dot.xmlを指定します。</li>
<li>入力XMLファイル<br />
      addressbook.xml を指定します。</li>
</ul>
<p>
Windowsユーザーでlibxmlを試したい方は<a href="http://xmlsoft.org/sources/win32/">libxmlの公式サイト</a>から取得してください。
</p>
<h5>MSXSL (Windows only)</h5>
<p>
Microsoft製のXSLTプロセッサで、Windows限定です。<br />
libxmlと引数の順番が異なるので注意して下さい。
</p>
<pre>$ msxsl  [入力XMLファイル]  [XSLTスタイルシート]  -o [出力ファイル名]</pre>
<p><a href="http://www.microsoft.com/downloads/details.aspx?familyid=2fb55371-c94e-4373-b0e9-db4816552e41&#038;displaylang=en">Microsoft Download Center</a>から取得できます。</p>
<h5>実行例</h5>
<p>次のように実行します。</p>
<h6>libxml</h6>
<pre>$ xsltproc -o addressbook.dot xml2dot.xsl addressbook.xml</pre>
<h6>MSXSL</h6>
<pre>$ msxsl addressbook.xml xml2dot.xsl -o addressbook.dot</pre>
<p>成功すれば、次のようなファイルが作成されます。ただし、生成する度にノード名は違う値になっていると思います。</p>
<h6>addressbook.dot</h6>
<pre>
digraph sample{ graph [rankdir = LR];
id2273096 [label = "addressbook", color=cyan, style=filled];
id2273047_text [label = "アドレス帳", shape=box, color=yellow, style=filled]; id2273096 -> id2273047_text;
id2272492 [label = "person", color=cyan, style=filled]; id2273096 -> id2272492;
id2273073 [label = "name", color=cyan, style=filled]; id2272492 -> id2273073;
id2273057_text [label = "foo", shape=box, color=yellow, style=filled]; id2273073 -> id2273057_text;
id2273123 [label = "address", color=cyan, style=filled]; id2272492 -> id2273123;
id2273124_text [label = "osaka", shape=box, color=yellow, style=filled]; id2273123 -> id2273124_text;
id2273121 [label = "phone", color=cyan, style=filled]; id2272492 -> id2273121;
id2272599_text [label = "0120-xxx-xxx", shape=box, color=yellow, style=filled]; id2273121 -> id2272599_text;
id2272603 [label = "person", color=cyan, style=filled]; id2273096 -> id2272603;
id2272938 [label = "name", color=cyan, style=filled]; id2272603 -> id2272938;
id2272939_text [label = "hoge hoge", shape=box, color=yellow, style=filled]; id2272938 -> id2272939_text;
id2272941 [label = "address", color=cyan, style=filled]; id2272603 -> id2272941;
id2272942_text [label = "kobe", shape=box, color=yellow, style=filled]; id2272941 -> id2272942_text;
id2272944 [label = "phone", color=cyan, style=filled]; id2272603 -> id2272944;
id2272945_text [label = "0120-yyy-yyy", shape=box, color=yellow, style=filled]; id2272944 -> id2272945_text;
}
</pre>
<p>これをdotコマンドを使って画像ファイルに変換します。</p>
<pre>$ dot -Tpng addressbook.dot -o addressbook.png</pre>
<p>出来た画像ファイルをブラウザ等で開いて確認して下さい。</p>
<p>
今回作成したXSLTスタイルシート(xml2dot.xml)を使えば色々なXMLファイルが変換可能です。<br />
皆さんの手元にあるXMLファイルで実験してみて下さい。
</p>
<h5>別の実行方法</h5>
<p>以下のように変換プログラムとdotコマンドをパイプで連結して1行で処理することもできます。</p>
<pre>$ xsltproc  xml2dot.xsl  addressbook.xml  | dot -Tpng  -o addressbook.png</pre>
<h3>最後に</h3>
<p>今回は、いかにGraphvizを実務に適用するかに焦点を絞って説明しました。</p>
<p>Graphvizを使ってグラフを作成するメリットとして、以下のような事が挙げられると思います。</p>
<ol>
<li>レイアウトの自動化
<p>ノードが増えても、Graphvizが賢くレイアウトを整えてくれます。私はこれが嬉しいです。</p>
</li>
<li>ソースコードのシンプル化
<p>よくある描画ライブラリのように、線を一本ずつ書いたり、色をつけたり、dot位置を調整する必要がありません。</p>
</li>
</ol>
<p>
有向／無向グラフにターゲットを絞ったツールなので万能という訳には行きませんが、適用できれば非常に便利なツールと思います。<br />
機会があれば、ぜひ使ってみてください。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/s34lab/graphviz/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) [...]]]></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>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; sizeof(c) &#60;&#60; endl;

結果は 8 でした。メンバ関数を追加してもサイズに変化はないようです。
ところが、仮想関数を追加すると&#8230;

class c {
  int x;
  int y;
public:
 [...]]]></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>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種類の新しいキャスト
キャストは使わずに済ませられるならそれに越したとはないのですが、どうしても使わざるを得ないシチュエーションは少なからず存在します。
ならばせめて少しでも安全にキャストするために新たに定められたキャスト構文が、以下に挙げるstatic_cast / reinterpret_cast / const_cast / dynamic_cast です。
static_cast
static_cast&#60;type&#62;(expr)
static_castはexprの型からtypeへの暗黙の型変換、あるいはtypeからexprへの暗黙の型変換が存在する場合にだけキャストします。キャスト不可能であればコンパイルエラーとなります。
// int から long へ
int ival;
long lval = static_cast&#60;long&#62;(ival);
reinterpret_cast
reinterpret_cast&#60;type&#62;(expr)
reinterpret_castはtype(expr)が許されるなら、exprをtypeに単にキャストします。
// long から int* へ
long lval;
int* iptr = [...]]]></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>9章: クラス RWBTreeOnDisk の使い方</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-9/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-9/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:58:28 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=1358</guid>
		<description><![CDATA[

9.1 生成
9.2 例

RWBTreeOnDisk クラスは、ディスク上のファイルで B ツリーを管理するために設計されています。このクラスは、キーと値の関連付けを順序よく並べたコレクションを表わします。その順序はキーを比較することによって内部的に決定されます。キーを指定すると、値を取り出すことができます。キーの重複は許可されません。
キーは char の配列で、キーの長さはコンストラクタによって設定されます。B ツリー内の順序は、キーを外部関数 (変更可能) と比較することによって決定されます。
値の型は次のとおりです。

typedef long RWstoredValue;


値は通常、オブジェクトが格納されているファイル内の場所へのオフセットを表わします。キーを指定すると、オブジェクトが格納されている位置を見つけ、それを取り出すことができます。ただし、RWBTreeOnDisk クラスに関するかぎり、この値は特別な意味を持ちません。これを解釈するのはプログラマ次第です。
RWBTreeOnDisk クラスは、RWFileManager クラスを使って B ツリーノードの領域の割り当ておよび解放を管理します。B ツリーとデータが同じファイル内にある場合は、同じ RWFileManager を使って、オブジェクト自体の領域を管理できます。または、別の RWFileManager を使って別のファイルを管理し、B ツリーとデータを別々のファイルに格納することも可能です。
RWBTreeOnDisk クラスのメンバ関数は、メモリにおける RWBTreeDictionary クラスのメンバ関数と似ています。違う点は、キーが RWCollectable ではなく、char の配列であることです。メンバ関数には、キーと値のペアを追加する、ペアを削除する、キーに関連付けられている値を置き換える、キーに関連付けられている情報を照会する、キーと値の全ペアで順序正しく操作する、ツリーに項目数を返す、キーがツリーに含まれているかどうかを決定する、などの関数があります。
]]></description>
			<content:encoded><![CDATA[<p><span id="more-1358"></span></p>
<ul>
<li><a href="/cpptechdoc/reference/tools-9-1/">9.1 生成</a></li>
<li><a href="/cpptechdoc/reference/tools-9-2/">9.2 例</a></li>
</ul>
<p><code>RWBTreeOnDisk</code> クラスは、ディスク上のファイルで B ツリーを管理するために設計されています。このクラスは、キーと値の関連付けを順序よく並べたコレクションを表わします。その順序はキーを比較することによって内部的に決定されます。キーを指定すると、値を取り出すことができます。キーの重複は許可されません。</p>
<p>キーは char の配列で、キーの長さはコンストラクタによって設定されます。B ツリー内の順序は、キーを外部関数 (変更可能) と比較することによって決定されます。</p>
<p>値の型は次のとおりです。</p>
<pre>
typedef long RWstoredValue;
</pre>
<p>
値は通常、オブジェクトが格納されているファイル内の場所へのオフセットを表わします。キーを指定すると、オブジェクトが格納されている位置を見つけ、それを取り出すことができます。ただし、<code>RWBTreeOnDisk</code> クラスに関するかぎり、この値は特別な意味を持ちません。これを解釈するのはプログラマ次第です。</p>
<p><code>RWBTreeOnDisk</code> クラスは、<code>RWFileManager</code> クラスを使って B ツリーノードの領域の割り当ておよび解放を管理します。B ツリーとデータが同じファイル内にある場合は、同じ <code>RWFileManager</code> を使って、オブジェクト自体の領域を管理できます。または、別の <code>RWFileManager</code> を使って別のファイルを管理し、B ツリーとデータを別々のファイルに格納することも可能です。</p>
<p><code>RWBTreeOnDisk</code> クラスのメンバ関数は、メモリにおける <code>RWBTreeDictionary</code> クラスのメンバ関数と似ています。違う点は、キーが <code>RWCollectable</code> ではなく、char の配列であることです。メンバ関数には、キーと値のペアを追加する、ペアを削除する、キーに関連付けられている値を置き換える、キーに関連付けられている情報を照会する、キーと値の全ペアで順序正しく操作する、ツリーに項目数を返す、キーがツリーに含まれているかどうかを決定する、などの関数があります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>18.5 多重継承</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2664</guid>
		<description><![CDATA[

  第 15章「RWCollectable クラスの設計」で、RWCollectable を継承する Bus クラスを構築しました。すでに Bus クラスが存在すると仮定すると、以下のように多重継承を使って、Bus と RWCollectable の機能を持つ新しいクラスを作成し、手間を省くことができたはずです。


class CollectableBus : public RWCollectable, public Bus {
  .
  .
  .
};


  これは、Rogue Wave の多くのコレクションクラスで取られている手段です。例えば、RWCollectableString は RWCollectable と RWCString の両方のクラスを継承します。一般的な概念は、まずオブジェクトを作成し、それを RWCollectable クラスに付け加えて、全体をコレクション可能にすることです。この方法を使うと、RWCollectable クラスを継承したくないような他の場合にもオブジェクトを使用できます。


  この手段をとる別の理由は、曖昧な基底クラスを避けることにあります。次に例を示します。


class A { };

class B : public A { };

class C : public A { };

class [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2664"></span></p>
<p>
  第 <a href="http://www.s34.co.jp/cpptechdoc/reference/tools-15/">15</a>章「RWCollectable クラスの設計」で、<strong>RWCollectable</strong> を継承する <strong>Bus</strong> クラスを構築しました。すでに Bus クラスが存在すると仮定すると、以下のように多重継承を使って、<strong>Bus</strong> と <strong>RWCollectable</strong> の機能を持つ新しいクラスを作成し、手間を省くことができたはずです。
</p>
<pre>
class CollectableBus : public RWCollectable, public Bus {
  .
  .
  .
};
</pre>
<p>
  これは、Rogue Wave の多くのコレクションクラスで取られている手段です。例えば、<strong>RWCollectableString</strong> は <strong>RWCollectable</strong> と <strong class="c3">RWCString</strong> の両方のクラスを継承します。一般的な概念は、まずオブジェクトを作成し、それを <strong>RWCollectable</strong> クラスに付け加えて、全体をコレクション可能にすることです。この方法を使うと、<strong>RWCollectable</strong> クラスを継承したくないような他の場合にもオブジェクトを使用できます。
</p>
<p>
  この手段をとる別の理由は、曖昧な基底クラスを避けることにあります。次に例を示します。
</p>
<pre>
class A { };

class B : public A { };

class C : public A { };

class D : public B, public C { };

void fun(A&amp;);

main () {
  D  d;
  fun(d);                                           // どちらの A ?
}
</pre>
<p>
  <code>fun()</code> の呼び出しから曖昧さを取り除くには、2 つのやり方があります。以下のように変更するか、
</p>
<pre>
fun((B)d);                         // B の、A の発生
</pre>
<p>
  または、A を仮想基底クラスにすることです。
</p>
<p>
  最初の方法は、適切なキャストを実行するためにユーザが継承階層の詳細を把握している必要があるため、エラーが発生しやすくなります。
</p>
<p>
  A を仮想関数にするという 2 番目のやり方でこの問題は解決しますが、別の問題が発生します。派生元のクラスへキャストし戻すのがほとんど不可能になります。これは、継承階層に複数のパスができてしまうからです。物理的に説明すると、コンパイラが仮想基底クラスを基底クラスへのポインタとして実装し、ポインタを逆方向にはたどれないからです。
</p>
<p>
  オブジェクトの継承階層で、一致を探して可能なパスをすべて検索しつくすことができます (これは、NIH クラスのやり方です)。ただし、キャストごとに検索しなければならないため、結果のアドレスを記憶することによって速度を上げたとしても、この検索は遅くなります。また、サイズが大きく常に複雑なため、この方法は受け入れられないと判断しました。
</p>
<p>
  これらの理由で最初の方法に戻りました。同じ基底クラスからすべてのクラスを派生しないようにして、継承ツリーを単純にして受け入れることができるようにしました。多くの機能を持つ大きな基底クラスではなく、機能を別々の小さな基底クラスに分けることにしました。
</p>
<p>
  目的は、まずオブジェクトを構築して、それから収集性などの必要な機能を基底クラスに付け加えることにあります。つまり、同じ型の複数基底クラスを避け、その結果、曖昧な呼び出しを避けることになります。
</p>
<p>　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-18-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>18.4 RWCollectable の格納と取り出しについて</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2657</guid>
		<description><![CDATA[

  以下のグローバル関係を用いて、クラスの参照関係あるいはポインタ関係を格納して取り出す方法は、14.5.1節で説明しました。


Rwvostream&#38;  operator&#60;&#60;(RWvostream&#38;, const RWCollectable&#38;);
RWFile&#38;      operator&#60;&#60;(RWFile&#38;,     const RWCollectable&#38;);
Rwvostream&#38;  operator&#60;&#60;(RWvostream&#38;, const RWCollectable*);
RWFile&#38;      operator&#60;&#60;(RWFile&#38;,     const RWCollectable*);
Rwvistream&#38;  operator&#62;&#62;(RWvistream&#38;, RWCollectable&#38;);
RWFile&#38;      operator&#62;&#62;(RWFile&#38;,     RWCollectable&#38;);
Rwvistream&#38;  operator&#62;&#62;(RWvistream&#38;, RWCollectable*&#38;);
RWFile&#38;    [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2657"></span></p>
<p>
  以下のグローバル関係を用いて、クラスの参照関係あるいはポインタ関係を格納して取り出す方法は、<a href="http://www.s34.co.jp/cpptechdoc/reference/tools-14-5/#toc1451">14.5.1</a>節で説明しました。
</p>
<pre>
Rwvostream&amp;  operator&lt;&lt;(RWvostream&amp;, const RWCollectable&amp;);
RWFile&amp;      operator&lt;&lt;(RWFile&amp;,     const RWCollectable&amp;);
Rwvostream&amp;  operator&lt;&lt;(RWvostream&amp;, const RWCollectable*);
RWFile&amp;      operator&lt;&lt;(RWFile&amp;,     const RWCollectable*);
Rwvistream&amp;  operator&gt;&gt;(RWvistream&amp;, RWCollectable&amp;);
RWFile&amp;      operator&gt;&gt;(RWFile&amp;,     RWCollectable&amp;);
Rwvistream&amp;  operator&gt;&gt;(RWvistream&amp;, RWCollectable*&amp;);
RWFile&amp;      operator&gt;&gt;(RWFile&amp;,     RWCollectable*&amp;);
</pre>
<p>
  これらの関数がどのように機能するかを理解することは、<strong><em>RWCollectable</em>で作業する上で大切です。まず、簡単に説明します。</strong>
</p>
<p>
  初めてコレクションオブジェクトのどれかに対して左シフト演算子 <code>&lt;&lt;</code> の 1 つを呼び出すとき、アイデンティティ辞書が内部的に作成されます。オブジェクトのアドレスが、その出力ファイルでの順序(例えば、1 番目、2 番目、6 番目など) と共に辞書に納められます。
</p>
<p>
  いったんこれが完了すると、オブジェクトの仮想関数である <code>saveGuts()</code> が呼び出されます。これが仮想関数であるため、呼び出しは派生クラスによって使用される <code>saveGuts()</code> の定義に行きます これまで説明したように、<code>saveGuts()</code> の役目はオブジェクトの内部コンポーネントを格納することです。オブジェクトに <em>RWCollectable</em> を継承する他のオブジェクトが含まれている場合、そのオブジェクトの <code>saveGuts()</code> は含まれている各オブジェクトごとに <code>operator&lt;&lt;()</code> を再帰的に呼び出します。
</p>
<p>
  その後の <code>operator&lt;&lt;()</code> の呼び出しでは、新しくアイデンティティ辞書は作成されず、すでに存在している辞書にオブジェクトのアドレスが格納されます。アドレスがすでに書き込まれているオブジェクトのアドレスと同一である場合は、<code>saveGuts()</code> は呼び出されません。その代わり、そのオブジェクトが以前のオブジェクトと同一であるという参照が書き込まれます。
</p>
<p>
  コレクション全体が走査され、<code>saveGuts()</code> への最初の呼び出しが戻ると、アイデンティティ辞書が削除され、<code>operator&lt;&lt;()</code> への呼び出しが戻ります。
</p>
<p>
  <code>operator&gt;&gt;()</code> 関数は、restoreGuts を呼び出してオブジェクトをストリームまたはファイルからメモリに復元し、この処理全体を本質的に逆に行います。既に作成されているオブジェクトへの参照に出会うと、この関数は、<em>RWFactory</em> に新しいオブジェクトの作成を要求するのではなく、単に旧オブジェクトのアドレスを返します。
</p>
<p>
  以下は、これらの関数を使うより複雑な例です。
</p>
<pre>
#include &lt;rw/collect.h&gt;
#include &lt;rw/rwfile.h&gt;
#include &lt;assert.h&gt;

class Tangle : public RWCollectable
{

public:

  RWDECLARE_COLLECTABLE(Tangle)

  Tangle* nextTangle;
  int     someData;

  Tangle(Tangle* t = 0, int dat = 0){nextTangle=t; someData=dat;}

  virtual void saveGuts(RWFile&amp;) const;
  virtual void restoreGuts(RWFile&amp;);

};

void Tangle::saveGuts(RWFile&amp; file) const{
   RWCollectable::saveGuts(file);          // 抽象基底クラスを保存する

   file.Write(someData);                        // 内部を保存する

   file &lt;&lt; nextTangle;                      // 次のリンクを保存する
}

void Tangle::restoreGuts(RWFile&amp; file){
   RWCollectable::restoreGuts(file);    // 抽象基底クラスを復元する

  file.Read(someData);                       // 内部を復元する

  file &gt;&gt; nextTangle;                    // 次のリンクを復元する
}

// ヘッド "p" を持つヌル区切りのリストの内部統合性を調べる
void checkList(Tangle* p){
   int i=0;
   while (p)
   {
     assert(p-&gt;someData==i);
     p = p-&gt;nextTangle;
     i++;
   }
}

RWDEFINE_COLLECTABLE(Tangle, 100)

main(){
   Tangle *head = 0, *head2 = 0;

   for (int i=9; i &gt;= 0; i--)
     head = new Tangle(head,i);

   checkList(head);                    // オリジナルのリストを調べる

   {
     RWFile file("junk.dat");
     file &lt;&lt; head;
   }

   RWFile file2("junk.dat");
   file2 &gt;&gt; head2;

   checkList(head2);                   // 復元したリストを調べる
   return 0;
}
</pre>
<p>
  この例では、Tangle クラスが循環リンクリストを実装します。何が起こるでしょうか? 初めて <code>operator&lt;&lt;()</code> 関数が Tangle のインスタンスに呼び出されると、<code>operator&lt;&lt;()</code> 関数は前述したようにアイデンティティ辞書を設定し、Tangle の <code>saveGuts()</code> を呼び出します。ここで定義されているように、これは Tangle のメンバデータを格納し、次のリンクで <code>operator&lt;&lt;()</code> を呼び出します。この再帰性は、環状に継続します。
</p>
<p>
  チェーンが nil オブジェクトで終わる場合 (すなわち、nextTangle がゼロ) の場合、<code>operator&lt;&lt;()</code> はこれを内部的に記録し、再帰を停止します。
</p>
<p>
  その一方で、リストが環状である場合は、<code>operator&lt;&lt;()</code> が Tangle の最初にインスタンス (このチェーンすべてを開始したインスタンス)で再び呼び出されます。これが発生すると、<code>operator&lt;&lt;()</code> はこれはすでに処理したことがあるインスタンスであることを認識し、再び <code>saveGuts()</code> を呼び出すのではなく、前に書き込んだリンクへの参照を作成します。これにより、一連の再帰的な呼び出しが停止し、スタックが解放されます。
</p>
<p>
  チェーンは、同様な方法で復元されます。次の呼び出しにより、
</p>
<pre>
RWFile&amp; operator&gt;&gt;(RWFile&amp;, RWCollectable*&amp;);
</pre>
<p>
  新しいオブジェクトがヒープ領域に作成され、そのオブジェクトへのポインタ、前に読み込んだオブジェクトのアドレス、あるいはヌルポインタが返されます。最後の 2 つの場合は、再帰が停止し、スタックが解放されます。
</p>
<p>　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-18-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.6 まとめ</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2204</guid>
		<description><![CDATA[
この章では、ストリームのバイトの最終的な出力先がメモリかディスクかを考えることなく、オブジェクトをどのようにストリームに格納し、読み出すかについて述べました。また、ストリームの最終形式が ASCII かバイナリかを考慮する必要もありません。
また、RWpostream および Rwpistream のように、独自の特殊仮想ストリームクラスを書くこともできます。仮想ストリームの大きな利点は、独自の特殊仮想ストリームを書く場合に、クライアントクラスのコードをまったく変更する必要がないことです。つまり、単にストリームクラスを引数として使用するだけです。

RWvostream&#38; operator&#60;&#60;(RWvostream&#38;, const ClassName&#38;);
RWvistream&#38; operator&#62;&#62;(RWvistream&#38;,       ClassName&#38;);

オブジェクトを仮想ストリームに格納し読み出すほかに、すべてのクラス自体をバイナリで RWFile に格納し読み出すことができます。このファイルは、ANSI-C スタイルのファイル I/O をカプセル化します。ストリーム I/O に比べその機能に、より多くの制限がありますが、仮想関数のディスパッチを必要としないため、この形式でディスクへ格納し読み出す方がやや速くなります。
]]></description>
			<content:encoded><![CDATA[<p><span id="more-2204"></span></p>
<p>この章では、ストリームのバイトの最終的な出力先がメモリかディスクかを考えることなく、オブジェクトをどのようにストリームに格納し、読み出すかについて述べました。また、ストリームの最終形式が ASCII かバイナリかを考慮する必要もありません。</p>
<p>また、<strong>RWpostream</strong> および <strong>Rwpistream</strong> のように、独自の特殊仮想ストリームクラスを書くこともできます。仮想ストリームの大きな利点は、独自の特殊仮想ストリームを書く場合に、クライアントクラスのコードをまったく変更する必要がないことです。つまり、単にストリームクラスを引数として使用するだけです。</p>
<pre>
RWvostream&amp; operator&lt;&lt;(RWvostream&amp;, const <em>ClassName</em>&amp;);
RWvistream&amp; operator&gt;&gt;(RWvistream&amp;,       <em>ClassName</em>&amp;);
</pre>
<p>オブジェクトを仮想ストリームに格納し読み出すほかに、すべてのクラス自体をバイナリで <strong>RWFile</strong> に格納し読み出すことができます。このファイルは、ANSI-C スタイルのファイル I/O をカプセル化します。ストリーム I/O に比べその機能に、より多くの制限がありますが、仮想関数のディスパッチを必要としないため、この形式でディスクへ格納し読み出す方がやや速くなります。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>6.4 DDE の例</title>
		<link>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/</link>
		<comments>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/#comments</comments>
		<pubDate>Fri, 14 Apr 2006 08:50:39 +0000</pubDate>
		<dc:creator>cpptechdoc</dc:creator>
				<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.s34.co.jp/?p=2198</guid>
		<description><![CDATA[
次の例はさらに複雑なもので、Windows DDE 機能を介して、RWDDEstreambuf クラスを使い、RWBinaryTree を交換する方法を示します。Windows クリップボードの場合にも、同様なテクニックを使用することができます。

#include &#60;rw/bintree.h&#62;
#include &#60;rw/collstr.h&#62;
#include &#60;rw/bstream.h&#62;
#include &#60;rw/winstrea.h&#62;
#include &#60;windows.h&#62;
#include &#60;dde.h&#62;

BOOL
PostCollection(HWND hwndServer, WORD cFormat){
   RWBinaryTree sc;                                     [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-2198"></span></p>
<p>次の例はさらに複雑なもので、Windows DDE 機能を介して、<strong>RWDDEstreambuf</strong> クラスを使い、<strong>RWBinaryTree</strong> を交換する方法を示します。Windows クリップボードの場合にも、同様なテクニックを使用することができます。</p>
<pre>
#include &lt;rw/bintree.h&gt;
#include &lt;rw/collstr.h&gt;
#include &lt;rw/bstream.h&gt;
#include &lt;rw/winstrea.h&gt;
#include &lt;windows.h&gt;
#include &lt;dde.h&gt;

BOOL
PostCollection(HWND hwndServer, WORD cFormat){
   RWBinaryTree sc;                                          // 1
   sc.insert(new RWCollectableString("Mary"));
   sc.insert(new RWCollectableString("Bill"));
   sc.insert(new RWCollectableString("Pierre"));

   // RWDDEstreambuf を割り当て、それを使って初期化する
   // RWbostream:
   RWDDEstreambuf* sbuf = new RWDDEstreambuf(cFormat,        // 2
                                             FALSE,          // 3
                                             TRUE,           // 4
                                             TRUE);          // 5
   RWbostream bostr( sbuf );                                 // 6

   // コレクションを Rwbostream に格納する:
   bostr &lt;&lt; sc;                                              // 7

   // 出力ストリームをロックし、そのハンドルを取得する:
   HANDLE hDDEData = sbuf-&gt;str();                            // 8

   // DDE メッセージを同定するアトムを取得する:
   ATOM atom = GlobalAddAtom("SortedNames");                 // 9

   // DDE の応答をポストする:
   return PostMessage(0xFFFF, WM_DDE_DATA, hwndServer,       //10
                      MAKELONG(hDDEData, atom));
}
</pre>
<p>のコードでは、大きなメモリモデルが前提とされています。次に、コードの説明を行ごとに示します。</p>
<dl>
<dt>//1</dt>
<dd><strong>RWBinaryTree</strong> が構築され、それに項目が挿入されます。</dd>
<dt>//2-//5</dt>
<dd><strong>RWDDEstreambuf</strong> が割り当てられます。コンストラクタはいくつかの引数をとります。最初の引数は Windows クリップボードの形式です。この例では、形式の種類が引数として渡されていますが、一般には Windows に形式を登録し (RegisterClipboardFormat() を使う)、それを使用します。<br />
  その他の引数は、DDE データ交換およびメモリ管理に関連しています。引数の一覧に関しては『<cite>Tools.h++ Class Reference</cite>』を参照してください。それらの意味については、『<cite>Programming Windows 3.0</cite>』、または 『<cite>Microsoft Windows Guide to Programming</cite>』を参照してください。</dd>
<dt>//6</dt>
<dd><strong>RWbostream</strong> が与えられた <strong>RWDDEstreambuf</strong> から生成されます。ここで <strong>RWpostream</strong> を使うこともできますが、DDE 交換は同じマシンアーキテクチャ内で行われるので、余分な手間のかかる移植可能な ASCII 形式を使っても意味がありません。また、<strong>streambuf</strong> の型によって設定されるバイトの状態が、どのように <strong>Rwvostream</strong>   の型によって設定される形式から切り離されるかに注意してください。</dd>
<dt>//7</dt>
<dd>コレクションは <strong>Rwbostream</strong> に保存されます。<strong>RWbostream</strong>と関連付けられている <strong>streambuf</strong> は実際には <strong>RWDDEstreambuf</strong> であるため、コレクションは GMEM_DDESHARE 属性を持つ Windows のグローバルメモリ割り当て領域に保存されます。この割り当て領域は、オーバーフローすると自動的にサイズ変更されます。その他の <strong>strstreambuf</strong> と同様、メンバ関数 setbuf() を使って割り当て領域の大きさを変更できます。</dd>
<dt>//8</dt>
<dd><strong>RWDDEstreambuf</strong> がロックされます。いったん str() を使ってロックしたら、他の <strong>strstreambuf</strong> と同様、この <strong>streambuf</strong> を再び使用することはできません。ただし、RWDDEstreambuf::str() は char* ではなく、ハンドルを返すことに注意してください。このハンドルは、返す前にロック解除されます。</dd>
<dt>//9</dt>
<dd>この DDE データを同定するためにアトムを生成します。</dd>
<dt>//10</dt>
<dd>RWDDEstreambuf::str() によって返されたハンドルが、アトムを同定するとともにポストされます。</dd>
</dl>
<p>クリップボードを介したデータのやり取りには、より簡単な同様の方法を使用できます。</p>
<p>特殊化された <strong>streambuf</strong> の <strong>RWCLIPstreambuf</strong> と <strong>RWDDEstreambuf</strong> は、Rogue Wave 仮想ストリーム機能と共に使わなければならないものではありません。それらは通常の <strong>istream</strong> および <strong>ostream</strong> と共に、きわめて簡単に使用することができます。ただし、実行時に形式を設定することができません。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.s34.co.jp/cpptechdoc/reference/tools-6-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
