#include <type_traits>
#include <vector>
namespace aux {
template<typename, typename>
class accumulator;
}
template<typename>
class signal;
template<typename R, typename... Args>
class signal<R(Args...)>
{
public:
template<typename Slot>
void bind(Slot slot);
template<typename Accum>
typename aux::accumulator<R, Accum>::result_type
emit(Accum f, Args... args);
private:
struct erased_slot;
std::vector<erased_slot> slots_;
};
// emits the signal without accumulating anything.
template<typename R, typename... Args>
void emit(signal<R(Args...)>& sig, Args... args);
#include <memory>
#include <utility>
namespace aux {
template<typename R, typename Accum>
class accumulator
{
public:
using result_type = typename std::result_of<Accum(R, R)>::type;
public:
explicit accumulator(Accum accum) : state_(), accum_(std::move(accum)) {}
public:
template<typename F, typename... Args>
void step(F&& f, Args&&... args)
{
state_ = accum_(state_,
std::forward<F>(f)(std::forward<Args>(args)...));
}
result_type const& get_state() const&
{
return state_;
}
result_type&& get_state() &&
{
return std::move(state_);
}
private:
result_type state_;
Accum accum_;
};
template<typename Accum>
class accumulator<void, Accum>
{
public:
using result_type = typename std::result_of<Accum()>::type;
public:
explicit accumulator(Accum accum) : state_(), accum_(std::move(accum)) {}
public:
template<typename F, typename... Args>
void step(F&& f, Args&&... args)
{
std::forward<F>(f)(std::forward<Args>(args)...);
state_ = accum_();
}
result_type const& get_state() const&
{
return state_;
}
result_type&& get_state() &&
{
return std::move(state_);
}
private:
result_type state_;
Accum accum_;
};
} // namespace aux-
template<typename R, typename... Args>
struct signal<R(Args...)>::erased_slot
{
public:
template<typename Slot>
explicit erased_slot(Slot slot)
: slot_(new model<Slot>(std::move(slot)))
{
}
erased_slot(erased_slot const& x) : slot_(x.slot_->clone()) { }
erased_slot& operator=(erased_slot const& x)
{
slot_.reset(x.slot_->clone());
return *this;
}
erased_slot(erased_slot&& x) = default;
erased_slot& operator=(erased_slot&& x) = default;
public:
R operator()(Args... args)
{
return (*slot_)(std::forward<Args>(args)...);
}
private:
struct concept
{
virtual R operator()(Args... args) = 0;
virtual concept* clone() const = 0;
};
template<typename Slot>
struct model final : public concept
{
Slot slot;
explicit model(Slot slot) : slot(std::move(slot)) { }
R operator()(Args... args) override
{
return slot(std::forward<Args>(args)...);
}
model* clone() const override { return new model(*this); }
};
std::unique_ptr<concept> slot_;
};
template<typename R, typename... Args>
template<typename Slot>
void signal<R(Args...)>::bind(Slot slot)
{
slots_.emplace_back(std::move(slot));
}
template<typename R, typename... Args>
template<class Accum>
typename aux::accumulator<R, Accum>::result_type
signal<R(Args...)>::emit(Accum f, Args... args)
{
aux::accumulator<R, Accum> accum(std::move(f));
for (auto& i : slots_) {
accum.step(i, args...);
}
return std::move(accum).get_state();
}
namespace aux {
struct do_nothing
{
template<typename... Args>
int operator()(Args const&...) { return 0; }
};
} // namespace aux
template<typename R, typename... Args>
void emit(signal<R(Args...)>& sig, Args... args)
{
sig.emit(aux::do_nothing(), std::forward<Args>(args)...);
}
#include <functional>
#include <iostream>
int main()
{
signal<int()> sig;
sig.bind([]() -> int { std::cout << "Hello, "; return 1; });
sig.bind([]() -> int { std::cout << "world!\n"; return 2; });
emit(sig);
std::cout << sig.emit(std::plus<int>()) << '\n';
signal<void(int, int)> sig2;
sig2.bind([](int const x, int const y) { std::cout << (x*y) << '\n'; });
sig2.bind([](int const x, int const y) { std::cout << (x+y) << '\n'; });
emit(sig2, 10, 2);
signal<std::string()> sig3;
sig3.bind([]{ return "Stack"; });
sig3.bind([]{ return "Overflow"; });
sig3.bind([]{ return " C++\n"; });
std::cout << sig3.emit(std::plus<std::string>()) << '\n';
}