【C++/CLI】std::functionにマネージドメソッドをバインドする

std::function にメソッドを関連付ける時は std::bind を使用しますが C++/CLI でマネージドメソッドを std::bind 渡したい場合の実装方法の紹介です。

C++11 以降で関数ポインタの代わりに std::function でコールバック呼び出しされるような局面でマネージクラスのメソッドを std::function に渡してネイティブ側から呼び出してもらいたいケースがあると思いますが std::bind にマネージドメソッドを指定すると以下のようにエラーが発生してしまいます。

// ネイティブ側の定義
class Native
{
public:

    void foo(std::function<void(int)> func) // コールバックがstd::functionなネイティブメソッド
    {
        func(999);
    };
}

// マネージド側の定義
public ref class Managed
{
public:

    Managed(Native* lib)
    {
        auto func_1 = std::bind(this->WhatBind, gcroot<Managed^>(this), std::placeholders::_1);
        // この式は指定不可能
        // E2071 pointer-to-member は マネージド クラスでは無効です

        auto func_2 = []()
        {
            this->Callback();
        };
        // マネージドなのでラムダも無理
        // E2093 マネージド クラスのメンバー関数ではローカルラムダは使用できません
        
        lib->foo(func_1) // ★★★渡せない
    }
    
    void WhatBind(int arg1) { /* ... */ }
}

言語仕様的に上無理なので マネージドメソッドは std::bind できません。そこで以下のようにフリー関数を1層経由させます。こうすることで制限を回避できます。

// ネイティブ側の定義
class Native
{
public:

    void foo(std::function func); // コールバックがstd::function
}

// マネージド側の定義
public ref class Managed
{
public:

    Managed(Native* lib);
    
    void WhatBind() { /* ... */ }
}

// 迂回用のネイティブ関数の定義
static void Proxy(Managed^ managed, int arg1)
{
    managed->WhatBind(arg1); // マネージドメソッドの呼び出し
}

Managed::Managed(Native* lib)
{
    // ★★★これならbindを作成できる
    auto func = std::bind(Proxy, gcroot<Managed^>(this), std::placeholders::_1);
    lib->foo(func);
}

以上です。

参考

How to use boost::bind in C++/CLI to bind a member of a managed class

https://stackoverflow.com/questions/163757/how-to-use-boostbind-in-c-cli-to-bind-a-member-of-a-managed-class