Reputation: 922
I'm using a std::variant
to store different signatures of std::function
s. Said functions are stored in a vector, the latter being retrieved from a map.
How do I invoke each function in a vector of std::variant
? I feel like I should use std::visit
but I can't figure out the correct usage.
#include <variant>
#include <functional>
#include <map>
#include <iostream>
#include <functional>
using var_t = std::variant<std::function<void(void)>, std::function<void(int)>>;
enum class EventEnum { A, B, C };
struct Controller {
template<EventEnum E>
void subscribe(var_t fn) {
auto& callbacksVec = callbacks.at(E);
callbacksVec.push_back(fn);
}
template<EventEnum E>
void notify() {
auto& callbacksVec = callbacks.at(E);
for (auto& func : callbacksVec) {
// std::visit([](auto& arg){ std::invoke(arg); }, func);
}
}
std::map<EventEnum, std::vector<var_t>> callbacks;
};
int main() {
auto fn = []() { std::cout << "lambda callback" << std::endl; };
Controller myController;
myController.subscribe<EventEnum::A>(fn);
myController.notify<EventEnum::A>();
return 0;
}
Upvotes: 5
Views: 3658
Reputation: 48998
std::visit
requires that every type inside the variant has a valid "action". If func
were to have a std::function<void(int)>
instead of std::function<void(void)>
as it does right now, then there would be no way act on it. And because this (can) depends on the runtime, std::visit
has to check at compile time that every possible alternative of the variant can be used to call your callable.
You can merge lambdas together for example, or have an if constexpr
cascade for each type in the variant.
template<typename ...Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<typename ...Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::visit(overloaded{
[](const std::function<void(void)> &Void) { std::invoke(Void); },
[](const std::function<void(int)> &Int) { std::invoke(Int, 1); }}, func);
Upvotes: 4
Reputation: 6418
Here is another example, adapted from cppreference page to your situation:
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
#include <functional>
using var_t = std::variant<std::function<void(void)>, std::function<void(int)>>;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::vector<var_t> vec = {
[]() {std::cout << "void" << std::endl;},
[](int arg) {std::cout << "int " << arg << std::endl;}
};
for (auto& v: vec) {
std::visit(overloaded {
[](const std::function<void(void)>& fv) { fv(); },
[](const std::function<void(int)>& fi) { fi(42); }
}, v);
}
}
Upvotes: 3