C++/CLIラッピング --- 逆引き
C++を.Netで利用する際、C++/CLIで必要になりそうなことをまとめていきます。
随時更新予定です。
クラスラップ基本構造
public ref class NetClass { public: NetClass() { _cppClass = new CppClass(); } ~NetClass() { this->!NetClass(); } // Dispose() !NetClass() { delete _cppClass; } // ファイナライザ private: CppClass* _cppClass; };
System::String → std::string (copy)
#include <msclr\marshal_cppstd.h> System::String^ net; std::string cpp = msclr::interop::marshal_as<std::string>(net);
std::string → System::String (copy)
std::string cpp; System::String^ net = gcnew String(cpp.c_str());
System::String → const wchar_t* (no copy)
System::StringはUnicodeのため、wchar_t*しか利用できません。
System::String^ net; pin_ptr<const wchar_t> pin = PtrToStringChars(net); const wchar_t* p = pin;
array<int> → int* (no copy)
array<int>^ net; pin_ptr<int> pin = &net[0]; int* p = pin;
std::vector<int> → array<int> (copy)
std::vector<int> cpp; auto net = gcnew array<int>(cpp.size()); System::Runtime::InteropServices::Marshal::Copy(System::IntPtr(cpp.data()), net, 0, net->Length);
プロパティ
デフォルトのgetter、setterでよいならproperty int Value;のみでもOKです。
public ref class NetClass { public: property int Value { int get() { return 10; } int set(int value) { Console::WriteLine(value); } } };
インターフェース
public interface class INetClass { public: virtual int GetValue(); }; public ref class NetClass : public INetClass { public: virtual int GetValue() { return 10; } };
抽象クラス
public ref class NetClassBase abstract { public: virtual int GetValue() abstract; }; public ref class NetClass : public NetClassBase { public: virtual int GetValue() override { return 10; } };
拡張メソッド
using namespace System; using namespace System::Runtime::CompilerServices; [ExtensionAttribute] public ref class Extension abstract sealed { public: [ExtensionAttribute] static String^ AddSpace(String^ src) { return src + " "; } };
GCにメモリ使用量通知
C++のnew等でアンマネッジドなメモリを確保した場合、GCはそれを感知しません。
GCに通知しておくと、適宜Collectが行われるようになります。
0より大きい値でないと例外が発生します。
using namespace System; GC::AddMemoryPressure(10); // 追加 GC::RemoveMemoryPressure(10); // 削除
ref classをスコープ脱出時にデストラクト
gcnewを用いずにインスタンス化すると、通常のcppのクラスのように、スコープを抜けるとデストラクタが呼ばれます。
ref class NetClass { public: ~NetClass() { Console::WriteLine("destructed"); } }; { NetClass c; } // "destructed"
メンバのDisposeを自動で呼ぶ
スタックセマンティック(gcnewで作成されていない)のメンバが存在すると、親のDispose時にメンバのDisposeも呼んでくれます。
また、親のDisposeは宣言しなくても自動で作成されます。(宣言した場合は、親のDisposeが先に処理されます。)
ref class Child { public: ~Child() { Console::WriteLine("child disposed"); } }; ref class Parent { Child _child; }; // C#側 Parent parent; parent.Dispose(); // "child disposed"