Commit e44c8110 authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'issue/989'

parents 87d2e60d b5d446af
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -198,20 +199,18 @@ public: ...@@ -198,20 +199,18 @@ public:
/// Dispatches on the runtime-type of `x`. /// Dispatches on the runtime-type of `x`.
template <class... Ts> template <class... Ts>
bool visit(result<Ts...>& x) { bool visit(result<Ts...>& res) {
switch (x.flag) { auto f = detail::make_overload(
case rt_value: [this](auto& x) {
(*this)(x.value); (*this)(x);
return true; return true;
case rt_error: },
(*this)(x.err); [this](delegated<Ts...>&) {
return true;
case rt_delegated:
(*this)(); (*this)();
return true; return true;
default: },
return false; [this](skip_t&) { return false; });
} return caf::visit(f, res);
} }
}; };
......
...@@ -33,14 +33,6 @@ ...@@ -33,14 +33,6 @@
namespace caf { namespace caf {
/// Helper class to construct an `expected<T>` that represents no error.
/// @relates expected
struct no_error_t {};
/// The only instance of ::no_error_t.
/// @relates expected
constexpr no_error_t no_error = no_error_t{};
/// Represents the result of a computation which can either complete /// Represents the result of a computation which can either complete
/// successfully with an instance of type `T` or fail with an `error`. /// successfully with an instance of type `T` or fail with an `error`.
/// @tparam T The type of the result. /// @tparam T The type of the result.
...@@ -85,10 +77,6 @@ public: ...@@ -85,10 +77,6 @@ public:
new (std::addressof(error_)) caf::error{std::move(e)}; new (std::addressof(error_)) caf::error{std::move(e)};
} }
expected(no_error_t) noexcept : engaged_(false) {
new (std::addressof(error_)) caf::error{};
}
expected(const expected& other) noexcept(nothrow_copy) { expected(const expected& other) noexcept(nothrow_copy) {
construct(other); construct(other);
} }
...@@ -375,10 +363,6 @@ public: ...@@ -375,10 +363,6 @@ public:
// nop // nop
} }
expected(no_error_t) noexcept {
// nop
}
expected(caf::error e) noexcept : error_(std::move(e)) { expected(caf::error e) noexcept : error_(std::move(e)) {
// nop // nop
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <type_traits> #include <type_traits>
#include "caf/default_sum_type_access.hpp"
#include "caf/delegated.hpp" #include "caf/delegated.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
...@@ -29,196 +30,217 @@ ...@@ -29,196 +30,217 @@
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/skip.hpp" #include "caf/skip.hpp"
#include "caf/sum_type.hpp"
#include "caf/variant.hpp"
namespace caf::detail {
// Tag type for selecting a protected constructor in `result_base`.
struct result_base_message_init {};
} // namespace caf::detail
namespace caf { namespace caf {
enum result_runtime_type { rt_value, rt_error, rt_delegated, rt_skip }; // -- base type for result<Ts...> ----------------------------------------------
/// Base type for all specializations of `result`.
template <class... Ts> template <class... Ts>
class result { class result_base {
public: public:
// clang-format off static_assert(sizeof...(Ts) > 0);
template <class... Us,
class = detail::enable_if_tt< using types = detail::type_list<delegated<Ts...>, message, error, skip_t>;
detail::all_constructible<
detail::type_list<Ts...>, result_base() = default;
detail::type_list<std::decay_t<Us>...>>>>
// clang-format on result_base(result_base&&) = default;
result(Us&&... xs) : flag(rt_value) {
value = make_message(Ts{std::forward<Us>(xs)}...); result_base(const result_base&) = default;
}
result_base& operator=(result_base&&) = default;
result_base& operator=(const result_base&) = default;
template <class Enum, uint8_t = error_category<Enum>::value> template <class Enum, uint8_t = error_category<Enum>::value>
result(Enum x) : flag(rt_error), err(make_error(x)) { result_base(Enum x) : content_(make_error(x)) {
// nop // nop
} }
result(error x) : flag(rt_error), err(std::move(x)) { result_base(error x) : content_(std::move(x)) {
// nop // nop
} }
template <class T, result_base(skip_t) : content_(skip) {
class = typename std::enable_if<
sizeof...(Ts) == 1
&& std::is_convertible<
T, detail::tl_head_t<detail::type_list<Ts...>>>::value>::type>
result(expected<T> x) {
if (x) {
flag = rt_value;
init(std::move(*x));
} else {
flag = rt_error;
err = std::move(x.error());
}
}
result(skip_t) : flag(rt_skip) {
// nop // nop
} }
result(delegated<Ts...>) : flag(rt_delegated) { result_base(delegated<Ts...> x) : content_(x) {
// nop // nop
} }
result(const typed_response_promise<Ts...>&) : flag(rt_delegated) { result_base(const typed_response_promise<Ts...>&)
: content_(delegated<Ts...>{}) {
// nop // nop
} }
result(const response_promise&) : flag(rt_delegated) { result_base(const response_promise&) : content_(delegated<Ts...>{}) {
// nop // nop
} }
result_runtime_type flag; /// @private
message value; auto& get_data() {
error err; return content_;
private:
void init(Ts... xs) {
value = make_message(std::move(xs)...);
} }
};
template <> /// @private
class result<message> { const auto& get_data() const {
public: return content_;
result(message msg) : flag(rt_value), value(std::move(msg)) {
// nop
} }
template <class Enum, uint8_t = error_category<Enum>::value> protected:
result(Enum x) : flag(rt_error), err(make_error(x)) { explicit result_base(detail::result_base_message_init) : content_(message{}) {
// nop // nop
} }
result(error x) : flag(rt_error), err(std::move(x)) { template <class... Us>
explicit result_base(detail::result_base_message_init, Us&&... xs)
: content_(make_message(std::forward<Us>(xs)...)) {
// nop // nop
} }
template <class T> variant<delegated<Ts...>, message, error, skip_t> content_;
result(expected<message> x) { };
if (x) {
flag = rt_value;
value = std::move(*x);
} else {
flag = rt_error;
err = std::move(x.error());
}
}
result(skip_t) : flag(rt_skip) { // -- result<Ts...> and its specializations ------------------------------------
/// Wraps the result of a message handler to represent either a value (wrapped
/// into a `message`), a `delegated<Ts...>` (indicates that another actor is
/// going to respond), or an `error`.
template <class... Ts>
class result;
template <>
class result<void> : public result_base<void> {
public:
using super = result_base<void>;
using super::super;
result() : super(detail::result_base_message_init{}) {
// nop // nop
} }
result(delegated<message>) : flag(rt_delegated) { result(unit_t) : super(detail::result_base_message_init{}) {
// nop // nop
} }
result(const typed_response_promise<message>&) : flag(rt_delegated) { result(delegated<unit_t>) : super(delegated<void>{}) {
// nop // nop
} }
result(const response_promise&) : flag(rt_delegated) { result(const typed_response_promise<unit_t>&) : super(delegated<void>{}) {
// nop // nop
} }
result_runtime_type flag;
message value;
error err;
}; };
template <> template <>
struct result<void> { class result<unit_t> : public result_base<void> {
public: public:
result() : flag(rt_value) { using super = result_base<void>;
using super::super;
result() : super(detail::result_base_message_init{}) {
// nop // nop
} }
result(const unit_t&) : flag(rt_value) { result(unit_t) : super(detail::result_base_message_init{}) {
// nop // nop
} }
template <class Enum, uint8_t = error_category<Enum>::value> result(delegated<unit_t>) : super(delegated<void>{}) {
result(Enum x) : flag(rt_error), err(make_error(x)) {
// nop // nop
} }
result(error x) : flag(rt_error), err(std::move(x)) { result(const typed_response_promise<unit_t>&) : super(delegated<void>{}) {
// nop // nop
} }
};
result(expected<void> x) { template <>
init(x); class result<message> : public result_base<message> {
} public:
using super = result_base<message>;
result(expected<unit_t> x) { using super::super;
init(x);
}
result(skip_t) : flag(rt_skip) { result(message x) {
// nop this->content_ = std::move(x);
} }
result(delegated<void>) : flag(rt_delegated) { result(expected<message> x) {
// nop if (x)
this->content_ = std::move(*x);
else
this->content_ = std::move(x.error());
} }
result(delegated<unit_t>) : flag(rt_delegated) { result& operator=(expected<message> x) {
// nop if (x)
this->content_ = std::move(*x);
else
this->content_ = std::move(x.error());
return *this;
} }
};
result(const typed_response_promise<void>&) : flag(rt_delegated) { template <class T>
class result<T> : public result_base<T> {
public:
using super = result_base<T>;
using super::super;
result(T x) : super(detail::result_base_message_init{}, std::move(x)) {
// nop // nop
} }
result(const typed_response_promise<unit_t>&) : flag(rt_delegated) { result(expected<T> x) {
// nop if (x)
this->content_ = make_message(std::move(*x));
else
this->content_ = std::move(x.error());
} }
result(const response_promise&) : flag(rt_delegated) { result& operator=(expected<T> x) {
// nop if (x)
this->content_ = make_message(std::move(*x));
else
this->content_ = std::move(x.error());
return *this;
} }
};
result_runtime_type flag; template <class T0, class T1, class... Ts>
message value; class result<T0, T1, Ts...> : public result_base<T0, T1, Ts...> {
error err; public:
using super = result_base<T0, T1, Ts...>;
using super::super;
private: result(T0 x0, T1 x1, Ts... xs)
template <class T> : super(detail::result_base_message_init{}, std::move(x0), std::move(x1),
void init(T& x) { std::move(xs)...) {
if (x) { // nop
flag = rt_value;
} else {
flag = rt_error;
err = std::move(x.error());
}
} }
}; };
template <> // -- sum type access to result<Ts...> -----------------------------------------
struct result<unit_t> : result<void> {
using super = result<void>;
using super::super; template <class... Ts>
struct sum_type_access<result<Ts...>> : default_sum_type_access<result<Ts...>> {
// nop
}; };
template <class T> template <class T>
......
...@@ -196,7 +196,7 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) { ...@@ -196,7 +196,7 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) {
case match_result::no_match: { // Blocking actors can have fallback case match_result::no_match: { // Blocking actors can have fallback
// handlers for catch-all rules. // handlers for catch-all rules.
auto sres = bhvr.fallback(self->current_element_->payload); auto sres = bhvr.fallback(self->current_element_->payload);
if (sres.flag != rt_skip) { if (!holds_alternative<skip_t>(sres)) {
visitor.visit(sres); visitor.visit(sres);
return check_if_done(); return check_if_done();
} }
......
...@@ -93,16 +93,9 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -93,16 +93,9 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
}); });
auto call_default_handler = [&] { auto call_default_handler = [&] {
auto sres = call_handler(default_handler_, this, x.payload); auto sres = call_handler(default_handler_, this, x.payload);
switch (sres.flag) { auto f = detail::make_overload([&](auto& x) { visitor.visit(x); },
default: [&](skip_t& x) { skipped = true; });
break; visit(f, sres);
case rt_error:
case rt_value:
visitor.visit(sres);
break;
case rt_skip:
skipped = true;
}
}; };
if (bhvr_stack_.empty()) { if (bhvr_stack_.empty()) {
call_default_handler(); call_default_handler();
......
...@@ -701,17 +701,10 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -701,17 +701,10 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
setf(has_timeout_flag); setf(has_timeout_flag);
}); });
auto call_default_handler = [&] { auto call_default_handler = [&] {
auto f = detail::make_overload([&](auto& val) { visitor.visit(val); },
[&](skip_t&) { skipped = true; });
auto sres = call_handler(default_handler_, this, x.payload); auto sres = call_handler(default_handler_, this, x.payload);
switch (sres.flag) { visit(f, sres);
default:
break;
case rt_error:
case rt_value:
visitor.visit(sres);
break;
case rt_skip:
skipped = true;
}
}; };
if (bhvr_stack_.empty()) { if (bhvr_stack_.empty()) {
call_default_handler(); call_default_handler();
...@@ -1138,15 +1131,14 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1138,15 +1131,14 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
// Utility for invoking the default handler. // Utility for invoking the default handler.
auto fallback = [&] { auto fallback = [&] {
auto sres = call_handler(default_handler_, this, x.payload); auto sres = call_handler(default_handler_, this, x.payload);
switch (sres.flag) { if (holds_alternative<skip_t>(sres)) {
default: CAF_LOG_DEBUG("default handler skipped open_stream_msg:" << osm.msg);
CAF_LOG_DEBUG( return invoke_message_result::skipped;
"default handler was called for open_stream_msg:" << osm.msg); } else {
fail(sec::stream_init_failed, "dropped open_stream_msg (no match)"); CAF_LOG_DEBUG(
return invoke_message_result::dropped; "default handler was called for open_stream_msg:" << osm.msg);
case rt_skip: fail(sec::stream_init_failed, "dropped open_stream_msg (no match)");
CAF_LOG_DEBUG("default handler skipped open_stream_msg:" << osm.msg); return invoke_message_result::dropped;
return invoke_message_result::skipped;
} }
}; };
// Invoke behavior and dispatch on the result. // Invoke behavior and dispatch on the result.
......
...@@ -116,13 +116,3 @@ CAF_TEST(construction_with_none) { ...@@ -116,13 +116,3 @@ CAF_TEST(construction_with_none) {
CHECK(!x); CHECK(!x);
CHECK(!x.error()); CHECK(!x.error());
} }
CAF_TEST(construction_with_no_error) {
e_int x{no_error};
CHECK(!x);
CHECK(!x.error());
auto f = []() -> e_int {
return no_error;
};
CHECK_EQ(f(), x);
}
...@@ -31,38 +31,34 @@ namespace { ...@@ -31,38 +31,34 @@ namespace {
template<class T> template<class T>
void test_unit_void() { void test_unit_void() {
auto x = result<T>{}; auto x = result<T>{};
CAF_CHECK_EQUAL(x.flag, rt_value); CAF_CHECK(holds_alternative<message>(x));
x = skip(); x = skip;
CAF_CHECK_EQUAL(x.flag, rt_skip); CAF_CHECK(holds_alternative<skip_t>(x));
x = expected<T>{};
CAF_CHECK_EQUAL(x.flag, rt_value);
x = expected<T>{sec::unexpected_message};
CAF_CHECK_EQUAL(x.flag, rt_error);
CAF_CHECK_EQUAL(x.err, make_error(sec::unexpected_message));
} }
} // namespace anonymous } // namespace anonymous
CAF_TEST(skip) { CAF_TEST(skip) {
auto x = result<>{skip()}; auto x = result<int>{skip};
CAF_CHECK_EQUAL(x.flag, rt_skip); CAF_CHECK(holds_alternative<skip_t>(x));
CAF_CHECK(x.value.empty());
} }
CAF_TEST(value) { CAF_TEST(value) {
auto x = result<int>{42}; auto x = result<int>{42};
CAF_CHECK_EQUAL(x.flag, rt_value); CAF_REQUIRE(holds_alternative<message>(x));
CAF_CHECK_EQUAL(x.value.get_as<int>(0), 42); if (auto view = make_typed_message_view<int>(get<message>(x)))
CAF_CHECK_EQUAL(get<0>(view), 42);
else
CAF_FAIL("unexpected types in result message");
} }
CAF_TEST(expected) { CAF_TEST(expected) {
auto x = result<int>{expected<int>{42}}; auto x = result<int>{expected<int>{42}};
CAF_CHECK_EQUAL(x.flag, rt_value); CAF_REQUIRE(holds_alternative<message>(x));
CAF_CHECK_EQUAL(x.value.get_as<int>(0), 42); if (auto view = make_typed_message_view<int>(get<message>(x)))
x = expected<int>{sec::unexpected_message}; CAF_CHECK_EQUAL(get<0>(view), 42);
CAF_CHECK_EQUAL(x.flag, rt_error); else
CAF_CHECK_EQUAL(x.err, make_error(sec::unexpected_message)); CAF_FAIL("unexpected types in result message");
CAF_CHECK(x.value.empty());
} }
CAF_TEST(void_specialization) { CAF_TEST(void_specialization) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment