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"