marshal_as, marshal_contextについては、以前に文字列の変換における利用方法を書きました。今回は独自の変換を行う方法について書きます。
変換する型
ここでは例として、以下のネイティブの構造体とマネージクラスの相互変換を行います。
typedef struct ActorNative { char name[64]; int hp; int mp; } ActorNative;
ref class ActorManaged { public: String^ name; Int32 hp; Int32 mp; };
ネイティブからマネージへ変換
変換先がマネージ型の場合はGCが効きますので、marshal_asで変換することができます。以下のように、marshal_asの際の処理を記述します。
namespace msclr { namespace interop { template<> inline ActorManaged^ marshal_as<ActorManaged^, ActorNative> (const ActorNative& from) { ActorManaged^ obj = gcnew ActorManaged(); obj->name = gcnew String(from.name); obj->hp = from.hp; obj->mp = from.mp; return obj; } } }
すると、以下のようにmarshal_asを使っての変換が可能となります。
ActorNative native = {"アレックス", 120, 30}; ActorManaged^ managed = marshal_as<ActorManaged^>(native);
マネージからネイティブへ変換
ネイティブ型の値そのものに変換する場合は、上の場合と同様にmarshal_asを定義します。これはマネージのvalue classからネイティブのstructへの変換の場合等が当てはまると思います。
template<> inline POINT marshal_as<POINT, System::Drawing::Point> (const System::Drawing::Point& from) { POINT p = {from.X, from.Y}; return p; }
それ以外の場合、例えば今回のように、マネージ型のref classからネイティブ型のポインタに変換する場合を考えます。これはGCは効きませんので、リソース解放の面倒を見てくれるmarshal_contextを使うことになります。以下のようにして、独自のcontext_nodeクラスを作成します。
namespace msclr { namespace interop { template<> ref class context_node<ActorNative*, ActorManaged^> : public context_node_base { private: ActorNative * _ptr; public: context_node(ActorNative*& to, ActorManaged^ from) { _ptr = new ActorNative(); _ptr->name = static_cast<char*>(Marshal::PtrToHGlobalAnsi(from->name)); _ptr->hp = from->hp; _ptr->mp = from->mp; to = _ptr; } ~context_node() { this->!context_node(); } protected: !context_node() { Marshal::FreeHGlobal( IntPtr(_ptr->name) ); delete _ptr; } }; } }
これにより、以下のような変換が可能となります。
ActorManaged^ managed = gcnew ActorManaged(); managed->name = "アレックス"; managed->hp = 120; managed->mp = 30; marshal_context context; ActorNative* native = context.marshal_as<ActorNative*>(managed);
変数nativeは、marshal_contextがスコープを抜けると同時に破棄されます。context_nodeの定義でちゃんとデストラクタを実装していれば、自分でdeleteをしたりする必要はありません。
逆にしばらくポインタを保持したい場合はmarshal_contextも保持しておく必要がありますが、そういう場合にはあまりmarshal_contextは向いていないと思います。ネイティブの関数に渡すために一時的に構造体に変換したい、という場合に適しています。