I am trying do to a basic signal/slot library with coroutines. The idea is that you have signal members, connect them with slots that have the same signature, and when you co_yield a signal it calls the registered slots. Signal class looks like this:
template<typename... T>
class Signal {
public:
using slot_member = std::function<Slot(T...)>;
struct SignalArgs {
SignalArgs(std::span<slot_member> slots, T... args): args(args...), slots(slots) {}
std::tuple<T...> args;
std::span<slot_member> slots;
};
SignalArgs operator()(T... args) {
return SignalArgs(slots, args...);
}
private:
(...)
};
The important part is SignalArgs, since when you do a co_yield my_sig(...) it's what the promise sees. the yield values is defined as such:
template<typename... T>
std::suspend_never yield_value(typename Signal<T...>::SignalArgs args)
{
for (auto f: args.slots) {
Slot s = std::apply(f(args.args));
s();
}
return {};
}
My problem is with the template deduction:
error: no matching member function for call to 'yield_value' co_yield s(3, "lil"); ^~~~~~~~ note: candidate function [with T = <>] not viable: no known conversion from 'Signal<int, const char *>::SignalArgs' to 'typename Signal<>::SignalArgs' for 1st argument std::suspend_never yield_value(typename Signal<T...>::SignalArgs args)
Clearly the template deduction doesn't work in this case and I wonder what can I do.
The problem is that Signal<T...>::
is a non-deduced context for T...
. What you can do is move SignalArgs
out to be it's own template.
namespace detail {
template<typename... T>
struct SignalArgs {
using slot_member = std::function<Slot(T...)>;
SignalArgs(std::span<slot_member> slots, T... args): args(args...), slots(slots) {}
std::tuple<T...> args;
std::span<slot_member> slots;
};
}
template<typename... T>
class Signal {
public:
using slot_member = std::function<Slot(T...)>;
detail::SignalArgs<T...> operator()(T... args) {
return { slots, args... };
}
private:
(...)
};
template<typename... T>
std::suspend_never yield_value(typename detail::SignalArgs<T...> args)
{
for (auto& f: args.slots) {
Slot s = std::apply(f, args.args);
s();
}
return {};
}