How to store pointers to callback functions with different signatures in an STL container (map<string, func>)?

Viewed 55

I'm currently in a situation where I want to store function pointers in an std::map<string, func> . Specifically, these functions are callback functions which have different signatures, and importantly they have different return types which is the heart of my problem.

For example:

// callback 1
GstPadProbeReturn callback_1(GstPad *pad, GstPadProbeInfo *info, gpointer u_data);

// callback 2
GstFlowReturn callback_2(GstPad *pad, GstPadProbeInfo *info, gpointer sharedPtr)

// callback 3
void callback_3(GstElement *element, GstPad *pad, gpointer u_data);

/* somewhere else in the code */
std::map<std::string, functionPtr> cb_map;

map["func1", callback_1];
map["func2", callback_2];
map["func3", callback_3];

and so I can pull the callback I want out of the map and connect the callbacks in the code, which with gstreamer can be done in the following way:

gst_pad_add_probe(probe_pad, GST_PAD_PROBE_TYPE_BUFFER, callback_1, NULL, NULL);

The problem here is callback_1 must match a return type as specified by gst_pad_add_probe while other functions can accept void.

I tried several approached but I keep getting stuck at either not being able to store functions of different signatures in std::map or not being able to pass std::function<void> in the calling function.

I tried the following:

template <class F, class... Args>
inline auto FuncWrapper(F &&f) -> decltype(f)
{
  return f;
}

std::map<std::string, std::function<void>()> cb_map;

auto cb1 = FuncWrapper(&callbacks_1);

this->cb_padprobereturn_map.emplace("one", cb1); // doesn't work 
}

Apologies if this is not clear, I wasn't sure how to succinctly explain this problem. I'm also not well versed enough in c++ to use things such as std::variant

Any help is appreciated!

1 Answers

Not exactly a solution provided by me. But it seemed to me like an interesting practice cause I'm also c++ beginner.

Bellow the code to store functions with different signatures in a map. I wrapped function in a structure func_wraper. Each function can be called using call(...)

#include <map>
#include <functional>
#include <string>
#include <iostream>

using F1 = std::function<bool(int, long)>;
using F2 = std::function<int(bool, bool)>;
using F3 = std::function<void(long, long)>;

struct func_wraper{
    F1 fun_type1;
    F2 fun_type2;
    F3 fun_type3;

    func_wraper(F1 f) : fun_type1(f), fun_type2(nullptr), fun_type3(nullptr) {}
    func_wraper(F2 f) : fun_type1(nullptr), fun_type2(f), fun_type3(nullptr) {}
    func_wraper(F3 f) : fun_type1(nullptr), fun_type2(nullptr), fun_type3(f) {}

    int getType()
    {
        if (fun_type1 != nullptr)
            return 1;
        if (fun_type2 != nullptr)
            return 2;
        if (fun_type3 != nullptr)
            return 3;
    }

    bool call (int a, long b) {
        return fun_type1(a, b);
    }
    bool call(bool a, bool b) {
        return fun_type2(a, b);
    }
    void call(long a, long b) {
        fun_type3(a, b);
    }

};

bool fun1(int a, long b) { 
    std::cout << "f1" << std::endl;
    return true; 
};
int fun2(bool a, bool b) {
    std::cout << "f2" << std::endl;
    return 1; 
};

void fun3(long a, long b){
    std::cout << "f3" << std::endl;
};

int fun4(long a, long b) { return 1; };

int main()
{
    std::map<std::string, func_wraper> cb_map{
        {"type1", F1(fun1)},
        {"type2", F2(fun2)},
        {"type3", F3(fun3)}    
    };

    cb_map.at("type1").call((int) 1, (long) 0.1);
    cb_map.at("type2").call(true, true);
    cb_map.at("type3").call((long) 0.1, (long) 0.2);

    return 0;
}

Output:

f1
f2
f3

Hope it somehow helps you

UPD. You can use std::any but then you should use std::any_cast to cast stored variable in a map to known type

template<typename T>
struct func_wrapper {
    T saved_func;

    func_wrapper(T fun) : saved_func(fun) {}

    template<typename... Args>
    auto operator()(Args... args)
    {
        saved_func(args...);
    }
};

int main()
{
    std::map<std::string, std::any> cb_map{
          {"type1", func_wrapper<F1>(fun1)},
          {"type2", func_wrapper<F2>(fun2)},
          {"type3", func_wrapper<F3>(fun3)}
    };

   std::any_cast<func_wrapper<F1>>(cb_map.at("type1"))(1, 0.1);
   std::any_cast<func_wrapper<F2>>(cb_map.at("type2"))(true, true);
   std::any_cast<func_wrapper<F3>>(cb_map.at("type3"))(0.1, 0.2);

   return 0;
}
Related