2

異なるパラメーターを持つ関数ポインターをvoidポインターのベクトルに格納します。

unordered_map<string, vector<void*> > List;

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

それから、それがに使用されるTのと同じタイプであると仮定して、それらを呼び出したいと思います。FireListen

template <typename T>
void Fire(string Name, T Data)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
    {
        (function<void(T)>)i)(Data);
    }
}

しかし、.を読み取るコンパイラエラーが発生しますerror C2064: term does not evaluate to a function taking 1 arguments in file ...\vc\include\xrefwrap 431 1

私は何が間違っているのですか?

4

2 に答える 2

2

1つは、ここでパラメータのアドレスを取得していることです。

List[Name].push_back(&Function);

std::function次に、イテレータオブジェクトをここのオブジェクトに変換しようとしています。

(function<void(T)>)i)

あなたがやろうとしていることは、このように行うことができますが、それはきれいではありませんが、穏やかに言えば:

unordered_map<string, vector<void*> > List;

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

template <typename T>
void Fire(string Name, T Data)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
    {
      function<void(T)> *ptr = *i;

      (*ptr) (Data);
    }
}

それは多くの方法で壊れる可能性があります。たとえば、の名前で登録された関数がListen、の正しい引数で呼び出されるように制御することはできません。Fire呼び出しListen<int> ("foo", f);てから実行することを検討してください。Fire<double> ("foo", 3.14);

別のアプローチ-コールバックのクロージャを渡すだけです:

unordered_map<string, std::vector<function<void()> > > List;

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

void Fire(string Name)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
      (*i) ();
}
于 2012-12-01T20:45:05.737 に答える
1
#include <functional>
#include <unordered_map>
#include <memory>
#include <string>
#include <vector>

template<typename T> struct BlockDeduction{typedef T type;};
struct BaseCallback {
  virtual ~BaseCallback();
  template<typename T>
  void DoCall( typename BlockDeduction<T>::type&& t ) const;
};
template<typename T>
struct Callback: BaseCallback
{
  std::function<void(T)> func;
  Callback( std::function<void(T)> const& f ):func(f) {}
};


template<typename T>
void BaseCallback::DoCall( typename BlockDeduction<T>::type&& t ) const {
  Assert( dynamic_cast<Callback<T>const*>(this) );
  static_cast<Callback<T>const*>(this).func(std::forward(t));
}

typedef std::unique_ptr<BaseCallback> upCallback;
template<typename T>
upCallback make_callback( std::function<void(T)> const& f ) {
  return upCallback( new Callback<T>( f ) );
}


struct Listener {
  std::unordered_map< std::string, std::vector<upCallback>> List;
  template<typename T>
  void Listen( std::string Name, std::function<void(T)> f) {
    List[Name].push_back( make_callback(f) );
  }
  template<typename T>
  void Fire( std::string Name, typename BlockDeduction<T>::type&& t ) {
    auto callbacks = List.find(Name);
    if (callbacks == List.end()) return;
    for(auto it = callbacks->second.begin(); it != callbacks->second.end(); ++it) {
      if (it +1 = callbacks->second.end())
      {
        (**it).DoCall<T>( std::forward(t) );
      } else {
        (**it).DoCall<T>( t );
      }
    }
  }
};

...またはそのようなもの。

これにより、のコピーがマップに保存std::functionされ、一般的にラップされます。メモリはを介して処理されunique_ptrます。型がインストール時に使用したものと正確に一致している必要があるポイントで、型の推定を慎重にブロックしましたListener(その時点での自動型の推定はかなり壊れやすいです)。

デバッグでは、Name <-> typeマッピングに違反すると、アサーションエラーが発生します。

nullaryコールバックには、追加の作業を行う必要があります。DoCallにキャストBaseCallbackするを記述し、nullaryラッパーに特化Callback<void>し、nullaryに特化し、bareを呼び出すためのメソッドを記述します。Callback<void>functionmake_callbackfunctionFire(string)ListenerDoCall

または、aを作成し、struct Emptyラムダを使用してnullary関数をでラップしますfunction<void(Empty)>。これにより、必要なコードはわずかに少なくなりますが、実行時には遅くなります。

于 2012-12-01T21:30:38.280 に答える