Commit 747abb49 authored by Dominik Charousset's avatar Dominik Charousset

Follow C++23 std::expected design in caf::expected

With `std::expected` standardized for C++23, we can now follow the API
of the standard class and deprecate all legacy APIs that differ from
that. Eventually, `caf::expected<T>` should become an alias for
`std::expected<T, caf::error>`.
parent d9aca2e1
...@@ -24,6 +24,8 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -24,6 +24,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
- Not initializing the meta objects table now prints a diagnosis message before - Not initializing the meta objects table now prints a diagnosis message before
aborting the program. Previously, the application would usually just crash due aborting the program. Previously, the application would usually just crash due
to a `nullptr`-access inside some CAF function. to a `nullptr`-access inside some CAF function.
- The class `expected` now implements the monadic member functions from C++23
`std::expected` as well as `value_or`.
### Fixed ### Fixed
...@@ -46,6 +48,14 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -46,6 +48,14 @@ is based on [Keep a Changelog](https://keepachangelog.com).
actors or activities, scheduled actors now limit the amount of actions that actors or activities, scheduled actors now limit the amount of actions that
may run in one iteration (#1364). may run in one iteration (#1364).
### Deprecated
- All member functions from `caf::expected` that have no equivalent in
`std::expected` are now deprecated. Further, `caf::expected<unit_t>` as well
as constructing from `unit_t` are deprecated as well. The reasoning behind
this decision is that `caf::expected` should eventually become an alias for
`std::expected<T, caf::error>`.
## [0.19.0-rc.1] - 2022-10-31 ## [0.19.0-rc.1] - 2022-10-31
### Added ### Added
......
...@@ -46,9 +46,10 @@ void caf_main(actor_system& system) { ...@@ -46,9 +46,10 @@ void caf_main(actor_system& system) {
auto cell2 = system.spawn(unchecked_cell); auto cell2 = system.spawn(unchecked_cell);
// --(rst-spawn-cell-end)-- // --(rst-spawn-cell-end)--
auto f = make_function_view(cell1); auto f = make_function_view(cell1);
cout << "cell value: " << f(get_atom_v) << endl; cout << "cell value: " << caf::to_string(f(get_atom_v)) << endl;
f(put_atom_v, 20); f(put_atom_v, 20);
cout << "cell value (after setting to 20): " << f(get_atom_v) << endl; cout << "cell value (after setting to 20): " << caf::to_string(f(get_atom_v))
<< endl;
// Get an unchecked cell and send it some garbage. Triggers an "unexpected // Get an unchecked cell and send it some garbage. Triggers an "unexpected
// message" error (and terminates cell2!). // message" error (and terminates cell2!).
anon_send(cell2, "hello there!"); anon_send(cell2, "hello there!");
......
...@@ -125,6 +125,7 @@ std::ostream& operator<<(std::ostream& out, const expected<int>& x) { ...@@ -125,6 +125,7 @@ std::ostream& operator<<(std::ostream& out, const expected<int>& x) {
} }
void caf_main(actor_system& sys) { void caf_main(actor_system& sys) {
using std::cout;
// Spawn our matrix. // Spawn our matrix.
static constexpr int rows = 3; static constexpr int rows = 3;
static constexpr int columns = 6; static constexpr int columns = 6;
...@@ -140,18 +141,18 @@ void caf_main(actor_system& sys) { ...@@ -140,18 +141,18 @@ void caf_main(actor_system& sys) {
// Print out matrix. // Print out matrix.
for (int row = 0; row < rows; ++row) { for (int row = 0; row < rows; ++row) {
for (int column = 0; column < columns; ++column) for (int column = 0; column < columns; ++column)
std::cout << std::setw(4) << f(get_atom_v, row, column) << ' '; cout << std::setw(4) << f(get_atom_v, row, column) << ' ';
std::cout << '\n'; cout << '\n';
} }
// Print out AVG for each row and column. // Print out AVG for each row and column.
for (int row = 0; row < rows; ++row) for (int row = 0; row < rows; ++row)
std::cout << "AVG(row " << row cout << "AVG(row " << row << ") = "
<< ") = " << f(get_atom_v, average_atom_v, row_atom_v, row) << caf::to_string(f(get_atom_v, average_atom_v, row_atom_v, row))
<< '\n'; << '\n';
for (int column = 0; column < columns; ++column) for (int column = 0; column < columns; ++column)
std::cout << "AVG(column " << column cout << "AVG(column " << column << ") = "
<< ") = " << f(get_atom_v, average_atom_v, column_atom_v, column) << caf::to_string(f(get_atom_v, average_atom_v, column_atom_v, column))
<< '\n'; << '\n';
} }
CAF_MAIN(id_block::fan_out_request) CAF_MAIN(id_block::fan_out_request)
...@@ -94,7 +94,8 @@ void client_repl(function_view<calculator> f) { ...@@ -94,7 +94,8 @@ void client_repl(function_view<calculator> f) {
usage(); usage();
else else
cout << " = " cout << " = "
<< (words[1] == "+" ? f(add_atom_v, *x, *y) : f(sub_atom_v, *x, *y)) << caf::to_string(words[1] == "+" ? f(add_atom_v, *x, *y)
: f(sub_atom_v, *x, *y))
<< "\n"; << "\n";
} }
} }
......
...@@ -544,6 +544,9 @@ struct is_expected : std::false_type {}; ...@@ -544,6 +544,9 @@ struct is_expected : std::false_type {};
template <class T> template <class T>
struct is_expected<expected<T>> : std::true_type {}; struct is_expected<expected<T>> : std::true_type {};
template <class T>
constexpr bool is_expected_v = is_expected<T>::value;
// Checks whether `T` and `U` are integers of the same size and signedness. // Checks whether `T` and `U` are integers of the same size and signedness.
// clang-format off // clang-format off
template <class T, class U, template <class T, class U,
......
This diff is collapsed.
...@@ -52,20 +52,6 @@ private: ...@@ -52,20 +52,6 @@ private:
std::tuple<Ts...>* storage_; std::tuple<Ts...>* storage_;
}; };
template <>
class function_view_storage<unit_t> {
public:
using type = function_view_storage;
explicit function_view_storage(unit_t&) {
// nop
}
void operator()() {
// nop
}
};
struct CAF_CORE_EXPORT function_view_storage_catch_all { struct CAF_CORE_EXPORT function_view_storage_catch_all {
message* storage_; message* storage_;
...@@ -95,11 +81,6 @@ struct function_view_flattened_result<std::tuple<T>> { ...@@ -95,11 +81,6 @@ struct function_view_flattened_result<std::tuple<T>> {
using type = T; using type = T;
}; };
template <>
struct function_view_flattened_result<std::tuple<void>> {
using type = unit_t;
};
template <class T> template <class T>
using function_view_flattened_result_t = using function_view_flattened_result_t =
typename function_view_flattened_result<T>::type; typename function_view_flattened_result<T>::type;
...@@ -174,13 +155,24 @@ public: ...@@ -174,13 +155,24 @@ public:
if (!impl_) if (!impl_)
return result_type{sec::bad_function_call}; return result_type{sec::bad_function_call};
error err; error err;
function_view_result<value_type> result; if constexpr (std::is_void_v<value_type>) {
self_->request(impl_, timeout, std::forward<Ts>(xs)...) self_->request(impl_, timeout, std::forward<Ts>(xs)...)
.receive([&](error& x) { err = std::move(x); }, .receive([&](error& x) { err = std::move(x); }, [] {});
typename function_view_storage<value_type>::type{result.value}); if (err)
if (err) return result_type{err};
return result_type{err}; else
return result_type{flatten(result.value)}; return result_type{};
} else {
function_view_result<value_type> result;
self_->request(impl_, timeout, std::forward<Ts>(xs)...)
.receive([&](error& x) { err = std::move(x); },
typename function_view_storage<value_type>::type{
result.value});
if (err)
return result_type{err};
else
return result_type{flatten(result.value)};
}
} }
void assign(type x) { void assign(type x) {
......
This diff is collapsed.
...@@ -788,12 +788,12 @@ expected<void> read_port(native_socket fd, SockAddrType& sa) { ...@@ -788,12 +788,12 @@ expected<void> read_port(native_socket fd, SockAddrType& sa) {
socket_size_type len = sizeof(SockAddrType); socket_size_type len = sizeof(SockAddrType);
CALL_CFUN(res, detail::cc_zero, "getsockname", CALL_CFUN(res, detail::cc_zero, "getsockname",
getsockname(fd, reinterpret_cast<sockaddr*>(&sa), &len)); getsockname(fd, reinterpret_cast<sockaddr*>(&sa), &len));
return unit; return {};
} }
expected<void> set_inaddr_any(native_socket, sockaddr_in& sa) { expected<void> set_inaddr_any(native_socket, sockaddr_in& sa) {
sa.sin_addr.s_addr = INADDR_ANY; sa.sin_addr.s_addr = INADDR_ANY;
return unit; return {};
} }
expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) { expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) {
...@@ -804,7 +804,7 @@ expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) { ...@@ -804,7 +804,7 @@ expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) {
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<setsockopt_ptr>(&off), reinterpret_cast<setsockopt_ptr>(&off),
static_cast<socket_size_type>(sizeof(off)))); static_cast<socket_size_type>(sizeof(off))));
return unit; return {};
} }
template <int Family, int SockType = SOCK_STREAM> template <int Family, int SockType = SOCK_STREAM>
......
...@@ -128,7 +128,7 @@ expected<void> child_process_inherit(native_socket fd, bool new_value) { ...@@ -128,7 +128,7 @@ expected<void> child_process_inherit(native_socket fd, bool new_value) {
// calculate and set new flags // calculate and set new flags
auto wf = (!new_value) ? (rf | FD_CLOEXEC) : (rf & (~(FD_CLOEXEC))); auto wf = (!new_value) ? (rf | FD_CLOEXEC) : (rf & (~(FD_CLOEXEC)));
CALL_CFUN(set_res, detail::cc_not_minus1, "fcntl", fcntl(fd, F_SETFD, wf)); CALL_CFUN(set_res, detail::cc_not_minus1, "fcntl", fcntl(fd, F_SETFD, wf));
return unit; return {};
} }
expected<void> keepalive(native_socket fd, bool new_value) { expected<void> keepalive(native_socket fd, bool new_value) {
...@@ -137,7 +137,7 @@ expected<void> keepalive(native_socket fd, bool new_value) { ...@@ -137,7 +137,7 @@ expected<void> keepalive(native_socket fd, bool new_value) {
CALL_CFUN(res, detail::cc_zero, "setsockopt", CALL_CFUN(res, detail::cc_zero, "setsockopt",
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &value, setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &value,
static_cast<unsigned>(sizeof(value)))); static_cast<unsigned>(sizeof(value))));
return unit; return {};
} }
expected<void> nonblocking(native_socket fd, bool new_value) { expected<void> nonblocking(native_socket fd, bool new_value) {
...@@ -147,7 +147,7 @@ expected<void> nonblocking(native_socket fd, bool new_value) { ...@@ -147,7 +147,7 @@ expected<void> nonblocking(native_socket fd, bool new_value) {
// calculate and set new flags // calculate and set new flags
auto wf = new_value ? (rf | O_NONBLOCK) : (rf & (~(O_NONBLOCK))); auto wf = new_value ? (rf | O_NONBLOCK) : (rf & (~(O_NONBLOCK)));
CALL_CFUN(set_res, detail::cc_not_minus1, "fcntl", fcntl(fd, F_SETFL, wf)); CALL_CFUN(set_res, detail::cc_not_minus1, "fcntl", fcntl(fd, F_SETFL, wf));
return unit; return {};
} }
expected<void> allow_sigpipe(native_socket fd, bool new_value) { expected<void> allow_sigpipe(native_socket fd, bool new_value) {
...@@ -157,12 +157,12 @@ expected<void> allow_sigpipe(native_socket fd, bool new_value) { ...@@ -157,12 +157,12 @@ expected<void> allow_sigpipe(native_socket fd, bool new_value) {
setsockopt(fd, SOL_SOCKET, no_sigpipe_socket_flag, &value, setsockopt(fd, SOL_SOCKET, no_sigpipe_socket_flag, &value,
static_cast<unsigned>(sizeof(value)))); static_cast<unsigned>(sizeof(value))));
} }
return unit; return {};
} }
expected<void> allow_udp_connreset(native_socket, bool) { expected<void> allow_udp_connreset(native_socket, bool) {
// nop; SIO_UDP_CONNRESET only exists on Windows // nop; SIO_UDP_CONNRESET only exists on Windows
return unit; return {};
} }
std::pair<native_socket, native_socket> create_pipe() { std::pair<native_socket, native_socket> create_pipe() {
...@@ -353,7 +353,7 @@ expected<void> send_buffer_size(native_socket fd, int new_value) { ...@@ -353,7 +353,7 @@ expected<void> send_buffer_size(native_socket fd, int new_value) {
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
reinterpret_cast<setsockopt_ptr>(&new_value), reinterpret_cast<setsockopt_ptr>(&new_value),
static_cast<socket_size_type>(sizeof(int)))); static_cast<socket_size_type>(sizeof(int))));
return unit; return {};
} }
expected<void> tcp_nodelay(native_socket fd, bool new_value) { expected<void> tcp_nodelay(native_socket fd, bool new_value) {
...@@ -363,7 +363,7 @@ expected<void> tcp_nodelay(native_socket fd, bool new_value) { ...@@ -363,7 +363,7 @@ expected<void> tcp_nodelay(native_socket fd, bool new_value) {
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<setsockopt_ptr>(&flag), reinterpret_cast<setsockopt_ptr>(&flag),
static_cast<socket_size_type>(sizeof(flag)))); static_cast<socket_size_type>(sizeof(flag))));
return unit; return {};
} }
bool is_error(signed_size_type res, bool is_nonblock) { bool is_error(signed_size_type res, bool is_nonblock) {
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
} \ } \
void CAF_UNIQUE(test)::run_test_impl() void CAF_UNIQUE(test)::run_test_impl()
#define SUBCASE(description) \
CAF_MESSAGE(description); \
if (true)
#define GIVEN(description) \ #define GIVEN(description) \
CAF_MESSAGE("GIVEN " description); \ CAF_MESSAGE("GIVEN " description); \
if (true) if (true)
......
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