Skip to content

Commit

Permalink
more variations of functional handler
Browse files Browse the repository at this point in the history
  • Loading branch information
artpaul committed Jan 18, 2025
1 parent 489d1b4 commit 19f8f11
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 66 deletions.
149 changes: 91 additions & 58 deletions include/acto/acto.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,66 +307,73 @@ class actor {
virtual void invoke(const std::unique_ptr<core::msg_t> msg) const = 0;
};

/** Wrapper for member function pointers. */
template <typename F, typename M>
struct is_handler_signature {
static constexpr bool value =
std::is_invocable_v<F> || std::is_invocable_v<F, const M&> ||
std::is_invocable_v<F, M&&> || std::is_invocable_v<F, actor_ref> ||
std::is_invocable_v<F, actor_ref, const M&> ||
std::is_invocable_v<F, actor_ref, M&&>;
};

template <typename M, typename P>
using message_reference_t =
std::conditional_t<core::msg_wrap_t<M>::is_value_movable &&
std::is_rvalue_reference_v<P>,
core::msg_wrap_t<M>&&,
const core::msg_wrap_t<M>&>;

/// Wrapper for member function pointers.
template <typename M, typename C, typename P>
class mem_handler_t : public handler_t {
class mem_handler_t final : public handler_t {
using F = std::function<void(C*, actor_ref, P)>;

public:
mem_handler_t(F&& func, C* ptr)
mem_handler_t(F&& func, C* ptr) noexcept
: func_(std::move(func))
, ptr_(ptr) {
assert(func_);
assert(ptr_);
}

void invoke(std::unique_ptr<core::msg_t> msg) const override {
using message_reference_t =
typename std::conditional<core::msg_wrap_t<M>::is_value_movable,
core::msg_wrap_t<M>&&,
const core::msg_wrap_t<M>&>::type;

void invoke(std::unique_ptr<core::msg_t> msg) const final {
func_(ptr_, actor_ref(msg->sender, true),
static_cast<message_reference_t>(*msg.get()).data());
static_cast<message_reference_t<M, P>>(*msg.get()).data());
}

private:
const F func_;
C* const ptr_;
};

/** Wrapper for functor objects. */
template <typename M>
class fun_handler_t : public handler_t {
using F = std::function<void(actor_ref sender, const M& msg)>;
/// Wrapper for functor objects.
template <typename M, typename... Args>
class fun_handler_t final : public handler_t {
using F = std::function<void(Args...)>;

public:
fun_handler_t(F&& func)
fun_handler_t(F&& func) noexcept
: func_(std::move(func)) {
assert(func_);
}

void invoke(std::unique_ptr<core::msg_t> msg) const override {
func_(actor_ref(msg->sender, true),
static_cast<core::msg_wrap_t<M>*>(msg.get())->data());
}

private:
const F func_;
};

template <typename M>
class fun_handler_no_args_t : public handler_t {
using F = std::function<void()>;

public:
fun_handler_no_args_t(F&& func)
: func_(std::move(func)) {
assert(func_);
}

void invoke(std::unique_ptr<core::msg_t>) const override {
func_();
void invoke(std::unique_ptr<core::msg_t> msg) const final {
if constexpr (sizeof...(Args) == 0) {
func_();
} else if constexpr (sizeof...(Args) == 1) {
using P0 = std::tuple_element_t<0, std::tuple<Args...>>;

if constexpr (std::is_same_v<actor_ref, std::decay_t<P0>>) {
func_(actor_ref(msg->sender, true));
} else {
func_(static_cast<message_reference_t<M, P0>>(*msg.get()).data());
}
} else if constexpr (sizeof...(Args) == 2) {
using P1 = std::tuple_element_t<1, std::tuple<Args...>>;

func_(actor_ref(msg->sender, true),
static_cast<message_reference_t<M, P1>>(*msg.get()).data());
}
}

private:
Expand All @@ -377,11 +384,11 @@ class actor {
virtual ~actor() noexcept = default;

protected:
inline actor_ref context() const {
actor_ref context() const {
return context_;
}

inline actor_ref self() const {
actor_ref self() const {
return self_;
}

Expand All @@ -391,9 +398,10 @@ class actor {
/// Stops itself.
void die() noexcept;

public:
/// Sets handler as member function pointer.
template <typename M, typename ClassName, typename P>
inline void handler(void (ClassName::*func)(actor_ref, P)) {
void handler(void (ClassName::*func)(actor_ref, P)) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
Expand All @@ -403,34 +411,59 @@ class actor {
}

/// Sets handler as functor object.
template <typename M>
inline void handler(std::function<void(actor_ref, const M&)> func) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M>>(std::move(func)));
}

/// Sets handler as functor object.
template <typename M>
inline void handler(std::function<void()> func) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_no_args_t<M>>(std::move(func)));
template <typename M,
typename F,
typename = std::enable_if_t<is_handler_signature<F, M>::value>>
void handler(F&& func) {
if constexpr (std::is_invocable_v<F>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M>>(std::move(func)));
} else if constexpr (std::is_invocable_v<F, const M&>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M, const M&>>(std::move(func)));
} else if constexpr (std::is_invocable_v<F, M&&>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M, M&&>>(std::move(func)));
} else if constexpr (std::is_invocable_v<F, actor_ref>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M, actor_ref>>(std::move(func)));
} else if constexpr (std::is_invocable_v<F, actor_ref, const M&>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M, actor_ref, const M&>>(
std::move(func)));
} else if constexpr (std::is_invocable_v<F, actor_ref, M&&>) {
set_handler(
// Type of the handler.
std::type_index(typeid(M)),
// Callback.
std::make_unique<fun_handler_t<M, actor_ref, M&&>>(std::move(func)));
}
}

/// Removes handler for the given type.
template <typename M>
inline void handler() {
void handler() {
set_handler(std::type_index(typeid(M)), nullptr);
}

/// Removes handler for the given type.
template <typename M>
inline void handler(std::nullptr_t) {
void handler(std::nullptr_t) {
set_handler(std::type_index(typeid(M)), nullptr);
}

Expand Down
62 changes: 54 additions & 8 deletions test/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,56 @@ TEST_CASE("Spawn actor") {
acto::shutdown();
}

TEST_CASE("Handler") {
TEST_CASE("Function handlers") {
struct A : acto::actor {
struct M { };
struct A1 { };

struct M0 { };

struct M1 {
std::string s;
};

struct MR {
std::string s;
};

struct M2 {
std::string s;
};

using MU = std::unique_ptr<std::string>;

A(std::atomic<int>& x)
: x_(x) {
actor::handler<M>([this]() { x_++; });
actor::handler<A1>([this](acto::actor_ref sender) {
REQUIRE(!bool(sender));
x_++;
});

actor::handler<M0>([this]() { x_++; });

actor::handler<M1>([this](const M1& m) {
REQUIRE(m.s == "M1");
x_++;
});

actor::handler<MR>([this](MR&& m) {
REQUIRE(m.s == "MR");
x_++;
});

actor::handler<M2>([this](acto::actor_ref sender, const M2& m) {
REQUIRE(!bool(sender));
REQUIRE(m.s == "M2");
x_++;
});

actor::handler<MU>([this](acto::actor_ref sender, MU m) {
REQUIRE(!bool(sender));
REQUIRE(*m == "MU");
x_++;
});
}

private:
Expand All @@ -80,14 +123,17 @@ TEST_CASE("Handler") {
std::atomic<int> x{0};
acto::actor_ref a = acto::spawn<A>(x);

CHECK(a.send<A::M>());
CHECK(a.send<A::M>());
CHECK(a.send<A::M>());
CHECK(a.send<A::A1>());
CHECK(a.send<A::M0>());
CHECK(a.send<A::M1>(A::M1{.s = "M1"}));
CHECK(a.send<A::M2>(A::M2{.s = "M2"}));
CHECK(a.send<A::MR>(A::MR{.s = "MR"}));
CHECK(a.send<A::MU>(std::make_unique<std::string>("MU")));
// Cleanup actor.
acto::destroy_and_wait(a);

// Check values.
REQUIRE(x.load() == 3);
REQUIRE(x.load() == 6);

// Shutdown.
acto::shutdown();
Expand All @@ -112,7 +158,7 @@ TEST_CASE("Process binded actors at exit") {
};

A() {
actor::handler<M>([](acto::actor_ref, const M& m) { (*m.counter)++; });
actor::handler<M>([](const M& m) { (*m.counter)++; });
}
};

Expand Down

0 comments on commit 19f8f11

Please sign in to comment.