简体   繁体   中英

Convert void* to std::function<void()>

Is store function pointers with different parameters in a vector of void pointers.

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

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

Then I want to call them, assuming that T is the same type for Fire as used for the Listen .

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);
    }
}

But I get a compiler error which reads error C2064: term does not evaluate to a function taking 1 arguments in file ...\\vc\\include\\xrefwrap 431 1 .

What am I doing wrong?

For one, you're taking the address of a parameter, here:

List[Name].push_back(&Function);

Then you're trying to convert an iterator object to a std::function object here:

(function<void(T)>)i)

What you trying to do can be done, like this, although it's not pretty, to put it mildly:

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);
    }
}

It can break in lot of ways, for example you have no control that the function, registered under some name in Listen is called with the correct argument in Fire - consider calling Listen<int> ("foo", f); and then doing Fire<double> ("foo", 3.14);

Another approach - just pass closures for callbacks:

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) ();
}
#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 );
      }
    }
  }
};

... or something like that.

This stores a copy of the std::function in the map, wrapped up generically. Memory is handled via a unique_ptr . I carefully blocked type deduction at points where the type must be exactly what you used when you installed the Listener (automatic type deduction at that point is rather fragile).

In debug, you'll get an assertion failure if you violate the Name<->type mapping.

Some extra work needs to be done for nullary callbacks. Just write a DoCall that casts BaseCallback to Callback<void> , specialize Callback<void> to be a nullary function wrapper, specialize make_callback on nullary function , and write a Fire(string) method for Listener that calls the bare DoCall .

Or create a struct Empty and use lambdas to wrap nullary functions in function<void(Empty)> , which would involve slightly less code, but would be slower at run-time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM