じゅぴの記録帳

いったイベントや勉強したこと等を記録するブログです 間違ってるとこあれば指摘お願いします

クラスのメンバの関数ポインタを格納したコンテナを作る

UnityのOnCollisionEnterとかの当たり判定をc++で再現する際にとても困ったのでそのメモです。

Gitに上げたソースです。

その1 普通の関数ポインタを試す

LInux上でのネットワークを勉強したときに引数と戻り値の型が同じならなんでもいれれる関数ポインタの存在を思い出しもっと調べてみると関数ポインタを格納できるstd::functionの存在を知り試してみました。

//インクルードは省いて書きます。

void ko(int)
{
    std::cout << "616" << std::endl;
}

int main(void)
{//普通に関数ポインタを格納
     std::function<void(int)> pointer;

     pointer = ko;
 }

普通の関数のアドレスの格納は成功。でもクラスのメンバ関数の格納がうまくいかない・・・
もっと調べてみるとstd::bindという便利そうなやつが!

その2 クラスのメンバ(引数なし)をコンテナに格納する

早速std::bindを使い引数ありのメンバをstd::functionを格納しようとすると失敗・・・
なのでまずは引数なしのメンバを試しました!

class Test
{
public:
    Test(){};
    ~Test(){};
    int member = 0;

    void Print(int num)
    {
        std::cout << "void Print(int)" << std::endl;
        std::cout << member << std::endl;
        std::cout << num << std::endl;
    }

    void Print2(void)
    {
        std::cout << "void Print2(void)" << std::endl;
    }
};

↑試すクラスです メソッド名は適当です

{
        //コンテナにメンバの関数ポインタを格納
        Test test1;
        //関数ポインタをいれるコンテナ
        std::unordered_map<int, std::function<void(void)>> map;

        map[0] = std::bind(&Test::Print2, &test1);

        //実行
        map[0]();
}

実行してみると数字が表示されてとても簡単にメソッドをコンテナに格納、実行できました。

その3 クラスのメンバ(引数あり)をコンテナに格納する

引数ありはしばらく試行錯誤しても実行できなかったのですがstd::bindの説明をよくみたらstd::placeholdersっていうものを発見。
早速使用。

{
        //コンテナに引数ありのメンバの関数ポインタを格納
        Test test2;
        //関数ポインタをいれるコンテナ
        std::unordered_map<int, std::function<void(int)>> map2;

        map2[0] = std::bind(&Test::Print, &test2, std::placeholders::_1);

        //引数を入れて実行
        map2[0](2);

        //本体のほうで引数を変更してみる
        test2.member = 100;

        //引数を入れて実行
        map2[0](200);
    }

クラスはさっきと同じものを使用。
あたりをクラスのほうで変えても表示内容が変わったのでちゃんと渡せてそうです。

最後に

調べるのめっちゃ時間かかりましたが当たり判定はこれを使えば簡単に行けそうです。
コンポーネント指向の当たり判定はこれとメタプログラミングを使えばUnityみたいに記述できそうです。
もしかしてこれはコールバックってやつなのでは…?

参考にしたサイト

std::bind - C++入門

std::function - cpprefjp

【C++】std::bindの使い方 - Qiita