<?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>Yoichi Kawasaki blog &#187; cplusplus</title>
	<atom:link href="http://yk55.com/blog/tags/cplusplus/feed/" rel="self" type="application/rss+xml" />
	<link>http://yk55.com/blog</link>
	<description>the place to organize and record my ideas ...</description>
	<lastBuildDate>Sat, 28 Aug 2010 03:47:26 +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>LibeventのRPCフレームワークによるC/Sプログラミング</title>
		<link>http://yk55.com/blog/2010/06/05/libevent_rpc_cs_programmin/</link>
		<comments>http://yk55.com/blog/2010/06/05/libevent_rpc_cs_programmin/#comments</comments>
		<pubDate>Sat, 05 Jun 2010 14:49:21 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming / プログラミング]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[libevent]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[rpc]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=361</guid>
		<description><![CDATA[前回の投稿ではlibeventのHTTP処理機能evhttpを使ったHTTPサーバを紹介したが今回はlibeventのRPCフレームワーク evrpcを使ったC/Sプログラミング方法を紹介してみる。このevrpcはかなりマイナーな部類のフレームワークといえる。これに関するドキュメントは少なく、検索してみるとまともなページがヒットしない。ヘッダやテストコードを読まない人にとってはちょっと厳しいかもしれない。 巷にはハイパフォーマンスで使いやすく少し検索すれば豊富なドキュメントやブログ記事がわさわさと出てくるようなフレームワークがごろごろしているのでこんなに地味で愛想ないフレームワークをふつーは選ばない。 そんな人気のないevrpcだけど実際に使ってみると思ったより悪くない。 動作は安定しているしAPIはシンプルで使いやすく拡張性も考慮されていてHook関数、Callback関数を駆使すれば好きに振る舞いを制御することができる。 少々粗挽きなところは全く気にしないでこれをベースに自分なりに使いやすいフレームワークを作ってやろうくらいに思ってる人には向いているかもしれない。
RPC marshalling用コードのジェネレート &#8211; event_rpcgen.py
marshallingとはRPC C/S処理で内部的に使用するデータ構造をネットワーク転送用のフォーマットに変換することで、その逆をdemarshallingと呼ぶ。厳密には意味が違うらしいがserializationとdeserializationみたいなもの。 libeventにはデータ構造をmarshalling、demarshallingするコードをジェネレートするためのスクリプトが用意されている。それがevent_rpcgen.py。RPC C/S処理で使用するデータ構造を定められたフォーマットで定義してやりそれをevent_rpcgen.pyに食わせてやることでコードが生成される。 データ構造の定義フォーマットは次のとおり。
[データ構造の定義フォーマット]

struct &#60;data struct name&#62; {
&#160; &#160; [optional] &#60;type&#62; &#60;varname&#62; = &#60;index&#62;;
&#160; &#160; &#160;...
}

&#60;type&#62; データ型
&#160; - string &#160;文字列
&#160; - bytes &#160;uint_tベクター。 [lenght]で長さを指定。　例、bytes hoge[10]
&#160; - int &#160;uint32_t
&#160; - struct[structname] &#160;構造体
&#160; - array &#60;type&#62; &#160;typeで指定されたデータ型のベクター。 typeはstring, bytes, int, または struct[structname]

&#60;varname&#62; 変数名
&#60;index&#62; インデックス番号。フィールド番号。

[optional] &#160;typeの前にoptionalを指定するとリクエスト、レスポンス処理時にそのフィールドへのデータセットを省略することができる。

※ &#160;定義ファイルの拡張子は.rpcである必要がある
※ &#160;&#60;varname&#62; と&#60;index&#62;の間は１半角スペースずつを開ける。 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://yk55.com/blog/2010/04/30/eventdriven_http_server_vs_httpd/">前回の投稿</a>では<a href="http://www.monkey.org/~provos/libevent/">libevent</a>のHTTP処理機能<a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evhttp_8h.html">evhttp</a>を使ったHTTPサーバを紹介したが今回はlibeventのRPCフレームワーク <a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evrpc_8h.html">evrpc</a>を使ったC/Sプログラミング方法を紹介してみる。このevrpcはかなりマイナーな部類のフレームワークといえる。これに関するドキュメントは少なく、検索してみるとまともなページがヒットしない。ヘッダやテストコードを読まない人にとってはちょっと厳しいかもしれない。 巷にはハイパフォーマンスで使いやすく少し検索すれば豊富なドキュメントやブログ記事がわさわさと出てくるようなフレームワークがごろごろしているのでこんなに地味で愛想ないフレームワークをふつーは選ばない。 そんな人気のないevrpcだけど実際に使ってみると思ったより悪くない。 動作は安定しているしAPIはシンプルで使いやすく拡張性も考慮されていてHook関数、Callback関数を駆使すれば好きに振る舞いを制御することができる。 少々粗挽きなところは全く気にしないでこれをベースに自分なりに使いやすいフレームワークを作ってやろうくらいに思ってる人には向いているかもしれない。</p>
<p><h2><strong>RPC marshalling用コードのジェネレート &#8211; event_rpcgen.py</strong></h2>
<p><a href="http://en.wikipedia.org/wiki/Marshalling_%28computer_science%29">marshalling</a>とはRPC C/S処理で内部的に使用するデータ構造をネットワーク転送用のフォーマットに変換することで、その逆をdemarshallingと呼ぶ。厳密には意味が違うらしいがserializationとdeserializationみたいなもの。 libeventにはデータ構造をmarshalling、demarshallingするコードをジェネレートするためのスクリプトが用意されている。それが<a href="http://levent.git.sourceforge.net/git/gitweb.cgi?p=levent/levent;a=blob;f=event_rpcgen.py;h=05f0a3622aa7d9343d8f3e86f6c42fc0465e46ae;hb=HEAD">event_rpcgen.py</a>。RPC C/S処理で使用するデータ構造を定められたフォーマットで定義してやりそれをevent_rpcgen.pyに食わせてやることでコードが生成される。 データ構造の定義フォーマットは次のとおり。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[データ構造の定義フォーマット]<br />
<br />
struct &lt;data struct name&gt; {<br />
&nbsp; &nbsp; [optional] &lt;type&gt; &lt;varname&gt; = &lt;index&gt;;<br />
&nbsp; &nbsp; &nbsp;...<br />
}<br />
<br />
&lt;type&gt; データ型<br />
&nbsp; - string &nbsp;文字列<br />
&nbsp; - bytes &nbsp;uint_tベクター。 [lenght]で長さを指定。　例、bytes hoge[10]<br />
&nbsp; - int &nbsp;uint32_t<br />
&nbsp; - struct[structname] &nbsp;構造体<br />
&nbsp; - array &lt;type&gt; &nbsp;typeで指定されたデータ型のベクター。 typeはstring, bytes, int, または struct[structname]<br />
<br />
&lt;varname&gt; 変数名<br />
&lt;index&gt; インデックス番号。フィールド番号。<br />
<br />
[optional] &nbsp;typeの前にoptionalを指定するとリクエスト、レスポンス処理時にそのフィールドへのデータセットを省略することができる。<br />
<br />
※ &nbsp;定義ファイルの拡張子は.rpcである必要がある<br />
※ &nbsp;&lt;varname&gt; と&lt;index&gt;の間は１半角スペースずつを開ける。 そうでない場合はevent_rpcgen.pyによるジェネレートが失敗するので要注意！<br />
&nbsp; &nbsp;× &nbsp; &nbsp;int errcode =1;<br />
&nbsp; &nbsp;× &nbsp; &nbsp;int errcode &nbsp; = 1;<br />
&nbsp; &nbsp;× &nbsp; &nbsp;int errcode=1;<br />
&nbsp; &nbsp;○ &nbsp; &nbsp;int errcode = 1</div></div>
<p>今回のサンプルで使用するデータ構造を定義する。 サンプルはユーザ情報を取得するRPCでありユーザ情報取得のためのリクエスト、レスポンス用データ構造が以下のGetUserRequest、GetUserResponse。</p>
<p><u>サンプルデータ構造の定義フォーマットファイル &#8211; <A href="http://github.com/yokawasa/any/blob/master/libevent_rpc/simple.rpc">simple.rpc</A></u></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">struct</span> GetUserRequest <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">string</span> id <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #993333;">struct</span> GetUserResponse <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">int</span> errcode <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; optional <span style="color: #993333;">string</span> name <span style="color: #339933;">=</span> <span style="color: #0000dd;">2</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; optional <span style="color: #993333;">string</span> email <span style="color: #339933;">=</span> <span style="color: #0000dd;">3</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>これを次のようにevent_rpcgen.pyを使ってデータ構造定義からコードをジェネレートする。 event_rpcgen.pyはlibeventをインストールするとデフォルトで/usr/local/bin/配下にコピーされる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ /usr/local/bin/event_rpcgen.py simple.rpc<br />
<br />
Reading &quot;./simple.rpc&quot;<br />
&nbsp; Created struct: GetUserRequest<br />
&nbsp; &nbsp; Added entry: id<br />
&nbsp; Created struct: GetUserResponse<br />
&nbsp; &nbsp; Added entry: errcode<br />
&nbsp; &nbsp; Added entry: name<br />
&nbsp; &nbsp; Added entry: email<br />
... creating &quot;./simple.gen.h&quot;<br />
... creating &quot;./simple.gen.c&quot;</div></div>
<p><a href="http://github.com/yokawasa/any/blob/master/libevent_rpc/simple.gen.h">simple.gen.h</a>と<a href="http://github.com/yokawasa/any/blob/master/libevent_rpc/simple.gen.c">simple.gen.c</a>の2つのファイルがジェネレートされる。 C/S両方でこのデータ構造を使用する。</p>
<p><h2><strong>RPCサーバの実装 &#8211; simple_rpc_server.cpp</strong></h2>
<p><a href="http://github.com/yokawasa/any/blob/master/libevent_rpc/simple_rpc_server.cpp">http://github.com/yokawasa/any/blob/master/libevent_rpc/simple_rpc_server.cpp</a></p>
<p>まずは利用するRPCメソッドの登録を行う。 <a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evrpc_8h.html#a526799df545537b2dc67358b0dfaa15">EVRPC_HEADER</a>でRPCコマンドのデータ構造とプロトタイプを作成して<a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evrpc_8h.html#f7abcafae087e3c4dce6a4cd85346114">EVRPC＿GENERATE</a>でRPCメッセージを送受信するためのコードを作成する。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#ifdef __cplusplus</span><br />
<span style="color: #000000; font-weight: bold;">extern</span> <span style="color: #ff0000;">&quot;C&quot;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #339933;">#endif</span><br />
<span style="color: #339933;">#include &quot;simple.gen.h&quot;</span><br />
<span style="color: #339933;">#ifdef __cplusplus</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #339933;">#endif</span><br />
<span style="color: #339933;">#include &quot;evrpc.h&quot;</span><br />
&nbsp; &nbsp; <br />
<span style="color: #808080; font-style: italic;">/* registe RPC : GetUser */</span><br />
EVRPC_HEADER<span style="color: #009900;">&#40;</span>GetUser<span style="color: #339933;">,</span> GetUserRequest<span style="color: #339933;">,</span> GetUserResponse<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #808080; font-style: italic;">/* generate RPC code : GetUser */</span><br />
EVRPC_GENERATE<span style="color: #009900;">&#40;</span>GetUser<span style="color: #339933;">,</span> GetUserRequest<span style="color: #339933;">,</span> GetUserResponse<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></div>
<p>RPCサーバの実装について説明する。実装は既に存在するHTTPサーバの実装をベースとする。RPCサーバの基本コードは以下の通り。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">**</span>argv<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>argc <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">3</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s &lt;server&gt; &lt;port&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> argv<span style="color: #009900;">&#91;</span>0<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>svr_addr <span style="color: #339933;">=</span> argv<span style="color: #009900;">&#91;</span>1<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">short</span> svr_port <span style="color: #339933;">=</span> atoi<span style="color: #009900;">&#40;</span>argv<span style="color: #009900;">&#91;</span>2<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evhttp <span style="color: #339933;">*</span>ev_http <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evrpc_base <span style="color: #339933;">*</span>rpc_base <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* initialize base event mechanism */</span><br />
&nbsp; &nbsp; event_init<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; ev_http <span style="color: #339933;">=</span> evhttp_start<span style="color: #009900;">&#40;</span>svr_addr<span style="color: #339933;">,</span> svr_port<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* create a base for the RPC protocol */</span><br />
&nbsp; &nbsp; rpc_base <span style="color: #339933;">=</span> evrpc_init<span style="color: #009900;">&#40;</span>ev_http<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* register all of the handlers for all RPCs */</span><br />
&nbsp; &nbsp; EVRPC_REGISTER<span style="color: #009900;">&#40;</span>rpc_base<span style="color: #339933;">,</span> GetUser<span style="color: #339933;">,</span> GetUserRequest<span style="color: #339933;">,</span> GetUserResponse<span style="color: #339933;">,</span> GetUserCallback<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* loop and dispatch events */</span><br />
&nbsp; &nbsp; event_dispatch<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; EVRPC_UNREGISTER<span style="color: #009900;">&#40;</span>rpc_base<span style="color: #339933;">,</span> GetUser<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; evrpc_free<span style="color: #009900;">&#40;</span>rpc_base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; evhttp_free<span style="color: #009900;">&#40;</span>ev_http<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<ul>
<li>event_init()でlibeventライブラリ初期化。とりあえずlibeventを使う際は最初に一度実行しておく。</li>
<li>evhttp_start(svr_addr, svr_port)にサーバアドレス、ポートを指定してHTTPサーバ処理開始準備。</li>
<li>evrpc_init()でrpc処理ハンドル用ポインタを作成。</li>
<li><a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evrpc_8h.html#bb204281e2555a1e91ff3885747d0533">EVRPC_REGISTER</a>で特定のRPCコマンドをサーバに登録する。ここではさきほどEVRPC_HEADERとEVRPC_GENERATEで登録されたRPCコマンドGetUser（リクエスト用データ構造GetUserRequest, レスポンス用データ構造GetUserResponse）をRPCサーバに登録する。また同時にGetUserCallbackというRPCリクエストを受け取った際に呼び出されるコールバック関数を登録する。GetUserCallbackは次で説明する。</li>
<li>event_dispatch()でloop開始しイベントdispatchを開始する。</li>
<li>後処理。サーバ処理を終えるときEVRPC_UNREGISTER、evrpc_free, evhttp_freeの順で後処理をする。</li>
</ul>
<p><p>
GetUserCallbackはさきほどEVRPC_REGISTERでサーバに登録されたRPCコマンドのコールバック関数。 RPCクライアントから送信されるリクエストデータからidを取得。そのidに対するnameとemailデータをレスポンスデータにセットしている。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span><br />
GetUserCallback<span style="color: #009900;">&#40;</span>EVRPC_STRUCT<span style="color: #009900;">&#40;</span>GetUser<span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span> rpc<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>arg<span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> GetUserRequest<span style="color: #339933;">*</span> req <span style="color: #339933;">=</span> rpc<span style="color: #339933;">-&gt;</span>request<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> GetUserResponse<span style="color: #339933;">*</span> res <span style="color: #339933;">=</span> rpc<span style="color: #339933;">-&gt;</span>reply<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* get request info */</span><br />
&nbsp; &nbsp; <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>id<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>EVTAG_HAS<span style="color: #009900;">&#40;</span>req<span style="color: #339933;">,</span> id<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;failed to get request info<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; EVTAG_ASSIGN<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> errcode<span style="color: #339933;">,</span> <span style="color: #339933;">-</span>1<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; EVTAG_GET<span style="color: #009900;">&#40;</span>req<span style="color: #339933;">,</span> id<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;REQUEST id = %s<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* do somthing due to request info */</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* set response info */</span><br />
&nbsp; &nbsp; EVTAG_ASSIGN<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> errcode<span style="color: #339933;">,</span> 0<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; EVTAG_ASSIGN<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> name<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Fooo Baaar&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; EVTAG_ASSIGN<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> email<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;baz@foo.bar&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* send the reply to the RPC */</span><br />
&nbsp; &nbsp; EVRPC_REQUEST_DONE<span style="color: #009900;">&#40;</span>rpc<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<ul>
<li>EVTAG_HASでリクエストデータ構造中のあるフィールドにデータがセットされているかをチェック(ここではid)してEVTAG_GETで実際にそのフィールドデータを取得。</li>
<li>EVTAG_ASSIGNでレスポンスデータ構造のフィールド(ここではerrcode, name, email)にデータをセット。</li>
<li>EVRPC_REQUEST_DONEでレスポンス送信。</li>
</ul>
<p><h2><strong>RPCクライアントの実装 &#8211; simple_rpc_client.cpp</strong></h2>
<p><a href="http://github.com/yokawasa/any/blob/master/libevent_rpc/simple_rpc_client.cpp">http://github.com/yokawasa/any/blob/master/libevent_rpc/simple_rpc_client.cpp<br />
</a><br />
RPCクライアントの実装について説明する。RPCクライアントでもサーバと同じようにEVRPC_HEADERとEVRPC＿GENERATEとで利用するRPCメソッドの登録を行う。以下RPCクライアントの基本コード。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">**</span>argv<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>argc <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">3</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s &lt;server&gt; &lt;port&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> argv<span style="color: #009900;">&#91;</span>0<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>svr_addr <span style="color: #339933;">=</span> argv<span style="color: #009900;">&#91;</span>1<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">short</span> svr_port <span style="color: #339933;">=</span> atoi<span style="color: #009900;">&#40;</span>argv<span style="color: #009900;">&#91;</span>2<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> event_base <span style="color: #339933;">*</span>ev_base <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evhttp <span style="color: #339933;">*</span>ev_http <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evrpc_base <span style="color: #339933;">*</span>rpc_base <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evrpc_pool <span style="color: #339933;">*</span>rpc_pool <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> GetUserRequest <span style="color: #339933;">*</span>req<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> GetUserResponse <span style="color: #339933;">*</span>res<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* initialize base event mechanism */</span><br />
&nbsp; &nbsp; ev_base <span style="color: #339933;">=</span> event_init<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* initialize http internal buffer */</span><br />
&nbsp; &nbsp; ev_http <span style="color: #339933;">=</span> evhttp_new<span style="color: #009900;">&#40;</span>ev_base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* create a base for the RPC protocol */</span><br />
&nbsp; &nbsp; rpc_base <span style="color: #339933;">=</span> evrpc_init<span style="color: #009900;">&#40;</span>ev_http<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; rpc_pool <span style="color: #339933;">=</span> get_rpc_pool<span style="color: #009900;">&#40;</span>ev_base<span style="color: #339933;">,</span> svr_addr<span style="color: #339933;">,</span> svr_port<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rpc_pool<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* set up request data */</span><br />
&nbsp; &nbsp; req <span style="color: #339933;">=</span> GetUserRequest_new<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; EVTAG_ASSIGN<span style="color: #009900;">&#40;</span>req<span style="color: #339933;">,</span> id<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;foobar&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* create a response structure */</span><br />
&nbsp; &nbsp; res <span style="color: #339933;">=</span> GetUserResponse_new<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* set request, response, callback func */</span><br />
&nbsp; &nbsp; EVRPC_MAKE_REQUEST<span style="color: #009900;">&#40;</span>GetUser<span style="color: #339933;">,</span> rpc_pool<span style="color: #339933;">,</span> req<span style="color: #339933;">,</span> res<span style="color: #339933;">,</span> GetUserCallback<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* process request and response */</span><br />
&nbsp; &nbsp; event_dispatch<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; GetUserRequest_free<span style="color: #009900;">&#40;</span>req<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; GetUserResponse_free<span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; evrpc_pool_free<span style="color: #009900;">&#40;</span>rpc_pool<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; evrpc_free<span style="color: #009900;">&#40;</span>rpc_base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; evhttp_free<span style="color: #009900;">&#40;</span>ev_http<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<ul>
<li>event_init()でlibeventライブラリ初期化。</li>
<li>evhttp_new()でhttp処理用内部バッファーを初期化。</li>
<li>evrpc_init()でrpc処理ハンドル用ポインタを作成。</li>
<li>get_rpc_pool()よりevrpc_poolポインタを取得。get_rpc_pool()は次で説明。 evrpc_poolは内部にリクエストとして送信するための接続ポインタ(evhttp_connection)を持っている。</li>
<li>GetUserRequest_new()とGetUserResponse_new()によりRPCリクエストとレスポンスのためのリソース確保。</li>
<li>EVTAG_ASSIGNによりリクエスト用データ構造のidフィールドに値(&#8221;foobar&#8221;)を設定。</li>
<li><a href="http://www.monkey.org/~provos/libevent/doxygen-1.4.10/evrpc_8h.html#ff0efcd7cb8b966256b5127960bd2c81">EVRPC_MAKE_REQUEST</a>でサーバへのリクエスト送信に使うRPCコマンドとリクエスト、レスポンスオブジェクトポインタ、evrpc_poolポインタ、コールバック関数をセットする。コールバック関数GetUserCallbackは後で説明する。</li>
<li>event_dispatch()を呼んで実際にリクエスト送信、レスポンス受信を行う。</li>
<li>後処理。GetUserRequest_free()、GetUserResponse_free()でそれぞれリクエストとレスポンスのために確保されたリソースを開放。 次にevrpc_pool_free、evrpc_free, evhttp_freeの順で後処理実行。</li>
</ul>
<p><p>
RPCリクエスト処理で使用するevrpc_poolポインタを取得するための関数。get_rpc_poolがコールされる度にサーバ接続ポインタ (evhttp_connection)を生成しevrpc_poolの接続プールに追加している。 ちなみにevrpc_poolとはいわゆるコネクションプールのこと。仕組みとしては内部に接続とリクエストを管理するQUEUEを持っていてリクエストごとに接続QUEUEから空いている接続をリクエストに割り当てる。もし割り当てる接続がなければそのリクエストをリクエストQUEUEに追加する。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">static</span> <span style="color: #993333;">struct</span> evrpc_pool <span style="color: #339933;">*</span><br />
&nbsp;get_rpc_pool<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> event_base <span style="color: #339933;">*</span>base<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>svr_addr<span style="color: #339933;">,</span> <span style="color: #993333;">short</span> svr_port<span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evhttp_connection <span style="color: #339933;">*</span>ev_conn<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> evrpc_pool <span style="color: #339933;">*</span>rpc_pool<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; rpc_pool <span style="color: #339933;">=</span> evrpc_pool_new<span style="color: #009900;">&#40;</span>base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rpc_pool<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;failed to get new rpc pool<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// create a connection to RPC server</span><br />
&nbsp; &nbsp; ev_conn <span style="color: #339933;">=</span> evhttp_connection_new<span style="color: #009900;">&#40;</span>svr_addr<span style="color: #339933;">,</span> svr_port<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ev_conn<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;failed to get new evhttp connection: %s:%d<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; svr_addr<span style="color: #339933;">,</span> svr_port<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// add request connection in the pool</span><br />
&nbsp; &nbsp; evrpc_pool_add_connection<span style="color: #009900;">&#40;</span>rpc_pool<span style="color: #339933;">,</span> ev_conn<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #009900;">&#40;</span>rpc_pool<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<ul>
<li>evrpc_pool_new()でrpcコネクションプールを作成。</li>
<li>evhttp_connection_newでリクエスト用接続ポインタを生成。</li>
<li>evrpc_pool_add_connectionでevrpc_poolの接続プールに生成されたリクエスト用接続ポインタを追加。</li>
</ul>
<p><p>
以下EVRPC_MAKE_REQUESTで指定するRPCリクエストを送信してサーバよりレスポンスが返ってきたときに呼ばれるコールバック関数。RPC クライアントからのリクエストに対するサーバからのレスポンス結果を表示している。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span><br />
&nbsp;GetUserCallback<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> evrpc_status <span style="color: #339933;">*</span>status<span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #993333;">struct</span> GetUserRequest <span style="color: #339933;">*</span>req<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> GetUserResponse <span style="color: #339933;">*</span>res<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>arg<span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; uint32_t errcode<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>name<span style="color: #339933;">,*</span>email<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* check return status */</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>status<span style="color: #339933;">-&gt;</span>error <span style="color: #339933;">!=</span> EVRPC_STATUS_ERR_NONE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">goto</span> exitloop<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">/* get response info */</span><br />
&nbsp; &nbsp; EVTAG_GET<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> errcode<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>errcode<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;RESPONSE errcode=%d<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> errcode<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>EVTAG_HAS<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> name<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; EVTAG_GET<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> name<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>name<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;RESPONSE name=%s<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> name<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>EVTAG_HAS<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> email<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; EVTAG_GET<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> email<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>email<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fprintf<span style="color: #009900;">&#40;</span>stdout<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;RESPONSE email=%s<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> email<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
exitloop<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; event_loopexit<span style="color: #009900;">&#40;</span>NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>
<h2><strong>ダウンロード、コンパイル、動作確認</strong></h2>
<p>git clone でソースコードを取得してmakeを行う。 リポジトリanyのサブディレクトリ any/libevent_rpcが今回のサンプルにあたる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ git clone git@github.com:yokawasa/any.git &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
<br />
Initialized empty Git repository in /home/m/dev/github/any/.git/<br />
remote: Counting objects: 33, done.<br />
remote: Compressing objects: 100% (32/32), done.<br />
remote: Total 33 (delta 7), reused 0 (delta 0)<br />
Receiving objects: 100% (33/33), 11.28 KiB, done.<br />
Resolving deltas: 100% (7/7), done.<br />
<br />
$ cd any/libevent_rpc<br />
$ make<br />
<br />
g++ -I/usr/include -I/usr/local/include -Wall -g -o simple_rpc_client.o -c simple_rpc_client.cpp<br />
gcc -I/usr/include -I/usr/local/include -Wall -g -o simple.gen.o -c simple.gen.c<br />
g++ -L/usr/local/lib -levent -o simple_rpc_client simple_rpc_client.o simple.gen.o<br />
g++ -I/usr/include -I/usr/local/include -Wall -g -o simple_rpc_server.o -c simple_rpc_server.cpp<br />
g++ -L/usr/local/lib -levent -o simple_rpc_server simple_rpc_server.o simple.gen.o</div></div>
<p>これで同ディレクトリにRPCサーバsimple_rpc_server とRPCクライアントsimple_rpc_client実行ファイルのできあがり。simple_rpc_server 、simple_rpc_clientともに実行時に第一引数にサーバアドレス、第二引数にIOポートを指定する。 まずサーバから立ち上げる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./simple_rpc_server localhost 8888</div></div>
<p>サーバが立ち上がったら次のようにクライアントからリクエスト送信する。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ ./simple_rpc_client localhost 8888<br />
RESPONSE errcode=0<br />
RESPONSE name=Fooo Baaar<br />
RESPONSE email=baz@foo.bar</div></div>
<p>上のような結果が出力されればOK。</p>
<p>おわり。</p>
]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/06/05/libevent_rpc_cs_programmin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>cURL MultiインターフェースでHTTP Pipeliningリクエストの送信</title>
		<link>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/</link>
		<comments>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 14:54:54 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming / プログラミング]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[keep-alive]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[pipelining]]></category>
		<category><![CDATA[tcpdump]]></category>
		<category><![CDATA[tcpip]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=303</guid>
		<description><![CDATA[cURLのC APIマニュアルを読んでいたらCURLMOPT_PIPELININGというおもしろそうなオプションを見つけた。これはlibcurl 7.16.0 より加わった並列実行用Multiインターフェースのオプションで、設定することでHTTP Pipeliningなリクエストが送信できるようになる。HTTP Pipeliningとは個々のレスポンスを待つことなく複数のリクエストを投げることを意味するHTTP/1.1よりサポートされた通信パフォーマンス向上のためのテクニックである。
通常N個のリクエストを処理する際はN個ソケットがオープンされOPEN → REQUEST → RESPONSE → CLOSEなサイクルがN回行われる。Multiインターフェースによる並列処理の場合はOPEN → REQUEST → RESPONSE → CLOSEが並列に行われる。 またこれがkeep-aliveな接続であればOPEN → (REQUEST → RESPONSE) x N → CLOSEのようにクローズされるまで１ソケットが再利用される。 そしてHTTP Pipeliningはkeep-aliveな接続で使うテクニックであり、これが有効な場合は1ソケットオープン後にOPEN → (REQUEST x N) → (RESPONSE x N) → CLOSEのようにリクエストN個をレスポンスを待つことなく1ソケットに書き込むことができる。 まとめて送信される分パケット効率がアップし全体的なネットワークを流れるパケットの数を減らすことができ、 さらにまとめてリクエスト送信するので高レイテンシーなネットワークにおいては速度面で効果的といえる。 see also 「Mozilla HTTP/1.1 パイプライン化 FAQ」。

(1) NOT keep-alive, single      [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://curl.haxx.se/">cURL</a>の<a href="http://curl.haxx.se/libcurl/">C APIマニュアル</a>を読んでいたら<a href="http://curl.haxx.se/libcurl/c/curl_multi_setopt.html">CURLMOPT_PIPELINING</a>というおもしろそうなオプションを見つけた。これはlibcurl 7.16.0 より加わった並列実行用Multiインターフェースのオプションで、設定することで<a href="http://en.wikipedia.org/wiki/HTTP_pipelining">HTTP Pipelining</a>なリクエストが送信できるようになる。HTTP Pipeliningとは個々のレスポンスを待つことなく複数のリクエストを投げることを意味する<a href="http://www.ietf.org/rfc/rfc2616.txt">HTTP/1.1</a>よりサポートされた通信パフォーマンス向上のためのテクニックである。</p>
<p>通常N個のリクエストを処理する際はN個ソケットがオープンされOPEN → REQUEST → RESPONSE → CLOSEなサイクルがN回行われる。Multiインターフェースによる並列処理の場合はOPEN → REQUEST → RESPONSE → CLOSEが並列に行われる。 またこれがkeep-aliveな接続であればOPEN → (REQUEST → RESPONSE) x N → CLOSEのようにクローズされるまで１ソケットが再利用される。 そしてHTTP Pipeliningはkeep-aliveな接続で使うテクニックであり、これが有効な場合は1ソケットオープン後にOPEN → (REQUEST x N) → (RESPONSE x N) → CLOSEのようにリクエストN個をレスポンスを待つことなく1ソケットに書き込むことができる。 まとめて送信される分パケット効率がアップし全体的なネットワークを流れるパケットの数を減らすことができ、 さらにまとめてリクエスト送信するので高レイテンシーなネットワークにおいては速度面で効果的といえる。 see also 「<a href="https://developer.mozilla.org/ja/HTTP_Pipelining_FAQ">Mozilla HTTP/1.1 パイプライン化 FAQ</a>」。</p>
<pre>
(1) NOT keep-alive, single         (OPEN → REQUEST → RESPONSE → CLOSE) x N
(2) NOT keep-alive, multi          (OPEN → REQUEST → RESPONSE → CLOSE) をN並列で行う
(3) keep-alive                     OPEN → (REQUEST → RESPONSE) x N → CLOSE
(4) keep-alive, Pipelining         OPEN → (REQUEST x N) → (RESPONSE x N) → CLOSE
</pre>
<p>というわけでいつものようにテストプログラムを作ってMultiインターフェースによる並列処理をHTTP Pipeliningありとなしで実行してみる。<br />
(環境 libcurl-7.19.5、httpd-2.2.2 on Debian-5.0.1 )</p>
<p><h2><strong>テストツールとそのコンパイル</strong></h2>
<p>cURL CAPIのMultiインターフェースを使って複数URLからfetchしてくるツールを作成した。</p>
<p><a href="http://github.com/yokawasa/any/blob/master/libcurl/multi_fetch.cpp">http://github.com/yokawasa/any/blob/master/libcurl/multi_fetch.cpp</a></p>
<p>標準的なcurl Multiインターフェースを使ったコードであるがポイントは次の部分。各リクエスト用のCURLハンドルでソケットの送信待ち時間を最小限にする<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html">CURLOPT_TCP_NODELAY</a>オプション（詳しくは「<a href="http://www.ibm.com/developerworks/jp/linux/library/l-hisock/index.html">Linuxにおけるソケット機能の向上</a>」を参照ください）と挙動把握のためにlibcurlのINFO情報を出力する<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html">CURLOPT_VERBOSE</a>オプションを有効にしている。またHTTP Pipeliningモード指定のときにMulti用ハンドルで<a href="http://curl.haxx.se/libcurl/c/curl_multi_setopt.html">CURLMOPT_PIPELINING</a>オプションを有効にしている。　以下該当箇所のコード断片。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span>vector<span style="color: #339933;">&lt;</span>string<span style="color: #339933;">&gt;::</span><span style="color: #202020;">iterator</span> it<span style="color: #339933;">=</span>urls.<span style="color: #202020;">begin</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; it<span style="color: #339933;">!=</span>urls.<span style="color: #202020;">end</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #339933;">++</span>it<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_URL<span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>it<span style="color: #009900;">&#41;</span>.<span style="color: #202020;">c_str</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_TCP_NODELAY<span style="color: #339933;">,</span> 1L<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_VERBOSE<span style="color: #339933;">,</span> 1L<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_multi_add_handle<span style="color: #009900;">&#40;</span>m_handle<span style="color: #339933;">,</span> c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; i<span style="color: #339933;">++;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pipelining<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; curl_multi_setopt<span style="color: #009900;">&#40;</span>m_handle<span style="color: #339933;">,</span> CURLMOPT_PIPELINING<span style="color: #339933;">,</span> 1L<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
...</div></div>
<p>上記URLのソースコードを取得して次のようにコンパイルを行う。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">g++ multi_fetch.cpp -o multi_fetch -I/usr/include -L/usr/lib -lcurl</div></div>
<p>使い方は次のように複数URLの書かれたファイルを指定する。-pオプションを加えてやることでHTTP Pipeliningモードでリクエスト送信を行う。 これはオプショナル。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Usage: ./multi_fetch &lt;options&gt;<br />
Options: -f file &nbsp;URL list file<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-p &nbsp; &nbsp; &nbsp; HTTP pipelining mode (optional)<br />
&nbsp;<br />
例) urls.txtに書かれた複数URLからHTTP Pipeliningモードでfetch<br />
$ ./multi_fetch -f urls.txt -p</div></div>
<p><h2><strong>NON HTTP Pipelining リクエスト送信</strong></h2>
<p>テスト用にホストfooとWebサーバ（apache）のあるホストbarを用意する。まずはHTTP Pipeliningオプションをはずした通常のmultiインターフェースによる並列実行を行う。リクエスト用CURLハンドルでCURLOPT_VERBOSEオプションが有効になっているので実行結果にlibcurlのINFO 情報も一緒に出力される。出力内容を全てここに貼り付けるには量が多すぎるので内容を簡略化してHTTP接続部分と各リクエストとそのレスポンスの最初の一行だけに絞る。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat urls.txt<br />
http://bar.yk55.com/test1.html<br />
http://bar.yk55.com/test2.html<br />
http://bar.yk55.com/test3.html<br />
http://bar.yk55.com/test4.html<br />
<br />
$ ./multi_fetch -f urls.txt &nbsp;2&gt;&amp;1 |tee /tmp/nonpipelined.out</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[出力結果]<br />
$ cat /tmp/nonpipelined.out<br />
<br />
* About to connect() to bar.yk55.com port 80 (#0) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* About to connect() to bar.yk55.com port 80 (#1)<br />
* About to connect() to bar.yk55.com port 80 (#2)<br />
* About to connect() to bar.yk55.com port 80 (#3)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#0)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#1)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#2)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#3)<br />
&gt; GET /test1.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test2.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test3.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test4.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #1 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #2 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #3 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #0 to host bar.yk55.com left intact</div></div>
<p>これを見ると並列にConnection #[0-3]の4つのソケットがオープンしてそれぞれにリクエスト、レスポンスが書き出され終了していることが分かる。 「(2)NOT keep-alive, multi」のパターンになっているといえる。想定どおり。</p>
<p><h2><strong>HTTP Pipeliningリクエスト送信</strong></h2>
<p>次に前テストと同様にホストfooからホストbar(Webサーバ)の4ファイル対してにHTTP Pipeliningなリクエストを送信してみる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat urls.txt<br />
http://bar.yk55.com/test1.html<br />
http://bar.yk55.com/test2.html<br />
http://bar.yk55.com/test3.html<br />
http://bar.yk55.com/test4.html<br />
&nbsp;<br />
$ ./multi_fetch -f urls.txt -p　 2&gt;&amp;1 |tee /tmp/pipelined.out</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[出力結果]<br />
$ cat /tmp/pipelined.out<br />
<br />
* About to connect() to bar.yk55.com port 80 (#0)<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#0) <br />
&gt; GET /test1.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test2.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test3.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test4.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
* Connection #0 to host bar.yk55.com left intact</div></div>
<p>出力結果から、1つのソケットオープン(Connection #0)後に他のリクエスト達はConnectoin#0を 再利用しているのが分かる。 またtest1.htmlをGETするためのリクエスト送信後に「HTTP/1.1 200 OK&#8230;.」とレスポンスを受け、次にtest2.html～test4.html GETの3リクエストがレスポンスを待つことなく連続で送られ、その後それらのレスポンスを受けている。 「(4) keep-alive, Pipelining」のパターンになると予想していたのだが１つ目のリクエスト（REQUEST-a）だけがPipeliningではない。 試しにリクエスト数を増やしてみても同じ、１つ目のリクエストがPipeliningではなく、2番目以降のリクエストはPipeliningになっている。 今一理由がわからない。 ひょっとして実際の処理とは別にログ出力に問題があるのではないかと思い<a href="http://www.linux.or.jp/JM/html/tcpdump/man1/tcpdump.1.html">tcpdump</a>で実際のTCPパケットのやりとり確認してみる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ sudo tcpdump -lX -s 1024 -i eth0 port 80 |tee /tmp/tcpdump.txt<br />
$ cat /tmp/tcpdump.txt<br />
<br />
*** SYN: Socketオープン<br />
01:27:14.155974 IP foo.43242 &gt; bar.http: S 3856091341:3856091341(0) win 5840 &lt;mss 1460,sackOK,timestamp 31004352 0,nop,wscale 6&gt;<br />
01:27:14.158135 IP bar.http &gt; foo.43242: S 2366776094:2366776094(0) ack 3856091342 win 5792 &lt;mss 1460,sackOK,timestamp 224898337 31004352,nop,wscale 2&gt;<br />
01:27:14.158197 IP foo.43242 &gt; bar.http: . ack 1 win 92 &lt;nop,nop,timestamp 31004352 224898337&gt;<br />
<br />
*** PUSH: 1stリクエスト<br />
01:27:14.156012 IP foo.43242 &gt; bar.http: P 1:63(62) ack 1 win 92 &lt;nop,nop,timestamp 31004352 224898337&gt;<br />
01:27:14.156080 IP bar.http &gt; foo.43242: . ack 63 win 1448 &lt;nop,nop,timestamp 224898337 31004352&gt;<br />
<br />
*** PUSH: 1stレスポンス<br />
01:27:14.158768 IP bar.http &gt; foo.43242: P 1:198(197) ack 63 win 1448 &lt;nop,nop,timestamp 224898340 31004352&gt;<br />
01:27:14.158930 IP foo.43242 &gt; bar.http: . ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
<br />
*** PUSH: 2nd, 3rd, 4thリクエスト<br />
01:27:14.159183 IP foo.43242 &gt; bar.http: P 63:125(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
01:27:14.159277 IP foo.43242 &gt; bar.http: P 125:187(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
01:27:14.159361 IP foo.43242 &gt; bar.http: P 187:249(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
<br />
*** PUSH: &nbsp;2nd, 3rd, 4thまとめてレスポンス<br />
01:27:14.163514 IP bar.http &gt; foo.43242: P 198:789(591) ack 249 win 1448 &lt;nop,nop,timestamp 224898344 31004353&gt;<br />
<br />
*** FIN: &nbsp;Socket クローズ<br />
01:27:14.164128 IP foo.43242 &gt; bar.http: F 249:249(0) ack 789 win 127 &lt;nop,nop,timestamp 31004354 224898344&gt;<br />
01:27:14.164634 IP bar.http &gt; foo.43242: F 789:789(0) ack 250 win 1448 &lt;nop,nop,timestamp 224898345 31004354&gt;<br />
01:27:14.164780 IP foo.43242 &gt; bar.http: . ack 790 win 127 &lt;nop,nop,timestamp 31004354 224898345&gt;</div></div>
<p>tcpdumpの結果も変わらず同じ。 ログを見る限りオープン処理（SYNフラグの出力部分、いわゆる3way handshake）が行われた後、１つ目のリクエスト直後にレスポンスを受け、その後2番目以降のリクエストはレスポンスを待つことなくソケット書き込み、つまりPipeliningリクエスト送受信が行われている。　どうして１つ目だけがダメで、２つ目以降からうまくいくのだろう？？ (T . T)</p>
<p>細かくlibcurlのコードを追えばよいのだろうがこれ以上調査する気持ちにならないので取り敢えずここで終えておく。 続きはいつか。</p>
<p>おわり。</p>
]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>簡単Boehm GCによるC/C++メモリリーク検知</title>
		<link>http://yk55.com/blog/2010/03/25/boehm_gc_detect_memory_leak/</link>
		<comments>http://yk55.com/blog/2010/03/25/boehm_gc_detect_memory_leak/#comments</comments>
		<pubDate>Thu, 25 Mar 2010 14:37:54 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming / プログラミング]]></category>
		<category><![CDATA[boehm]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[gc]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=269</guid>
		<description><![CDATA[Boehm GCはガベージコレクションの標準実装がないC/C++でガベージコレクションのようなことを実現可能にしてくれるライブラリ。 一度確保されたリソースは明示的に解放処理が行われない限りメモリリークが発生してしまうけれどもBoehm GCで用意されている関数群（通常はGC_MALLOC）を使ってリソースを確保すれば不要になった段階で自動的に解放してくれる。 ソースコード全体で統一してこのライブラリを利用していればオブジェクトの寿命管理の手間は減るだろうし自分がコードの責任者であるならば難しいところを枯れたライブラリにお任せできるので安心感はあるのだろう。ちなみにこのライブラリ、以前からその存在は知ってはいたけれどスマートポインタ派の自分としてはオブジェクトの寿命管理のためにこれを使うモチベーションはいまいち感じられない。
そんなBoehm GCは「Using the Garbage Collector as Leak Detector」で紹介されているようにメモリリーク検知用ユーティリティとしても使える。ドキュメントやソースコードを覗いてみて少し工夫すれば手軽に既存のコードに殆ど手を加えること無くメモリリークの検知ができそうだったので試しにサンプルコードを書いてみた。以下、Boehm GCのインストールからC/C++コードでのメモリリーク検知について。
Boehm GCインストール
まずは自分の環境(debina5.0.1&#8243;lenny&#8221;)にBoehm GCをインストールしてみる。 Boehm GCライブラリをつかって開発するにはlibgc-devとlibgc1c2が必要。もし自分の環境に合うパッケージが存在しない場合は直接ここから tar.gzボールをダウンロードして例のconfigure、make、make install。
$ sudo apt-get install libgc-dev
$ sudo apt-get install libgc1c2
$ dpkg --list &#124;grep libgc
ii &#160;libgc-dev &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; 1:6.8-1.1 &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;conservative garbage collector for [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm GC</a>はガベージコレクションの標準実装がないC/C++でガベージコレクションのようなことを実現可能にしてくれるライブラリ。 一度確保されたリソースは明示的に解放処理が行われない限りメモリリークが発生してしまうけれどもBoehm GCで用意されている関数群（通常はGC_MALLOC）を使ってリソースを確保すれば不要になった段階で自動的に解放してくれる。 ソースコード全体で統一してこのライブラリを利用していればオブジェクトの寿命管理の手間は減るだろうし自分がコードの責任者であるならば難しいところを枯れたライブラリにお任せできるので安心感はあるのだろう。ちなみにこのライブラリ、以前からその存在は知ってはいたけれどスマートポインタ派の自分としてはオブジェクトの寿命管理のためにこれを使うモチベーションはいまいち感じられない。</p>
<p>そんなBoehm GCは「<a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/leak.html">Using the Garbage Collector as Leak Detector</a>」で紹介されているようにメモリリーク検知用ユーティリティとしても使える。ドキュメントやソースコードを覗いてみて少し工夫すれば手軽に既存のコードに殆ど手を加えること無くメモリリークの検知ができそうだったので試しにサンプルコードを書いてみた。以下、Boehm GCのインストールからC/C++コードでのメモリリーク検知について。</p>
<h2><strong>Boehm GCインストール</strong></h2>
<p>まずは自分の環境(debina5.0.1&#8243;lenny&#8221;)にBoehm GCをインストールしてみる。 Boehm GCライブラリをつかって開発するには<a href="http://packages.debian.org/lenny/libgc-dev">libgc-dev</a>と<a href="http://packages.debian.org/lenny/libgc1c2">libgc1c2</a>が必要。もし自分の環境に合うパッケージが存在しない場合は直接<a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/">ここ</a>から tar.gzボールをダウンロードして例のconfigure、make、make install。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ sudo apt-get install libgc-dev<br />
$ sudo apt-get install libgc1c2<br />
$ dpkg --list |grep libgc<br />
ii &nbsp;libgc-dev &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1:6.8-1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;conservative garbage collector for C (develo<br />
ii &nbsp;libgc1c2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1:6.8-1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;conservative garbage collector for C and C++</div></div>
<p><h2><strong>Cコードメモリリーク検知 &#8211; *alloc/freeをGC_関数に置換</strong></h2>
<p>Boehm GCにはGC_MALLOC等のリソース確保系関数でリソース確保されたにもかかわらずGC_FREE等の解放系関数でリソース解放されずにリークしてしまっているオブジェクトを検知してそれらをレポートする機能がある。 このメモリリーク検知レポートはGC_gcollect関数で実行される。ただしこのときメモリリーク検知レポート機能をオンにするためにはそもそもBoehm GC を-DFIND_LEAKオプションでコンパイルするか、または実行時にダイナミックにGC_find_leak=1をセットしてやる必要がある。</p>
<ul>
<li>対象オブジェクト: GC_MALLOC等でリソース確保されたにもかかわらずGC_FREE等で解放されないオブジェクト</li>
<li>検知レポート実行関数： GC_gcollect</li>
<li>前提条件： -DFIND_LEAKオプションでコンパイルされたBoehmGC or 実行時にGC_find_leak=1をセット</li>
</ul>
<p>
「<a href="http：//www.hpl.hp.com/personal/Hans_Boehm/gc/leak.html">Using the Garbage Collector as Leak Detector</a>」ではこのメモリリーク検知レポート機能を利用して実際のCコードのメモリリークを検知するために直接ヒープからメモリ確保、解放を行うための基本関数をBoehmGCの関数で置き換えるためのヘッダを用意している。</p>
<p><span style="text-decoration: underline;">gc_detect_leaks.h</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#define GC_DEBUG</span><br />
<span style="color: #339933;">#include &quot;gc.h&quot;</span><br />
<span style="color: #339933;">#define malloc(n) GC_MALLOC(n)</span><br />
<span style="color: #339933;">#define calloc(m,n) GC_MALLOC((m)*(n))</span><br />
<span style="color: #339933;">#define free(p) GC_FREE(p)</span><br />
<span style="color: #339933;">#define realloc(p,n) GC_REALLOC((p),(n))</span><br />
<span style="color: #339933;">#define CHECK_LEAKS() GC_gcollect()</span></div></div>
<p>試しにメモリリークを引き起こすコードに上記ヘッダをインクルードしたサンプルコード(t1.c)を用意してコンパイル+ 実行してみる。<br />
<span style="text-decoration: underline;">サンプルコード: t1.c</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#include &lt;stdio.h&gt;</span><br />
<span style="color: #339933;">#include &quot;gc_detect_leaks.h&quot;</span><br />
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; GC_find_leak <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>a<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span>1<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; a <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span>malloc<span style="color: #009900;">&#40;</span>100<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>　　<span style="color: #666666; font-style: italic;">// t1.c: line7</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">//free(a);</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CHECK_LEAKS<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[コンパイルと実行結果]<br />
$ gcc -g -Wall t1.c -o t1 -I/usr/include/gc -L/usr/lib -lgc<br />
$ ./t1<br />
Leaked composite object at 0x805af90 (t1.c:7, sz=100, NORMAL)<br />
Leaked composite object at 0x805af10 (t1.c:7, sz=100, NORMAL)<br />
Leaked composite object at 0x805af90 (t1.c:7, sz=100, NORMAL)<br />
...</div></div>
<p>無事メモリリークを検知。メモリリークの原因となっている箇所（t1.cの７行目）も特定されているので合格。ちなみに上記ヘッダでGC_DEBUGをdefineしているは理由は<a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gch.txt">gc.h</a>で定義されているGC_MALLOCをデバック用出力にモードにするため。以下gc.hの該当部分だけを略して抜粋。</p>
<p><span style="text-decoration: underline;">gc6.8 &#8211; /usr/local/gc/gc.h</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#ifdef GC_ADD_CALLER</span><br />
<span style="color: #339933;"># &nbsp;define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__</span><br />
...略...<br />
<span style="color: #339933;">#else</span><br />
<span style="color: #339933;"># &nbsp;define GC_EXTRAS __FILE__, __LINE__</span><br />
...略...<br />
<span style="color: #339933;">#endif</span><br />
<br />
<span style="color: #339933;"># ifdef GC_DEBUG</span><br />
<span style="color: #339933;"># &nbsp; define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS)</span><br />
... 略...<br />
<span style="color: #339933;"># else</span><br />
<span style="color: #339933;"># &nbsp; define GC_MALLOC(sz) GC_malloc(sz)</span><br />
...略...<br />
<span style="color: #339933;"># endif</span></div></div>
<p><h2><strong>C++ コードメモリリーク検知 &#8211; gc/gc_cleanupクラスで継承</strong></h2>
<p>次にC++コードのメモリリーク検知を行う。C++のデータ構造の基本はクラス（細かいことを省けば構造体もクラス）でありクラスはnewオペレーターでメモリを動的に確保する。 当然のことながらデフォルトのnewオペレータでリソース確保した場合はGC回収不能であるためBoehm GCが用意しているC++用ライブラリを利用する。<a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/gc_cpph.txt">gc_cpp.h</a>をみるとこれは単なるCライブラリのラップクラス。以下のようにgcやgc_cleanupクラスをベースクラスとすることでGC回収可能なオブジェクトとして扱うことができる。</p>
<p><span style="text-decoration: underline;">サンプルコード: t2.cpp</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#include &lt;stdio.h&gt;</span><br />
<span style="color: #339933;">#define GC_DEBUG</span><br />
<span style="color: #339933;">#include &quot;gc_cpp.h&quot;</span><br />
<span style="color: #339933;">#define CHECK_LEAKS() GC_gcollect()</span><br />
class Foo<span style="color: #339933;">:</span> public gc<span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; GC_find_leak <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; Foo <span style="color: #339933;">*</span>f<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span>1<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f <span style="color: #339933;">=</span>new Foo<span style="color: #339933;">;</span>　　　<span style="color: #666666; font-style: italic;">// t2.cpp: line10</span><br />
<span style="color: #666666; font-style: italic;">// &nbsp; &nbsp; &nbsp; &nbsp;delete f;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CHECK_LEAKS<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[コンパイルと実行結果]<br />
$ g++ -g -Wall t2.cpp -o t2 -I/usr/include/gc -L/usr/lib -lgc<br />
$ ./t2<br />
Leaked composite object at 0x805afe8 (/usr/include/gc/gc_cpp.h:274, sz=1, NORMAL)<br />
Leaked composite object at 0x805afd0 (/usr/include/gc/gc_cpp.h:274, sz=1, NORMAL)<br />
Leaked composite object at 0x805afe8 (/usr/include/gc/gc_cpp.h:274, sz=1, NORMAL)<br />
....</div></div>
<p>これも無事メモリリーク検知はされている。ただしリーク箇所が/usr/include/gc/gc_cpp.hのL274行目を指している。これではリークの原因箇所がわからない。できればt2.cppのL10行目(f=new Foo)を指してもらいたい。さらにこの方法では単に既存のコードのメモリーリーク検知のみを行いたい場合でも、クラスをgc やgc_cleanupクラスで継承させるといったコードに手を加えていく作業が必要があるのでとても面倒でありコード量が多ければ多いほど手間がかかる。</p>
<p><h2><strong>C++コードメモリリーク検知 &#8211; new/deleteオペレータのオーバーロード</strong></h2>
<p>そこで既存のコードにあまり手を加えなくてもメモリリーク検知ができ、且つリークの原因箇所を特定できる方法を考えてみた。まずは既存のコードの手を加えないでメモリリーク検知する方法。クラスはリソースの確保、解放をそれぞれnew、 deleteオペレータで行うのでこれらをグローバルにオーバーロードしてやり内部のリソース確保、解放のための実装をGC関数で置き換えてやる。そしてそのヘッダを既存のコードにインクルードしてやれば既存のコードに手を加えないでメモリーリークの検知ができるのではないかと。 ヘッダは以下のような感じ。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#define GC_DEBUG</span><br />
<span style="color: #339933;">#include &quot;gc.h&quot;</span><br />
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> operator new<span style="color: #009900;">&#40;</span>size_t size<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> GC_MALLOC<span style="color: #009900;">&#40;</span>size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> operator new<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#40;</span>size_t size<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> GC_MALLOC<span style="color: #009900;">&#40;</span>size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span> operator delete<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*</span> p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> GC_FREE<span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span> &nbsp;operator delete<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*</span> p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> GC_FREE<span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>ただしこれだけではメモリリークの検知はできてもリークの原因箇所の特定が難しい。このままだと上記ヘッダのGC_MALLOC()コール箇所がリーク箇所としてレポートされてしまう。 理想はnewオペレータをコールしている箇所がレポートされて欲しい。ということで次のように GC_debug_malloc()にnewオペレータコール箇所のファイル名と行を渡すように変更してみる。</p>
<p><span style="text-decoration: underline;">gc_detect_leaks.h</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#define GC_DEBUG</span><br />
<span style="color: #339933;">#include &quot;gc.h&quot;</span><br />
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> operator new<span style="color: #009900;">&#40;</span>size_t size<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span> s<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> i<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> GC_debug_malloc<span style="color: #009900;">&#40;</span>size<span style="color: #339933;">,</span>s<span style="color: #339933;">,</span>i<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span><span style="color: #339933;">*</span> operator new<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#40;</span>size_t size<span style="color: #339933;">,</span> &nbsp;<span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span> s<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> i<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> GC_debug_malloc<span style="color: #009900;">&#40;</span>size<span style="color: #339933;">,</span>s<span style="color: #339933;">,</span>i<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span> operator delete<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*</span> p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> GC_debug_free<span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #993333;">void</span> &nbsp;operator delete<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #339933;">*</span> p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span> GC_debug_free<span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #339933;"># &nbsp; define new new(__FILE__, __LINE__)</span><br />
<span style="color: #339933;"># &nbsp; define CHECK_LEAKS() GC_gcollect()</span><br />
<span style="color: #993333;">int</span> GC_find_leak <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span></div></div>
<p>ためしにメモリリークを引き起こすC++コードに上記ヘッダをインクルードしたものを用意してコンパイル+ 実行してみる。今度はヘッダにint GC_find_leak = 1を含めているのでサンプルコードにGC_find_leak = 1をセットする必要はない。</p>
<p><span style="text-decoration: underline;">サンプルコード: t3.cpp</span></p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#include &lt;stdio.h&gt;</span><br />
<span style="color: #339933;">#include &quot;gc_detect_leaks.h&quot;</span><br />
class Foo<span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; Foo <span style="color: #339933;">*</span>f<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span>1<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f <span style="color: #339933;">=</span>new Foo<span style="color: #339933;">;</span> &nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// t3.cpp: line7</span><br />
<span style="color: #666666; font-style: italic;">// &nbsp; &nbsp; &nbsp; &nbsp;delete f;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CHECK_LEAKS<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[コンパイルと実行結果]<br />
$ g++ -g -Wall t3.cpp -o t3 -I/usr/include/gc -L/usr/lib -lgc<br />
$ ./t3<br />
Leaked composite object at 0x805afe8 (t3.cpp:7, sz=1, NORMAL)<br />
Leaked composite object at 0x805afd0 (t3.cpp:7, sz=1, NORMAL)<br />
Leaked composite object at 0x805afe8 (t3.cpp:7, sz=1, NORMAL)<br />
...</div></div>
<p>見事メモリリークを検知しnewオペレータをコールしている行がレポートされた。というわけでこのヘッダをインクルードするだけで他の既存のコードに手を加えることなくメモリリークを検知することができた。</p>
<p>最後に、今回使ったサンプルコードとここで紹介したヘッダを少し体裁を整えて<a href="http://github.com/yokawasa/any/tree/master/boehmgc/">github</a>にあげておきました。ちなみにgithubにあげたgc_detect_leaks.hを使う際は-DGC_DETECT_MEM_LEAKでコンパイルすることをお忘れなく。</p>
<p><a href="http://github.com/yokawasa/any/tree/master/boehmgc/">http://github.com/yokawasa/any/tree/master/boehmgc/</a></p>
<p>おわり</p>
]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/03/25/boehm_gc_detect_memory_leak/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dynamic Binding &amp; VTable Concept in C++</title>
		<link>http://yk55.com/blog/2010/02/27/dynamic-binding-vtable-concept/</link>
		<comments>http://yk55.com/blog/2010/02/27/dynamic-binding-vtable-concept/#comments</comments>
		<pubDate>Sat, 27 Feb 2010 10:32:11 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming / プログラミング]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[polymorphism]]></category>
		<category><![CDATA[vpointer]]></category>
		<category><![CDATA[vtable]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=197</guid>
		<description><![CDATA[たまにc++コードのコンパイル時にエラー文言でvtableというキーワードを見たことはないだろうか？Polymorphismという有名なワードに対しこのvtableはあまりにも目立たない存在だ。とはいえPolymorphismを実現するためになくてはならない（最）重要なポジションを占めているのがvtable。別にvtableを理解しなくともPolymorphismの理解はできる。 ただし、骨太になりたいのならばPolymorphismを実現するためにどのようにvtableが使われているのかを理解しておくべきである。
UPCASTING
Virtual関数との比較のためにVirtual関数を持たないクラス継承を説明する。以下のようにvirtual関数を持たないSuperClassをSubClassが継承する。 SuperClassを継承したSubClassの参照をSuperClassポインタ変数に入れた場合は、子クラスへの参照がUPCASTされ親クラスへの参照として扱われる。実行結果はSuperClassへの参照へとUPCASTされるのでSuperClassの関数の内容が表示される。
#include &#60;iostream&#62;
using std::cout;
using std::endl;
class SuperClass &#123;
public:
&#160; &#160; void func&#40;&#41; &#123; cout &#60;&#60; &#34;SuperClass::func()&#34; &#60;&#60; endl; &#125;
&#125;;
class SubClass : public SuperClass &#123;
public:
&#160; &#160; void func&#40;&#41; &#123; cout &#60;&#60; &#34;SubClass::func()&#34; &#60;&#60; endl; &#125;
&#125;;
int main&#40;int argc, char **argv &#41; &#123;
&#160; &#160; &#160;SubClass sub;
&#160; &#160; &#160;SuperClass *super =&#38;sub; 
&#160; &#160; &#160;super-&#62;func&#40;&#41;;
&#160; &#160; &#160;return 0;
&#125;
[実行結果]
SuperClass::func()
VIRTUAL FUNCTION, VTABLE, and [...]]]></description>
			<content:encoded><![CDATA[<p>たまにc++コードのコンパイル時にエラー文言でvtableというキーワードを見たことはないだろうか？Polymorphismという有名なワードに対しこのvtableはあまりにも目立たない存在だ。とはいえPolymorphismを実現するためになくてはならない（最）重要なポジションを占めているのがvtable。別にvtableを理解しなくともPolymorphismの理解はできる。 ただし、骨太になりたいのならばPolymorphismを実現するためにどのようにvtableが使われているのかを理解しておくべきである。</p>
<h2>UPCASTING</h2>
<p>Virtual関数との比較のためにVirtual関数を持たないクラス継承を説明する。以下のようにvirtual関数を持たないSuperClassをSubClassが継承する。 SuperClassを継承したSubClassの参照をSuperClassポインタ変数に入れた場合は、子クラスへの参照がUPCASTされ親クラスへの参照として扱われる。実行結果はSuperClassへの参照へとUPCASTされるのでSuperClassの関数の内容が表示される。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#include &lt;iostream&gt;</span><br />
using std<span style="color: #339933;">::</span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a><span style="color: #339933;">;</span><br />
using std<span style="color: #339933;">::</span><span style="color: #202020;">endl</span><span style="color: #339933;">;</span><br />
class SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> func<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SuperClass::func()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
class SubClass <span style="color: #339933;">:</span> public SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> func<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SubClass::func()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">**</span>argv <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp;SubClass sub<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp;SuperClass <span style="color: #339933;">*</span>super <span style="color: #339933;">=&amp;</span>sub<span style="color: #339933;">;</span> <br />
&nbsp; &nbsp; &nbsp;super<span style="color: #339933;">-&gt;</span>func<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[実行結果]<br />
SuperClass::func()</div></div>
<p><h2>VIRTUAL FUNCTION, VTABLE, and VPOINTER</h2>
<p>次にVirtual関数の例を説明する。さきほどの例ではUPCASTによりSubClassクラスではなくSuperClassクラスのfunc関数がcallされた。ここではSuperClassの関数にvirtualをつけた場合のfunc関数の振る舞いを確かめてみる。virtual関数func1、func2をもつSuperClassを継承した3つのSubClassを用意する。これら3つはそれぞれfunc1、func2両方とも、func1のみ、func2のみをoverrideしている。 実行結果はSubClassでvirtual関数をoverrideした場合はその内容が、そうでない場合はSuperClassの内容が表示される。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339933;">#include &lt;iostream&gt;</span><br />
using std<span style="color: #339933;">::</span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a><span style="color: #339933;">;</span><br />
using std<span style="color: #339933;">::</span><span style="color: #202020;">endl</span><span style="color: #339933;">;</span><br />
class SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; virtual <span style="color: #993333;">void</span> funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SuperClass::funcA()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; virtual <span style="color: #993333;">void</span> funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SuperClass::funcB()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
class SubClass1 <span style="color: #339933;">:</span> public SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SubClass1::funcA()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SubClass1::funcB()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
class SubClass2 <span style="color: #339933;">:</span> public SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SubClass2::funcA()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
class SubClass3 <span style="color: #339933;">:</span> public SuperClass <span style="color: #009900;">&#123;</span><br />
public<span style="color: #339933;">:</span><br />
&nbsp; &nbsp; <span style="color: #993333;">void</span> funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;SubClass2::funcB()&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span><br />
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">**</span>argv <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; SubClass1 sub1<span style="color: #339933;">;</span> SubClass2 sub2<span style="color: #339933;">;</span> SubClass3 sub3<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> &nbsp;<span style="color: #ff0000;">&quot;***************SubClass1***************&quot;</span> <span style="color: #339933;">&lt;&lt;</span> &nbsp;endl<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; SuperClass <span style="color: #339933;">*</span>super <span style="color: #339933;">=&amp;</span>sub1<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; super<span style="color: #339933;">-&gt;</span>funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> super<span style="color: #339933;">-&gt;</span>funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;***************SubClass2***************&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; super <span style="color: #339933;">=&amp;</span>sub2<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; super<span style="color: #339933;">-&gt;</span>funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> super<span style="color: #339933;">-&gt;</span>funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span style="color: #000066;">cout</span></a> <span style="color: #339933;">&lt;&lt;</span> <span style="color: #ff0000;">&quot;***************SubClass3***************&quot;</span> <span style="color: #339933;">&lt;&lt;</span> endl<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; super <span style="color: #339933;">=&amp;</span>sub3<span style="color: #339933;">;</span><br />
&nbsp; &nbsp; super<span style="color: #339933;">-&gt;</span>funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>super<span style="color: #339933;">-&gt;</span>funcB<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[実行結果]<br />
***************SubClass1***************<br />
SubClass1::funcA()<br />
SubClass1::funcB()<br />
***************SubClass2***************<br />
SubClass2::funcA()<br />
SuperClass::funcB()<br />
***************SubClass3***************<br />
SuperClass::funcA()<br />
SubClass2::funcB()</div></div>
<p>virtualな関数の場合はコンパイラは型ではなくてオブジェクトのポインタを見ていて実行時にどのfuncが実行されるべきかを判断する。非virtual関数の時と違いコンパイラーはコンパイル時にはどのfuncが呼ばれるのか判別できない。この実行時にどのfuncが呼ばれるのか決定することを<a href="http://en.wikipedia.org/wiki/Dynamic_binding_%28computer_science%29">Dynamic Binding</a>と呼ぶ。そしてこのDynamic Bindingは<a href="http://en.wikipedia.org/wiki/Virtual_method_table">Virtual Function table(Vtable)</a>と呼ばれるメカニズムによって実現される。ようやく本題。</p>
<p>Vtableとはvirtual関数を持っているクラスや親クラスで定義されているvirtual関数をoverrideしたクラスに対してコンパイラーが作成する（その名のとおり）仮想テーブルである。コンパイラーはvirtual関数を持っている/virtaul関数をoverrideしているクラスにのみクラスごとのVtableを作成してその中にbindすべき関数ポインターを持っている。<br />
またこのVtableを指すポインタのことをvpointerと呼ぶ。コンパイラはVtableを持っているクラスに対してvpointerを隠しメンバー変数として追加する。さらにコンストラクタにそのvpointer変数の初期化を行うコードを追加する。よってオブジェクトが作成されるとき隠しメンバー変数vpointerは対応するVtableアドレスで初期化され、実行時の実行関数の決定は内部でvpointerを通じてVtableをlookupすることで実現される。　（参照: <a href="http://en.wikipedia.org/wiki/Virtual_method_table#Implementation">wikipedia:vtable implementation</a>）</p>
<p>Vtableに作成される関数ポインターは、そのクラスで持っているvirtual関数のポインター、親クラスで定義されているvirtaul関数をoverrideした関数のポインター、また親クラスでvirtual定義されている関数をoverrideしない場合はその親のvirtual関数ポインタが含まれることになる。上記サンプルにあるSuperClass, SubClass[1-3]に対するオブジェクトとvpointer、vtableとそのvtableに含まれる関数ポインターの関係を図にすると以下のようになる。</p>
<p style="text-align: center;">
<a href="http://www.flickr.com/photos/yk55/4391268309/" title="vtable - Virtual Function Table by yoichi*, on Flickr"><img src="http://farm3.static.flickr.com/2686/4391268309_604635db6c_o.png" width="589" height="467" alt="vtable - Virtual Function Table" /></a>
</p>
<p>上図を元にサンプル中のSubClass1::funcAのDynamic Bindingイメージを式化してみると次のような感じになる・ vptr1はSubClass1のvpointerとする。 あくまでイメージであり(vptr->)は実際は見えません。</p>
<div class="codecolorer-container c default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SubClass1 sub1<span style="color: #339933;">;</span><br />
SuperClass <span style="color: #339933;">*</span>super <span style="color: #339933;">=&amp;</span>sub1<span style="color: #339933;">;</span><br />
super<span style="color: #339933;">-&gt;</span>vptr<span style="color: #339933;">-&gt;</span>funcA<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> &nbsp; &nbsp;<span style="color: #666666; font-style: italic;">// SubClass1::funcA(),</span></div></div>
<p><h2>g++ -fdump-class-hierarchのダンプ結果</h2>
<p>最後にg++の-fdump-class-hierarchオプションによるVtableのダンプ結果を見てみる。上記サンプルファイルをvtable.cppとして次のようにコンパイルを行う。　（参考: <a href="http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Debugging-Options.html#Debugging-Options">Options for Debugging Your Program or GCC</a>）</p>
<pre>
 g++ -fdump-class-hierarchy vtable.cc -o vtable
</pre>
<p>これでコンパイルが終わりvtable実行ファイルができあがる。また同一ディレクトリにvtable.cpp.002t.classという名前のファイルが出来上がる。このファイルにVtableのダンプ結果が出力されている。各クラスのVtable中を見るといまいち意味のわからないものはあるが上図のとおりの関数が含まれており、また各クラスにはvptrを見つけることができる。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Vtable for SubClass1<br />
SubClass1::_ZTV9SubClass1: 4u entries<br />
0 &nbsp; &nbsp; (int (*)(...))0<br />
4 &nbsp; &nbsp; (int (*)(...))(&amp; _ZTI9SubClass1)<br />
8 &nbsp; &nbsp; SubClass1::funcA<br />
12 &nbsp; &nbsp;SubClass1::funcB<br />
<br />
Class SubClass1<br />
&nbsp; &nbsp;size=4 align=4<br />
&nbsp; &nbsp;base size=4 base align=4<br />
SubClass1 (0xb7254a80) 0 nearly-empty<br />
&nbsp; &nbsp; vptr=((&amp; SubClass1::_ZTV9SubClass1) + 8u)<br />
&nbsp; SuperClass (0xb707c1e0) 0 nearly-empty<br />
&nbsp; &nbsp; &nbsp; primary-for SubClass1 (0xb7254a80)<br />
<br />
Vtable for SubClass2<br />
SubClass2::_ZTV9SubClass2: 4u entries<br />
0 &nbsp; &nbsp; (int (*)(...))0<br />
4 &nbsp; &nbsp; (int (*)(...))(&amp; _ZTI9SubClass2)<br />
8 &nbsp; &nbsp; SubClass2::funcA<br />
12 &nbsp; &nbsp;SuperClass::funcB<br />
<br />
Class SubClass2<br />
&nbsp; &nbsp;size=4 align=4<br />
&nbsp; &nbsp;base size=4 base align=4<br />
SubClass2 (0xb7254b80) 0 nearly-empty<br />
&nbsp; &nbsp; vptr=((&amp; SubClass2::_ZTV9SubClass2) + 8u)<br />
&nbsp; SuperClass (0xb707c3c0) 0 nearly-empty<br />
&nbsp; &nbsp; &nbsp; primary-for SubClass2 (0xb7254b80)<br />
<br />
Vtable for SubClass3<br />
SubClass3::_ZTV9SubClass3: 4u entries<br />
0 &nbsp; &nbsp; (int (*)(...))0<br />
4 &nbsp; &nbsp; (int (*)(...))(&amp; _ZTI9SubClass3)<br />
8 &nbsp; &nbsp; SuperClass::funcA<br />
12 &nbsp; &nbsp;SubClass3::funcB<br />
<br />
Class SubClass3<br />
&nbsp; &nbsp;size=4 align=4<br />
&nbsp; &nbsp;base size=4 base align=4<br />
SubClass3 (0xb7254c40) 0 nearly-empty<br />
&nbsp; &nbsp; vptr=((&amp; SubClass3::_ZTV9SubClass3) + 8u)<br />
&nbsp; SuperClass (0xb707c528) 0 nearly-empty<br />
&nbsp; &nbsp; &nbsp; primary-for SubClass3 (0xb7254c40)</div></div>
<p>おわり</p>
]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/02/27/dynamic-binding-vtable-concept/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++ :: typeidとdemangleで実行時クラス名取得</title>
		<link>http://yk55.com/blog/2010/01/20/get_cpp_classname_by_typeid_and_demangle/</link>
		<comments>http://yk55.com/blog/2010/01/20/get_cpp_classname_by_typeid_and_demangle/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 20:27:20 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming / プログラミング]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[rtti]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=59</guid>
		<description><![CDATA[Javaでは普通に行われている実行時の型情報取得（ Object.getClass(), Class.getName()とかinstanceOfとかね ）ですが、CではできないのだからどうせC++でもできないのだろうなと思っていたら予想に反しRunＴime Type Identification (RTTI)という実行時の型情報取得機能がばっちり用意されてました。 確かにdynamic_castができているわけだし、まあそういうことだよねと納得。 ということでC++で実行時クラス名の取得を試みました。
実行時型情報取得はtypeid演算子を使います。typeid演算子はtype_info型クラスの参照を返し、そのtype_infoのメンバname()より型情報の文字列が取得できます。 それでは次のコードで型情報を取得してみます。
#include &#60;iostream&#62;
#include &#60;typeinfo&#62;
using std::cout;
using std::endl;
using std::type_info;

class Parent&#123;
public:
&#160; virtual void x&#40;&#41;&#123;&#125;
&#125;;
class Child : public Parent &#123;&#125;;

main&#40;&#41;&#123;
&#160; &#160; Parent* p;
&#160; &#160; p = new Parent&#40;&#41;;
&#160; &#160; const type_info &#38; id_p = typeid&#40;*p&#41;;
&#160; &#160; cout &#60;&#60; id_p.name&#40;&#41; &#60;&#60; endl;
&#160; &#160; p = new Child&#40;&#41;;
&#160; &#160; const type_info &#38; id_c [...]]]></description>
			<content:encoded><![CDATA[<p>Javaでは普通に行われている実行時の型情報取得（ Object.getClass(), Class.getName()とかinstanceOfとかね ）ですが、CではできないのだからどうせC++でもできないのだろうなと思っていたら予想に反し<a href="http://en.wikipedia.org/wiki/Run-time_type_information">RunＴime Type Identification (RTTI)</a>という実行時の型情報取得機能がばっちり用意されてました。 確かにdynamic_castができているわけだし、まあそういうことだよねと納得。 ということでC++で実行時クラス名の取得を試みました。</p>
<p>実行時型情報取得は<a href="http://en.wikipedia.org/wiki/Typeid">typeid</a>演算子を使います。typeid演算子は<a href="http://www.cplusplus.com/reference/std/typeinfo/type_info/">type_info</a>型クラスの参照を返し、そのtype_infoのメンバname()より型情報の文字列が取得できます。 それでは次のコードで型情報を取得してみます。</p>
<div class="codecolorer-container cpp default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;iostream&gt;</span><br />
<span style="color: #339900;">#include &lt;typeinfo&gt;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #007788;">type_info</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">class</span> Parent<span style="color: #008000;">&#123;</span><br />
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> x<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">class</span> Child <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> Parent <span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<br />
main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Parent<span style="color: #000040;">*</span> p<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> Parent<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> type_info <span style="color: #000040;">&amp;</span> id_p <span style="color: #000080;">=</span> <span style="color: #0000ff;">typeid</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>p<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> id_p.<span style="color: #007788;">name</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> Child<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> type_info <span style="color: #000040;">&amp;</span> id_c <span style="color: #000080;">=</span> <span style="color: #0000ff;">typeid</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>p<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> id_c.<span style="color: #007788;">name</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></div>
<p>これをコンパイル(gcc 4.3.2)後に実行した結果は次のとおり。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">6Parent<br />
5Child</div></div>
<p>ParentクラスとChildクラスそれぞれの結果は確かにそれっぽいものが得られましたがなぜかクラス名の前に文字数がくっついてます。 どうしたもんだと思ってググってみるとまさに<a href="http://ml.tietew.jp/cppll/cppll/thread_articles/8646">ピンポイントなMLでの議論</a>を発見。 クラスの前の数字がゴミに見えて気づかなかったのですがこれは<a href="http://en.wikipedia.org/wiki/Name_mangling">マングリング</a>された文字列でした。 というわけで問題はデマングリングで解決します。上記MLではその法についても紹介されていたのでそれを参考にデマングリングを試みます。</p>
<p>デマングリングにはcxxabi.hに定義されている__cxa_demangle()関数を使用します。まずはヘッダを覗いてみます。</p>
<p><u>__cxa_demangle() @ /usr/include/c++/4.1.3/cxxabi.h</u></p>
<div class="codecolorer-container cpp default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#ifdef __cplusplus</span><br />
<span style="color: #0000ff;">namespace</span> __cxxabiv1<br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; <span style="color: #0000ff;">extern</span> <span style="color: #FF0000;">&quot;C&quot;</span><br />
&nbsp; <span style="color: #008000;">&#123;</span><br />
<span style="color: #339900;">#endif</span><br />
...略...<br />
&nbsp; <span style="color: #666666;">// Demangling routines.</span><br />
&nbsp; <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span><br />
&nbsp; __cxa_demangle<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> __mangled_name, <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> __output_buffer,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #0000ff;">size_t</span><span style="color: #000040;">*</span> __length, <span style="color: #0000ff;">int</span><span style="color: #000040;">*</span> __status<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #339900;">#ifdef __cplusplus</span><br />
&nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span> <span style="color: #666666;">// namespace __cxxabiv1</span><br />
<span style="color: #339900;">#endif</span><br />
...略...<br />
<span style="color: #666666;">// User programs should use the alias `abi'.</span><br />
<span style="color: #0000ff;">namespace</span> abi <span style="color: #000080;">=</span> __cxxabiv1<span style="color: #008080;">;</span></div></div>
<p>次にこの__cxa_demangle()を使って先のマングルされた文字列をデマングルしてみます。</p>
<div class="codecolorer-container cpp default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;iostream&gt;</span><br />
<span style="color: #339900;">#include &lt;typeinfo&gt;</span><br />
<span style="color: #339900;">#include &lt;cxxabi.h&gt;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">using</span> std<span style="color: #008080;">::</span><span style="color: #007788;">type_info</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">class</span> Parent<span style="color: #008000;">&#123;</span><br />
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> x<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span> &nbsp;<span style="color: #666666;">// 1. 仮想メソッド宣言</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">class</span> Child <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> Parent <span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<br />
main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Parent<span style="color: #000040;">*</span> p<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> status<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> Parent<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> type_info <span style="color: #000040;">&amp;</span> id_p <span style="color: #000080;">=</span> <span style="color: #0000ff;">typeid</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>p<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> abi<span style="color: #008080;">::</span>__cxa_demangle<span style="color: #008000;">&#40;</span>id_p.<span style="color: #007788;">name</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, 0, 0, <span style="color: #000040;">&amp;</span>status<span style="color: #008000;">&#41;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> Child<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> type_info <span style="color: #000040;">&amp;</span> id_c <span style="color: #000080;">=</span> <span style="color: #0000ff;">typeid</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>p<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> abi<span style="color: #008080;">::</span>__cxa_demangle<span style="color: #008000;">&#40;</span>id_c.<span style="color: #007788;">name</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, 0, 0, <span style="color: #000040;">&amp;</span>status<span style="color: #008000;">&#41;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></div>
<p>デマングル版コードをコンパイル(gcc 4.3.2)後に実行してみると無事クラス名が出力。 ということで見事実行時クラス名の取得に成功しました。</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Parent<br />
Child</div></div>
<p>おわり</p>
]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/01/20/get_cpp_classname_by_typeid_and_demangle/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
