1

ベクトルに異なるパラメーターを持つ void 関数を格納する方法はありますか? パラメータの数は常に 1 つで、型のみが異なります。パラメーターの型は、 のような既定の型int、自分のオブジェクトへのポインター、example*またはその他の型にすることができます。

今のところ、パラメーターとして void ポインターを持つ関数のベクトルを使用しています。だから私はすべてを渡すことができます。しかし、すべての関数でバックキャストを取り除きたいです。

unordered_map<string, function<void(void*)> > List;

void Callback(string Name, function<void(void*)> Function)
{
    List[Name].push_back(&Function);
}

Callback("event", [](void* x){
    int value = *(int*)x;
    cout << value << endl;
});

これは、私が何をしたいのかを説明するための例です。私が好むテンプレートの構文に注意してください。したがって、すべての関数をコンテナーに格納する必要があります。

vector<function<void(...)> > List; // need something other than a std vector

template <typename T>
void Callback(string Name, function<void(T)> Function)
{
    List[Name].push_back(&Function);
}

Callback<int>([](int x){
    cout << x << endl;
});

このアプリケーションは、リアルタイム レンダリング エンジンの重要な部分であるため、パフォーマンスに関連しています。

編集:パラメーターなしで関数を保存するという点を解決したので、これはもう質問の一部ではありません。質問をより明確かつ簡単にするものです。

4

3 に答える 3

1

関数に渡すことができるパラメーターのタイプが制限されている場合、1つのオプションはboost :: Variant:のようなものを使用しています。

typedef boost::variant<
    std::function<void()>,
    std::function<void(int)>,
    std::function<void(long)>,
    std::function<void(std::string const&)>
> my_functions;
typedef std::vector<my_functions> functions_list;

次に、コールバックをコンテナに直接挿入できます。

于 2012-12-01T11:35:02.590 に答える
1

nmがコメントで指摘したように、問題は値をどのように使用するかです。正式には、C ++を使用すると、(非メンバー)関数へのポインターをaに変換し、値void (*)(void)を失うことなく元の型に戻すことができます。これは、関数へのポインターvoid (*)(void)の一種と見なすことができます。void*そして実際には、そのような変換の実行時コストはゼロです。ただし、関数を使用するには、ある時点で、ポインタを元の型に戻すために、元の型を知っている必要があります。

ユースケースは示しませんが、通常の状況では、コールバックの登録にvoid (*)( void* )(または)があり、コールバックがを正しい型にvoid (*)( void const* )変換し、その上でメンバー関数を呼び出します。void*この場合、void*一般的な引数として使用することが正しい(そしておそらく唯一の)解決策です。

もちろん、これはCソリューションであり、通常、コールバックを使用するインターフェイスがC APIを使用して定義されている場合にのみ使用する必要があります(pthread_createたとえば、などの関数)。C ++での解決策は、同じ抽象基本クラスから派生したオブジェクトを登録し、特定の純粋仮想関数を実装することです。

于 2012-12-01T12:02:15.713 に答える
1

ボイド ポインター ベースのイベント システムを開発しましたが、現在は機能しています。データの受け渡しにテンプレートを使用します。パラメーターを持たない関数、または任意の型のパラメーターを 1 つ持つ関数の両方がサポートされています。void ポインターを使用してコールバック関数を格納するため、boost フレームワークの any 型を使用するソリューションと比較して非常に高速であると思います。

#include <string>
#include <vector>
#include <unordered_map>
#include <functional> 
#include <memory>

using namespace std;

class ManagerEvent
{
    typedef unordered_map<string, unordered_map<int, vector<pair<void*, bool> > > > Events;
public:
    void Listen(string Name, function<void()> Function)
    {
        Listen(Name, 0, Function);
    }
    void Listen(string Name, int State, function<void()> Function)
    {
        List[Name][State].push_back(make_pair(new function<void()>(Function), false));
    }
    template <typename T>
    void Listen(string Name, function<void(T)> Function)
    {
        Listen<T>(Name, 0, Function);
    }
    template <typename T>
    void Listen(string Name, int State, function<void(T)> Function)
    {
        List[Name][State].push_back(make_pair(new function<void(T)>(Function), true));
    }
    void Fire(string Name)
    {
        Fire(Name, 0);
    }
    void Fire(string Name, int State)
    {
        auto Functions = List[Name][State];

        for (auto i = Functions.begin(); i != Functions.end(); ++i)
        {
            if(i->second) continue;
            else          (*(function<void()>*)(i->first))();
        }
    }
    void FireRange(string Name, int From, int To)
    {
        for(int i = From; i <= To; ++i) Fire(Name, i);
    }
    template <typename T>
    void Fire(string Name, T Data)
    {
        Fire(Name, 0, Data);
    }
    template <typename T>
    void Fire(string Name, int State, T Data)
    {
        auto Functions = List[Name][State];

        for (auto i = Functions.begin(); i != Functions.end(); ++i)
        {
            if(i->second) (*(function<void(T)>*)i->first)(Data);
            else          (*(function<void()>*)i->first)();
        }
    }
private:
    Events List;
};

これが私の頭に浮かんだことであり、非常にうまく機能しています。ただし、改善を提案したり、独自のプロジェクトにコードを使用したりしてください。

于 2012-12-07T13:19:34.620 に答える