The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#pragma once
#include "function.h"
#include <tuple>
#include <utility>
#include <panda/CallbackDispatcher.h>
namespace xs { namespace callback_dispatcher {
using panda::function;
using panda::CallbackDispatcher;
using xs::func::ConverterIn;
using xs::func::ConverterOut;
struct XSCallbackDispatcher {
virtual ~XSCallbackDispatcher () {}
virtual void add (const Sub& cv) = 0;
virtual void prepend (const Sub& cv) = 0;
virtual void add_event_listener (const Sub& cv) = 0;
virtual void prepend_event_listener (const Sub& cv) = 0;
virtual void remove (const Sub& cv) = 0;
virtual void remove_event_listener (const Sub& cv) = 0;
virtual Sv call (SV** args, size_t items) = 0;
virtual void remove_all () = 0;
virtual bool has_listeners () = 0;
template <typename Ret, class...Args, class...Rest>
static XSCallbackDispatcher* create (CallbackDispatcher<Ret(Args...)>&, Rest&&...);
};
template <class T, class OutArg, class InArg, typename OutF = std::decay_t<OutArg>, typename InF = std::decay_t<InArg>>
std::pair<ConverterOut<T, OutF>, ConverterIn<T, InF>> create_converter_pair (const std::pair<OutArg, InArg>& p) {
return { ConverterOut<T, OutF>(p.first), ConverterIn<T, InF>(p.second) };
}
template <class T, class OutArg, class InArg, typename OutF = std::decay_t<OutArg>, typename InF = std::decay_t<InArg>>
std::pair<ConverterOut<T, OutF>, ConverterIn<T, InF>> create_converter_pair (std::pair<OutArg, InArg>& p) {
return { ConverterOut<T, OutF>(p.first), ConverterIn<T, InF>(p.second) };
}
template <class T, class OutArg, class InArg, typename OutF = std::decay_t<OutArg>, typename InF = std::decay_t<InArg>>
std::pair<ConverterOut<T, OutF>, ConverterIn<T, InF>> create_converter_pair (std::pair<OutArg, InArg>&& p) {
return { ConverterOut<T, OutF>(std::move(p.first)), ConverterIn<T, InF>(std::move(p.second)) };
}
template <class T, class OutArg, typename OutF = std::decay_t<OutArg>>
std::pair<ConverterOut<T, OutF>, ConverterIn<T, std::nullptr_t>> create_converter_pair (OutArg&& arg) {
return { ConverterOut<T, OutF>(std::forward<OutArg>(arg)), ConverterIn<T, std::nullptr_t>(nullptr) };
}
template <class Ret, class RetConv, class...Convs>
struct XSCallbackDispatcherImpl : XSCallbackDispatcher {
static constexpr size_t ARGS_COUNT = sizeof...(Convs);
using Dispatcher = CallbackDispatcher<Ret(typename Convs::first_type::type...)>;
using Event = typename Dispatcher::Event;
using Callback = typename Dispatcher::Callback;
using SimpleCallback = typename Dispatcher::SimpleCallback;
using OptRet = typename Dispatcher::OptionalRet;
using Tuple = std::tuple<Convs...>;
using Indices = std::make_index_sequence<ARGS_COUNT>;
using VoidIn = ConverterIn<void, std::nullptr_t>;
using IFuncSimple = panda::Ifunction<void, typename Convs::first_type::type...>;
using IFuncExt = panda::Ifunction<OptRet, Event&, typename Convs::first_type::type...>;
struct EventOut {
using type = Event&;
RetConv ret_conv;
Tuple arg_convs;
// we can't store XSCallbackDispatcher reference in EventOut object, because XSCallbackDispatcher is a temporary wrapper
// and may no longer exists when EventOut is called
EventOut (XSCallbackDispatcherImpl& d) : ret_conv(d.ret_conv), arg_convs(d.arg_convs) {}
Sv out (Event& e) { return _out(e, Indices{}); }
template <std::size_t...I>
Sv _out (Event& e, std::index_sequence<I...>) {
Event* ep = &e;
auto ret = function2sub_with_convs([ep](typename Convs::first_type::type...args) -> OptRet {
return ep->next(args...);
}, ret_conv.first, std::get<I>(arg_convs).second...);
return Ref::create(ret);
}
};
template <class RetConvArg, class...ConvArgs>
XSCallbackDispatcherImpl (Dispatcher& d, RetConvArg&& rcarg, ConvArgs&&... cargs)
: dispatcher(d),
ret_conv(create_converter_pair<OptRet>(std::forward<RetConvArg>(rcarg))),
arg_convs(create_converter_pair<typename Convs::first_type::type>(std::forward<ConvArgs>(cargs))...)
{}
void add (const Sub& sub) override { _add(sub, true, Indices{}); }
void prepend (const Sub& sub) override { _add(sub, false, Indices{}); }
void add_event_listener (const Sub& sub) override { _add_event_listener(sub, true, Indices{}); }
void prepend_event_listener (const Sub& sub) override { _add_event_listener(sub, false, Indices{}); }
void remove (const Sub& sub) override { _remove(sub, Indices{}); }
void remove_event_listener (const Sub& sub) override { _remove_event_listener(sub, Indices{}); }
void remove_all () override {
dispatcher.remove_all();
}
bool has_listeners () override {
return dispatcher.has_listeners();
}
Sv call (SV** svs, size_t items) override {
if (items != ARGS_COUNT) throw Simple::format("wrong number of arguments for CallbackDispatcher::call(): expected %d, passed %d", ARGS_COUNT, items);
return call_impl(svs, Indices{}, (Ret*)nullptr);
}
private:
Dispatcher& dispatcher;
RetConv ret_conv;
Tuple arg_convs;
template <std::size_t...I>
void _add (const Sub& sub, bool back, std::index_sequence<I...>) {
auto f = xs::func::sub2function_with_convs(sub, VoidIn(nullptr), std::get<I>(arg_convs).first...);
dispatcher.add(SimpleCallback(f), back);
}
template <std::size_t...I>
void _add_event_listener (const Sub& sub, bool back, std::index_sequence<I...>) {
auto f = xs::func::sub2function_with_convs(sub, ret_conv.second, EventOut(*this), std::get<I>(arg_convs).first...);
dispatcher.add_event_listener(Callback(f), back);
}
template <std::size_t...I>
void _remove (const Sub& sub, std::index_sequence<I...>) {
dispatcher.remove(xs::func::SubCallerComparator<void, typename Convs::first_type::type...>(sub));
}
template <std::size_t...I>
void _remove_event_listener (const Sub& sub, std::index_sequence<I...>) {
dispatcher.remove(xs::func::SubCallerComparator<typename RetConv::second_type::type, Event&, typename Convs::first_type::type...>(sub));
}
template <size_t...I, typename _Ret, typename = std::enable_if_t<!std::is_void<_Ret>::value>>
Sv call_impl (SV** svs, std::index_sequence<I...>, _Ret*) {
auto args = std::make_tuple(std::get<I>(arg_convs).second.in(svs[I])...);
(void)args;
return ret_conv.first.out(dispatcher(std::get<I>(args)...));
}
template <size_t...I>
Sv call_impl (SV** svs, std::index_sequence<I...>, void*) {
auto args = std::make_tuple(std::get<I>(arg_convs).second.in(svs[I])...);
(void)args;
dispatcher(std::get<I>(args)...);
return {};
}
};
template <class Ret, class...Args, class RetConvArg, class...ConvArgs>
std::enable_if_t<(sizeof...(ConvArgs) == sizeof...(Args)), XSCallbackDispatcher*>
fill_default_args (CallbackDispatcher<Ret(Args...)>& d, RetConvArg&& rcarg, ConvArgs&&...cargs) {
using OptRet = typename CallbackDispatcher<Ret(Args...)>::OptionalRet;
return new XSCallbackDispatcherImpl<Ret,
decltype(create_converter_pair<OptRet>(std::forward<RetConvArg>(rcarg))),
decltype(create_converter_pair<Args>(std::forward<ConvArgs>(cargs)))...
>(d, std::forward<RetConvArg>(rcarg), std::forward<ConvArgs>(cargs)...);
}
template <class Ret, class...Args, class...Rest>
std::enable_if_t<(sizeof...(Rest) <= sizeof...(Args)), XSCallbackDispatcher*>
fill_default_args (CallbackDispatcher<Ret(Args...)>& d, Rest&&...rest) {
return fill_default_args(d, std::forward<Rest>(rest)..., nullptr);
}
template <class Ret, class...Args, class...Rest>
std::enable_if_t<std::is_void<Ret>::value, XSCallbackDispatcher*>
create_impl (CallbackDispatcher<Ret(Args...)>& d, Rest&&...rest) {
return fill_default_args(d, nullptr, std::forward<Rest>(rest)...);
}
template <class Ret, class...Args, class...Rest>
std::enable_if_t<!std::is_void<Ret>::value, XSCallbackDispatcher*>
create_impl (CallbackDispatcher<Ret(Args...)>& d, Rest&&...rest) {
return fill_default_args(d, std::forward<Rest>(rest)...);
}
template <class Ret, class...Args, class...Rest>
XSCallbackDispatcher* XSCallbackDispatcher::create (CallbackDispatcher<Ret(Args...)>& d, Rest&&...rest) {
return create_impl(d, std::forward<decltype(rest)>(rest)...);
}
}}
namespace xs {
using XSCallbackDispatcher = callback_dispatcher::XSCallbackDispatcher;
template <> struct Typemap<XSCallbackDispatcher*> : TypemapObject<XSCallbackDispatcher*, XSCallbackDispatcher*, ObjectTypePtr, ObjectStorageMG> {
static panda::string_view package () { return "XS::Framework::CallbackDispatcher"; }
};
template <typename Ret, class...Args> struct Typemap<panda::CallbackDispatcher<Ret(Args...)>*> {
static Sv out (panda::CallbackDispatcher<Ret(Args...)>* d, const Sv& = {}) {
return xs::out(XSCallbackDispatcher::create(*d));
}
};
}