commit f313d1abbed0c7a3ba04a0b5c04826db5c1c1ab8 Author: Sara Date: Wed Apr 16 20:33:18 2025 +0200 feat: added callabe and signal diff --git a/callable.hpp b/callable.hpp new file mode 100644 index 0000000..91e687c --- /dev/null +++ b/callable.hpp @@ -0,0 +1,69 @@ +#ifndef CANVAS_CALLABLE_HPP +#define CANVAS_CALLABLE_HPP + +#include +#include + +namespace ce { +/* Base interface template for a member function pointer object + */ +template +struct HiddenCallableBase { + virtual ~HiddenCallableBase() = default; + virtual Return call(Args... args) const = 0; + virtual bool equals(HiddenCallableBase const &other) const; +}; + +template bool HiddenCallableBase::equals(HiddenCallableBase const &other) const { + return &other == this; +} + +/* Hidden component of a Callable, specialized for a specific target object + */ +template +struct HiddenCallable : public HiddenCallableBase { +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 const &other) const override; +}; + +template bool HiddenCallable::equals(HiddenCallableBase const &other) const { + HiddenCallable const *cast{dynamic_cast const *>(&other)}; + return cast != nullptr && &other == this && cast->target == this->target && cast->function == this->function; +} + +template HiddenCallable::HiddenCallable(Target *target, Signature function) : target{target}, function{function} {} +template void HiddenCallable::call(Args... args) const { + std::invoke(this->function, this->target, args...); +} + +/* Class for referring to a callable pointer to a member function. + */ +template +class Callable { +public: + std::shared_ptr const> hidden; + template static Callable make(Target *target, Return (Target::*function)(Args...)); + Return call(Args... args); +}; + +template template Callable +Callable::make(Target *target, Return (Target::*function)(Args...)) { + Callable callable; + callable.hidden = std::make_unique>(target, function); + return callable; +}; + +template Return Callable::call(Args... args) { + return this->hidden->call(args...); +} + +template bool operator==(Callable const &lhs, Callable const &rhs) { + return lhs.hidden->equals(*rhs.hidden.get()); +} +} + +#endif // !CANVAS_CALLABLE_HPP diff --git a/signal.hpp b/signal.hpp new file mode 100644 index 0000000..8acc3f0 --- /dev/null +++ b/signal.hpp @@ -0,0 +1,65 @@ +#ifndef CANVAS_SIGNAL_HPP +#define CANVAS_SIGNAL_HPP + +#include "callable.hpp" +#include +#include + +namespace ce { +/*! Observer-listener implementation + */ +template +class Signal { + std::vector> listeners{}; +public: + void connect(Callable callable); + void disconnect(Callable callable); + void invoke(Args...); +}; + +template void Signal::connect(Callable callable) { + this->listeners.push_back(callable); +} + +template void Signal::disconnect(Callable callable) { + std::erase_if(this->listeners, + [&callable](Callable &listener) -> bool { + return listener == callable; + }); +} + +template void Signal::invoke(Args... args) { + for(Callable &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 signal; + + Callable a_callable{Callable::make(&a_object, &A::f)}; + Callable b_callable{Callable::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