I have a value which is of type std::variant<char, int, double>
. In fact, I have several values stored in a std::vector<value>
.
I want to be able to pass these values to several callback
functions, if their signature matches the types of values. Here is my implementation:
using value = std::variant<char, int, double>;
using values = std::vector<value>;
template<typename... Args>
using callable = std::function<void (Args...)>;
struct callback
{
template<typename... Args>
callback(callable<Args...> fn)
{
constexpr auto N = sizeof...(Args);
fn_ = adapt(std::move(fn), std::make_index_sequence<N>());
}
void operator()(const values& vv) const { fn_(vv); }
private:
std::function<void (const values&)> fn_;
template<typename... Args, std::size_t... Is>
auto adapt(callable<Args...> fn, std::index_sequence<Is...>)
{
return [fn_ = std::move(fn)](const values& vv) {
/* if signature matches */ fn_(std::get<Args>(vv[Is])...);
};
}
};
using callbacks = std::vector<callback>;
Now I can do this and it works mostly well:
void fn(char c, int i) { std::cout << "fn:" << c << i << '\n'; }
int main()
{
values vv = { 'x', 42 };
callback cb1{ std::function<void (char, int)>(fn) };
callback cb2{ std::function<void (char, int)>([](char c, int i) { std::cout << "lambda:" << c << i << '\n'; }) };
callbacks cbs = { cb1, cb2 };
for(auto const& cb : cbs) cb(vv);
return 0;
}
You can see working example here.
I've reviewed several similar questions about (im)possibility of implicitly converting function pointers, lambdas, functors, etc. to std::function
.
So my question is, what "glue" do I need into order to be able to do this:
callback cb3 { fn };
callback cb4 { [](char c, int i) { } };
// same for other callable types
I thought of some sort of function_traits
implementation to extract signature from a callable type and pass it to std::function
. But looking at the code of std::is_function makes me think this will require a lot of boilerplate code.
I'd like to ask here if anyone can think of a more concise solution. Thank you in advance.
UPDATE
I was able to accomplish this using sweat, blood and tears partial specialization, SFINAE and void_t trick. I am now able to do this:
callback cb1 { fn };
callback cb2 { lambda };
callback cb3 { [](char c, int i) { std::cout << "lambda2:" << c << i << '\n'; } };
int x = 69;
callback cb4 { [x](char c, int i) { std::cout << "lambda3[x]:" << x << c << i << '\n'; } };
callback cb5 { ftor(x) };
Here is the demo. In this implementation callback
can accept function pointers, lambdas (including capturing, but not generic) and functors.