2007 年 9 月 4 日 18 時 44 分

C++/CLI #5: safe_cast


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


GCHandle を使って割り当てた ID はただの数値なので、
利用するときにはハンドルに戻さなければならない。

ID からハンドルに戻すのは、
ハンドルから ID に変換した時と逆の手順を踏めば良い。


    // ウィンドウのユーザデータから ID を取得
    LONG_PTR value = GetWindowLongPtr(hwnd, GWLP_USERDATA);
    void *pointer = reinterpret_cast<void *>(value);

    // 参照は void * と相互変換可能
    IntPtr id(pointer);
    GCHandle holder = GCHandle::FromIntPtr(id);


昨日の例では数値をウィンドウ属性に格納しておいたので、
GetWindowLongPtr で取得し、void * に戻す。
IntPtr は void * を受け取るコンストラクタを持つので、
IntPtr に変換して CLI の世界に突入する。

IntPtr から GCHandle 構造体に戻すためには、
GCHandle::ToIntPtr と対称となる、
GCHandle::FromIntPtr を使えば良い。

GCHandle に戻すことができたら、
この Target プロパティの値を取得することで、
参照されているハンドルを取得することができる。

GCHandle はマネージハンドル全般を保持できるように
汎用的に作られているので、このハンドルは Object 型だ。
必要に応じて、もとの型にキャストする必要がある。


    // 参照が保持するハンドルを取得
    ISaver ^saver = safe_cast<ISaver ^>(holder.Target);


さて、Object ^ から ISaver ^ への変換は、
ダウンキャストと呼ばれ、一般的には危険な操作である。
C++/CLI において、マネージオブジェクトのハンドルを
ダウンキャストする構文には、以下の 3 つがある。

・static_cast
・dynamic_cast
・safe_cast (C++/CLI 新構文)

static_cast は必ず成功するため、例外は発生しない。
必ず成功というのは、優れているということではなく、
型の互換性は何も検証されず、強制的に変換され、
そのまま新しい型として扱われるということである。

万が一、実行時に型の互換性のないハンドルが渡されたら、
何が起きるか分からない。下手するとクラッシュするだろう。
このようなキャストは、C# では不可能だ。

dynamic_cast は C# の as 構文に相当する。
型の互換性がない場合は nullptr を返すため、
dynamic_cast でも例外は発生しない。
なお、nullptr は C# の null に相当し、
NULL ポインタのハンドル版である。

safe_cast は C# のキャスト構文 (T) に相当し。
型の互換性がない場合は InvalidCastException が発生する。
これが最も一般的なキャストだろうか。

余談だが、dynamic_cast が例外を発生させないのは、
ハンドルやポインタ等、間接型を使った場合に限られる。
C++/CLI は C++ の構文規則に基づくので、
参照型のキャストに失敗した場合は
マネージ・アンマネージ問わず、例外が発生するので注意。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.