[ADP開発日誌-公開1周年記念特集 Part6] プログラミング言語の制御構造のいろいろ(4)

ちょっと余計な記事が入りましたが、続きを

C++の仮想関数の欠点

 話が少し前後しますが、Part4の記事でC++の仮想関数呼び出しの仕組みについて説明しましが、ここではC++の仮想関数の欠点について指摘します。C++ではvtableというメンバ関数のアドレスを集めたテーブルを用いて仮想関数の呼び出しを実現していました。この方式は効率がよいのですが『コンパイル時に呼び出すべき仮想関数が決定しなければならない』という弱点があります。
どういうことかといいますとC++でのメンバ関数呼び出し

 object.virtual_method( arg1, arg2, arg3)

という呼び出しで、virtual_methodというメンバ関数名はコンパイル時に参照されますが、実行時には内部的に振られた番号(vtableのインデックス)になります。つまり実行時にはこの名前は参照できません。と同時にvtableのインデックスを取得する手段もないので、実行時に呼び出すメンバ関数を選択したいということができません。
これの何が欠点かピンとこないかもしれませんが、例えば、バッチファイルからVBScriptを使ってExcelを操ったりしますが、この特にExcelのバージョンをあまり気にせずにExcelを操作(メソッドを呼び出す)するでしょう。これと同じことは、C++の仮想関数の仕組みではストレートに実装できないということです。Windowsでは皆さんご存知のとおり、COMという仕組みをOSに実装することで実行時に呼び出すメソッドを特定することを行っています。
COMというとえらく古いと思われるかもしれませんが、.NET Framkework からExcelを呼び出す場合もCOM相互運用性という仕組みを使って.NET Framework → COM → Excel という風に呼び出しいます。
話が脱線しますが、私は.NET Frameworkが廃れるのではないか? と思っていますが、その理由のひとつが .NET FrameworkがCOMやOLE DB等のようにWindows APIを充分に置き換えていないと思えるところにあります(もっとも先のことは解りませんのでなんともいえませんが)。

関数の動的なロード&実行の例

  場合によって呼び出す関数を変える
というプログラミングテクニックは、オブジェクト指向プログラミング以外にもあります。典型的な例のひとつにデバイスドライバがあります。
デバイスドライバはご存知のとおりハードウェアとOSのAPIを橋渡しするソフトウェアでハードウェアに合わせて作成されています。ハードウェアを変えるとそれにあわせてデバイスドライバも変えます。
デバイスドライバはCで記述されることが多いです。最近のOSではPlag&Playが一般的になりましたし、USB接続機器ではOSを再起動せずに、デバイスドライバがロードされます。このような動的なソフトウェアのロードの仕組みはどうなっているのでしょうか?

続いては、公開1周年記念特集記事として『プログラミング言語の制御構造のいろいろ(5)』を書いてみます。

2011-08-14 | コメント:0件

素直さは如何に大切か


ちょっとADPの記念記事からは外れますが思ったことがありますので記事にしてみます。

技術的な論争を行ったときにどうしても、ご自身の主張(例えば『SQLはオブジェクト指向言語の数十倍の効率』)が否定されたことを認めたくないばかりに、『サルに何を言っても意味がないので、次に来ても消します。』等と言って自説を強弁したり相手を中傷したりすることがあります。
ちなみに、ここで私を文盲のサル扱いしていますが、そういうこと自体が褒められたことではないし見る人がみれば議論から逃げているとして取られないかと思います。

私も含めて人間誰しもそういう面があります。ので、そんなことをいちいち追求しても仕方がないことだと思っていましたし、それを追求すること自体あまり程度のよいものではないと思っている面もありました。

要するに零細企業の一社長が何をいっても社会にはあまり影響がないので言わせておけと思っていましたが、私はこの『事実より自分の主張を優先する』という性が少なくとも日本人の特性として思った以上に根ざしており、零細企業の一社長だけでなくそれなりの企業や組織にもそういうことがあるのではないかと心配しております。

私はこの議論を行っているなかで、『仮にこれが今の原発事故の話ならどうだろう』と思って寒いものを感じました。

つまり、原発事故で某電力会社は事故から2ヶ月後にメルトダウンしたことを認めています。記事
最終的に某電力会社はメルトダウンを認めたから事故の重大さが周知され、我々国民も対応について選択が出来る(または覚悟が出来る)ようになったかと思います。当初はメルトダウンはないといっていたり、発表が2ヶ月後になったということを考えると充分ではないことも明白で、ここでも事実を素直に受け入れることの重要性がわかります。

また、他の電力会社のやらせメールもありましたが、これに関しても『技術的には取り扱いを慎重に行わなければならないのに一方の考え方を押し通す為に、一見技術的には正当とみえるような議論を行う。つまり煙に巻く。』ということで、そう多くないとしても私の回りでもしばしば見受けられる現象です。

ここで重要な点は、技術的な内容についての真偽は技術者しか分からないということで、もし議論する一方が事実をありのまま受け入れずにそれを隠したりねじ曲げたりすると場合によっては大変な過ちを生み出すことになるということです。つまり議論を見ている専門知識を持たない観衆は判断がつかないところで勝手に誤った方向に持っていかれるということです。

私も過去に議論に巻き込まれて事実を受け入れることから逃げたくなったり逃げたこともありますし、やり込められると徹底的に逃げる人もみてきました。さらに裏で手を回して私を追い落とそうとした人もいます。
今回はただの議論なので『まぁいいか』ですみますがそれでも『SQLはオブジェクト指向言語の数十倍の効率』という間違った知識を若い人に伝えたくないのでがんばりましたが、私は今回のことは改めて如何に技術者として特に重要な技術的な内容については正直でなければならないと思い知らされました。

私も頭の体操ということで議論していまし今後も議論を行うでしょうが、こういうことを肝に銘じて技術的な事実関係は真摯に受け止めるように勤めたいですし、相手がそういう態度に出るのなら厳しく追求したいと思います。と同時に権力者が同じように国民を煙に巻いてこの国を誤った方向に持っていこうとしないようにしっかりと見ていきたいと思います(まぁこれには限界があるのですが・・・)。

2011-08-11 | コメント:0件

[ADP開発日誌-公開1周年記念特集 Part4] プログラミング言語の制御構造のいろいろ(2)

前回からちょっと間が空いてしまいましたが、ADPの1周年記念記事のPart4です。

関数呼び出しのスタックの使われ方


前回の記事の終わりにスタックという言葉が出てきましたが、スタックとはプロセス(正確にはスレッド)毎に用意されているメモリエリアで、関数呼び出しやローカル変数の保持に使われます。

以下のC言語での関数呼び出し時のスタックの使われ方の例を図1に示します。
func( arg1, arg2, arg3); /* ------- ※1 */

         図1


スタックは伝統的にアドレスの上位(数字が大きい)から下位に向かって領域が確保されます。
※1の関数が呼び出されるとき、先ず引数がスタックに積まれ、次いでリターンアドレス、そしてローカル変数の領域が確保されます。関数というのはどこから呼び出されても元の場所に戻ることが出来ますが、それが実現できるのは、呼び出し後に実行すべき命令のアドレス(リターンアドレス)をスタックに保持しているからです。
また、同時にどこから呼び出されてもローカル変数が『関数内で一時的に有効な変数』として機能できるのもスタックに変数のエリアを確保しているからになります。

ちなみに、数年前に流行したセキュリティリスクでバッファオーバーランというものがありますが、これはローカル変数の領域を溢れさせアドレスの上位にある戻りアドレスを書き換えてウイルスのプログラムを実行しようというC言語の関数呼び出しの仕組みを悪用したものになります。現在ではCPUレベルでの対策(NXビットとかXDビットとか呼ばれものでデータ領域の実行の禁止)が行われ、バッファオーバーランの脆弱性が起こりにくくなっています。

スタックには引数が積まれていますが、引数が積まれる順番には2通りのやり方があります。図1ではリターンアドレスに次いで arg1,arg2,arg3 と積まれていますが、反対に arg3,arg2,arg1 というやり方もあります。arg3,arg2,arg1の順番ですが、一見すると反対に見えますが、スタックに積む順番はarg1,arg2,arg3となります。ややこしいですが、※1の擬似アセンブラコードを示すと意味が良く分かるかと思います。

	※2 ※1の擬似アセンブラコード(cdecl呼び出し)

	PUSH arg3
	PUSH arg2
	PUSH arg1
	CALL func

PUSH命令の発行順とスタック上のリターンアドレスから見た順番が反対になります。
関数の呼び出し方法(つまりどのように機械語に翻訳するか)を呼び出し規約(主にx86のCPUで用いられている表現)といい、※2のような呼び出し方法をcdeclと呼びます。呼び出し規約はその他にPASCAL(文字通りPASCALで採用されている)とかstdcall(Windows-APIで採用)とかthiscall(C++のメンバ関数呼び出し)等があります。

メンバ関数の呼び出しでのスタックの使われ方


続いて、C++のメンバ関数呼び出しでのスタックの使われ方について説明します。
以下のC++でのメンバ関数の呼び出し時のスタックの使われ方の例を図2に示します。
object.method( arg1, arg2, arg3); // ------- ※3

            図2



※3の擬似アセンブラコードを以下に示します。

	※4 ※3の擬似アセンブラコード(thiscall呼び出し)

	PUSH arg3
	PUSH arg2
	PUSH arg1
	PUSH object
	CALL method

違いは、object(正確にはobjectのアドレス)がthisポインタとして引数の一つとしてスタックに積まれていることです。その他の違いはありません。こうしてみるとオブジェクト指向というのは単純に

method( &object, arg1, arg2, arg3)

というコードを、

object.method( arg1, arg2, arg3)

という風に記述できる構文上の違いであるに過ぎないということに気づくかと思います。
ADPでは、この考え方を推し進めて、メソッド形式(メンバ関数呼び出しとほぼ同じ意味)として通常の述語形式での呼び出しとメソッドの呼び出しを混ぜて使うことができるようにしています。

ちなみに、私も含めて、多くのC言語の上級エンジニアがこのような見方をしてC言語からC++(オブジェクト指向)に移行していたかと思います。

もっとも、この話は、『仮想関数はどのように機械語に翻訳されるのか?』の話をしなければ終わりになりません。
次いで、仮想関数の呼び出しの話をします。

仮想関数の呼び出しでのスタックの使われ方

前節で説明したメンバ関数の呼び出しは従来の関数呼び出しの延長線上のものですが、ここでは、仮想関数と呼ばれるオブジェクト指向独特の呼び出し方法について説明します。ちなみに仮想関数の説明自体は省略します(コメント欄でリクエストを頂ければ記事を追加するかもしれません)。 仮想関数の説明は次の記事で行います。

以下の仮想関数の呼び出しについて考えます。ちなみにスタックの構成は図2で仮想関数・通常のメンバ関数(非仮想関数)での違いはありません。
object.virtual_method( arg1, arg2, arg3); // ------- ※5
※5の擬似C++コードを以下に示します。

	※6 ※5の擬似アセンブラコード(thiscall呼び出し)

	PUSH arg3
	PUSH arg2
	PUSH arg1
	PUSH object
	MOV	 EAX, [object + vptr] ; ------------------- A
	MOV	 EDX, [EAX + virtual_method_offset] ; ----- B
	CALL EDX ; ------------------------------------ C

object + vptrなどや、EAX + virtual_method_number の部分がかなり曖昧ですが、エッセンスとして読んでいただければと思います。
※6のアセンブラコードではよく分からないかと思いますので、まずはオブジェクトのメモリレイアウトを図3に示します。

            図3



vtableと呼ばれるテーブルに呼び出すべき仮想関数の場所(アドレス)が格納されています。
また各objectはvtableの場所(アドレス)を保持する変数(ポインタ)を持っています。
さらに、機械語の特徴のとして関数呼び出し(CALL命令)は、常に同じ場所(アドレス)の関数を呼び出すだけでなく、変数(レジスタ)を通して間接的に呼び出すこともできるようになっています。

以上を踏まえて再度、擬似アセンブラコードを説明しますと、

Aでは、vtableを参照しています。EAXとはレジスタというCPUが持っている変数になりますがそこへvtableのアドレス(vptr)を代入しています。[] というのはアセンブラでのポインタ参照(間接演算子 *)になります。

Bでは、virtual_methodの呼び出すべきアドレスを、EDXに代入します。このvirtual_method_offsetですが配列のインデックスのようなもので、図3では0ということになります。

最後のCのCALL命令が、A,Bを通して取得した呼び出すべき仮想関数の呼び出しを行っていることになります。

このように擬似アセンブラコードを通してみますと、説明は難しいですが、たったの2命令の追加で仮想関数呼び出しを実現しており、C++での仮想関数呼び出しというのはかなり効率的であることが分かります。

もともと、私はアセンブラが大好き(ハードウェアを直接制御できるので)だったのですが、時代に押されてC言語を使うようになりましたが、その理由の一つとしてC言語が高級アセンブラとして設計された(つまりこのように簡単にアセンブラに置き換えられる)から動作がよく理解しやすい面があったからで、その設計思想はC++にも引き継がれていることが分かります。

続いては、公開1周年記念特集記事として『プログラミング言語の制御構造のいろいろ(3)』を書いてみます。
2011-08-04 | コメント:0件

[ADP開発日誌-公開1周年記念特集 Part3] プログラミング言語の制御構造のいろいろ(1)

ADPの1周年記念特集のPart3です。『プログラミング言語の制御構造のいろいろ』ということで数回にわたって記事をアップします。ちなみに本日でちょうどADPの初回リリースから1年になります。
「なぜ、制御構造?」と思われるかもしれませんが、それはADP(Prolog)が持っている制御構造(バックトラック)が独特のものということと、JavaScriptやRubyにありますクロージャが本格的に普及してきて私自身が持っている制御構造に対する考え方(というか感覚)を変える必要があるので記事にしてみます。

制御構造とは

制御構造とはプログラムの流れ、広くはその命令(for文とかif文)を指します。制御構造を有名なものにしたのは、かのダイクストラ氏が提唱した構造化プログラミングがあります。今となっては『構造化プログラミング』という言葉を始めて聞いた人もいらっしゃるかと思いますが、『構造化プログラミング』が提唱された後に、今ではおなじみの制御構造文
・選択(if)
・反復(for,while等のループ)
が明確になりました。それまでの言語ではif文やfor文もありましたが充分でなく、本格的なプログラムの記述にはgoto文を使う必要がありました。そのれに加えてgoto文では様々なプログラムの流れを作ることが出来、流れの追いにくいいわゆるスパゲティプログラムというものもありました。私が駆け出しの頃(20年程前)にはよく可読性の悪いプログラムに対して『このスパゲティプログラムが~』という表現を聞いていました。

機械語ではどうしているのか?

なぜ、「機械語の話が出てくるのか?」と思われるかもしれませんが、制御構造の発展の歴史のルーツを探ることと、コンパイラ言語では制御構造が機械語に変換されるのでその仕組みを探るという意味で、続いて機械語の話をします。
機械語では初期のプログラミング言語のように比較文(if文)とgoto文のみで制御を行います。今となっては逆に難しいかもしれませんが、for文やwhile文がなくてもif文とgoto文の組み合わせでループを記述することが出来ます。
意外に思われるかもしれませんが、もう一つの制御構造文である関数呼び出し(サブルーチン呼び出し)も機械語にCALL命令という形で存在します。初期のCPUにはCALL命令がないものもあったらしいですが、今われわれが主に使っているパソコンのx86と呼ばれるCPUにもCALL命令があります。さらにx86の先祖をたどりますと、8080というパソコン用の8ビットCPUがありますが、そのCPUにもCALL命令があります(それから先は8008、4004とたどれますがこれらにCALL命令があるかどうかは不明です・・・)。
もちろんCALL命令が関数呼び出しとイコールではありません。CALL命令と関数呼び出しの違いは引数の受け渡しになります。CALL命令には引数の概念がありません。引数の受け渡しはレジスタまたはスタックまたはグローバル変数ということになります。C言語の関数呼び出しが機械語に翻訳されるるとCALL命令に翻訳されますが、その引数はスタックで渡されます。

続いては、公開1周年記念特集記事として『プログラミング言語の制御構造のいろいろ(2)』を書いてみます。
2011-07-30 | コメント:0件

だじゃれくらうど

以前にも、震災関連で何かお役に立てないかと思いとりあえず電力量表示アプリとかを作ってみたのですが、さらになにかないか思っていましたら、Hack For Japanでイベントをやっていると言うことで、5/22のハッカソンに行ってまいりました。
 
もともと、5/21にアイデアソンがあり、そこでプロジェクトの内容を詰めて、5/22に開発を行うのですが、どこかのプロジェクトのお手伝いをしようと思っていましたとこで、朝にプロジェクトの割り振りがあり、ちょうど2人しかいないとのことで、『だじゃれくらうど』に参加させて頂きました。
 
軽いノリで参加したのですが、『だじゃれと言えば私でしょう!』ということで参加してよかったかと思います。

で、このプロジェクトですが、後で知ったのですが、ニッポン放送さんのapp10という番組との共同開発企画ということで、その後ニッポン放送さんにおじゃまして打ち合わせしたり、27日のapp10に出演(予定)だったりと思わぬところでプロジェクトが進んでいます。

たまにこういう企画に参加しますと、いろんなエンジニアの方と交流し色々な技術的な刺激を受けます。
Twitterからデータを拾うのですが、TwitterのAPIって『何にそれ?』ってなノリだったので、勉強にはなり、それはそれで楽しかったりします。
まぁ、今は時間的に余裕があるだけなのですが・・・。どこまでお手伝いが出来るかですが出来る範囲でがんばります。

もっとも、ということでモックアップをADPで開発してたりします。

5/22のハッカソンの写真です。
2011-05-25 | コメント:0件
Previous Page | Next Page