ものがたり(旧)

atsushieno.hatenablog.com に続く

Mono 2.11 released

しばらく時間がかかってしまいましたが、Mono 2.11が出ました。
http://tirania.org/blog/archive/2012/Mar-22.html

時間がかかったのは、昨年のXamarinへの移行に伴って、ビルドサーバとかQAまわりのインフラをいろいろと立て直さないといけなかったというのが大きいです。MonoTouch/MonoDroidまわりも並行して進めないといけなかったので、割とテンパッていたようです。それはさておき…

2.11は安定板2.12系列に向けての最初の開発版リリースということになります。

わたしは実のところもうあまりmono本体をいじっていないので(XMLまわりのバグフィックスをやっているくらい?*1)、細かい部分はあまり把握していないのですが、実装としてはランタイムとコンパイラ周り以外で大きな追加はないです。とは言っても、ツールの主要ターゲットが.NET 4.0になって、.NET 4.5ベースのプロファイルが追加されているのは地味に影響があるかもしれませんね。

sgen-gcは改善を続けていて、mono 3.0が出る頃にはデフォルトになっていると思います。MonoTouchでもMonoDroidでも使われているsgen*2が本体でデフォルトになっていない理由は…よく分かりませんw

C# 5.0の新機能は、asyncもcaller info attributesもforeachスコープの変更も実装されています。ちなみにASP.NET 4.5のasync HTTP supportなどは今年のGoogle Summer of Codeproject ideasに上がっています。

*1:WCFとかオワコンだし。System.Jsonは諸般の事情で今ちょっと止めています

*2:MTではオプションですが

ABC 2012 Springには参加しません(できません)

http://www.android-group.jp/conference/abc2012s/

ABC2011WinterにはMonoDroidの展示ブースで参加していたので一応書いておきます。

今回も、2月の時点では出展では参加しようと考えていたのですが、今回から(?)ブースの出展は個人または協賛企業*のみ*ということになったようです。わたしは日本でのこの手の活動は業務外でやっているのですが*1、基本的にXamarinは日本の企業でもなく協賛金を出す日本法人も無いので、今後も今回と同じ条件が出ている限り、Mono for Androidのブース展示は出来ない予定です。

ABC2011SummerはMonospaceとかぶっていたので日本にいなかったし、今もふらふらと国外逃亡しているので残念ながら(?)一般参加もしない予定です。

*1:Xamarinから参加してくれた人向けのクーポンが出たりするのでそれは出したりしますが

updates

ここに書くのがひさしぶりに and 遅くなってしまいましたが、4/7にプログラミング生放送(プロ生)でMono for Androidについて話します。
http://atnd.org/events/26346

大まかに知らない人向けのmonoの話を少し、使ってみる話を少し、近日リリースされるであろう最新バージョンの話を少し、内部解剖の話(マニアック)を少しずつやって、誰も満足できないセッションを目指そうと思います。わたしは別にマーケティングの仕事をやっているわけではないので、ホントは最後の話だけやりたいんですけど、誰得すぎるので他を混ぜてお茶を濁す予定です。

…ともあれ、そんなわけで興味が出てきた方がいしゃっしゃいましたらぜひどうぞ。わたしは参加申込みのアンケートを見てやめようかと思いましたが…w

Rx覚え書き (Observable以外編)

mono-reactiveを実装していた時の経験をもとに、Rxの諸クラスについて書けることをつらつら書いてみようと思う。とりあえずObservableクラスは膨大なので、それ以外について書く。例によってRxの入門的な記事はぐぐればいくつか出てくると思うので、それらを読んでもらえればと思う。

System.Reactive.Concurrency

ISchedulerの実装がこのネームスペースで公開されている。

ISchedulerのObservableクラスにおける利用には、2つのパターンがある:

  • 時間に依存するはずのスケジューリングを、そのISchedulerが定義する「時間」の上で行うたとえば、HistoricalSchedulerを経由することで、時間の経過をプログラマブルに操作することができる。
  • 実際のタスク呼び出し処理を、特定の条件下で行う。たとえば、SynchronizationContextSchedulerは、必ずSynchronizationContextを経由してタスクを実行するため、UI変更を行うコードをRxでもUIスレッドで実行できるようになる。

ImmediateSchedulerとCurrentThreadSchedulerは、現在のスレッド上で同期的に指定されたタスクを実行する(つまり後者)。この2つの違いは、再帰的なタスクの扱いにあるのだけど、FAQなので詳しい説明は余所に丸投げする。

EventLoopScheduler, NewThreadScheduler, TaskPoolScheduler, ThreadPoolSchedulerは、それぞれの方法で生成されたスレッド上でタスクを実行するために存在するスケジューラーだ(つまり後者)。

SynchronizationContextSchedulerは、指定されたタスクを、コンストラクタ引数のSynchronizationContext上で実行する(つまり後者)。その利点は例示で説明してきた通りだ。

HistoricalScheduler(Base)とVirtualTimeScheduler(Base)、そしてTestScheduler (Microsoft.Reactive.TestingでもMono.Reactive.Testingでも)は、時間を任意に進めることが出来るスケジューラーだ(つまり前者)。もしIObservableの実装クラスが、そのSubscribe()の内部において、適切にIScheduler.Schedule()を使用して時間依存の処理を実装していれば、これらのスケジューラーを使うことで、「時間」を好きなように制御しながらタスクを実行することができる。「1分待ってタイムアウトする」コードをテストするのに、実際に1分待つ必要はなくなる。

ISchedulerで時間を制御するために必要なこと

独自のObservable拡張メソッドを定義する場合など、IObservableを返すコードを実装する場合、時間を制御するISchedulerを有効に活用するためには、以下の点に注意する必要がある。

  • IObservable上で時間に依存する処理を自ら書かずにIScheduler.Schedule()で処理することが重要だ。たとえば(初期のmono-reactiveがそうだったように)、Delay()関数の中でThread.Sleep(1000)を呼び出していたら、いかにこれらのスケジューラーを使用していても、1秒経過しない限り実際のタスクは実行されない。 https://github.com/atsushieno/mono-reactive/commit/55c94c2119b4d3aa398874ff33615ba2520f82d3
  • 時間の計算を行う場合は、ISchedulerのNowプロパティを使って計算することが重要だ。たとえば「土日にはタスクAを、そうでなければタスクBを行う」といった処理を書く場合、DateTime.NowやDateTimeOffset.Nowを使用していたら、その結果はテストを実行した日時に影響を受けてしまう。注意すべきは、Scheduler.Nowも使ってはいけないということだ。これはあくまでIScheduler実装の内部で使われるためにあると考えた方が良い。

(mono-reactiveのObservable実装も、これらを考慮している。)

System.Reactive.Disposables

さまざまなIDisposableの実装がこのネームスペースで公開されている。

IDisposableはRxでは重要なインターフェースであり、様々な場面で利用されている。

  • IScheduler.Schedule()を使用して何らかのコード (ActionやFunc) をスケジュールした場合、戻り値のIDisposableをDispose()することで、スケジュールした時間に至っていないタスクをキャンセルできる。
  • IObservable.Subscribe()を使用して何らかのリアクション (IObserverやAction) を登録した場合、戻り値のIDisposableをDispose()することで、その登録を解除できる。イベントハンドラであれば、IObservable.Subscribe()がadd、IDisposable.Dispose()がremoveに相当する。
  • IConnectableObservable.Connect()を使用してhot observableからIObserverが通知を受け取るようにした場合、戻り値のIDisposableをDispose()することで、通知を受け取らない状態に戻すことができる。

System.Reactive.Disposablesの各クラスは、Rxの機能を使うだけの人にとっては、あまり使う機会が無い、かもしれない。IObservableを実装したり、Rxの諸機能を拡張したりする人が活用するためにあると言える。

  • ContextDisposable : SynchronizationContextを使用するクラスで使用する。渡された(別の)IDisposableオブジェクトをSynchronizationContext.Post()の中でDispose()する。(mono-reactive 0.1ではこれが使われていなかったが、修正した)
  • ScheduledDisposable : ISchedulerを経由して、(別の)IDisposableをDispose()しなければならない場面で使用する(UI処理を呼び出すためにSynchronizationContextScheduler.Schedule()を経由しなければならない場合など)。
  • CancellationDisposable : TaskPoolSchedulerの内部で活用される。(特に必須の存在でもなかったので、mono-reactive 0.1では使用していなかった。)
  • CompositeDisposable : 関連する複数のIDisposableを一括してDispose()する場合に活用出来る。たとえば複数のIObservableをMerge()する場合、Merge()は戻り値にCompositeDisposableを返し、その中に引数の各IObservableにSubscribe()した結果をまとめて保持しておくことができる。
  • MultipleAssignmentDisposable : DisposableプロパティにsetされたIDisposableオブジェクトを1つだけ保持し、自身がDispose()された時にはそれを直ちにDispose()する。(なかなか使いどころが無かったが、TaskPoolSchedulerで、タスクをSchedule()するまで待つ間はCancellationDisposableを、それ以降はSchedule()の戻り値を設定するように活用した。)
  • SerialDisposable : Disposableプロパティに設定されたIDisposableを、自身がDispose()された時に直ちにDispose()する点はMultipleAssignmentDisposableと同様だが、このプロパティが再度設定された場合、以前に設定されたIDisposableプロパティが、直ちにDispose()される。これはConcat()のように、常に1つのIObservableがSchedule()されてIDisposableを保持・破棄する必要がある場面で活用出来る(mono-reactiveではまだ問題があってこれを実現出来ていない)
  • SingleAssignmentDisposable : Disposableプロパティに設定されたIDisposableをdisposeするやり方はMultipleAssignmentDisposableと同様だが、複数回IDisposableが設定されると例外を投げる。複数回の設定を想定していない場面で使用する。
  • BooleanDisposable : 自身がDispose()されたかどうかをIsDisposableプロパティで明示する。(同プロパティがSingleAssignmentDisposableなどで存在しているため、mono-reactiveでは今のところ使用していない。)
  • RefCountDisposable : RefCount()関数で活用出来る(mono-reactiveではまだ使用していない)
DisposableプロパティとIsDisposedのメリット

SingleAssignmentDisposable, MultipleAssignmentDisposable, SerialDisposable, CompositeDisposableのもう一つの大きな利点は、Dispose()を遅延評価出来る点である。

たとえば、EventLoopSchedulerにdueTime付きでFuncを登録した場合、一定時間が経過したらそのfuncを呼び出してIDisposableをdisposeする処理対象にしなければならない。一方でdueTimeに至る前にSchedule()の戻り値がキャンセルされた場合は、そのfunc自体が呼び出されないことになるので、結果的にIDisposableも返されず、処理する必要がない。

Rxではこういう場面が多々存在するが、その度に if (childDisposable != null) childDisposable.Dispose() のようなコードを書くのは煩雑だ。また、Schedule()の戻り値が先にDispose()されてから、タスクがIDisposableを生成する(返す)ような状況が発生することもしばしばある。このような場面では、「Disposableプロパティに設定されたオブジェクトがあれば、それをDispose()する。既に自身がDispose()されていたら、その後Disposableプロパティに設定されたIDisposableも直ちにDispose()する」という動作が、処理を簡潔にしてくれるというわけだ。

System.Reactive.Subjects

ここにはobservableでありかつobserverにもなるISubjectの型がある。

ISubjectの実装であるAsyncSubject, BehaviorSubject, ReplaySubjectの挙動の違いとそれがどのように活かされているかを知るには、IConnectableObservableを返すObservableのメソッドを見ると良い。以下のクラスが、それぞれ対称的に使用されている。

  • Publish() : BehaviorSubject(デフォルト値あり)またはSubject(デフォルト値なし)
  • PublishLast() : AsyncSubject
  • Replay() : ReplaySubject

ISubjectは、他のIObservableで受け取ったnext/error/completedの各イベントを、そのまま他のIObserverにSubscribe()で転送できるので、mono-reactiveではObservableの実装で多大に活用している。(実際には受け取り済みのイベントをバッファリング出来るようにReplaySubjectが多く使用されているが、将来のバージョンで不必要なバッファリングを行わないように変更したいと考えている。)

その他のネームスペース

System.Reactive.Joinsネームスペースには、Observable.And()で使用されるPatternと、さらにThen()で返されるPlanがある。PlanはObservable.When()の引数になる(それ以外に用途が無い)。これらの型はpublic APIとしてはほとんど意味が無く、実装側としても特別に重要な存在ではない。ActionやFuncの引数の数に引きずられた結果として型が多すぎるので、ネームスペースを隔離したものと考えられる。

System.Reactive.Linqネームスペースには、RxのコアとなるObservableと、IGroupedObservableがある。ObservableはRxの中心的な存在であり、そのメソッドを個別に説明されるべきものだ。IGroupedObservableはGroupBy()で使われるkeyed observableで、それほど重要な存在ではない。

System.Reactive.Threading.Tasksネームスペースでは、Task Parallel Library (System.Threading.Tasks) に基づく機能を実装している。Silverlight4 / WP7.5以前の環境でRxを使えるようにするために切り離されたネームスペースということになろう。

System.Reactiveネームスペースには、「その他」とでも言うべきいくつかのクラスが存在している。

  • EventPattern、IEventPatternSource、IEventSourceは、イベント処理まわりのメソッドに関連する。
  • NotificationおよびNotificationKindは、Observable.Materialize()に関連する。
  • TimeIntervalは、Observable.TimeInterval()の戻り値として使われる。
  • Timestampedは、Observable.Timestamp()の戻り値として使われる。
  • UnitはIObservableのTが意味を持たない場合に使われる。たとえばTask`1.ToObservable()とは異なりTask.ToObservable()の戻り値のIObservableのジェネリック型引数には意味がないので、Unitが使われる。
  • ObserverはIObserverの拡張メソッドを定義している。

Systemネームスペースには、ObservableExtensionsのみ存在しており、大半がObserverのメソッドを呼び出すだけで実装出来ている。いずれもObservableクラスで拡張することは出来たはずだが、Systemネームスペースにある型のみで実現出来るメソッドは、このネームスペースで実装しておけば、System.Reactive.*をusingでインポートする必要もなくなる、という意図であろう。

続く...?

Observable以外編と書いたのだけど、Observableについて書くかどうかは今後の気分次第。

mono-reactive v0.1 released

昨年末の冬休み中に思い立って着手し、冬休みが明けてからも週末に時間をとって実装していたのですが、Reactive Extensions (Rx)のmono用の実装としてmono-reactiveを開発していましたが、今日バージョン0.1として公開することにしました。(ソースの公開自体はgithubで前々から行っていますが、ダウンロードページにバイナリを置いておきました。)

Rxについての入門的な情報は、ごく一部の人たちwの努力によって、日本語でも割と充実しているので、丸投げしたいと思います。連載:Reactive Extensions(Rx)入門のシリーズはちゃんと読みたい人にいいかも。

mono-reactiveは、version 0.1の時点では、System.Reactive.dllの実装ということになります。本家Rxには、MSTestのRx向け拡張であるRx-Testingと呼ばれるライブラリ (Microsoft.Reactive.Testing.dll) があるのですが、mono-reactiveには、NUnitを対象とするMono.Reactive.Testing.dllが存在します*1

mono-reactiveは、mono(-core)で動作することを第一の目標として作られましたが、MonoTouchやMono for Androidでも動作するようになっています(のはず)。Mono for Android用にはcsprojが用意されています。Button.ClickにObservable.FromEvent()を仕掛ける程度のことは出来ました。

mono-reactive 0.1は、MSDNにあるドキュメントにあったSystem.Reactive.dllの型を全て実装した (feature complete) バージョンということになります。System.Reactive.Linq.Observableだけでメソッドが100以上あるとか…

実用性はまだまだ分かりません。実装したもののテストしていないものが多々あります。Rx 101 Samplesなんかも全部動かしたりはしていません。テストのマトリックスのようなものは書き残していて、まあRx 101と同じくらいの数のNUnitテストは書いているつもりにはなっています。

というわけで、Rxに興味がある人やいじっている人は、ぜひ一度試してみてくださいませ。と言ってもRx使ってる人って大半がWPF/SL/WP7だからユーザー層がかぶらないんですけどねー。本家Rxとmono-reactiveで挙動が違うものとか、github issuesその他で教えてもらえると助かります。

追記: いくつか修正を加えた0.1.1を公開しました。

*1:まだRx-Testingに完全対応はしていません。実際に書いているテストではHistoricalSchedulerを使っていたり…

2011年お気に入り作品感謝リスト

例年クリスマスシーズンから年末にかけて書いていた感謝祭的なリストの2011年版です。

//ja.wikipedia.org/wiki/%E3%82%A2%E3%83%AF%E3%83%BC%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%B8%E3%83%83%E3%82%AF_%28%E7%9B%B8%E5%AF%BE%E6%80%A7%E7%90%86%E8%AB%96%E3%81%AE%E6%9B%B2%29">相対性理論+渋谷慶一郎 - アワーミュージック : 相対性理論はこれまでほとんど1回聴いてスルーに近い状態だったのですが、これは作風が全然違い、今年一番リピートしていた曲です。病みつきで聞いていました。特別にコメントすることも無いのですが、やくしまるえつこの声がハマる。
//code.google.com/p/bridj/">BridJ: 会社を追い出されて(ry、次の会社(ryに入るまで、暇な時間がたっぷりできたときに、JavaでP/Invokeと同じくらい簡単なものがほしいと思ってJNAのクローンを作っていた時に発見したもので、直ちに自分のハッキング計画を投げ出したくなるくらいよく出来ていてかつ野心的なプロジェクトでした。休職期間が短すぎたこともあってAndroidサポートだけ協力させてもらいましたが、作者のochafik氏がたいへんsupportiveで、ひさしぶりにOSS活動を楽しませてもらいました。
//twitter.com/ryu_umemoto">@ryu_umemoto: (作品ではありませんが)FM音源使いの - というほど最近は使ってないですが - 僕にとってFM音源の魔術師のような氏はただひたすらスゲー人でした。僕はほとんど成人向けゲームに手を出さなかったので、彼が曲提供されていたゲームも実はほとんど知らず、yu-noとかも見てみたいんですが、PC98版でとなると(音源が目当てなので)、いろいろ難しいようです。僕が訃報を目にした時は札幌に出ていて、途方に暮れて小一時間ばかり呆然と市街をさまよってしまいました。この界隈ではシナリオの人も最近なくなったそうで、知っている人には寂しい話なのだろうと思います。夏の終わり頃は彼の作品を何度も聴いていました。
//www.falcom.co.jp/sora/">英雄伝説 空の軌跡シリーズ: 僕の200時間を返せという気持ちでいっぱいです。本当にありがとうございました。冗談はさておき、Falcomのゲームとしては、EDシリーズはどうも苦手で(プレイ時間が冗長すぎる)、今まで1,2,3,4,6しかやったことがなく、First Chapterもぱっとしないという印象だったので、Second Chapterは手を出していなかったのですが、震災の後どうにもやる気が出なかった時についうっかり始めてしまったら…意外にも話が面白い。それにしても長すぎたので、クリアした時3rdは来年にでもやろうと決めたのですが、その2週間後くらいには3rdを始めてしまい…今度は音楽が良く出来てる(話は…)。これは無限ループで再生できなくてどうすると思って、Androidで↑のBridJを使ってtremoloを直接叩いてAndroid SDKAPIでは出来ないことをやるアプリを書いて、Marketに置いてしまう程度にはハマりました。つい先日も冬休みということで"零"をクリアしたばかりなのですが、また長大な時間を費やしてしまったので、"碧"はもうしばらくやらないつもりです。
//msdn.microsoft.com/en-us/data/gg577609">Reactive Extensions: .NETにもまだ面白いライブラリがあった! 非同期処理がアツイというのはまあ今年?よく言われていましたが、mcsはもうasync/awaitをサポートしちゃったし別に自分は使う立場でしかないからいいかなーと思ってスルーしていたのですが(それ以前に今年はあんまり.NETらしいコードを書いていないか)、RxはEventHandlerやらAsyncCallbackやらを無理なく統一的に操れそうで、興味深いアプローチとして見ています。この辺のブログエントリにあるコードサンプルがRx適用のbefore/afterとしては面白いですね。Rxまわりはneue.ccでいろいろ書かれているので日本語情報が読めるのもイイです(ここで今さら紹介の二番煎じをすることもないでしょう)。そのうちMono for Androidにも適用できるようにしようと思っています。

…まだいくつか今年になって発見できた古い作品もあるのですが、ちまちまとtwitterで書いたりなどしていたので、今年のぶんはこのくらいにしておこうと思います。来年も素晴らしい作品に会えるのを楽しみにしています。

CXXIがクロスプラットフォームでC++/C# interopを実現する(予定)

monoチームからCXXIという新しいコンポーネントが登場しました。これはC++C#のinteroperabilityを実現するフレームワークだそうです。

》 CXXI: Briding the C++ and C# worlds. http://tirania.org/blog/archive/2011/Dec-19.html

以前からcppinteropという名前で作っていたハカーたちがいて、7月のMonospace conferenceでも発表されていたのですが、それがブラッシュアップされて登場したのがコレです。

》 mono / cxxi https://github.com/mono/cxxi

以下、6割方↑記事の内容を引っ張ってきています。

CXXIの機能・特徴

CXXIが実際にサポートする機能は、ざっと次のようなものだそうです(便宜上C#と.NETはまとめてC#と書きます):

  • C++のオブジェクトをC#から操作できる
  • C#C++のオブジェクトを生成できる
  • C++クラスのメソッドをC#から呼び出す
    • gccに-fkeep-inline-functionsなどを指定すればインラインメソッドも呼び出し可能
  • C++クラスをC#で派生できる
  • C++のメソッドをC#でオーバーライドできる
  • C++のクラスやC++/C#の混合クラスを、ネイティブクラスであるかのようにC#またはC++に公開できる

通常のP/InvokeやMarshalByRefObjectよりは、C++を実用的に使えるものと言えるでしょう。他のC++ interopはどうなっていたんでしょうか?

  • Microsoftの.NETの場合は(まあmono on Windowsにもありますが)COM interopがありました。これはC++のクラスをCOM化しないとできません。めんどくさいですね。(そもそもCOMはVBC++のコード共有のために存在している側面もあるので、RCW/CCWの呼び出し規約が直感的でないのは仕方ないとも言えます。)
  • C++の内容を呼び出すCのコードを作成することもできます。(.NETとは無関係に)Cヘッダを公開しているけど中身はC++で書かれているようなライブラリでは、この手法を採用しているものが多いと思います(具体的なコード例が先のリンク先にあります)。これは数が増えるとめんどくさいですし、メソッドをオーバーライドすることもできません(swigもそうですね)。
  • Microsoftは、C++/CLIという別のアプローチを採用しました。これはmixed mode assemblyというネイティブとマネージドの混合アセンブリを生成するもので、生成されたコードには可搬性がありません。MicrosoftWindowsを活かしWindowsと心中する企業なので、それで問題ないのですが、monoにはクロスプラットフォームのやり方が必要ですし相応しいでしょう。

CXXIランタイム

ではCXXIはどのように実装されているのかというと、バイナリレベルでC++のオブジェクトの内容を読み取って、それをもとにC#ベースのクラス構造やメソッド呼び出しを実現します。その具体的な仕組みを知るために、まずC++コンパイルされたネイティブコードがどのようなバイナリ構造をもっているかを知る必要があるでしょう:

  • オブジェクトレイアウト: CXXIではプラットフォーム依存のメモリ上のバイナリ構造をもとに、オブジェクト情報を保持しています。これはコンパイラやプラットフォームによってバラバラなので、それぞれに合わせて動作することになります。
  • vtableレイアウト: これはクラス階層構造における仮想メソッドのオーバーライドを実現するために用いられるバイナリ構造です。(一般的に)vtableは関数ポインタを保持していて、メソッドがオーバーライドされたオブジェクトの場合は基底クラスと異なる関数を指し示すことになります。ちなみに非仮想メソッドはvtableには含まれず、Cの関数としてコンパイルされることになります。
  • mangled name: C++コンパイラはCコンパイラであり、コンパイルされたオブジェクトコードには、ソースコード上のメソッド名にそれぞれの命名規則を適用して生成された名前をもつ関数が含まれています。この命名規則コンパイラ依存で、逆に言えばそれに従ってさえいれば、バイナリレベルでのinteroperabilityが実現できるというわけです(名前さえ分かればP/Invokeと同じように名前から関数のアドレスを知ることが出来ます)。

これらがどのようにコード上で展開されるかは上記リンク先に例があります。

CXXIでは、引数やオブジェクト自体のメモリレイアウトを操作するために、プラットフォーム依存のオブジェクトレイアウトを知る必要があり、また非仮想メソッドを呼び出すためにmangled nameを把握しておく必要があります。

以上でも随所で説明しましたが、CXXIは、その仕組み上、プラットフォーム/アーキテクチャコンパイラに依存します。

CXXIでは、対象ライブラリのC++ヘッダを入力として、CXXIランタイム(Mono.Cxxi.dll)を経由してC++オブジェクトを操作するC#クラスのソースを生成します。

how cxxi uses C++ headers and libs

CXXIランタイムの実際の動作は次のような感じになります:

  • C#でクラスのインスタンスを生成すると、C++インスタンスが生成されます。
  • 生成されたC#コードのクラスは、C#で派生クラスを定義するのに利用できます。C++の仮想関数は仮想メソッドとなり、オーバーライドすればそれが呼び出せます。
  • C++の多重継承に対応しています。具体的には、生成されたC#のコードには、多重継承したクラスに対応する型へのキャスト演算子オーバーロードが追加されるので、それらを利用します。
  • オーバーライドされたメソッドでは、baseを呼び出して、C++コード上にある基底クラスの実装を呼び出すことができます。
  • 多重継承したクラスにある仮想メソッドをオーバーライドすることができます。
  • (自分で生成せずに)既存のC++インスタンスをラップするようなC#のオブジェクト(peer)を生成することもできます。

CXXIジェネレータの仕組み

次はCXXIでC++ヘッダからC#ソースを生成するジェネレータについて。CXXIでは、C++ヘッダからgcc-xml(gcc4の中間コードgimpleのXML表現にコンバートするツール)で生成したXMLを入力として、C#のソースを生成します。生成されるのはソースなので、これに手を加えて最終的なライブラリを生成できます。

how cxxi generator works

ここでポイントとなるのは、生成されたC#コードには、具体的なメモリレイアウトに当てはめられたオブジェクトの情報が含まれているわけではない、つまりまだ環境/アーキテクチャ中立のコードになっている、ということです。具体的なメモリレイアウトの解決は、実行時に行われます。

(ジェネレータではgcc-xmlのみをサポートしていますが、生成されるC#ソースにはgcc依存の部分もMSVC依存の部分も存在せず、またC#ソースの生成に必要なのはヘッダファイルのみなので、gccで解析できるヘッダファイルでさえあれば、困ることは無いはずです。Windowsのヘッダファイルの多くは、cygwinにも含まれるgcc-mingwのw32apiパッケージにも含まれています。)

現状と今後

CXXIは大きな実用の可能性を見せるものですが、未完成の部分もいくつかあります。

  • 現在のcxxiは、System.Reflection.Emitを利用してブリッジを動的に生成しています。これで動的にC++コンパイラのABIに合わせて対応できるようになっています。静的コンパイルは出来ていません(iPhonePS3環境はまだ)。
  • 現在のcxxiはgcc / Itanium ABIにのみ対応しています。MSVC ABI対応のコードは存在しますが、未完成です。(わたしの理解が正しければ、armeabiにも対応していません。)
  • 現在のcxxiでは、deleteできるオブジェクトは、C#から生成したオブジェクトのみです。そうでないオブジェクトは、自分でdeleteする必要があります。
  • -

というわけで、CXXIのざっとした説明でした。わたしの理解では、cxxiが目指しているのはid:atsushieno:20110628:p1 で書いたBridJやJNAeratorのようなツールなんだろうと思っています。Javaの世界では、この種のC++バインディングツールが百花繚乱というか、それぞれ利点と欠点があって、それぞれが短いライフサイクルで生きているなあという印象がありましたが、C#バインディングツールとして、CXXIもこれまでのツールとは一線を画した存在として、一石を投じることになりそうです。