More C++ Idioms
先週末は、友人と一緒に本を読んでいたのだが、その中で C++ のソースコードが出てきた。
私は、オブジェクト指向を C++ から学んだ世代なので C++ のソースコードを読んでいると郷愁を感じる。
しかし、いまもう一度読むと C++ はなかなか難しいというか、書くべきことが多い言語だな、と思い直した。
今日、ふと C++ を勉強しなおそうと思って検索してみたら
More C++ Idioms
がみつかった。
ここには、97種類も C++ のいろいろなイディオムが紹介されている。
いろいろと見て勉強してみたが、覚えているものもあるし、知らなかったテクニックもある。ここ数年間はそれほど C++ の知識をブラッシュアップしていなかったので、最近のモダンな C++ 向けのテクニックなどについては改めて勉強になった。
とはいえ、C++ は書かなければならない量が多い。
簡単な例だが、単純な足し算を行う Functor を定義するだけで、
#includeclass plus_binded { private: int # public: plus_binded(int &n) : num(n) {} int operator()(int n) { return num+n; } }; int main() { plus_binded plus = plus_binded(10); cout << plus(5); // 15 を表示 return 0; }
となる。Functor の定義だけで7行だ。コーディング規約が {} を1行にすることが認めていない場合は10行以上にもなりうる。
(もちろんこの例であれば、std::bind1st などでもっと短く実現できるが)
ほかにも Pimpl (Bridge, Handle Body とも呼ばれる)パターンを実現するために、何十行もコードを書き続ける単純作業に耐えなければならなかったりとかして、耐え難かった。
Pimpl イディオムに関連した単純作業では、Java や C# ではそもそも必要なくなり、大きく楽になったと感じたものだった。
とはいえ、C++ は昔大変お世話になった言語なので本格的に使う機会はもうないかもしれないが、C++0x など今後も新しい仕様についても追いかけていきたいと思う。
JS-WMI (2)
cuzic です。
JS-WMI というライブラリを開発していて、 WMI の Qaulifier について、ちょっと詳しくなりました。
WMI ではオブジェクト、プロパティ、メソッド、メソッドパラメータについて、付加的な情報を Qualifier で取得できます。
Qualifier は CIM Studio を使うことで、実際にどのような値が割り振られているかを確認できます。
頻繁に利用する Qualifier の中には Description というものがあります。Description から、そのプロパティ等の概要について、知ることができます。WMI の各オブジェクト等にはこの Description がかなり充実して記載されているのです。
そこで、この Description 情報を利用して、JS-WMI では各メソッド等についてコメントを作成するようにしました。
具体的には下記のとおりです。
WMI クラスとしては、Win32_Process の Terminate メソッドを例にしています。
/** METHOD NAME: Terminate /* Terminate メソッドはプロセスとそのスレッドのすべてを強制終了します。メソッドは次のように変換される整数値を返します: 0 - 正常に完了しました。 2 - ユーザーは要求された情報に対するアクセス権がありません。 3 - ユーザーには十分な特権がありません。 8 - 不明なエラーです。 9 - 指定されたパスは存在しません。 21 - 指定されたパラメータは無効です。 その他 - 上の一覧以外の整数値については Win32 エラー コードのドキュメントを参照してください。*/ // (in) Reason AS uint32 /* Reason パラメータにより、プロセスとこの呼び出しの結果強制終了したスレッドすべてに対する終了コードが指定されます。*/ function Terminate(Reason){ var in_params = wmiobject_.Methods_.Item("Terminate").InParameters.SpawnInstance_(); if(Reason !== undefined ){ in_params.Properties_.Item("Reason").Value = Reason; } var out_params = wmiobject_.ExecMethod_("Terminate", in_params); return WMIClass_convert_out_params(out_params); }
この JS-WMI で自動生成されたコードを見ることで、WMI メソッドが実際にはどのようなメソッドで、何を引数にとり、何を返すのかが JavaScript の形で簡単に理解できるようになっています。
IronRuby-PowerShell (1)
cuzic です。
ちょっと取り組んでみたら意外とあっさりとできたので、IronRuby による PowerShell のコマンドレットを操作できるようにするライブラリについて、書きます。
もうちょっと、頭の中を整理したいというか、練りたいので、 GitHub 等に公開は特にまだしていません。
たとえば、下記のスクリプトが動作します。
[1, 2, 3, 4].powershell <
これは、PowerShell で
$square = {foreach($x in $input) { $x * $x } 1, 2, 3, 4 | &$square
と同じ効果を持ちます。
iAsyncResult はあえて内部的に非同期的な呼び出しを使っているので必要になるおまじないです。内部での呼び出しを同期的にすれば解消するのですが、インタフェース的にどうするのかについて、考えているところです。
ほかに、WMI で Win32_Process を使って実現したような現在実行中のプロセスの一覧を出力する処理は、
powershell("Get-Process") do |iAsyncResult, output| output.each do |i| puts i end end
のように簡潔に書けます。
このライブラリを使えば、変数名の最初の $ や 関数呼び出しのときの & など目障りな記号を使わずに手馴れた Ruby スクリプトで、PowerShell に用意されているコマンドレットを数多く利用することができます。
現在の仕様としては、
- ブロック呼び出しで呼ばれた場市歩は、非同期処理、ブロックがあれば同期処理
- Object クラスを拡張して、powershell メソッドを実装
- System.Management.Automation.dll の位置はわりと決め打ち
というようなかんじになっています。
PowerShell の呼び出しに関する部分のコードについては、わずか50行ほどの記述で完結しておりますので、理解することは簡単でしょう。
このコードをベースに JScript.Net に移植することも考えたのですが、JScript.Net は Generics がサポートされていないとのことで、PowerShell との連携は不可能そうです。
# MSDN のあちこちで、「JScript では、ジェネリックな型およびメソッドは使用できません。」との記述が・・・。
smafile = ENV["PROGRAMFILES"] + %q(\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation) require smafile SMA = System::Management::Automation class Object def powershell source powershell = SMA::PowerShell.create if block_given? then # ブロック引数があれば同期処理 input = nil case self when Array # IronRuby では Generics を of メソッドで実現 input = SMA::PSDataCollection.of(self.first.class).new each do |elem| input.Add elem end input.Complete else input = SMA::PSDataCollection.of(self.class).new # トップレベルメソッドとして呼ばれた場合 unless self.inspect == "main" && self.class == Object then input.Add self end input.Complete end output = SMA::PSDataCollection.of(System::Object).new completed = false asyncCallback = proc do |iAsyncResult| completed = true if iAsyncResult.IsCompleted yield iAsyncResult, output powershell.Dispose if completed end at_exit do sleep 1 until completed end powershell.AddScript source powershell.BeginInvoke input, output, SMA::PSInvocationSettings.new, asyncCallback, nil else # ブロック引数がなければ同期処理 begin powershell.add_script source if self.is_a? Array then return powershell.invoke(self) else return powershell.invoke([self]) end ensure powershell.Dispose end end end end def test_gps powershell("Get-Process") do |iAsyncResult, output| output.each do |i| puts i end end end def test_gps_html powershell("Get-Process | ConvertTo-Html") do |iAsyncResult, output| output.each do |i| puts i end end end def test_square [1, 2, 3, 4].powershell <
JS-WMI (1)
cuzic です。
さて、前回、IronRuby-WMI を作ってみましたが、本来の趣旨としては WMI を JavaScript で操作するライブラリの作成を目標としていたのですが、脱線してできあがったものでした。
前回の記事を書いたあと、プロパティを実現する方法は JScript にはないと書いておりましたが、ふと Windows には Windows Script Component (以下、WSC)という技術があることを思い出しました。
WSC というのは、JScript または VBScript のような ActiveScript に対応した言語を利用して、COMクライアントから再利用可能にする技術です。
超簡単な例を書くと increment.wsc というファイルを下記のとおり作成すると、
test wsc
increment_client.js というファイルで、
var comobj = GetObject("script:increment.wsc"); WScript.Echo(comobj.increment(1));
とすると、さきほど作成したコンポーネントを利用することができます。
このコンポーネントでは、上記の method の代わりに
しかしながら、利用する、WMI のクラスやオブジェクトとして、何を使うかは分かりません。WMI のクラスの数は数百個存在しているので、事前に作るのは困難です。
というわけで、JScript で利用するクラスごとに、動的に WMIクラスをラップする WSC を自動生成して、その WSC を読み込んで、WMIオブジェクトをラップしたものを順に渡すことができようなスクリプトを作成してみました。
github に JS-WMI という名前で公開してみました。
JScript には Ruby の require に相当する機能がなく、WSF(Windows スクリプトファイル)を利用することが一般的ですので、WSF で今回作成した wmi.js を利用した例を書くと、
というスクリプトで、「メモ帳」を起動して、すぐに終了することができます。
終了するときに、その終了したプログラムの名称(notepad.exe)とそのプログラムが動いていたコンピュータの名称とそのユーザを表示させています。
ポイントとしては、
- WMIクラス「Win32_Process」の Create メソッドを、process.Create のように呼び出し可能
- ExecQuery メソッドは非同期な呼び出しを行うようにラップしており、返ってきたオブジェクトを1個づつ、OnObjectReady コールバック関数に委譲。
- ExecQuery メソッドでは、where 句だけを書けば、内部で WQL クエリを自動生成。
上記の場合は、下記の WQL を生成。
SELECT * FROM Win32_Process WHERE caption = "notepad.exe"
- instance.Caption のように自然に WMI のプロパティにアクセスが可能
- OutParameter を返り値として表現。GetOwner メソッドのように複数 OutParameterが存在する場合は、返り値を配列とする。
といった特徴があります。
WSC を利用している点がかなりズルをしている気がしなくもありませんが、JavaScript で WMI を利用する処理を書きやすくするという目標を達成できて満足しています。
MessagePack-JS (5)
cuzic です。
http://d.hatena.ne.jp/uupaa/20100525/1274729065
で、uupaa さんが私が作ったバージョンより大幅に高速なバージョンの MessagePack の JavaScript 実装を作成されたそうです。
私の JavaScript スキルはそれほど高くないですので、uupaa さんの実装を読んで、どのように実装すると高速化できるかについて、かなり勉強になりました。
私がソースコードを読んで理解した中で、高速化につながった理由について、効果があったであろう順に書きます。識者の方は間違っている点など指摘してもらえれば、うれしいです。
- メソッド呼び出しが最小限。処理はできる限り直接記述する。
- 下記の書き方のように、1番外側でメソッド定義。
function function_name(){ ... }
- 変数宣言はメソッドの最初に必要な分を一気に実施。
- 何度も使う定数については、事前に生成し、複数回計算は行わせない。
- 何度も使うメソッドについても、別の変数に事前に代入し、不要な「.」演算などを避ける
- switch-case の書き方、処理の流れをコンパクトに記述。
- 上とつながるが、全体にソースコードが短く。処理が簡潔。
どれがどのくらい高速化に効いているかについては私にはよく分かりませんが、http://javascript.g.hatena.ne.jp/edvakf/20100604 によると、全体として FireFox の場合10倍程度高速化できているようなので、劇的に高速に動作するようになっていますね。
IronRuby-WMI (1)
cuzic です。
JavaScript の柔軟な使い方の勉強として、WMI (Windows Management Instrument)のラッパーを、Windows Script Host で実行する環境向けに作ってみようと思ってみました。
PowerShell と似たノリで、
var klass = get_wmiobject("Win32_Process"); var instances = klass.GetInstances(); forEach(instances, function(instance){ // forEach の実装は省略 WScript.Echo(instance.Caption); // 実行中のプロセス名称を表示 });
というようなインタフェースで使えるようにできたらいいな、と思ったのだが、いろいろ調べてみるとなかなか大変であることが分かった。
例を使って説明すると、
var klass = get_wmiobject("StdRegProv"); HKEY_LOCAL_MACHINE = 0x80000002; var arrValueNames, arrValueTypes = klass.EnumValues(HKEY_LOCAL_MACHINE, "SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers");
のように、複数の返り値(ActiveX のメソッドでは out パラメータ)がある場合は、このように自然に書きたかったが、この仕様を満たすような実装は JavaScript では実現できない。
実現できない理由は、
- instance.Caption のようなアクセスを許容するには、WMI オブジェクトを生で扱うよりない(JavaScript には C# でいうプロパティの実装手段がないため)
- 一方、この場合の EnumValues を実現するには ActiveX を生で呼び出すのではなく、JavaScript のクラスなどでラップする必要がある
- そもそも、JavaScript には多重代入がない。
からだ。
これらは、Ruby であれば実現できる。というわけで、ふと習作として Ruby で実現したら、どうなるかを考えて、JavaScript 版をどうするかを考え直してみたいと思った。
単純に、Ruby で実装してみても仕方がないので、最近話題になっている IronRuby で書いてみた。
http://github.com/cuzic/ironruby-wmi
require 'System'; require 'System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' class String def underscore scan(/[A-Z][a-z]+/).to_a.map(&:downcase).join("_") end end module WMI class WMIError
IronRuby で Ruby の柔軟性をどこまで引き出せるかのテストみたいな状態になってしまっているが、
- const_missing を活用することで
WMI::Win32_Process.get_instances
などのように即座に WMI クラスを利用可能
- define_method も駆使して動的なメソッド定義を行い、WMI のプロパティを自然にアクセスできるように工夫
- get_instances_async で非同期の WMI オブジェクトの取得の実現も可能に
- get_instances_async はすべてのインスタンス取得が完了するまで、スクリプトを終了しないように at_exit で sleep する。
といった点を工夫したつもりである。
このライブラリを使うことで、
WMI::Win32_Process.get_instances_async do |process| puts caption end
のような簡潔なコードで、現在実行中のプロセスの名称を非同期に取得できるようになる。
最初の目標から大きく外れ、IronRuby で Ruby の柔軟な機構をごりごりと使う実験みたいな例になってしまったが、define_method, instance_eval などを含むコードでもちゃんと動いてもらえて、非常に満足である。
なんとなく作ってしまったので、公開してみたが、今後の展開としては、
- IronRuby ベースで PowerShell のラッパーとしても使えるライブラリの作成(この路線を発展)
- IronRuby から、CRuby で動作する RubyCLR ベースに移植
- IronRuby ではなく、JScript.NET に移植。(eval の連打での実装???)
- 最初、本当に作りたかった WMI の JavaScript ラッパーの作成。
といった選択肢のどれかを進めていこうと思っている。
やってみて、一息ついてみると、JavaScript (JScript) で実現する場合での限界などについても理解が深まった。私がやりたいことは JScript.NET でプロパティの部分については実現可能そうだ。高速さは犠牲になるが、JScript.NET で動的なメソッド定義も実現可能だ。JScript.NET コンパイラは、.NET フレームワークがインストールされている環境であればインストールされているようなので、普通の XP パソコンであれば使えて、どこでも動くといいやすい。PowerShell についても JScript.NET であれば、自然に扱えそうである。そのため、JScript.NET への移植などを進めていくかもしれない。(実際、今後どうするかは私の気分で決まるわけだが)
リファクタリング - Rubyエディション読書会(1)
cuzic です。
「リファクタリング - Ruby エディション」読書会を主催してきました。
「リファクタリング - Ruby エディション」は、かの名著 Martin Fowler のリファクタリングをサンプルコードを Ruby で書き換え、説明しなおすという野心的な試みを行っている書籍です。
その読書会を行った結果について、簡単に書くと、
- 結構人が集まった、(10人以上)
- みんないいペースで一章を読み終えることができた。
- わりとなごやかで発言しやすい雰囲気で進めることができた。
- 懇親会を非常に安価で実現できた。(近くにショッピングモールがあると便利)
- リファクタリング - Ruby エディションはいろいろと不具合が多い書籍
というようなかんじでした。
「リファクタリング - Ruby エディション」には、下記の修正すべき点がありました。
- コードのインデントが一部ずれている
- P27 の下にある図の days_rented の矢印が誤り(正しくは、a rental まで)
- P28 の Movie.NEW_RELEASE は Movie::NEW_RELEASE の誤り(ほかにも同じ誤り多数)
- 文章中には、テストの重要性を繰り返し述べているにもかかわらず、テストコードが記載されていない。
- P33 P40 の each.movie.title は element.movie.title の誤り
- P71 の include Price は include DefaultPrice の誤り
- P72 の Movie クラスの定義は def initialize が欠けている。
あと、個人的な感想として本にツッコミたい点として、
- P35 のコードは書き換えた後に、変数 result があるが、これはそもそも不要な変数と感じました。
具体的に引用すると、
def amount_for(rental) result = 0 case rental.movie.price_code when Movie::REGULAR result += 2 result += (rental.days_rented - 2)*1.5 if rental.days_rented > 2 when Movie::NEW_RELEASE result += rental.days_rented * 3 when Movie::CHILDRENS result += 1.5 result += (rental.days_rented - 3) * 1.5 if rental.days_rented > 3 end result end
のところについては私であれば
def amount_for(rental) case rental.movie.price_code when Movie::REGULAR if rental.days_rented > 2 return 2 + (rental.days_rented - 2)*1.5 else return 2 end when Movie::NEW_RELEASE result rental.days_rented * 3 when Movie::CHILDRENS if rental.days_rented > 3 return 1.5 + (rental.days_rented - 3) * 1.5 else return 1.5 end end return 0 end
のように書いて、不要なローカル変数を削除するかな、と思いました。
P65 から P72 で導入されているポリモーフィックにするための変更だが、ポリモーフィズムな振る舞いをさせる必要があるメソッドが1つである間は、私なら case when 文のままにしておきます。この本の中では今後の料金計算の方法の変更など仕様変更が待っているとのことであるので、そのような場合であればポリモーフィズムを導入させるかもしれませんが。
最後にこれはたんなる感想になるが、最終形のコードが
# 最新作の映画の作成 movie = Movie.new("The Watchman", NewReleasePrice.new) # 最新作から通常作品に価格体系が変更 movie.price = RegularPrice.new
というように、 Dependency Injection というか Constructor Injection のパターンにしているのは、自分としては少し違和感を感じた。私であれば、
class Movie def self.createNewRelease return self.new("The Watchman", NewReleasePrice.new) end end movie = Movie.createNewRelease("The Watchman")
というようにあわせて Factory Method も作成して、使う側からはファクトリメソッドを使わせるようなインタフェース設計にするように思った。
しかしながら、今書きながら考えていると、結局、
movie.price = RegularPrice.new
とするケースがあることなど、内部の実装を使う側も意識する必要があり、完全な隠蔽はできないし、この場合する必要もないことから、この本の書いている方法でよいですね。