feat: added callabe and signal
This commit is contained in:
commit
f313d1abbe
69
callable.hpp
Normal file
69
callable.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef CANVAS_CALLABLE_HPP
|
||||
#define CANVAS_CALLABLE_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace ce {
|
||||
/* Base interface template for a member function pointer object
|
||||
*/
|
||||
template <class Return, class... Args>
|
||||
struct HiddenCallableBase {
|
||||
virtual ~HiddenCallableBase() = default;
|
||||
virtual Return call(Args... args) const = 0;
|
||||
virtual bool equals(HiddenCallableBase<Return, Args...> const &other) const;
|
||||
};
|
||||
|
||||
template <class Return, class... Args> bool HiddenCallableBase<Return, Args...>::equals(HiddenCallableBase<Return, Args...> const &other) const {
|
||||
return &other == this;
|
||||
}
|
||||
|
||||
/* Hidden component of a Callable, specialized for a specific target object
|
||||
*/
|
||||
template <class Target, class Return, class... Args>
|
||||
struct HiddenCallable : public HiddenCallableBase<Return, Args...> {
|
||||
typedef Return (Target::*Signature)(Args...);
|
||||
HiddenCallable(Target *target, Signature function);
|
||||
Target *target{nullptr};
|
||||
Signature function{nullptr};
|
||||
virtual void call(Args...) const override;
|
||||
virtual bool equals(HiddenCallableBase<Return, Args...> const &other) const override;
|
||||
};
|
||||
|
||||
template <class Target, class Return, class... Args> bool HiddenCallable<Target, Return, Args...>::equals(HiddenCallableBase<Return, Args...> const &other) const {
|
||||
HiddenCallable<Target, Return, Args...> const *cast{dynamic_cast<HiddenCallable<Target, Return, Args...> const *>(&other)};
|
||||
return cast != nullptr && &other == this && cast->target == this->target && cast->function == this->function;
|
||||
}
|
||||
|
||||
template <class Target, class Return, class... Args> HiddenCallable<Target, Return, Args...>::HiddenCallable(Target *target, Signature function) : target{target}, function{function} {}
|
||||
template <class Target, class Return, class... Args> void HiddenCallable<Target, Return, Args...>::call(Args... args) const {
|
||||
std::invoke(this->function, this->target, args...);
|
||||
}
|
||||
|
||||
/* Class for referring to a callable pointer to a member function.
|
||||
*/
|
||||
template <class Return, class... Args>
|
||||
class Callable {
|
||||
public:
|
||||
std::shared_ptr<HiddenCallableBase<Return, Args...> const> hidden;
|
||||
template <class Target> static Callable<Return, Args...> make(Target *target, Return (Target::*function)(Args...));
|
||||
Return call(Args... args);
|
||||
};
|
||||
|
||||
template <class Return, class... Args> template <class Target> Callable<Return, Args...>
|
||||
Callable<Return, Args...>::make(Target *target, Return (Target::*function)(Args...)) {
|
||||
Callable<Return, Args...> callable;
|
||||
callable.hidden = std::make_unique<HiddenCallable<Target, Return, Args...>>(target, function);
|
||||
return callable;
|
||||
};
|
||||
|
||||
template <class Return, class... Args> Return Callable<Return, Args...>::call(Args... args) {
|
||||
return this->hidden->call(args...);
|
||||
}
|
||||
|
||||
template <class Return, class... Args> bool operator==(Callable<Return, Args...> const &lhs, Callable<Return, Args...> const &rhs) {
|
||||
return lhs.hidden->equals(*rhs.hidden.get());
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !CANVAS_CALLABLE_HPP
|
65
signal.hpp
Normal file
65
signal.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef CANVAS_SIGNAL_HPP
|
||||
#define CANVAS_SIGNAL_HPP
|
||||
|
||||
#include "callable.hpp"
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
namespace ce {
|
||||
/*! Observer-listener implementation
|
||||
*/
|
||||
template <class... Args>
|
||||
class Signal {
|
||||
std::vector<Callable<void, Args...>> listeners{};
|
||||
public:
|
||||
void connect(Callable<void, Args...> callable);
|
||||
void disconnect(Callable<void, Args...> callable);
|
||||
void invoke(Args...);
|
||||
};
|
||||
|
||||
template <class... Args> void Signal<Args...>::connect(Callable<void, Args...> callable) {
|
||||
this->listeners.push_back(callable);
|
||||
}
|
||||
|
||||
template <class... Args> void Signal<Args...>::disconnect(Callable<void, Args...> callable) {
|
||||
std::erase_if(this->listeners,
|
||||
[&callable](Callable<void, Args...> &listener) -> bool {
|
||||
return listener == callable;
|
||||
});
|
||||
}
|
||||
|
||||
template <class... Args> void Signal<Args...>::invoke(Args... args) {
|
||||
for(Callable<void, Args...> &listener : this->listeners) {
|
||||
listener.call(args...);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline
|
||||
void TEST_signals() {
|
||||
struct A {inline void f(int val) {
|
||||
std::printf("A: %d\n", val);
|
||||
}};
|
||||
struct B { inline void f(int val) {
|
||||
std::printf("B: %d\n", val);
|
||||
}};
|
||||
|
||||
A a_object;
|
||||
B b_object;
|
||||
|
||||
Signal<int> signal;
|
||||
|
||||
Callable<void, int> a_callable{Callable<void, int>::make(&a_object, &A::f)};
|
||||
Callable<void, int> b_callable{Callable<void, int>::make(&b_object, &B::f)};
|
||||
|
||||
signal.connect(a_callable);
|
||||
signal.invoke(5);
|
||||
signal.connect(b_callable);
|
||||
signal.invoke(12);
|
||||
signal.disconnect(a_callable);
|
||||
signal.invoke(10);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !CANVAS_SIGNAL_HPP
|
Loading…
Reference in a new issue