简体   繁体   中英

Container of Functions with different Parameters

Is there a way to store void functions with different parameters in a vector? The number of parameters is always one, only the type differs. The parameter type can be a default type like int , a pointer to my own object like example* or any other type.

What I do for now is using a vector of functions with a void pointer as parameter. So I can pass everything. But I want to get rid of the back casting in all the functions.

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

Here is an example to illustrate what I would like to have. Please note the template syntax I would prefer. Therefore I would need to store all the functions in a container.

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

This application is performance related since it is an essential part of a realtime rendering engine.

Edit: I solved the point of storing functions without parameters, so this is not part of the question anymore what makes the question more clear and straightforward.

If type of parameters that could passed to the function is limited, then one option is using something like 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;

Then you can insert your callbacks directly into container.

As nm pointed out in a comment, the issue is how you use the value. Formally, C++ allows you to convert any pointer to (non-member) function to a void (*)(void) and back to its original type, without loss of value— void (*)(void) can be considered a sort of void* for pointers to functions. And practically, the runtime cost of such conversions is zero. But in order to use the function, at some point, you have to know the original type, in order to convert the pointer back to it.

You don't give your use case, but the usual situation involves callbacks, where the registration of the callback has a void (*)( void* ) (or void (*)( void const* ) ), and the callback converts the void* to the correct type, and calls a member function on it. In this case, using void* as the generic argument is the correct (and probably the only) solution.

Of course, this is the C solution, and should usually only be used when the interface using the callback is defined using the C API (functions like pthread_create , for example). In C++, the solution is to register objects, which derive from the same abstract base class, and implement a specific pure virtual function.

I developed a void pointer based event system and it works now. It uses templates for passing and receiving data. Functions with none or one parameter of any type both are supported. Since it uses void pointers to store the callback functions I suppose it is very fast compared to a solution using the any type from boost framework.

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

This is what came to my mind and it works quite well. However, please feel free to suggest improvements or use the code for your own projects.

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