Commit 7edfed6e authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/add-message-type'

parents 2ee9e994 78b5ac58
...@@ -514,10 +514,22 @@ endif() ...@@ -514,10 +514,22 @@ endif()
# all projects need the headers of the core components # all projects need the headers of the core components
include_directories("${CAF_INCLUDE_DIRS}") include_directories("${CAF_INCLUDE_DIRS}")
# -- unit tests setup ----------------------------------------------------------
################################################################################ if(NOT CAF_NO_UNIT_TESTS)
# add targets # enable_testing()
################################################################################ function(caf_add_test_suites target)
foreach(suiteName ${ARGN})
string(REPLACE "." "/" suitePath ${suiteName})
target_sources(${target} PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/test/${suitePath}.cpp")
add_test(NAME ${suiteName}
COMMAND ${target} -r300 -n -v5 -s"^${suiteName}$")
endforeach()
endfunction()
endif()
# -- add targets ---------------------------------------------------------------
macro(add_caf_lib name) macro(add_caf_lib name)
string(TOUPPER ${name} upper_name) string(TOUPPER ${name} upper_name)
...@@ -653,48 +665,6 @@ add_optional_caf_binaries(examples) ...@@ -653,48 +665,6 @@ add_optional_caf_binaries(examples)
# build tools if not being told otherwise # build tools if not being told otherwise
add_optional_caf_binaries(tools) add_optional_caf_binaries(tools)
################################################################################
# unit tests setup #
################################################################################
if(NOT CAF_NO_UNIT_TESTS)
# setup unit test binary
add_executable(caf-test
libcaf_test/src/caf-test.cpp
libcaf_test/caf/test/unit_test.hpp
libcaf_test/caf/test/unit_test_impl.hpp
${CAF_ALL_UNIT_TESTS})
target_link_libraries(caf-test
${CAF_EXTRA_LDFLAGS}
${CAF_LIBRARIES}
${PTHREAD_LIBRARIES})
add_custom_target(all_unit_tests)
add_dependencies(caf-test all_unit_tests)
# enumerate all test suites.
foreach(test ${CAF_ALL_UNIT_TESTS})
file(STRINGS ${test} contents)
foreach(line ${contents})
if ("${line}" MATCHES "CAF_SUITE (.*)")
string(REGEX REPLACE ".* CAF_SUITE (.*)" "\\1" suite ${line})
list(APPEND suites ${suite})
endif()
endforeach()
endforeach()
list(REMOVE_DUPLICATES suites)
# creates one CMake test per test suite.
macro (make_test suite)
string(REPLACE " " "_" test_name ${suite})
set(caf_test ${EXECUTABLE_OUTPUT_PATH}/caf-test)
add_test(${test_name} ${caf_test} -r 300 -n -v 5 -s "${suite}" ${ARGN})
endmacro ()
list(LENGTH suites num_suites)
message(STATUS "Found ${num_suites} test suites")
foreach(suite ${suites})
make_test("${suite}")
endforeach ()
endif()
# -- Setup for building manual and API documentation --------------------------- # -- Setup for building manual and API documentation ---------------------------
if(NOT caf_is_subproject) if(NOT caf_is_subproject)
......
...@@ -7,6 +7,7 @@ defaultReleaseBuildFlags = [ ...@@ -7,6 +7,7 @@ defaultReleaseBuildFlags = [
'CAF_ENABLE_RUNTIME_CHECKS:BOOL=yes', 'CAF_ENABLE_RUNTIME_CHECKS:BOOL=yes',
'CAF_NO_OPENCL:BOOL=yes', 'CAF_NO_OPENCL:BOOL=yes',
'CAF_INSTALL_UNIT_TESTS:BOOL=yes', 'CAF_INSTALL_UNIT_TESTS:BOOL=yes',
'CAF_ENABLE_TYPE_ID_CHECKS:BOOL=yes',
] ]
// Default CMake flags for debug builds. // Default CMake flags for debug builds.
...@@ -14,6 +15,7 @@ defaultDebugBuildFlags = defaultReleaseBuildFlags + [ ...@@ -14,6 +15,7 @@ defaultDebugBuildFlags = defaultReleaseBuildFlags + [
'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes', 'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes',
'CAF_LOG_LEVEL:STRING=TRACE', 'CAF_LOG_LEVEL:STRING=TRACE',
'CAF_ENABLE_ACTOR_PROFILER:BOOL=yes', 'CAF_ENABLE_ACTOR_PROFILER:BOOL=yes',
'CAF_ENABLE_TYPE_ID_CHECKS:BOOL=yes',
] ]
// Configures the behavior of our stages. // Configures the behavior of our stages.
...@@ -25,7 +27,6 @@ config = [ ...@@ -25,7 +27,6 @@ config = [
'build', 'build',
'style', 'style',
'tests', 'tests',
'coverage',
], ],
// Our build matrix. Keys are the operating system labels and values are build configurations. // Our build matrix. Keys are the operating system labels and values are build configurations.
buildMatrix: [ buildMatrix: [
...@@ -36,7 +37,6 @@ config = [ ...@@ -36,7 +37,6 @@ config = [
['Linux', [ ['Linux', [
builds: ['debug'], builds: ['debug'],
tools: ['gcc8'], tools: ['gcc8'],
extraSteps: ['coverageReport'],
]], ]],
['Linux', [ ['Linux', [
builds: ['debug'], builds: ['debug'],
...@@ -92,20 +92,6 @@ config = [ ...@@ -92,20 +92,6 @@ config = [
], ],
], ],
], ],
// Configures what binary the coverage report uses and what paths to exclude.
coverage: [
binary: 'build/bin/caf-test',
relativeExcludePaths: [
'examples',
'tools',
'libcaf_test',
'libcaf_core/test',
'libcaf_io/test',
'libcaf_openssl/test',
'libcaf_opencl',
'libcaf_core/caf/scheduler/profiled_coordinator.hpp',
],
],
] ]
// Declarative pipeline for triggering all stages. // Declarative pipeline for triggering all stages.
......
...@@ -32,3 +32,5 @@ ...@@ -32,3 +32,5 @@
#cmakedefine CAF_NO_EXCEPTIONS #cmakedefine CAF_NO_EXCEPTIONS
#cmakedefine CAF_ENABLE_ACTOR_PROFILER #cmakedefine CAF_ENABLE_ACTOR_PROFILER
#cmakedefine CAF_ENABLE_TYPE_ID_CHECKS
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp"
using namespace caf; using namespace caf;
using std::endl; using std::endl;
......
...@@ -30,8 +30,7 @@ using kickoff_atom = atom_constant<atom("kickoff")>; ...@@ -30,8 +30,7 @@ using kickoff_atom = atom_constant<atom("kickoff")>;
// utility function to print an exit message with custom name // utility function to print an exit message with custom name
void print_on_exit(scheduled_actor* self, const std::string& name) { void print_on_exit(scheduled_actor* self, const std::string& name) {
self->attach_functor([=](const error& reason) { self->attach_functor([=](const error& reason) {
aout(self) << name << " exited: " << self->home_system().render(reason) aout(self) << name << " exited: " << to_string(reason) << endl;
<< endl;
}); });
} }
...@@ -174,8 +173,8 @@ void run_server(actor_system& system, const config& cfg) { ...@@ -174,8 +173,8 @@ void run_server(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port, auto server_actor = system.middleman().spawn_server(server, cfg.port,
pong_actor); pong_actor);
if (!server_actor) if (!server_actor)
cerr << "unable to spawn server: " cerr << "unable to spawn server: " << to_string(server_actor.error())
<< system.render(server_actor.error()) << endl; << endl;
} }
void run_client(actor_system& system, const config& cfg) { void run_client(actor_system& system, const config& cfg) {
...@@ -184,8 +183,8 @@ void run_client(actor_system& system, const config& cfg) { ...@@ -184,8 +183,8 @@ void run_client(actor_system& system, const config& cfg) {
auto io_actor = system.middleman().spawn_client(protobuf_io, cfg.host, auto io_actor = system.middleman().spawn_client(protobuf_io, cfg.host,
cfg.port, ping_actor); cfg.port, ping_actor);
if (!io_actor) { if (!io_actor) {
cout << "cannot connect to " << cfg.host << " at port " << cfg.port cout << "cannot connect to " << cfg.host << " at port " << cfg.port << ": "
<< ": " << system.render(io_actor.error()) << endl; << to_string(io_actor.error()) << endl;
return; return;
} }
send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor); send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor);
......
...@@ -5,32 +5,33 @@ ...@@ -5,32 +5,33 @@
* Minimal setup: * * Minimal setup: *
* - ./build/bin/broker -s 4242 * * - ./build/bin/broker -s 4242 *
* - ./build/bin/broker -c localhost 4242 * * - ./build/bin/broker -c localhost 4242 *
\ ******************************************************************************/ \
******************************************************************************/
// Manual refs: 46-50 (Actors.tex) // Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp" #include "caf/config.hpp"
#ifdef WIN32 #ifdef CAF_WINDOWS
# define _WIN32_WINNT 0x0600 # define _WIN32_WINNT 0x0600
# include <winsock2.h> # include <winsock2.h>
#else #else
# include <arpa/inet.h> // htonl # include <arpa/inet.h> // htonl
#endif #endif
#include <vector>
#include <string>
#include <limits>
#include <memory>
#include <cstdint>
#include <cassert> #include <cassert>
#include <cstdint>
#include <iostream> #include <iostream>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
using std::cout;
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using namespace caf; using namespace caf;
...@@ -38,41 +39,41 @@ using namespace caf::io; ...@@ -38,41 +39,41 @@ using namespace caf::io;
namespace { namespace {
using ping_atom = atom_constant<atom("ping")>; // Utility function to print an exit message with custom name.
using pong_atom = atom_constant<atom("pong")>;
using kickoff_atom = atom_constant<atom("kickoff")>;
// utility function to print an exit message with custom name
void print_on_exit(const actor& hdl, const std::string& name) { void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](const error& reason) { hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl; cout << name << " exited: " << to_string(reason) << endl;
}); });
} }
enum class op : uint8_t {
ping,
pong,
};
behavior ping(event_based_actor* self, size_t num_pings) { behavior ping(event_based_actor* self, size_t num_pings) {
auto count = std::make_shared<size_t>(0); auto count = std::make_shared<size_t>(0);
return { return {
[=](kickoff_atom, const actor& pong) { [=](ok_atom, const actor& pong) {
self->send(pong, ping_atom::value, int32_t(1)); self->send(pong, ping_atom_v, int32_t(1));
self->become ( self->become([=](pong_atom, int32_t value) -> result<ping_atom, int32_t> {
[=](pong_atom, int32_t value) -> result<ping_atom, int32_t> { if (++*count >= num_pings)
if (++*count >= num_pings) self->quit(); self->quit();
return {ping_atom::value, value + 1}; return {ping_atom_v, value + 1};
} });
); },
}
}; };
} }
behavior pong() { behavior pong() {
return { return {
[](ping_atom, int32_t value) -> result<pong_atom, int32_t> { [](ping_atom, int32_t value) -> result<pong_atom, int32_t> {
return {pong_atom::value, value}; return {pong_atom_v, value};
} },
}; };
} }
// utility function for sending an integer type // Utility function for sending an integer type.
template <class T> template <class T>
void write_int(broker* self, connection_handle hdl, T value) { void write_int(broker* self, connection_handle hdl, T value) {
using unsigned_type = typename std::make_unsigned<T>::type; using unsigned_type = typename std::make_unsigned<T>::type;
...@@ -81,13 +82,7 @@ void write_int(broker* self, connection_handle hdl, T value) { ...@@ -81,13 +82,7 @@ void write_int(broker* self, connection_handle hdl, T value) {
self->flush(hdl); self->flush(hdl);
} }
void write_int(broker* self, connection_handle hdl, uint64_t value) { // Utility function for reading an ingeger from incoming data.
// write two uint32 values instead (htonl does not work for 64bit integers)
write_int(self, hdl, static_cast<uint32_t>(value));
write_int(self, hdl, static_cast<uint32_t>(value >> sizeof(uint32_t)));
}
// utility function for reading an ingeger from incoming data
template <class T> template <class T>
void read_int(const void* data, T& storage) { void read_int(const void* data, T& storage) {
using unsigned_type = typename std::make_unsigned<T>::type; using unsigned_type = typename std::make_unsigned<T>::type;
...@@ -95,38 +90,30 @@ void read_int(const void* data, T& storage) { ...@@ -95,38 +90,30 @@ void read_int(const void* data, T& storage) {
storage = static_cast<T>(ntohl(static_cast<unsigned_type>(storage))); storage = static_cast<T>(ntohl(static_cast<unsigned_type>(storage)));
} }
void read_int(const void* data, uint64_t& storage) { // Implementation of our broker.
uint32_t first;
uint32_t second;
read_int(data, first);
read_int(reinterpret_cast<const char*>(data) + sizeof(uint32_t), second);
storage = first | (static_cast<uint64_t>(second) << sizeof(uint32_t));
}
// implemenation of our broker
behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) { behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
// we assume io_fsm manages a broker with exactly one connection, // We assume io_fsm manages a broker with exactly one connection,
// i.e., the connection ponted to by `hdl` // i.e., the connection ponted to by `hdl`.
assert(self->num_connections() == 1); assert(self->num_connections() == 1);
// monitor buddy to quit broker if buddy is done // Monitor buddy to quit broker if buddy is done.
self->monitor(buddy); self->monitor(buddy);
self->set_down_handler([=](down_msg& dm) { self->set_down_handler([=](down_msg& dm) {
if (dm.source == buddy) { if (dm.source == buddy) {
aout(self) << "our buddy is down" << endl; aout(self) << "our buddy is down" << endl;
// quit for same reason // Quit for same reason.
self->quit(dm.reason); self->quit(dm.reason);
} }
}); });
// setup: we are exchanging only messages consisting of an atom // Setup: we are exchanging only messages consisting of an atom
// (as uint64_t) and an integer value (int32_t) // (as uint64_t) and an integer value (int32_t).
self->configure_read(hdl, receive_policy::exactly(sizeof(uint64_t) + self->configure_read(
sizeof(int32_t))); hdl, receive_policy::exactly(sizeof(uint64_t) + sizeof(int32_t)));
// our message handlers // Our message handlers.
return { return {
[=](const connection_closed_msg& msg) { [=](const connection_closed_msg& msg) {
// brokers can multiplex any number of connections, however // Brokers can multiplex any number of connections, however
// this example assumes io_fsm to manage a broker with // this example assumes io_fsm to manage a broker with
// exactly one connection // exactly one connection.
if (msg.handle == hdl) { if (msg.handle == hdl) {
aout(self) << "connection closed" << endl; aout(self) << "connection closed" << endl;
// force buddy to quit // force buddy to quit
...@@ -134,29 +121,39 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) { ...@@ -134,29 +121,39 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
self->quit(exit_reason::remote_link_unreachable); self->quit(exit_reason::remote_link_unreachable);
} }
}, },
[=](atom_value av, int32_t i) { [=](ping_atom, int32_t i) {
assert(av == ping_atom::value || av == pong_atom::value); aout(self) << "send {ping, " << i << "}" << endl;
aout(self) << "send {" << to_string(av) << ", " << i << "}" << endl; write_int(self, hdl, static_cast<uint8_t>(op::ping));
// cast atom to its underlying type, i.e., uint64_t write_int(self, hdl, i);
write_int(self, hdl, static_cast<uint64_t>(av)); },
[=](pong_atom, int32_t i) {
aout(self) << "send {pong, " << i << "}" << endl;
write_int(self, hdl, static_cast<uint8_t>(op::pong));
write_int(self, hdl, i); write_int(self, hdl, i);
}, },
[=](const new_data_msg& msg) { [=](const new_data_msg& msg) {
// read the atom value as uint64_t from buffer // Read the operation value as uint8_t from buffer.
uint64_t atm_val; uint8_t op_val;
read_int(msg.buf.data(), atm_val); read_int(msg.buf.data(), op_val);
// cast to original type // Read integer value from buffer, jumping to the correct
auto atm = static_cast<atom_value>(atm_val); // position via offset_data(...).
// read integer value from buffer, jumping to the correct
// position via offset_data(...)
int32_t ival; int32_t ival;
read_int(msg.buf.data() + sizeof(uint64_t), ival); read_int(msg.buf.data() + sizeof(uint8_t), ival);
// show some output // Show some output.
aout(self) << "received {" << to_string(atm) << ", " << ival << "}" aout(self) << "received {" << op_val << ", " << ival << "}" << endl;
<< endl; // Send composed message to our buddy.
// send composed message to our buddy switch (static_cast<op>(op_val)) {
self->send(buddy, atm, ival); case op::ping:
self->send(buddy, ping_atom_v, ival);
break;
case op::pong:
self->send(buddy, pong_atom_v, ival);
break;
default:
aout(self) << "invalid value for op_val, stop" << endl;
self->quit(sec::invalid_argument);
} }
},
}; };
} }
...@@ -165,13 +162,13 @@ behavior server(broker* self, const actor& buddy) { ...@@ -165,13 +162,13 @@ behavior server(broker* self, const actor& buddy) {
return { return {
[=](const new_connection_msg& msg) { [=](const new_connection_msg& msg) {
aout(self) << "server accepted new connection" << endl; aout(self) << "server accepted new connection" << endl;
// by forking into a new broker, we are no longer // By forking into a new broker, we are no longer
// responsible for the connection // responsible for the connection.
auto impl = self->fork(broker_impl, msg.handle, buddy); auto impl = self->fork(broker_impl, msg.handle, buddy);
print_on_exit(impl, "broker_impl"); print_on_exit(impl, "broker_impl");
aout(self) << "quit server (only accept 1 connection)" << endl; aout(self) << "quit server (only accept 1 connection)" << endl;
self->quit(); self->quit();
} },
}; };
} }
...@@ -195,8 +192,8 @@ void run_server(actor_system& system, const config& cfg) { ...@@ -195,8 +192,8 @@ void run_server(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port, auto server_actor = system.middleman().spawn_server(server, cfg.port,
pong_actor); pong_actor);
if (!server_actor) { if (!server_actor) {
std::cerr << "failed to spawn server: " std::cerr << "failed to spawn server: " << to_string(server_actor.error())
<< system.render(server_actor.error()) << endl; << endl;
return; return;
} }
print_on_exit(*server_actor, "server"); print_on_exit(*server_actor, "server");
...@@ -208,13 +205,13 @@ void run_client(actor_system& system, const config& cfg) { ...@@ -208,13 +205,13 @@ void run_client(actor_system& system, const config& cfg) {
auto io_actor = system.middleman().spawn_client(broker_impl, cfg.host, auto io_actor = system.middleman().spawn_client(broker_impl, cfg.host,
cfg.port, ping_actor); cfg.port, ping_actor);
if (!io_actor) { if (!io_actor) {
std::cerr << "failed to spawn client: " std::cerr << "failed to spawn client: " << to_string(io_actor.error())
<< system.render(io_actor.error()) << endl; << endl;
return; return;
} }
print_on_exit(ping_actor, "ping"); print_on_exit(ping_actor, "ping");
print_on_exit(*io_actor, "protobuf_io"); print_on_exit(*io_actor, "protobuf_io");
send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor); send_as(*io_actor, ping_actor, ok_atom_v, *io_actor);
} }
void caf_main(actor_system& system, const config& cfg) { void caf_main(actor_system& system, const config& cfg) {
......
...@@ -13,8 +13,6 @@ using namespace caf::io; ...@@ -13,8 +13,6 @@ using namespace caf::io;
namespace { namespace {
using tick_atom = atom_constant<atom("tick")>;
constexpr const char http_ok[] = R"__(HTTP/1.1 200 OK constexpr const char http_ok[] = R"__(HTTP/1.1 200 OK
Content-Type: text/plain Content-Type: text/plain
Connection: keep-alive Connection: keep-alive
...@@ -51,7 +49,7 @@ behavior server(broker* self) { ...@@ -51,7 +49,7 @@ behavior server(broker* self) {
self->set_down_handler([=](down_msg&) { self->set_down_handler([=](down_msg&) {
++*counter; ++*counter;
}); });
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value); self->delayed_send(self, std::chrono::seconds(1), tick_atom_v);
return { return {
[=](const new_connection_msg& ncm) { [=](const new_connection_msg& ncm) {
auto worker = self->fork(connection_worker, ncm.handle); auto worker = self->fork(connection_worker, ncm.handle);
...@@ -61,7 +59,7 @@ behavior server(broker* self) { ...@@ -61,7 +59,7 @@ behavior server(broker* self) {
[=](tick_atom) { [=](tick_atom) {
aout(self) << "Finished " << *counter << " requests per second." << endl; aout(self) << "Finished " << *counter << " requests per second." << endl;
*counter = 0; *counter = 0;
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value); self->delayed_send(self, std::chrono::seconds(1), tick_atom_v);
} }
}; };
} }
...@@ -79,8 +77,8 @@ public: ...@@ -79,8 +77,8 @@ public:
void caf_main(actor_system& system, const config& cfg) { void caf_main(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port); auto server_actor = system.middleman().spawn_server(server, cfg.port);
if (!server_actor) { if (!server_actor) {
cerr << "*** cannot spawn server: " cerr << "*** cannot spawn server: " << to_string(server_actor.error())
<< system.render(server_actor.error()) << endl; << endl;
return; return;
} }
cout << "*** listening on port " << cfg.port << endl; cout << "*** listening on port " << cfg.port << endl;
......
...@@ -38,19 +38,12 @@ relaxed-sleep-duration=10ms ...@@ -38,19 +38,12 @@ relaxed-sleep-duration=10ms
; configures whether MMs try to span a full mesh ; configures whether MMs try to span a full mesh
enable-automatic-connections=false enable-automatic-connections=false
; application identifier of this node, prevents connection to other CAF ; application identifier of this node, prevents connection to other CAF
; instances with incompatible identifiers (at least one entry must match) ; instances with different identifier
app-identifiers=["generic-caf-app"] app-identifier=""
; maximum number of consecutive I/O reads per broker ; maximum number of consecutive I/O reads per broker
max-consecutive-reads=50 max-consecutive-reads=50
; heartbeat message interval for periodic traffic ; heartbeat message interval in ms (0 disables heartbeating)
; (0 disables heartbeating) heartbeat-interval=0ms
heartbeat-interval=0s
; force disconnects of CAF nodes after receiving no traffic for this amount of
; time (0 disables this feature, but we highly recommend using heartbeats and
; connection timeouts), be careful when deploying CAF applications with
; different heartbeat intervals and connection timeouts: this timeout should
; line up with the *longest* heartbeat interval (ideally it's a multiple)
connection-timeout=0s
; configures whether the MM attaches its internal utility actors to the ; configures whether the MM attaches its internal utility actors to the
; scheduler instead of dedicating individual threads (needed only for ; scheduler instead of dedicating individual threads (needed only for
; deterministic testing) ; deterministic testing)
...@@ -59,6 +52,10 @@ attach-utility-actors=false ...@@ -59,6 +52,10 @@ attach-utility-actors=false
; setting this to true allows fully deterministic execution in unit test and ; setting this to true allows fully deterministic execution in unit test and
; requires the user to trigger I/O manually ; requires the user to trigger I/O manually
manual-multiplexing=false manual-multiplexing=false
; disables communication via TCP
disable-tcp=false
; enable communication via UDP
enable-udp=false
; configures how many background workers are spawned for deserialization, ; configures how many background workers are spawned for deserialization,
; by default CAF uses 1-4 workers depending on the number of cores ; by default CAF uses 1-4 workers depending on the number of cores
workers=<min(3, number of cores / 4) + 1> workers=<min(3, number of cores / 4) + 1>
......
...@@ -31,15 +31,15 @@ ...@@ -31,15 +31,15 @@
******************************************************************************/ ******************************************************************************/
// C includes // C includes
#include <ctime>
#include <csignal> #include <csignal>
#include <cstdlib> #include <cstdlib>
#include <ctime>
// C++ includes // C++ includes
#include <iostream>
#include <random>
#include <string> #include <string>
#include <vector> #include <vector>
#include <random>
#include <iostream>
// CAF // CAF
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -56,16 +56,23 @@ CAF_POP_WARNINGS ...@@ -56,16 +56,23 @@ CAF_POP_WARNINGS
# pragma clang diagnostic ignored "-Wunused-const-variable" # pragma clang diagnostic ignored "-Wunused-const-variable"
#endif // __clang__ #endif // __clang__
CAF_BEGIN_TYPE_ID_BLOCK(curl_fuse, first_custom_type_id)
CAF_ADD_TYPE_ID(curl_fuse, (std::vector<char>) )
CAF_ADD_ATOM(curl_fuse, custom, read_atom, "read")
CAF_ADD_ATOM(curl_fuse, custom, fail_atom, "fail")
CAF_ADD_ATOM(curl_fuse, custom, next_atom, "next")
CAF_ADD_ATOM(curl_fuse, custom, reply_atom, "reply")
CAF_ADD_ATOM(curl_fuse, custom, finished_atom, "finished")
CAF_END_TYPE_ID_BLOCK(curl_fuse)
using namespace caf; using namespace caf;
using namespace custom;
using buffer_type = std::vector<char>; using buffer_type = std::vector<char>;
using read_atom = atom_constant<atom("read")>;
using fail_atom = atom_constant<atom("fail")>;
using next_atom = atom_constant<atom("next")>;
using reply_atom = atom_constant<atom("reply")>;
using finished_atom = atom_constant<atom("finished")>;
namespace color { namespace color {
// UNIX terminal color codes // UNIX terminal color codes
...@@ -132,8 +139,7 @@ struct base_state { ...@@ -132,8 +139,7 @@ struct base_state {
behavior client_job(stateful_actor<base_state>* self, const actor& parent) { behavior client_job(stateful_actor<base_state>* self, const actor& parent) {
if (!self->state.init("client-job", color::blue)) if (!self->state.init("client-job", color::blue))
return {}; // returning an empty behavior terminates the actor return {}; // returning an empty behavior terminates the actor
self->send(parent, read_atom::value, self->send(parent, read_atom_v, "http://www.example.com/index.html",
"http://www.example.com/index.html",
uint64_t{0}, uint64_t{4095}); uint64_t{0}, uint64_t{4095});
return { return {
[=](reply_atom, const buffer_type& buf) { [=](reply_atom, const buffer_type& buf) {
...@@ -144,7 +150,7 @@ behavior client_job(stateful_actor<base_state>* self, const actor& parent) { ...@@ -144,7 +150,7 @@ behavior client_job(stateful_actor<base_state>* self, const actor& parent) {
[=](fail_atom) { [=](fail_atom) {
self->state.print() << "failure" << color::reset_endl; self->state.print() << "failure" << color::reset_endl;
self->quit(); self->quit();
} },
}; };
} }
...@@ -169,20 +175,18 @@ behavior client(stateful_actor<client_state>* self, const actor& parent) { ...@@ -169,20 +175,18 @@ behavior client(stateful_actor<client_state>* self, const actor& parent) {
self->link_to(parent); self->link_to(parent);
if (!self->state.init("client", color::green)) if (!self->state.init("client", color::green))
return {}; // returning an empty behavior terminates the actor return {}; // returning an empty behavior terminates the actor
self->send(self, next_atom::value); self->send(self, next_atom_v);
return { return {[=](next_atom) {
[=](next_atom) {
auto& st = self->state; auto& st = self->state;
st.print() << "spawn new client_job (nr. " << ++st.count << ")" st.print() << "spawn new client_job (nr. " << ++st.count << ")"
<< color::reset_endl; << color::reset_endl;
// client_job will use IO // client_job will use IO
// and should thus be spawned in a separate thread // and should thus be spawned in a separate thread
self->spawn<detached+linked>(client_job, parent); self->spawn<detached + linked>(client_job, parent);
// compute random delay until next job is launched // compute random delay until next job is launched
auto delay = st.dist(st.re); auto delay = st.dist(st.re);
self->delayed_send(self, milliseconds(delay), next_atom::value); self->delayed_send(self, milliseconds(delay), next_atom_v);
} }};
};
} }
struct curl_state : base_state { struct curl_state : base_state {
...@@ -221,9 +225,8 @@ struct curl_state : base_state { ...@@ -221,9 +225,8 @@ struct curl_state : base_state {
behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) { behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) {
if (!self->state.init("curl-worker", color::yellow)) if (!self->state.init("curl-worker", color::yellow))
return {}; // returning an empty behavior terminates the actor return {}; // returning an empty behavior terminates the actor
return { return {[=](read_atom, const std::string& fname, uint64_t offset,
[=](read_atom, const std::string& fname, uint64_t offset, uint64_t range) uint64_t range) -> message {
-> message {
auto& st = self->state; auto& st = self->state;
st.print() << "read" << color::reset_endl; st.print() << "read" << color::reset_endl;
for (;;) { for (;;) {
...@@ -240,8 +243,7 @@ behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) { ...@@ -240,8 +243,7 @@ behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) {
// launch file transfer // launch file transfer
auto res = curl_easy_perform(st.curl); auto res = curl_easy_perform(st.curl);
if (res != CURLE_OK) { if (res != CURLE_OK) {
st.print() << "curl_easy_perform() failed: " st.print() << "curl_easy_perform() failed: " << curl_easy_strerror(res)
<< curl_easy_strerror(res)
<< color::reset_endl; << color::reset_endl;
} else { } else {
long hc = 0; // http return code long hc = 0; // http return code
...@@ -249,32 +251,26 @@ behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) { ...@@ -249,32 +251,26 @@ behavior curl_worker(stateful_actor<curl_state>* self, const actor& parent) {
switch (hc) { switch (hc) {
default: default:
st.print() << "http error: download failed with " st.print() << "http error: download failed with "
<< "'HTTP RETURN CODE': " << "'HTTP RETURN CODE': " << hc << color::reset_endl;
<< hc
<< color::reset_endl;
break; break;
case 200: // ok case 200: // ok
case 206: // partial content case 206: // partial content
st.print() << "received " st.print() << "received " << st.buf.size()
<< st.buf.size() << " bytes with 'HTTP RETURN CODE': " << hc
<< " bytes with 'HTTP RETURN CODE': "
<< hc
<< color::reset_endl; << color::reset_endl;
// tell parent that this worker is done // tell parent that this worker is done
self->send(parent, finished_atom::value); self->send(parent, finished_atom_v);
return make_message(reply_atom::value, std::move(st.buf)); return make_message(reply_atom_v, std::move(st.buf));
case 404: // file does not exist case 404: // file does not exist
st.print() << "http error: download failed with " st.print() << "http error: download failed with "
<< "'HTTP RETURN CODE': 404 (file does " << "'HTTP RETURN CODE': 404 (file does "
<< "not exist!)" << "not exist!)" << color::reset_endl;
<< color::reset_endl;
} }
} }
// avoid 100% cpu utilization if remote side is not accessible // avoid 100% cpu utilization if remote side is not accessible
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
} }};
};
} }
struct master_state : base_state { struct master_state : base_state {
...@@ -289,8 +285,9 @@ behavior curl_master(stateful_actor<master_state>* self) { ...@@ -289,8 +285,9 @@ behavior curl_master(stateful_actor<master_state>* self) {
if (!self->state.init("curl-master", color::magenta)) if (!self->state.init("curl-master", color::magenta))
return {}; // returning an empty behavior terminates the actor return {}; // returning an empty behavior terminates the actor
// spawn workers // spawn workers
for(size_t i = 0; i < num_curl_workers; ++i) for (size_t i = 0; i < num_curl_workers; ++i)
self->state.idle.push_back(self->spawn<detached+linked>(curl_worker, self)); self->state.idle.push_back(
self->spawn<detached + linked>(curl_worker, self));
auto worker_finished = [=] { auto worker_finished = [=] {
auto sender = self->current_sender(); auto sender = self->current_sender();
auto last = self->state.busy.end(); auto last = self->state.busy.end();
...@@ -301,8 +298,8 @@ behavior curl_master(stateful_actor<master_state>* self) { ...@@ -301,8 +298,8 @@ behavior curl_master(stateful_actor<master_state>* self) {
self->state.busy.erase(i); self->state.busy.erase(i);
self->state.print() << "worker is done" << color::reset_endl; self->state.print() << "worker is done" << color::reset_endl;
}; };
self->state.print() << "spawned " << self->state.idle.size() self->state.print() << "spawned " << self->state.idle.size() << " worker(s)"
<< " worker(s)" << color::reset_endl; << color::reset_endl;
return { return {
[=](read_atom rd, std::string& str, uint64_t x, uint64_t y) { [=](read_atom rd, std::string& str, uint64_t x, uint64_t y) {
auto& st = self->state; auto& st = self->state;
...@@ -315,18 +312,13 @@ behavior curl_master(stateful_actor<master_state>* self) { ...@@ -315,18 +312,13 @@ behavior curl_master(stateful_actor<master_state>* self) {
st.print() << st.busy.size() << " active jobs" << color::reset_endl; st.print() << st.busy.size() << " active jobs" << color::reset_endl;
if (st.idle.empty()) { if (st.idle.empty()) {
// wait until at least one worker finished its job // wait until at least one worker finished its job
self->become ( self->become(keep_behavior, [=](finished_atom) {
keep_behavior,
[=](finished_atom) {
worker_finished(); worker_finished();
self->unbecome(); self->unbecome();
} });
);
} }
}, },
[=](finished_atom) { [=](finished_atom) { worker_finished(); },
worker_finished();
}
}; };
} }
...@@ -371,4 +363,4 @@ void caf_main(actor_system& system) { ...@@ -371,4 +363,4 @@ void caf_main(actor_system& system) {
curl_global_cleanup(); curl_global_cleanup();
} }
CAF_MAIN(io::middleman) CAF_MAIN(id_block::curl_fuse, io::middleman)
...@@ -3,34 +3,45 @@ ...@@ -3,34 +3,45 @@
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications) // Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection) // 23-33 (TypeInspection)
#include <string>
#include <vector>
#include <cassert> #include <cassert>
#include <utility>
#include <iostream> #include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
using std::cout; // --(rst-type-id-block-begin)--
struct foo;
struct foo2;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_1, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_1, (foo))
CAF_ADD_TYPE_ID(custom_types_1, (foo2))
CAF_ADD_TYPE_ID(custom_types_1, (std::pair<int32_t, int32_t>) )
CAF_END_TYPE_ID_BLOCK(custom_types_1)
// --(rst-type-id-block-end)--
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::vector; using std::vector;
using namespace caf; using namespace caf;
namespace { // --(rst-foo-begin)--
// POD struct foo
struct foo { struct foo {
std::vector<int> a; std::vector<int> a;
int b; int b;
}; };
// foo needs to be serializable
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, foo& x) { typename Inspector::result_type inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a, x.b); return f(meta::type_name("foo"), x.a, x.b);
} }
// --(rst-foo-end)--
// a pair of two ints // a pair of two ints
using foo_pair = std::pair<int, int>; using foo_pair = std::pair<int, int>;
...@@ -58,7 +69,7 @@ void testee(event_based_actor* self, size_t remaining) { ...@@ -58,7 +69,7 @@ void testee(event_based_actor* self, size_t remaining) {
else else
self->quit(); self->quit();
}; };
self->become ( self->become(
// note: we sent a foo_pair2, but match on foo_pair // note: we sent a foo_pair2, but match on foo_pair
// that works because both are aliases for std::pair<int, int> // that works because both are aliases for std::pair<int, int>
[=](const foo_pair& val) { [=](const foo_pair& val) {
...@@ -68,20 +79,10 @@ void testee(event_based_actor* self, size_t remaining) { ...@@ -68,20 +79,10 @@ void testee(event_based_actor* self, size_t remaining) {
[=](const foo& val) { [=](const foo& val) {
aout(self) << to_string(val) << endl; aout(self) << to_string(val) << endl;
set_next_behavior(); set_next_behavior();
} });
);
} }
class config : public actor_system_config { void caf_main(actor_system& sys) {
public:
config() {
add_message_type<foo>("foo");
add_message_type<foo2>("foo2");
add_message_type<foo_pair>("foo_pair");
}
};
void caf_main(actor_system& system, const config&) {
// two variables for testing serialization // two variables for testing serialization
foo2 f1; foo2 f1;
foo2 f2; foo2 f2;
...@@ -90,34 +91,30 @@ void caf_main(actor_system& system, const config&) { ...@@ -90,34 +91,30 @@ void caf_main(actor_system& system, const config&) {
f1.b.resize(1); f1.b.resize(1);
f1.b.back().push_back(42); f1.b.back().push_back(42);
// I/O buffer // I/O buffer
vector<char> buf; binary_serializer::container_type buf;
// write f1 to buffer // write f1 to buffer
binary_serializer bs{system, buf}; binary_serializer bs{sys, buf};
auto e = bs(f1); auto e = bs(f1);
if (e) { if (e) {
std::cerr << "*** unable to serialize foo2: " std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
<< system.render(e) << std::endl;
return; return;
} }
// read f2 back from buffer // read f2 back from buffer
binary_deserializer bd{system, buf}; binary_deserializer bd{sys, buf};
e = bd(f2); e = bd(f2);
if (e) { if (e) {
std::cerr << "*** unable to serialize foo2: " std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
<< system.render(e) << std::endl;
return; return;
} }
// must be equal // must be equal
assert(to_string(f1) == to_string(f2)); assert(to_string(f1) == to_string(f2));
// spawn a testee that receives two messages of user-defined type // spawn a testee that receives two messages of user-defined type
auto t = system.spawn(testee, 2u); auto t = sys.spawn(testee, 2u);
scoped_actor self{system}; scoped_actor self{sys};
// send t a foo // send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5}); self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2 // send t a foo_pair2
self->send(t, foo_pair2{3, 4}); self->send(t, foo_pair2{3, 4});
} }
} // namespace CAF_MAIN(id_block::custom_types_1)
CAF_MAIN()
...@@ -6,14 +6,20 @@ ...@@ -6,14 +6,20 @@
#include "caf/all.hpp" #include "caf/all.hpp"
class foo;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_2, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_2, (foo))
CAF_END_TYPE_ID_BLOCK(custom_types_2)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::make_pair; using std::make_pair;
using namespace caf; using namespace caf;
namespace {
// a simple class using getter and setter member functions // a simple class using getter and setter member functions
class foo { class foo {
public: public:
...@@ -58,17 +64,8 @@ behavior testee(event_based_actor* self) { ...@@ -58,17 +64,8 @@ behavior testee(event_based_actor* self) {
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& sys) {
public: anon_send(sys.spawn(testee), foo{1, 2});
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
} }
} // namespace CAF_MAIN(id_block::custom_types_2)
CAF_MAIN()
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide
// friend access to the inspect() function. // friend access to the inspect() function.
// Manual refs: 20-49, 76-103 (TypeInspection)
#include <utility>
#include <iostream> #include <iostream>
#include <utility>
#include "caf/all.hpp" #include "caf/all.hpp"
class foo;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_3, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_3, (foo))
CAF_END_TYPE_ID_BLOCK(custom_types_3)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::make_pair; using std::make_pair;
using namespace caf; using namespace caf;
namespace { // Identical to our second custom type example, but no friend access for
// `inspect`.
// identical to our second custom type example, but // --(rst-foo-begin)--
// no friend access for `inspect`
class foo { class foo {
public: public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) { foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
...@@ -47,72 +52,31 @@ private: ...@@ -47,72 +52,31 @@ private:
int a_; int a_;
int b_; int b_;
}; };
// --(rst-foo-end)--
// A lightweight scope guard implementation. // --(rst-inspect-begin)--
template <class Fun>
class scope_guard {
public:
scope_guard(Fun f) : fun_(std::move(f)), enabled_(true) { }
scope_guard(scope_guard&& x) : fun_(std::move(x.fun_)), enabled_(x.enabled_) {
x.enabled_ = false;
}
~scope_guard() {
if (enabled_) fun_();
}
private:
Fun fun_;
bool enabled_;
};
// Creates a guard that executes `f` as soon as it goes out of scope.
template <class Fun>
scope_guard<Fun> make_scope_guard(Fun f) {
return {std::move(f)};
}
template <class Inspector>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a(), x.b());
}
template <class Inspector> template <class Inspector>
typename std::enable_if<Inspector::writes_state, typename Inspector::result_type inspect(Inspector& f, foo& x) {
typename Inspector::result_type>::type auto a = x.a();
inspect(Inspector& f, foo& x) { auto b = x.b();
int a; auto load = meta::load_callback([&]() -> error {
int b; // Write back to x when loading values from the inspector.
// write back to x at scope exit
auto g = make_scope_guard([&] {
x.set_a(a); x.set_a(a);
x.set_b(b); x.set_b(b);
return none;
}); });
return f(meta::type_name("foo"), a, b); return f(meta::type_name("foo"), a, b, load);
} }
// --(rst-inspect-end)--
behavior testee(event_based_actor* self) { behavior testee(event_based_actor* self) {
return { return {
[=](const foo& x) { [=](const foo& x) { aout(self) << to_string(x) << endl; },
aout(self) << to_string(x) << endl;
}
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& system) {
public:
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2}); anon_send(system.spawn(testee), foo{1, 2});
} }
} // namespace CAF_MAIN(id_block::custom_types_3)
CAF_MAIN()
...@@ -3,33 +3,36 @@ ...@@ -3,33 +3,36 @@
* exercise using only libcaf's event-based actor implementation. * * exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/ \******************************************************************************/
#include <chrono>
#include <iostream>
#include <map> #include <map>
#include <sstream>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <chrono>
#include <sstream>
#include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
using std::cout; CAF_BEGIN_TYPE_ID_BLOCK(dining_philosophers, first_custom_type_id)
CAF_ADD_ATOM(dining_philosophers, custom, take_atom, "take")
CAF_ADD_ATOM(dining_philosophers, custom, taken_atom, "taken")
CAF_ADD_ATOM(dining_philosophers, custom, eat_atom, "eat")
CAF_ADD_ATOM(dining_philosophers, custom, think_atom, "think")
CAF_END_TYPE_ID_BLOCK(dining_philosophers)
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
using namespace custom;
namespace { namespace {
// atoms for chopstick interface // atoms for chopstick and philosopher interfaces
using put_atom = atom_constant<atom("put")>;
using take_atom = atom_constant<atom("take")>;
using taken_atom = atom_constant<atom("taken")>;
// atoms for philosopher interface
using eat_atom = atom_constant<atom("eat")>;
using think_atom = atom_constant<atom("think")>;
// a chopstick // a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>, using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
...@@ -41,26 +44,24 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer, ...@@ -41,26 +44,24 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer,
// either taken by a philosopher or available // either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) { chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return { return {
[=](take_atom) -> std::tuple<taken_atom, bool> { [=](take_atom) -> result<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender())); self->become(taken_chopstick(self, self->current_sender()));
return std::make_tuple(taken_atom::value, true); return {taken_atom_v, true};
}, },
[](put_atom) { [](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; },
cerr << "chopstick received unexpected 'put'" << endl;
}
}; };
} }
chopstick::behavior_type taken_chopstick(chopstick::pointer self, chopstick::behavior_type taken_chopstick(chopstick::pointer self,
const strong_actor_ptr& user) { const strong_actor_ptr& user) {
return { return {
[](take_atom) -> std::tuple<taken_atom, bool> { [](take_atom) -> result<taken_atom, bool> {
return std::make_tuple(taken_atom::value, false); return {taken_atom_v, false};
}, },
[=](put_atom) { [=](put_atom) {
if (self->current_sender() == user) if (self->current_sender() == user)
self->become(available_chopstick(self)); self->become(available_chopstick(self));
} },
}; };
} }
...@@ -94,10 +95,7 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, ...@@ -94,10 +95,7 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
class philosopher : public event_based_actor { class philosopher : public event_based_actor {
public: public:
philosopher(actor_config& cfg, philosopher(actor_config& cfg, std::string n, chopstick l, chopstick r)
std::string n,
chopstick l,
chopstick r)
: event_based_actor(cfg), : event_based_actor(cfg),
name_(std::move(n)), name_(std::move(n)),
left_(std::move(l)), left_(std::move(l)),
...@@ -105,60 +103,48 @@ public: ...@@ -105,60 +103,48 @@ public:
// we only accept one message per state and skip others in the meantime // we only accept one message per state and skip others in the meantime
set_default_handler(skip); set_default_handler(skip);
// a philosopher that receives {eat} stops thinking and becomes hungry // a philosopher that receives {eat} stops thinking and becomes hungry
thinking_.assign( thinking_.assign([=](eat_atom) {
[=](eat_atom) {
become(hungry_); become(hungry_);
send(left_, take_atom::value); send(left_, take_atom_v);
send(right_, take_atom::value); send(right_, take_atom_v);
} });
);
// wait for the first answer of a chopstick // wait for the first answer of a chopstick
hungry_.assign( hungry_.assign([=](taken_atom, bool result) {
[=](taken_atom, bool result) {
if (result) if (result)
become(granted_); become(granted_);
else else
become(denied_); become(denied_);
} });
);
// philosopher was able to obtain the first chopstick // philosopher was able to obtain the first chopstick
granted_.assign( granted_.assign([=](taken_atom, bool result) {
[=](taken_atom, bool result) {
if (result) { if (result) {
aout(this) << name_ aout(this) << name_ << " has picked up chopsticks with IDs "
<< " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id() << left_->id() << " and " << right_->id()
<< " and starts to eat\n"; << " and starts to eat\n";
// eat some time // eat some time
delayed_send(this, seconds(5), think_atom::value); delayed_send(this, seconds(5), think_atom_v);
become(eating_); become(eating_);
} else { } else {
send(current_sender() == left_ ? right_ : left_, put_atom::value); send(current_sender() == left_ ? right_ : left_, put_atom_v);
send(this, eat_atom::value); send(this, eat_atom_v);
become(thinking_); become(thinking_);
} }
} });
);
// philosopher was *not* able to obtain the first chopstick // philosopher was *not* able to obtain the first chopstick
denied_.assign( denied_.assign([=](taken_atom, bool result) {
[=](taken_atom, bool result) {
if (result) if (result)
send(current_sender() == left_ ? left_ : right_, put_atom::value); send(current_sender() == left_ ? left_ : right_, put_atom_v);
send(this, eat_atom::value); send(this, eat_atom_v);
become(thinking_); become(thinking_);
} });
);
// philosopher obtained both chopstick and eats (for five seconds) // philosopher obtained both chopstick and eats (for five seconds)
eating_.assign( eating_.assign([=](think_atom) {
[=](think_atom) { send(left_, put_atom_v);
send(left_, put_atom::value); send(right_, put_atom_v);
send(right_, put_atom::value); delayed_send(this, seconds(5), eat_atom_v);
delayed_send(this, seconds(5), eat_atom::value); aout(this) << name_ << " puts down his chopsticks and starts to think\n";
aout(this) << name_
<< " puts down his chopsticks and starts to think\n";
become(thinking_); become(thinking_);
} });
);
} }
const char* name() const override { const char* name() const override {
...@@ -168,15 +154,15 @@ public: ...@@ -168,15 +154,15 @@ public:
protected: protected:
behavior make_behavior() override { behavior make_behavior() override {
// start thinking // start thinking
send(this, think_atom::value); send(this, think_atom_v);
// philosophers start to think after receiving {think} // philosophers start to think after receiving {think}
return ( return {
[=](think_atom) { [=](think_atom) {
aout(this) << name_ << " starts to think\n"; aout(this) << name_ << " starts to think\n";
delayed_send(this, seconds(5), eat_atom::value); delayed_send(this, seconds(5), eat_atom_v);
become(thinking_); become(thinking_);
} },
); };
} }
private: private:
...@@ -203,10 +189,10 @@ void caf_main(actor_system& system) { ...@@ -203,10 +189,10 @@ void caf_main(actor_system& system) {
} }
aout(self) << endl; aout(self) << endl;
// spawn five philosophers // spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant", std::vector<std::string> names{"Plato", "Hume", "Kant", "Nietzsche",
"Nietzsche", "Descartes"}; "Descartes"};
for (size_t i = 0; i < 5; ++i) for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]); self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
} }
CAF_MAIN() CAF_MAIN(id_block::dining_philosophers)
#include "caf/all.hpp" #include "caf/all.hpp"
using namespace caf;
using idle_atom = atom_constant<atom("idle")>; using namespace caf;
using request_atom = atom_constant<atom("request")>;
using response_atom = atom_constant<atom("response")>;
behavior server(event_based_actor* self) { behavior server(event_based_actor* self) {
self->set_default_handler(skip);
return { return {
[=](idle_atom, const actor& worker) { [=](idle_atom, const actor& worker) {
self->become ( self->become(keep_behavior, [=](ping_atom atm) {
keep_behavior,
[=](request_atom atm) {
self->delegate(worker, atm); self->delegate(worker, atm);
self->unbecome(); self->unbecome();
});
}, },
[=](idle_atom) {
return skip();
}
);
},
[=](request_atom) {
return skip();
}
}; };
} }
behavior client(event_based_actor* self, const actor& serv) { behavior client(event_based_actor* self, const actor& serv) {
self->link_to(serv); self->link_to(serv);
self->send(serv, idle_atom::value, self); self->send(serv, idle_atom_v, self);
return { return {
[=](request_atom) { [=](ping_atom) {
self->send(serv, idle_atom::value, self); self->send(serv, idle_atom_v, self);
return response_atom::value; return pong_atom_v;
} },
}; };
} }
...@@ -40,20 +29,18 @@ void caf_main(actor_system& system) { ...@@ -40,20 +29,18 @@ void caf_main(actor_system& system) {
auto serv = system.spawn(server); auto serv = system.spawn(server);
auto worker = system.spawn(client, serv); auto worker = system.spawn(client, serv);
scoped_actor self{system}; scoped_actor self{system};
self->request(serv, std::chrono::seconds(10), request_atom::value).receive( self->request(serv, std::chrono::seconds(10), ping_atom_v)
[&](response_atom) { .receive(
[&](pong_atom) {
aout(self) << "received response from " aout(self) << "received response from "
<< (self->current_sender() == worker ? "worker\n" << (self->current_sender() == worker ? "worker\n"
: "server\n"); : "server\n");
}, },
[&](error& err) { [&](error& err) {
aout(self) << "received error " aout(self) << "received error " << to_string(err) << " from "
<< system.render(err)
<< " from "
<< (self->current_sender() == worker ? "worker\n" << (self->current_sender() == worker ? "worker\n"
: "server\n"); : "server\n");
} });
);
self->send_exit(serv, exit_reason::user_shutdown); self->send_exit(serv, exit_reason::user_shutdown);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* for both the blocking and the event-based API. * * for both the blocking and the event-based API. *
\******************************************************************************/ \******************************************************************************/
// Manual refs: lines 19-21, 31-72, 74-108, 140-145 (Actor) // Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream> #include <iostream>
...@@ -14,11 +14,9 @@ using namespace caf; ...@@ -14,11 +14,9 @@ using namespace caf;
namespace { namespace {
using add_atom = atom_constant<atom("add")>; using calculator_actor
using sub_atom = atom_constant<atom("sub")>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
using calculator_actor = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
// prototypes and forward declarations // prototypes and forward declarations
behavior calculator_fun(event_based_actor* self); behavior calculator_fun(event_based_actor* self);
...@@ -31,43 +29,30 @@ class typed_calculator; ...@@ -31,43 +29,30 @@ class typed_calculator;
// function-based, dynamically typed, event-based API // function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) { behavior calculator_fun(event_based_actor*) {
return { return {
[](add_atom, int a, int b) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int b) {
return a - b;
}
}; };
} }
// function-based, dynamically typed, blocking API // function-based, dynamically typed, blocking API
void blocking_calculator_fun(blocking_actor* self) { void blocking_calculator_fun(blocking_actor* self) {
bool running = true; bool running = true;
self->receive_while(running) ( self->receive_while(running)( //
[](add_atom, int a, int b) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int b) {
return a - b;
},
[&](exit_msg& em) { [&](exit_msg& em) {
if (em.reason) { if (em.reason) {
self->fail_state(std::move(em.reason)); self->fail_state(std::move(em.reason));
running = false; running = false;
} }
} });
);
} }
// function-based, statically typed, event-based API // function-based, statically typed, event-based API
calculator_actor::behavior_type typed_calculator_fun() { calculator_actor::behavior_type typed_calculator_fun() {
return { return {
[](add_atom, int a, int b) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int b) {
return a - b;
}
}; };
} }
...@@ -113,25 +98,25 @@ void tester(scoped_actor&) { ...@@ -113,25 +98,25 @@ void tester(scoped_actor&) {
// tests a calculator instance // tests a calculator instance
template <class Handle, class... Ts> template <class Handle, class... Ts>
void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) { void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
Ts&&... xs) {
auto handle_err = [&](const error& err) { auto handle_err = [&](const error& err) {
aout(self) << "AUT (actor under test) failed: " aout(self) << "AUT (actor under test) failed: " << to_string(err) << endl;
<< self->system().render(err) << endl;
}; };
// first test: x + y = z // first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive( self->request(hdl, infinite, add_atom_v, x, y)
[&](int res1) { .receive(
[&](int32_t res1) {
aout(self) << x << " + " << y << " = " << res1 << endl; aout(self) << x << " + " << y << " = " << res1 << endl;
// second test: x - y = z // second test: x - y = z
self->request(hdl, infinite, sub_atom::value, x, y).receive( self->request(hdl, infinite, sub_atom_v, x, y)
[&](int res2) { .receive(
[&](int32_t res2) {
aout(self) << x << " - " << y << " = " << res2 << endl; aout(self) << x << " - " << y << " = " << res2 << endl;
}, },
handle_err handle_err);
);
}, },
handle_err handle_err);
);
tester(self, std::forward<Ts>(xs)...); tester(self, std::forward<Ts>(xs)...);
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 18-44, and 49-50 (Actor.tex) // Manual references: lines 18-44, and 49-50 (Actor.tex)
#include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -15,33 +16,21 @@ using std::cout; ...@@ -15,33 +16,21 @@ using std::cout;
using std::endl; using std::endl;
using namespace caf; using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int>>; replies_to<get_atom>::with<int32_t>>;
struct cell_state { struct cell_state {
int value = 0; int32_t value = 0;
}; };
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) { cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return { return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](put_atom, int val) { [=](get_atom) { return self->state.value; }};
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
} }
behavior unchecked_cell(stateful_actor<cell_state>* self) { behavior unchecked_cell(stateful_actor<cell_state>* self) {
return { return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](put_atom, int val) { [=](get_atom) { return self->state.value; }};
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
...@@ -49,9 +38,9 @@ void caf_main(actor_system& system) { ...@@ -49,9 +38,9 @@ void caf_main(actor_system& system) {
auto cell1 = system.spawn(type_checked_cell); auto cell1 = system.spawn(type_checked_cell);
auto cell2 = system.spawn(unchecked_cell); auto cell2 = system.spawn(unchecked_cell);
auto f = make_function_view(cell1); auto f = make_function_view(cell1);
cout << "cell value: " << f(get_atom::value) << endl; cout << "cell value: " << f(get_atom_v) << endl;
f(put_atom::value, 20); f(put_atom_v, 20);
cout << "cell value (after setting to 20): " << f(get_atom::value) << endl; cout << "cell value (after setting to 20): " << f(get_atom_v) << endl;
// get an unchecked cell and send it some garbage // get an unchecked cell and send it some garbage
anon_send(cell2, "hello there!"); anon_send(cell2, "hello there!");
} }
......
/******************************************************************************\ /******************************************************************************\
* This example illustrates how to do time-triggered loops in libcaf. * * This example illustrates how to do time-triggered loops in libcaf. *
\ ******************************************************************************/ \******************************************************************************/
#include <algorithm>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <algorithm>
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify // This file is partially included in the manual, do not modify
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 56-75 (MessagePassing.tex) // Manual references: lines 58-75 (MessagePassing.tex)
using std::cout; using std::cout;
using std::endl; using std::endl;
...@@ -17,23 +18,24 @@ using std::pair; ...@@ -17,23 +18,24 @@ using std::pair;
using namespace caf; using namespace caf;
using step_atom = atom_constant<atom("step")>;
// ASCII art figures // ASCII art figures
constexpr const char* figures[] = { constexpr const char* figures[] = {
"<(^.^<)", "<(^.^<)",
"<(^.^)>", "<(^.^)>",
"(>^.^)>" "(>^.^)>",
}; };
struct animation_step { size_t figure_idx; size_t offset; }; struct animation_step {
size_t figure_idx;
size_t offset;
};
// array of {figure, offset} pairs // array of {figure, offset} pairs
constexpr animation_step animation_steps[] = { constexpr animation_step animation_steps[] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6}, {1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9}, {2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
{1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13}, {1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7} {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7},
}; };
constexpr size_t animation_width = 20; constexpr size_t animation_width = 20;
...@@ -56,9 +58,8 @@ void draw_kirby(const animation_step& animation) { ...@@ -56,9 +58,8 @@ void draw_kirby(const animation_step& animation) {
// uses a message-based loop to iterate over all animation steps // uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) { void dancing_kirby(event_based_actor* self) {
// let's get it started // let's get it started
self->send(self, step_atom::value, size_t{0}); self->send(self, update_atom_v, size_t{0});
self->become ( self->become([=](update_atom, size_t step) {
[=](step_atom, size_t step) {
if (step == sizeof(animation_step)) { if (step == sizeof(animation_step)) {
// we've printed all animation steps (done) // we've printed all animation steps (done)
cout << endl; cout << endl;
...@@ -68,10 +69,9 @@ void dancing_kirby(event_based_actor* self) { ...@@ -68,10 +69,9 @@ void dancing_kirby(event_based_actor* self) {
// print given step // print given step
draw_kirby(animation_steps[step]); draw_kirby(animation_steps[step]);
// animate next step in 150ms // animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150), self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v,
step_atom::value, step + 1); step + 1);
} });
);
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify // This file is partially included in the manual, do not modify
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 15-42 (MessagePassing.tex) // Manual references: lines 15-36 (MessagePassing.tex)
using std::endl;
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) // using add_atom = atom_constant<atom("add")>; (defined in atom.hpp)
using calc = typed_actor<replies_to<add_atom, int, int>::with<int>>; using calc = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
void actor_a(event_based_actor* self, const calc& worker) { void actor_a(event_based_actor* self, const calc& worker) {
self->request(worker, std::chrono::seconds(10), add_atom::value, 1, 2).then( self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2)
[=](int result) { .then([=](int32_t result) { //
aout(self) << "1 + 2 = " << result << endl; aout(self) << "1 + 2 = " << result << std::endl;
} });
);
} }
calc::behavior_type actor_b(calc::pointer self, const calc& worker) { calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
return { return {
[=](add_atom add, int x, int y) { [=](add_atom add, int32_t x, int32_t y) {
return self->delegate(worker, add, x, y); return self->delegate(worker, add, x, y);
} },
}; };
} }
calc::behavior_type actor_c() { calc::behavior_type actor_c() {
return { return {
[](add_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return x + y;
}
}; };
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* A very basic, interactive divider. * * A very basic, interactive divider. *
\******************************************************************************/ \******************************************************************************/
// Manual refs: 19-25, 35-48, 68-77 (MessagePassing); // Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 19-34, 50-58 (Error) // 17-47 (Error)
#include <iostream> #include <iostream>
...@@ -14,16 +14,10 @@ using std::endl; ...@@ -14,16 +14,10 @@ using std::endl;
using std::flush; using std::flush;
using namespace caf; using namespace caf;
namespace {
enum class math_error : uint8_t { enum class math_error : uint8_t {
division_by_zero = 1 division_by_zero = 1,
}; };
error make_error(math_error x) {
return {static_cast<uint8_t>(x), atom("math")};
}
std::string to_string(math_error x) { std::string to_string(math_error x) {
switch (x) { switch (x) {
case math_error::division_by_zero: case math_error::division_by_zero:
...@@ -33,7 +27,13 @@ std::string to_string(math_error x) { ...@@ -33,7 +27,13 @@ std::string to_string(math_error x) {
} }
} }
using div_atom = atom_constant<atom("div")>; CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM(math_error)
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>; using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>;
...@@ -43,21 +43,11 @@ divider::behavior_type divider_impl() { ...@@ -43,21 +43,11 @@ divider::behavior_type divider_impl() {
if (y == 0.0) if (y == 0.0)
return math_error::division_by_zero; return math_error::division_by_zero;
return x / y; return x / y;
} },
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& system) {
public:
config() {
auto renderer = [](uint8_t x, atom_value, const message&) {
return "math_error" + deep_to_string_as_tuple(static_cast<math_error>(x));
};
add_error_category(atom("math"), renderer);
}
};
void caf_main(actor_system& system, const config&) {
double x; double x;
double y; double y;
cout << "x: " << flush; cout << "x: " << flush;
...@@ -66,17 +56,13 @@ void caf_main(actor_system& system, const config&) { ...@@ -66,17 +56,13 @@ void caf_main(actor_system& system, const config&) {
std::cin >> y; std::cin >> y;
auto div = system.spawn(divider_impl); auto div = system.spawn(divider_impl);
scoped_actor self{system}; scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom::value, x, y).receive( self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
[&](double z) { .receive(
aout(self) << x << " / " << y << " = " << z << endl; [&](double z) { aout(self) << x << " / " << y << " = " << z << endl; },
},
[&](const error& err) { [&](const error& err) {
aout(self) << "*** cannot compute " << x << " / " << y << " => " aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< system.render(err) << endl; << to_string(err) << endl;
} });
);
} }
} // namespace CAF_MAIN(id_block::divider)
CAF_MAIN()
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
using std::endl; enum class ec : uint8_t {
using namespace caf; push_to_full = 1,
pop_from_empty,
};
namespace { CAF_BEGIN_TYPE_ID_BLOCK(fixed_stack, first_custom_type_id)
using pop_atom = atom_constant<atom("pop")>; CAF_ADD_TYPE_ID(fixed_stack, (ec))
using push_atom = atom_constant<atom("push")>;
enum class fixed_stack_errc : uint8_t { push_to_full = 1, pop_from_empty }; CAF_ADD_ATOM(fixed_stack, custom, pop_atom, "pop")
CAF_ADD_ATOM(fixed_stack, custom, push_atom, "push")
error make_error(fixed_stack_errc x) { CAF_END_TYPE_ID_BLOCK(fixed_stack)
return error{static_cast<uint8_t>(x), atom("FixedStack")};
} CAF_ERROR_CODE_ENUM(ec)
using std::endl;
using namespace caf;
using namespace custom;
class fixed_stack : public event_based_actor { class fixed_stack : public event_based_actor {
public: public:
fixed_stack(actor_config& cfg, size_t stack_size) fixed_stack(actor_config& cfg, size_t stack_size)
: event_based_actor(cfg), : event_based_actor(cfg), size_(stack_size) {
size_(stack_size) { full_.assign( //
full_.assign( [=](push_atom, int) -> error { return ec::push_to_full; },
[=](push_atom, int) -> error {
return fixed_stack_errc::push_to_full;
},
[=](pop_atom) -> int { [=](pop_atom) -> int {
auto result = data_.back(); auto result = data_.back();
data_.pop_back(); data_.pop_back();
become(filled_); become(filled_);
return result; return result;
} });
); filled_.assign( //
filled_.assign(
[=](push_atom, int what) { [=](push_atom, int what) {
data_.push_back(what); data_.push_back(what);
if (data_.size() == size_) if (data_.size() == size_)
...@@ -45,17 +48,13 @@ public: ...@@ -45,17 +48,13 @@ public:
if (data_.empty()) if (data_.empty())
become(empty_); become(empty_);
return result; return result;
} });
); empty_.assign( //
empty_.assign(
[=](push_atom, int what) { [=](push_atom, int what) {
data_.push_back(what); data_.push_back(what);
become(filled_); become(filled_);
}, },
[=](pop_atom) -> error { [=](pop_atom) -> error { return ec::pop_from_empty; });
return fixed_stack_errc::pop_from_empty;
}
);
} }
behavior make_behavior() override { behavior make_behavior() override {
...@@ -76,24 +75,17 @@ void caf_main(actor_system& system) { ...@@ -76,24 +75,17 @@ void caf_main(actor_system& system) {
auto st = self->spawn<fixed_stack>(5u); auto st = self->spawn<fixed_stack>(5u);
// fill stack // fill stack
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
self->send(st, push_atom::value, i); self->send(st, push_atom_v, i);
// drain stack // drain stack
aout(self) << "stack: { "; aout(self) << "stack: { ";
bool stack_empty = false; bool stack_empty = false;
while (!stack_empty) { while (!stack_empty) {
self->request(st, std::chrono::seconds(10), pop_atom::value).receive( self->request(st, std::chrono::seconds(10), pop_atom_v)
[&](int x) { .receive([&](int x) { aout(self) << x << " "; },
aout(self) << x << " "; [&](const error&) { stack_empty = true; });
},
[&](const error&) {
stack_empty = true;
}
);
} }
aout(self) << "}" << endl; aout(self) << "}" << endl;
self->send_exit(st, exit_reason::user_shutdown); self->send_exit(st, exit_reason::user_shutdown);
} }
} // namespace CAF_MAIN(id_block::fixed_stack)
CAF_MAIN()
...@@ -15,16 +15,13 @@ using std::endl; ...@@ -15,16 +15,13 @@ using std::endl;
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
using adder = typed_actor<replies_to<add_atom, int, int>::with<int>>;
// function-based, statically typed, event-based API // function-based, statically typed, event-based API
adder::behavior_type worker() { adder::behavior_type worker() {
return { return {
[](add_atom, int a, int b) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b;
}
}; };
} }
...@@ -32,19 +29,19 @@ adder::behavior_type worker() { ...@@ -32,19 +29,19 @@ adder::behavior_type worker() {
adder::behavior_type calculator_master(adder::pointer self) { adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker); auto w = self->spawn(worker);
return { return {
[=](add_atom x, int y, int z) -> result<int> { [=](add_atom x, int32_t y, int32_t z) -> result<int32_t> {
auto rp = self->make_response_promise<int>(); auto rp = self->make_response_promise<int32_t>();
self->request(w, infinite, x, y, z).then([=](int result) mutable { self->request(w, infinite, x, y, z).then([=](int32_t result) mutable {
rp.deliver(result); rp.deliver(result);
}); });
return rp; return rp;
} },
}; };
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master)); auto f = make_function_view(system.spawn(calculator_master));
cout << "12 + 13 = " << f(add_atom::value, 12, 13) << endl; cout << "12 + 13 = " << f(add_atom_v, 12, 13) << endl;
} }
CAF_MAIN() CAF_MAIN()
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex) // Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <vector>
#include <chrono> #include <chrono>
#include <cstdint>
#include <iostream> #include <iostream>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -17,50 +18,46 @@ using std::vector; ...@@ -17,50 +18,46 @@ using std::vector;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int>>; replies_to<get_atom>::with<int32_t>>;
struct cell_state { struct cell_state {
int value = 0; int32_t value = 0;
}; };
cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self, int x0) { cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self,
int32_t x0) {
self->state.value = x0; self->state.value = x0;
return { return {
[=](put_atom, int val) { [=](put_atom, int32_t val) { self->state.value = val; },
self->state.value = val; [=](get_atom) { return self->state.value; },
},
[=](get_atom) {
return self->state.value;
}
}; };
} }
void waiting_testee(event_based_actor* self, vector<cell> cells) { void waiting_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).await([=](int y) { self->request(x, seconds(1), get_atom_v).await([=](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl; aout(self) << "cell #" << x.id() << " -> " << y << endl;
}); });
} }
void multiplexed_testee(event_based_actor* self, vector<cell> cells) { void multiplexed_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).then([=](int y) { self->request(x, seconds(1), get_atom_v).then([=](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl; aout(self) << "cell #" << x.id() << " -> " << y << endl;
}); });
} }
void blocking_testee(blocking_actor* self, vector<cell> cells) { void blocking_testee(blocking_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).receive( self->request(x, seconds(1), get_atom_v)
[&](int y) { .receive(
[&](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl; aout(self) << "cell #" << x.id() << " -> " << y << endl;
}, },
[&](error& err) { [&](error& err) {
aout(self) << "cell #" << x.id() aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
<< " -> " << self->system().render(err) << endl; });
}
);
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
/******************************************************************************\ /******************************************************************************\
* This example is a very basic, non-interactive math service implemented * * This example is a very basic, non-interactive math service implemented *
* using typed actors. * * using typed actors. *
\ ******************************************************************************/ \******************************************************************************/
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
using std::endl; using std::endl;
using namespace caf; using namespace caf;
namespace { namespace {
using plus_atom = atom_constant<atom("plus")>; using calculator_type
using minus_atom = atom_constant<atom("minus")>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
using result_atom = atom_constant<atom("result")>; replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
using calculator_type =
typed_actor<replies_to<plus_atom, int, int>::with<result_atom, int>,
replies_to<minus_atom, int, int>::with<result_atom, int>>;
calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) { calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) {
return { return {
[](plus_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return std::make_tuple(result_atom::value, x + y); [](sub_atom, int32_t x, int32_t y) { return x - y; },
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
}; };
} }
...@@ -40,12 +32,8 @@ public: ...@@ -40,12 +32,8 @@ public:
protected: protected:
behavior_type make_behavior() override { behavior_type make_behavior() override {
return { return {
[](plus_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return std::make_tuple(result_atom::value, x + y); [](sub_atom, int32_t x, int32_t y) { return x - y; },
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
}; };
} }
}; };
...@@ -53,26 +41,23 @@ protected: ...@@ -53,26 +41,23 @@ protected:
void tester(event_based_actor* self, const calculator_type& testee) { void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee); self->link_to(testee);
// first test: 2 + 1 = 3 // first test: 2 + 1 = 3
self->request(testee, infinite, plus_atom::value, 2, 1).then( self->request(testee, infinite, add_atom_v, 2, 1)
[=](result_atom, int r1) { .then(
[=](int32_t r1) {
// second test: 2 - 1 = 1 // second test: 2 - 1 = 1
self->request(testee, infinite, minus_atom::value, 2, 1).then( self->request(testee, infinite, sub_atom_v, 2, 1).then([=](int32_t r2) {
[=](result_atom, int r2) {
// both tests succeeded // both tests succeeded
if (r1 == 3 && r2 == 1) { if (r1 == 3 && r2 == 1) {
aout(self) << "AUT (actor under test) seems to be ok" aout(self) << "AUT (actor under test) seems to be ok" << endl;
<< endl;
} }
self->send_exit(testee, exit_reason::user_shutdown); self->send_exit(testee, exit_reason::user_shutdown);
} });
);
}, },
[=](const error& err) { [=](const error& err) {
aout(self) << "AUT (actor under test) failed: " aout(self) << "AUT (actor under test) failed: " << to_string(err)
<< self->system().render(err) << endl; << endl;
self->quit(exit_reason::user_shutdown); self->quit(exit_reason::user_shutdown);
} });
);
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
...@@ -79,7 +79,7 @@ void ChatWidget::sendChatMessage() { ...@@ -79,7 +79,7 @@ void ChatWidget::sendChatMessage() {
auto x = system().groups().get(mod, g); auto x = system().groups().get(mod, g);
if (! x) if (! x)
print("*** error: " print("*** error: "
+ QString::fromUtf8(system().render(x.error()).c_str())); + QString::fromUtf8(to_string(x.error()).c_str()));
else else
self()->send(self(), atom("join"), std::move(*x)); self()->send(self(), atom("join"), std::move(*x));
}, },
...@@ -128,7 +128,7 @@ void ChatWidget::joinGroup() { ...@@ -128,7 +128,7 @@ void ChatWidget::joinGroup() {
string gid = gname.midRef(pos+1).toUtf8().constData(); string gid = gname.midRef(pos+1).toUtf8().constData();
auto x = system().groups().get(mod, gid); auto x = system().groups().get(mod, gid);
if (! x) if (! x)
QMessageBox::critical(this, "Error", system().render(x.error()).c_str()); QMessageBox::critical(this, "Error", to_string(x.error()).c_str());
else else
self()->send(self(), join_atom::value, std::move(*x)); self()->send(self(), join_atom::value, std::move(*x));
} }
......
...@@ -59,9 +59,8 @@ int main(int argc, char** argv) { ...@@ -59,9 +59,8 @@ int main(int argc, char** argv) {
auto group_uri = cfg.group_id.substr(p + 1); auto group_uri = cfg.group_id.substr(p + 1);
auto g = system.groups().get(module, group_uri); auto g = system.groups().get(module, group_uri);
if (! g) { if (! g) {
cerr << "*** unable to get group " << group_uri cerr << "*** unable to get group " << group_uri << " from module "
<< " from module " << module << ": " << module << ": " << to_string(g.error()) << endl;
<< system.render(g.error()) << endl;
return -1; return -1;
} }
grp = std::move(*g); grp = std::move(*g);
......
...@@ -9,21 +9,21 @@ ...@@ -9,21 +9,21 @@
// Run client at the same host: // Run client at the same host:
// - ./build/bin/distributed_math_actor -c -p 4242 // - ./build/bin/distributed_math_actor -c -p 4242
// Manual refs: 222-234 (ConfiguringActorSystems) // Manual refs: 206-218 (ConfiguringActorSystems)
#include <array> #include <array>
#include <vector>
#include <string>
#include <sstream>
#include <cassert> #include <cassert>
#include <iostream>
#include <functional> #include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
using std::cout;
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::string; using std::string;
...@@ -33,18 +33,11 @@ namespace { ...@@ -33,18 +33,11 @@ namespace {
constexpr auto task_timeout = std::chrono::seconds(10); constexpr auto task_timeout = std::chrono::seconds(10);
using plus_atom = atom_constant<atom("plus")>;
using minus_atom = atom_constant<atom("minus")>;
// our "service" // our "service"
behavior calculator_fun() { behavior calculator_fun() {
return { return {
[](plus_atom, int a, int b) { [](add_atom, int a, int b) { return a + b; },
return a + b; [](sub_atom, int a, int b) { return a - b; },
},
[](minus_atom, int a, int b) {
return a - b;
}
}; };
} }
...@@ -75,9 +68,9 @@ behavior calculator_fun() { ...@@ -75,9 +68,9 @@ behavior calculator_fun() {
namespace client { namespace client {
// a simple calculater task: operation + operands // a simple calculator task: operation + operands
struct task { struct task {
atom_value op; caf::variant<add_atom, sub_atom> op;
int lhs; int lhs;
int rhs; int rhs;
}; };
...@@ -92,8 +85,7 @@ struct state { ...@@ -92,8 +85,7 @@ struct state {
behavior unconnected(stateful_actor<state>*); behavior unconnected(stateful_actor<state>*);
// prototype definition for transition to `connecting` with Host and Port // prototype definition for transition to `connecting` with Host and Port
void connecting(stateful_actor<state>*, void connecting(stateful_actor<state>*, const std::string& host, uint16_t port);
const std::string& host, uint16_t port);
// prototype definition for transition to `running` with Calculator // prototype definition for transition to `running` with Calculator
behavior running(stateful_actor<state>*, const actor& calculator); behavior running(stateful_actor<state>*, const actor& calculator);
...@@ -113,35 +105,36 @@ behavior init(stateful_actor<state>* self) { ...@@ -113,35 +105,36 @@ behavior init(stateful_actor<state>* self) {
behavior unconnected(stateful_actor<state>* self) { behavior unconnected(stateful_actor<state>* self) {
return { return {
[=](plus_atom op, int x, int y) { [=](add_atom op, int x, int y) {
self->state.tasks.emplace_back(task{op, x, y}); self->state.tasks.emplace_back(task{op, x, y});
}, },
[=](minus_atom op, int x, int y) { [=](sub_atom op, int x, int y) {
self->state.tasks.emplace_back(task{op, x, y}); self->state.tasks.emplace_back(task{op, x, y});
}, },
[=](connect_atom, const std::string& host, uint16_t port) { [=](connect_atom, const std::string& host, uint16_t port) {
connecting(self, host, port); connecting(self, host, port);
} },
}; };
} }
void connecting(stateful_actor<state>* self, void connecting(stateful_actor<state>* self, const std::string& host,
const std::string& host, uint16_t port) { uint16_t port) {
// make sure we are not pointing to an old server // make sure we are not pointing to an old server
self->state.current_server = nullptr; self->state.current_server = nullptr;
// use request().await() to suspend regular behavior until MM responded // use request().await() to suspend regular behavior until MM responded
auto mm = self->system().middleman().actor_handle(); auto mm = self->system().middleman().actor_handle();
self->request(mm, infinite, connect_atom::value, host, port).await( self->request(mm, infinite, connect_atom_v, host, port)
.await(
[=](const node_id&, strong_actor_ptr serv, [=](const node_id&, strong_actor_ptr serv,
const std::set<std::string>& ifs) { const std::set<std::string>& ifs) {
if (!serv) { if (!serv) {
aout(self) << R"(*** no server found at ")" << host << R"(":)" aout(self) << R"(*** no server found at ")" << host << R"(":)" << port
<< port << endl; << endl;
return; return;
} }
if (!ifs.empty()) { if (!ifs.empty()) {
aout(self) << R"(*** typed actor found at ")" << host << R"(":)" aout(self) << R"(*** typed actor found at ")" << host << R"(":)"
<< port << ", but expected an untyped actor "<< endl; << port << ", but expected an untyped actor " << endl;
return; return;
} }
aout(self) << "*** successfully connected to server" << endl; aout(self) << "*** successfully connected to server" << endl;
...@@ -151,40 +144,37 @@ void connecting(stateful_actor<state>* self, ...@@ -151,40 +144,37 @@ void connecting(stateful_actor<state>* self,
self->become(running(self, hdl)); self->become(running(self, hdl));
}, },
[=](const error& err) { [=](const error& err) {
aout(self) << R"(*** cannot connect to ")" << host << R"(":)" aout(self) << R"(*** cannot connect to ")" << host << R"(":)" << port
<< port << " => " << self->system().render(err) << endl; << " => " << to_string(err) << endl;
self->become(unconnected(self)); self->become(unconnected(self));
} });
);
} }
// prototype definition for transition to `running` with Calculator // prototype definition for transition to `running` with Calculator
behavior running(stateful_actor<state>* self, const actor& calculator) { behavior running(stateful_actor<state>* self, const actor& calculator) {
auto send_task = [=](const task& x) { auto send_task = [=](atom_value op, int x, int y) {
self->request(calculator, task_timeout, x.op, x.lhs, x.rhs).then( self->request(calculator, task_timeout, op, x, y)
.then(
[=](int result) { [=](int result) {
aout(self) << x.lhs << (x.op == plus_atom::value ? " + " : " - ") const char* op_str = op == add_atom_v ? " + " : " - ";
<< x.rhs << " = " << result << endl; aout(self) << x << op_str << y << " = " << result << endl;
}, },
[=](const error&) { [=](const error&) {
// simply try again by enqueueing the task to the mailbox again // simply try again by enqueueing the task to the mailbox again
self->send(self, x.op, x.lhs, x.rhs); self->send(self, op, x, y);
} });
);
}; };
for (auto& x : self->state.tasks) for (auto& x : self->state.tasks) {
send_task(x); auto f = [&](atom_value op) { send_task(op, x.lhs, x.rhs); };
caf::visit(f, x.op);
}
self->state.tasks.clear(); self->state.tasks.clear();
return { return {
[=](plus_atom op, int x, int y) { [=](add_atom op, int x, int y) { send_task(op, x, y); },
send_task(task{op, x, y}); [=](sub_atom op, int x, int y) { send_task(op, x, y); },
},
[=](minus_atom op, int x, int y) {
send_task(task{op, x, y});
},
[=](connect_atom, const std::string& host, uint16_t port) { [=](connect_atom, const std::string& host, uint16_t port) {
connecting(self, host, port); connecting(self, host, port);
} },
}; };
} }
...@@ -209,15 +199,6 @@ optional<int> toint(const string& str) { ...@@ -209,15 +199,6 @@ optional<int> toint(const string& str) {
return none; return none;
} }
// converts "+" to the atom '+' and "-" to the atom '-'
optional<atom_value> plus_or_minus(const string& str) {
if (str == "+")
return plus_atom::value;
if (str == "-")
return minus_atom::value;
return none;
}
class config : public actor_system_config { class config : public actor_system_config {
public: public:
uint16_t port = 0; uint16_t port = 0;
...@@ -246,7 +227,7 @@ void client_repl(actor_system& system, const config& cfg) { ...@@ -246,7 +227,7 @@ void client_repl(actor_system& system, const config& cfg) {
bool done = false; bool done = false;
auto client = system.spawn(client::init); auto client = system.spawn(client::init);
if (!cfg.host.empty() && cfg.port > 0) if (!cfg.host.empty() && cfg.port > 0)
anon_send(client, connect_atom::value, cfg.host, cfg.port); anon_send(client, connect_atom_v, cfg.host, cfg.port);
else else
cout << "*** no server received via config, " cout << "*** no server received via config, "
<< R"(please use "connect <host> <port>" before using the calculator)" << R"(please use "connect <host> <port>" before using the calculator)"
...@@ -270,25 +251,27 @@ void client_repl(actor_system& system, const config& cfg) { ...@@ -270,25 +251,27 @@ void client_repl(actor_system& system, const config& cfg) {
cout << R"(")" << arg2 << R"(" > )" cout << R"(")" << arg2 << R"(" > )"
<< std::numeric_limits<uint16_t>::max() << endl; << std::numeric_limits<uint16_t>::max() << endl;
else else
anon_send(client, connect_atom::value, move(arg1), anon_send(client, connect_atom_v, move(arg1),
static_cast<uint16_t>(lport)); static_cast<uint16_t>(lport));
} } else {
else {
auto x = toint(arg0); auto x = toint(arg0);
auto op = plus_or_minus(arg1);
auto y = toint(arg2); auto y = toint(arg2);
if (x && y && op) if (x && y) {
anon_send(client, *op, *x, *y); if (arg1 == "+")
anon_send(client, add_atom_v, *x, *y);
else if (arg1 == "-")
anon_send(client, sub_atom_v, *x, *y);
} }
} }
}; }};
// read next line, split it, and feed to the eval handler // read next line, split it, and feed to the eval handler
string line; string line;
while (!done && std::getline(std::cin, line)) { while (!done && std::getline(std::cin, line)) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces line = trim(std::move(line)); // ignore leading and trailing whitespaces
std::vector<string> words; std::vector<string> words;
split(words, line, is_any_of(" "), token_compress_on); split(words, line, is_any_of(" "), token_compress_on);
if (!message_builder(words.begin(), words.end()).apply(eval)) auto msg = message_builder(words.begin(), words.end()).move_to_message();
if (!eval(msg))
usage(); usage();
} }
} }
...@@ -299,8 +282,8 @@ void run_server(actor_system& system, const config& cfg) { ...@@ -299,8 +282,8 @@ void run_server(actor_system& system, const config& cfg) {
cout << "*** try publish at port " << cfg.port << endl; cout << "*** try publish at port " << cfg.port << endl;
auto expected_port = io::publish(calc, cfg.port); auto expected_port = io::publish(calc, cfg.port);
if (!expected_port) { if (!expected_port) {
std::cerr << "*** publish failed: " std::cerr << "*** publish failed: " << to_string(expected_port.error())
<< system.render(expected_port.error()) << endl; << endl;
return; return;
} }
cout << "*** server successfully published at port " << *expected_port << endl cout << "*** server successfully published at port " << *expected_port << endl
......
...@@ -6,59 +6,63 @@ ...@@ -6,59 +6,63 @@
* - ./build/bin/group_chat -s -p 4242 * * - ./build/bin/group_chat -s -p 4242 *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * * - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob * * - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\ ******************************************************************************/ \******************************************************************************/
#include <set>
#include <map>
#include <vector>
#include <cstdlib> #include <cstdlib>
#include <sstream>
#include <iostream> #include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
using namespace std; CAF_BEGIN_TYPE_ID_BLOCK(group_chat, first_custom_type_id)
using namespace caf;
namespace { CAF_ADD_ATOM(group_chat, custom, broadcast_atom, "broadcast")
using broadcast_atom = atom_constant<atom("broadcast")>; CAF_END_TYPE_ID_BLOCK(group_chat)
struct line { string str; }; using namespace caf;
using namespace custom;
istream& operator>>(istream& is, line& l) { struct line {
getline(is, l.str); std::string str;
};
std::istream& operator>>(std::istream& is, line& l) {
std::getline(is, l.str);
return is; return is;
} }
behavior client(event_based_actor* self, const string& name) { behavior client(event_based_actor* self, const std::string& name) {
return { return {
[=](broadcast_atom, const string& message) { [=](broadcast_atom, const std::string& message) {
for(auto& dest : self->joined_groups()) { for (auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message); self->send(dest, name + ": " + message);
} }
}, },
[=](join_atom, const group& what) { [=](join_atom, const group& what) {
for (const auto& g : self->joined_groups()) { for (const auto& g : self->joined_groups()) {
cout << "*** leave " << to_string(g) << endl; std::cout << "*** leave " << to_string(g) << std::endl;
self->send(g, name + " has left the chatroom"); self->send(g, name + " has left the chatroom");
self->leave(g); self->leave(g);
} }
cout << "*** join " << to_string(what) << endl; std::cout << "*** join " << to_string(what) << std::endl;
self->join(what); self->join(what);
self->send(what, name + " has entered the chatroom"); self->send(what, name + " has entered the chatroom");
}, },
[=](const string& txt) { [=](const std::string& txt) {
// don't print own messages // don't print own messages
if (self->current_sender() != self) if (self->current_sender() != self)
cout << txt << endl; std::cout << txt << std::endl;
}, },
[=](const group_down_msg& g) { [=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl; std::cout << "*** chatroom offline: " << to_string(g.source) << std::endl;
} },
}; };
} }
...@@ -80,72 +84,72 @@ public: ...@@ -80,72 +84,72 @@ public:
void run_server(actor_system& system, const config& cfg) { void run_server(actor_system& system, const config& cfg) {
auto res = system.middleman().publish_local_groups(cfg.port); auto res = system.middleman().publish_local_groups(cfg.port);
if (! res) { if (!res) {
std::cerr << "*** publishing local groups failed: " std::cerr << "*** publishing local groups failed: "
<< system.render(res.error()) << endl; << to_string(res.error()) << std::endl;
return; return;
} }
cout << "*** listening at port " << *res << endl std::cout << "*** listening at port " << *res << std::endl
<< "*** press [enter] to quit" << endl; << "*** press [enter] to quit" << std::endl;
string dummy; std::string dummy;
std::getline(std::cin, dummy); std::getline(std::cin, dummy);
cout << "... cya" << endl; std::cout << "... cya" << std::endl;
} }
void run_client(actor_system& system, const config& cfg) { void run_client(actor_system& system, const config& cfg) {
auto name = cfg.name; auto name = cfg.name;
while (name.empty()) { while (name.empty()) {
cout << "please enter your name: " << flush; std::cout << "please enter your name: " << std::flush;
if (!getline(cin, name)) { if (!std::getline(std::cin, name)) {
cerr << "*** no name given... terminating" << endl; std::cerr << "*** no name given... terminating" << std::endl;
return; return;
} }
} }
cout << "*** starting client, type '/help' for a list of commands" << endl; std::cout << "*** starting client, type '/help' for a list of commands\n";
auto client_actor = system.spawn(client, name); auto client_actor = system.spawn(client, name);
for (auto& uri : cfg.group_uris) { for (auto& uri : cfg.group_uris) {
auto tmp = system.groups().get(uri); auto tmp = system.groups().get(uri);
if (tmp) if (tmp)
anon_send(client_actor, join_atom::value, std::move(*tmp)); anon_send(client_actor, join_atom_v, std::move(*tmp));
else else
cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )" std::cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< system.render(tmp.error()) << endl; << to_string(tmp.error()) << std::endl;
} }
istream_iterator<line> eof; std::istream_iterator<line> eof;
vector<string> words; std::vector<std::string> words;
for (istream_iterator<line> i(cin); i != eof; ++i) { for (std::istream_iterator<line> i{std::cin}; i != eof; ++i) {
auto send_input = [&] { auto send_input = [&] {
if (!i->str.empty()) if (!i->str.empty())
anon_send(client_actor, broadcast_atom::value, i->str); anon_send(client_actor, broadcast_atom_v, i->str);
}; };
words.clear(); words.clear();
split(words, i->str, is_any_of(" ")); split(words, i->str, is_any_of(" "));
auto res = message_builder(words.begin(), words.end()).apply({ message_handler f{
[&](const string& cmd, const string& mod, const string& id) { [&](const std::string& cmd, const std::string& mod,
const std::string& id) {
if (cmd == "/join") { if (cmd == "/join") {
auto grp = system.groups().get(mod, id); auto grp = system.groups().get(mod, id);
if (grp) if (grp)
anon_send(client_actor, join_atom::value, *grp); anon_send(client_actor, join_atom_v, *grp);
} } else {
else {
send_input(); send_input();
} }
}, },
[&](const string& cmd) { [&](const std::string& cmd) {
if (cmd == "/quit") { if (cmd == "/quit") {
cin.setstate(ios_base::eofbit); std::cin.setstate(std::ios_base::eofbit);
} } else if (cmd[0] == '/') {
else if (cmd[0] == '/') { std::cout << "*** available commands:\n"
cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n" " /join <module> <group> join a new chat channel\n"
" /quit quit the program\n" " /quit quit the program\n"
" /help print this text\n" << flush; " /help print this text\n";
} } else {
else {
send_input(); send_input();
} }
} },
}); };
auto msg = message_builder(words.begin(), words.end()).move_to_message();
auto res = f(msg);
if (!res) if (!res)
send_input(); send_input();
} }
...@@ -158,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) { ...@@ -158,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) {
f(system, cfg); f(system, cfg);
} }
} // namespace CAF_MAIN(id_block::group_chat, io::middleman)
CAF_MAIN(io::middleman)
...@@ -7,45 +7,46 @@ ...@@ -7,45 +7,46 @@
// Run client at the same host: // Run client at the same host:
// - remote_spawn -H localhost -p 4242 // - remote_spawn -H localhost -p 4242
// Manual refs: 33-39, 99-101,106,110 (ConfiguringActorApplications)
// 125-143 (RemoteSpawn)
#include <array> #include <array>
#include <vector>
#include <string>
#include <sstream>
#include <cassert> #include <cassert>
#include <iostream>
#include <functional> #include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
using std::cout; // --(rst-calculator-begin)--
using calculator = caf::typed_actor<
caf::replies_to<caf::add_atom, int32_t, int32_t>::with<int32_t>,
caf::replies_to<caf::sub_atom, int32_t, int32_t>::with<int32_t>>;
// --(rst-calculator-end)--
CAF_BEGIN_TYPE_ID_BLOCK(remote_spawn, first_custom_type_id)
CAF_ADD_TYPE_ID(remote_spawn, (calculator))
CAF_END_TYPE_ID_BLOCK(remote_spawn)
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::string; using std::string;
using namespace caf; using namespace caf;
namespace {
using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;
using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
calculator::behavior_type calculator_fun(calculator::pointer self) { calculator::behavior_type calculator_fun(calculator::pointer self) {
return { return {
[=](add_atom, int a, int b) -> int { [=](add_atom, int32_t a, int32_t b) {
aout(self) << "received task from a remote node" << endl; aout(self) << "received task from a remote node" << endl;
return a + b; return a + b;
}, },
[=](sub_atom, int a, int b) -> int { [=](sub_atom, int32_t a, int32_t b) {
aout(self) << "received task from a remote node" << endl; aout(self) << "received task from a remote node" << endl;
return a - b; return a - b;
} },
}; };
} }
...@@ -64,8 +65,9 @@ void client_repl(function_view<calculator> f) { ...@@ -64,8 +65,9 @@ void client_repl(function_view<calculator> f) {
auto usage = [] { auto usage = [] {
cout << "Usage:" << endl cout << "Usage:" << endl
<< " quit : terminate program" << endl << " quit : terminate program" << endl
<< " <x> + <y> : adds two integers" << endl << " <x> + <y> : adds two int32_tegers" << endl
<< " <x> - <y> : subtracts two integers" << endl << endl; << " <x> - <y> : subtracts two int32_tegers" << endl
<< endl;
}; };
usage(); usage();
// read next line, split it, and evaluate user input // read next line, split it, and evaluate user input
...@@ -79,20 +81,21 @@ void client_repl(function_view<calculator> f) { ...@@ -79,20 +81,21 @@ void client_repl(function_view<calculator> f) {
usage(); usage();
continue; continue;
} }
auto to_int = [](const string& str) -> optional<int> { auto to_int32_t = [](const string& str) -> optional<int32_t> {
char* end = nullptr; char* end = nullptr;
auto res = strtol(str.c_str(), &end, 10); auto res = strtol(str.c_str(), &end, 10);
if (end == str.c_str() + str.size()) if (end == str.c_str() + str.size())
return static_cast<int>(res); return static_cast<int32_t>(res);
return none; return none;
}; };
auto x = to_int(words[0]); auto x = to_int32_t(words[0]);
auto y = to_int(words[2]); auto y = to_int32_t(words[2]);
if (!x || !y || (words[1] != "+" && words[1] != "-")) if (!x || !y || (words[1] != "+" && words[1] != "-"))
usage(); usage();
else else
cout << " = " << (words[1] == "+" ? f(add_atom::value, *x, *y) cout << " = "
: f(sub_atom::value, *x, *y)) << "\n"; << (words[1] == "+" ? f(add_atom_v, *x, *y) : f(sub_atom_v, *x, *y))
<< "\n";
} }
} }
...@@ -112,31 +115,28 @@ struct config : actor_system_config { ...@@ -112,31 +115,28 @@ struct config : actor_system_config {
void server(actor_system& system, const config& cfg) { void server(actor_system& system, const config& cfg) {
auto res = system.middleman().open(cfg.port); auto res = system.middleman().open(cfg.port);
if (!res) { if (!res) {
cerr << "*** cannot open port: " cerr << "*** cannot open port: " << to_string(res.error()) << endl;
<< system.render(res.error()) << endl;
return; return;
} }
cout << "*** running on port: " cout << "*** running on port: " << *res << endl
<< *res << endl
<< "*** press <enter> to shutdown server" << endl; << "*** press <enter> to shutdown server" << endl;
getchar(); getchar();
} }
// --(rst-client-begin)--
void client(actor_system& system, const config& cfg) { void client(actor_system& system, const config& cfg) {
auto node = system.middleman().connect(cfg.host, cfg.port); auto node = system.middleman().connect(cfg.host, cfg.port);
if (!node) { if (!node) {
cerr << "*** connect failed: " cerr << "*** connect failed: " << to_string(node.error()) << endl;
<< system.render(node.error()) << endl;
return; return;
} }
auto type = "calculator"; // type of the actor we wish to spawn auto type = "calculator"; // type of the actor we wish to spawn
auto args = make_message(); // arguments to construct the actor auto args = make_message(); // arguments to construct the actor
auto tout = std::chrono::seconds(30); // wait no longer than 30s auto tout = std::chrono::seconds(30); // wait no longer than 30s
auto worker = system.middleman().remote_spawn<calculator>(*node, type, auto worker = system.middleman().remote_spawn<calculator>(*node, type, args,
args, tout); tout);
if (!worker) { if (!worker) {
cerr << "*** remote spawn failed: " cerr << "*** remote spawn failed: " << to_string(worker.error()) << endl;
<< system.render(worker.error()) << endl;
return; return;
} }
// start using worker in main loop // start using worker in main loop
...@@ -144,12 +144,11 @@ void client(actor_system& system, const config& cfg) { ...@@ -144,12 +144,11 @@ void client(actor_system& system, const config& cfg) {
// be a good citizen and terminate remotely spawned actor before exiting // be a good citizen and terminate remotely spawned actor before exiting
anon_send_exit(*worker, exit_reason::kill); anon_send_exit(*worker, exit_reason::kill);
} }
// --(rst-client-end)--
void caf_main(actor_system& system, const config& cfg) { void caf_main(actor_system& system, const config& cfg) {
auto f = cfg.server_mode ? server : client; auto f = cfg.server_mode ? server : client;
f(system, cfg); f(system, cfg);
} }
} // namespace CAF_MAIN(id_block::remote_spawn, io::middleman)
CAF_MAIN(io::middleman)
...@@ -2,21 +2,28 @@ ...@@ -2,21 +2,28 @@
* Basic, non-interactive streaming example for processing integers. * * Basic, non-interactive streaming example for processing integers. *
******************************************************************************/ ******************************************************************************/
// Manual refs: lines 17-46, 48-78, 80-107, 121-125 (Streaming)
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
CAF_BEGIN_TYPE_ID_BLOCK(integer_stream, first_custom_type_id)
CAF_ADD_TYPE_ID(integer_stream, (caf::stream<int32_t>) )
CAF_ADD_TYPE_ID(integer_stream, (std::vector<int32_t>) )
CAF_END_TYPE_ID_BLOCK(integer_stream)
using std::endl; using std::endl;
using namespace caf; using namespace caf;
namespace { namespace {
// --(rst-source-begin)--
// Simple source for generating a stream of integers from [0, n). // Simple source for generating a stream of integers from [0, n).
behavior int_source(event_based_actor* self) { behavior int_source(event_based_actor* self) {
return {[=](open_atom, int n) { return {
[=](open_atom, int32_t n) {
// Produce at least one value. // Produce at least one value.
if (n <= 0) if (n <= 0)
n = 1; n = 1;
...@@ -27,7 +34,7 @@ behavior int_source(event_based_actor* self) { ...@@ -27,7 +34,7 @@ behavior int_source(event_based_actor* self) {
self, self,
// Initializer. The type of the first argument (state) is freely // Initializer. The type of the first argument (state) is freely
// chosen. If no state is required, `caf::unit_t` can be used here. // chosen. If no state is required, `caf::unit_t` can be used here.
[](int& x) { x = 0; }, [](int32_t& x) { x = 0; },
// Generator. This function is called by CAF to produce new stream // Generator. This function is called by CAF to produce new stream
// elements for downstream actors. The `x` argument is our state again // elements for downstream actors. The `x` argument is our state again
// (with our freely chosen type). The second argument `out` points to // (with our freely chosen type). The second argument `out` points to
...@@ -35,19 +42,23 @@ behavior int_source(event_based_actor* self) { ...@@ -35,19 +42,23 @@ behavior int_source(event_based_actor* self) {
// elements downstream actors receive in this stream. Finally, `num` is // elements downstream actors receive in this stream. Finally, `num` is
// a hint from CAF how many elements we should ideally insert into // a hint from CAF how many elements we should ideally insert into
// `out`. We can always insert fewer or more items. // `out`. We can always insert fewer or more items.
[n](int& x, downstream<int>& out, size_t num) { [n](int32_t& x, downstream<int32_t>& out, size_t num) {
auto max_x = std::min(x + static_cast<int>(num), n); auto max_x = std::min(x + static_cast<int>(num), n);
for (; x < max_x; ++x) for (; x < max_x; ++x)
out.push(x); out.push(x);
}, },
// Predicate. This function tells CAF when we reached the end. // Predicate. This function tells CAF when we reached the end.
[n](const int& x) { return x == n; }); [n](const int32_t& x) { return x == n; });
}}; },
};
} }
// --(rst-source-end)--
// --(rst-stage-begin)--
// Simple stage that only selects even numbers. // Simple stage that only selects even numbers.
behavior int_selector(event_based_actor* self) { behavior int_selector(event_based_actor* self) {
return {[=](stream<int> in) { return {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream stage. Similar to // Create a stream manager for implementing a stream stage. Similar to
// `make_source`, we need three functions: initialzer, processor, and // `make_source`, we need three functions: initialzer, processor, and
// finalizer. // finalizer.
...@@ -61,24 +72,29 @@ behavior int_selector(event_based_actor* self) { ...@@ -61,24 +72,29 @@ behavior int_selector(event_based_actor* self) {
}, },
// Processor. This function takes individual input elements as `val` // Processor. This function takes individual input elements as `val`
// and forwards even integers to `out`. // and forwards even integers to `out`.
[](unit_t&, downstream<int>& out, int val) { [](unit_t&, downstream<int32_t>& out, int32_t val) {
if (val % 2 == 0) if (val % 2 == 0)
out.push(val); out.push(val);
}, },
// Finalizer. Allows us to run cleanup code once the stream terminates. // Finalizer. Allows us to run cleanup code once the stream terminates.
[=](unit_t&, const error& err) { [=](unit_t&, const error& err) {
if (err) { if (err) {
aout(self) << "int_selector aborted with error: " << err << std::endl; aout(self) << "int_selector aborted with error: " << err
<< std::endl;
} else { } else {
aout(self) << "int_selector finalized" << std::endl; aout(self) << "int_selector finalized" << std::endl;
} }
// else: regular stream shutdown // else: regular stream shutdown
}); });
}}; },
};
} }
// --(rst-stage-end)--
// --(rst-sink-begin)--
behavior int_sink(event_based_actor* self) { behavior int_sink(event_based_actor* self) {
return {[=](stream<int> in) { return {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream sink. Once more, we // Create a stream manager for implementing a stream sink. Once more, we
// have to provide three functions: Initializer, Consumer, Finalizer. // have to provide three functions: Initializer, Consumer, Finalizer.
return attach_stream_sink( return attach_stream_sink(
...@@ -93,9 +109,9 @@ behavior int_sink(event_based_actor* self) { ...@@ -93,9 +109,9 @@ behavior int_sink(event_based_actor* self) {
}, },
// Consumer. Takes individual input elements as `val` and stores them // Consumer. Takes individual input elements as `val` and stores them
// in our history. // in our history.
[](std::vector<int>& xs, int val) { xs.emplace_back(val); }, [](std::vector<int32_t>& xs, int32_t val) { xs.emplace_back(val); },
// Finalizer. Allows us to run cleanup code once the stream terminates. // Finalizer. Allows us to run cleanup code once the stream terminates.
[=](std::vector<int>& xs, const error& err) { [=](std::vector<int32_t>& xs, const error& err) {
if (err) { if (err) {
aout(self) << "int_sink aborted with error: " << err << std::endl; aout(self) << "int_sink aborted with error: " << err << std::endl;
} else { } else {
...@@ -103,8 +119,10 @@ behavior int_sink(event_based_actor* self) { ...@@ -103,8 +119,10 @@ behavior int_sink(event_based_actor* self) {
<< std::endl; << std::endl;
} }
}); });
}}; },
};
} }
// --(rst-sink-end)--
struct config : actor_system_config { struct config : actor_system_config {
config() { config() {
...@@ -114,17 +132,19 @@ struct config : actor_system_config { ...@@ -114,17 +132,19 @@ struct config : actor_system_config {
} }
bool with_stage = false; bool with_stage = false;
int n = 100; int32_t n = 100;
}; };
// --(rst-main-begin)--
void caf_main(actor_system& sys, const config& cfg) { void caf_main(actor_system& sys, const config& cfg) {
auto src = sys.spawn(int_source); auto src = sys.spawn(int_source);
auto snk = sys.spawn(int_sink); auto snk = sys.spawn(int_sink);
auto pipeline = cfg.with_stage ? snk * sys.spawn(int_selector) * src auto pipeline = cfg.with_stage ? snk * sys.spawn(int_selector) * src
: snk * src; : snk * src;
anon_send(pipeline, open_atom::value, cfg.n); anon_send(pipeline, open_atom_v, cfg.n);
} }
// --(rst-main-end)--
} // namespace } // namespace
CAF_MAIN() CAF_MAIN(id_block::integer_stream)
// Manual refs: lines 12-65 (Testing) // Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong #define CAF_SUITE ping_pong
...@@ -11,24 +11,19 @@ using namespace caf; ...@@ -11,24 +11,19 @@ using namespace caf;
namespace { namespace {
using ping_atom = atom_constant<atom("ping")>;
using pong_atom = atom_constant<atom("pong")>;
behavior ping(event_based_actor* self, actor pong_actor, int n) { behavior ping(event_based_actor* self, actor pong_actor, int n) {
self->send(pong_actor, ping_atom::value, n); self->send(pong_actor, ping_atom_v, n);
return { return {
[=](pong_atom, int x) { [=](pong_atom, int x) {
if (x > 1) if (x > 1)
self->send(pong_actor, ping_atom::value, x - 1); self->send(pong_actor, ping_atom_v, x - 1);
} },
}; };
} }
behavior pong() { behavior pong() {
return { return {
[=](ping_atom, int x) { [=](ping_atom, int x) { return make_result(pong_atom_v, x); },
return std::make_tuple(pong_atom::value, x);
}
}; };
} }
......
...@@ -213,3 +213,126 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/caf" ...@@ -213,3 +213,126 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/caf"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.hpp" FILES_MATCHING PATTERN "*.hpp"
) )
# -- build unit tests ----------------------------------------------------------
if(CAF_NO_UNIT_TESTS)
return()
endif()
add_executable(caf-core-test test/core-test.cpp)
target_include_directories(caf-core-test PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/test")
if (CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
target_link_libraries(caf-core-test libcaf_core_static ${CAF_EXTRA_LDFLAGS})
else()
target_link_libraries(caf-core-test libcaf_core_shared ${CAF_EXTRA_LDFLAGS})
endif()
caf_add_test_suites(caf-core-test
actor_clock
actor_factory
actor_lifetime
actor_pool
actor_profiler
actor_registry
actor_system_config
actor_termination
aout
behavior
blocking_actor
broadcast_downstream_manager
byte
composition
config_option
config_option_set
config_value
config_value_adaptor
constructor_attach
continuous_streaming
cow_tuple
decorator.sequencer
deep_to_string
detached_actors
detail.bounds_checker
detail.ini_consumer
detail.limited_vector
detail.parse
detail.parser.read_bool
detail.parser.read_floating_point
detail.parser.read_ini
detail.parser.read_number
detail.parser.read_number_or_timespan
detail.parser.read_signed_integer
detail.parser.read_string
detail.parser.read_timespan
detail.parser.read_unsigned_integer
detail.ringbuffer
detail.ripemd_160
detail.serialized_size
detail.tick_emitter
detail.unique_function
detail.unordered_flat_map
dictionary
dynamic_spawn
expected
function_view
fused_downstream_manager
handles
inspector
intrusive.drr_cached_queue
intrusive.drr_queue
intrusive.fifo_inbox
intrusive.lifo_inbox
intrusive.task_queue
intrusive.wdrr_dynamic_multiplexed_queue
intrusive.wdrr_fixed_multiplexed_queue
intrusive_ptr
ipv4_address
ipv4_endpoint
ipv4_subnet
ipv6_address
ipv6_endpoint
ipv6_subnet
local_group
logger
mailbox_element
make_config_value_field
message
message_id
message_lifetime
metaprogramming
mixin.requester
mixin.sender
mock_streaming_classes
native_streaming_classes
node_id
optional
or_else
pipeline_streaming
policy.categorized
request_timeout
result
selective_streaming
serialization
settings
simple_timeout
span
stateful_actor
string_algorithms
string_view
sum_type
thread_hook
typed_behavior
typed_response_promise
typed_spawn
unit
uri
variant
)
if(NOT CAF_NO_EXCEPTIONS)
caf_add_test_suites(caf-core-test custom_exception_handler)
endif()
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "caf/stream.hpp" #include "caf/stream.hpp"
#include "caf/thread_hook.hpp" #include "caf/thread_hook.hpp"
#include "caf/type_erased_value.hpp" #include "caf/type_erased_value.hpp"
#include "caf/type_id.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
...@@ -167,12 +168,7 @@ public: ...@@ -167,12 +168,7 @@ public:
/// Adds message type `T` with runtime type info `name`. /// Adds message type `T` with runtime type info `name`.
template <class T> template <class T>
actor_system_config& add_message_type(std::string name) { actor_system_config& add_message_type(std::string name) {
static_assert(std::is_empty<T>::value assert_message_type_eligible<T>();
|| std::is_same<T, actor>::value // silence add_actor_type err
|| is_typed_actor<T>::value
|| (std::is_default_constructible<T>::value
&& std::is_copy_constructible<T>::value),
"T must provide default and copy constructors");
std::string stream_name = "stream<"; std::string stream_name = "stream<";
stream_name += name; stream_name += name;
stream_name += ">"; stream_name += ">";
...@@ -185,6 +181,14 @@ public: ...@@ -185,6 +181,14 @@ public:
return *this; return *this;
} }
/// Loads new types by calling add_message_type for each type in `IdBlock`.
template <class IdBlock>
actor_system_config& add_message_types() {
typename detail::il_range<IdBlock::begin, IdBlock::end>::type token;
add_message_types(token);
return *this;
}
/// Enables the actor system to convert errors of this error category /// Enables the actor system to convert errors of this error category
/// to human-readable strings via `renderer`. /// to human-readable strings via `renderer`.
actor_system_config& add_error_category(atom_value x, actor_system_config& add_error_category(atom_value x,
...@@ -211,13 +215,25 @@ public: ...@@ -211,13 +215,25 @@ public:
/// Loads module `T` with optional template parameters `Ts...`. /// Loads module `T` with optional template parameters `Ts...`.
template <class T, class... Ts> template <class T, class... Ts>
actor_system_config& load() { detail::enable_if_t<std::is_base_of<actor_system::module, T>::value,
actor_system_config&>
load() {
module_factories.push_back([](actor_system& sys) -> actor_system::module* { module_factories.push_back([](actor_system& sys) -> actor_system::module* {
return T::make(sys, detail::type_list<Ts...>{}); return T::make(sys, detail::type_list<Ts...>{});
}); });
return *this; return *this;
} }
/// @private
template <class IdBlock>
detail::enable_if_t<!std::is_base_of<actor_system::module, IdBlock>::value,
actor_system_config&>
load() {
// Allows exec_main to use one syntax for IdBlocks and modules.
add_message_types<IdBlock>();
return *this;
}
/// Adds a factory for a new hook type to the middleman (if loaded). /// Adds a factory for a new hook type to the middleman (if loaded).
template <class Factory> template <class Factory>
actor_system_config& add_hook_factory(Factory f) { actor_system_config& add_hook_factory(Factory f) {
...@@ -400,6 +416,17 @@ protected: ...@@ -400,6 +416,17 @@ protected:
config_option_set custom_options_; config_option_set custom_options_;
private: private:
template <class T>
static void assert_message_type_eligible() {
static_assert(
std::is_empty<T>::value
|| std::is_same<T, actor>::value // silence add_actor_type err
|| is_typed_actor<T>::value
|| (std::is_default_constructible<T>::value
&& std::is_copy_constructible<T>::value),
"T must provide default and copy constructors");
}
template <class T> template <class T>
void add_message_type_impl(std::string name) { void add_message_type_impl(std::string name) {
type_names_by_rtti.emplace(std::type_index(typeid(T)), name); type_names_by_rtti.emplace(std::type_index(typeid(T)), name);
...@@ -408,6 +435,18 @@ private: ...@@ -408,6 +435,18 @@ private:
&make_type_erased_value<T>); &make_type_erased_value<T>);
} }
void add_message_types(detail::int_list<>) {
// End of recursion.
}
template <long I, long... Is>
void add_message_types(detail::int_list<I, Is...>) {
using type = typename type_by_id<I>::type;
assert_message_type_eligible<type>();
add_message_type_impl<type>(type_name_by_id<I>::value);
add_message_types(detail::int_list<Is...>{});
}
actor_system_config& set_impl(string_view name, config_value value); actor_system_config& set_impl(string_view name, config_value value);
error extract_config_file_path(string_list& args); error extract_config_file_path(string_list& args);
......
...@@ -115,105 +115,6 @@ std::string to_string(const atom_constant<V>&) { ...@@ -115,105 +115,6 @@ std::string to_string(const atom_constant<V>&) {
template <atom_value V> template <atom_value V>
const atom_constant<V> atom_constant<V>::value = atom_constant<V>{}; const atom_constant<V> atom_constant<V>::value = atom_constant<V>{};
/// Used for request operations.
using add_atom = atom_constant<atom("add")>;
/// Used for request operations.
using get_atom = atom_constant<atom("get")>;
/// Used for request operations.
using put_atom = atom_constant<atom("put")>;
/// Used for signalizing resolved paths.
using resolve_atom = atom_constant<atom("resolve")>;
/// Used for signalizing updates, e.g., in a key-value store.
using update_atom = atom_constant<atom("update")>;
/// Used for request operations.
using delete_atom = atom_constant<atom("delete")>;
/// Used for response messages.
using ok_atom = atom_constant<atom("ok")>;
/// Used for triggering system-level message handling.
using sys_atom = atom_constant<atom("sys")>;
/// Used for signaling group subscriptions.
using join_atom = atom_constant<atom("join")>;
/// Used for signaling group unsubscriptions.
using leave_atom = atom_constant<atom("leave")>;
/// Used for signaling forwarding paths.
using forward_atom = atom_constant<atom("forward")>;
/// Used for buffer management.
using flush_atom = atom_constant<atom("flush")>;
/// Used for I/O redirection.
using redirect_atom = atom_constant<atom("redirect")>;
/// Used for link requests over network.
using link_atom = atom_constant<atom("link")>;
/// Used for removing networked links.
using unlink_atom = atom_constant<atom("unlink")>;
/// Used for monitor requests over network.
using monitor_atom = atom_constant<atom("monitor")>;
/// Used for removing networked monitors.
using demonitor_atom = atom_constant<atom("demonitor")>;
/// Used for publishing actors at a given port.
using publish_atom = atom_constant<atom("publish")>;
/// Used for publishing actors at a given port.
using publish_udp_atom = atom_constant<atom("pub_udp")>;
/// Used for removing an actor/port mapping.
using unpublish_atom = atom_constant<atom("unpublish")>;
/// Used for removing an actor/port mapping.
using unpublish_udp_atom = atom_constant<atom("unpub_udp")>;
/// Used for signalizing group membership.
using subscribe_atom = atom_constant<atom("subscribe")>;
/// Used for withdrawing group membership.
using unsubscribe_atom = atom_constant<atom("unsubscrib")>;
/// Used for establishing network connections.
using connect_atom = atom_constant<atom("connect")>;
/// Used for contacting a remote UDP endpoint
using contact_atom = atom_constant<atom("contact")>;
/// Used for opening ports or files.
using open_atom = atom_constant<atom("open")>;
/// Used for closing ports or files.
using close_atom = atom_constant<atom("close")>;
/// Used for spawning remote actors.
using spawn_atom = atom_constant<atom("spawn")>;
/// Used for migrating actors to other nodes.
using migrate_atom = atom_constant<atom("migrate")>;
/// Used for triggering periodic operations.
using tick_atom = atom_constant<atom("tick")>;
/// Used for pending out of order messages.
using pending_atom = atom_constant<atom("pending")>;
/// Used as timeout type for `timeout_msg`.
using receive_atom = atom_constant<atom("receive")>;
/// Used as timeout type for `timeout_msg`.
using stream_atom = atom_constant<atom("stream")>;
} // namespace caf } // namespace caf
namespace std { namespace std {
...@@ -227,3 +128,8 @@ struct hash<caf::atom_value> { ...@@ -227,3 +128,8 @@ struct hash<caf::atom_value> {
}; };
} // namespace std } // namespace std
// Ugly, but pull in type_id header since it declares atom constants that used
// to live in this header.
#include "caf/type_id.hpp"
...@@ -22,106 +22,141 @@ ...@@ -22,106 +22,141 @@
#include <type_traits> #include <type_traits>
#include "caf/actor_traits.hpp" #include "caf/actor_traits.hpp"
#include "caf/fwd.hpp" #include "caf/detail/squashed_int.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"
#include "caf/error.hpp"
#include "caf/fwd.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class T, constexpr int integral_type_flag = 0x01;
bool IsDyn = std::is_base_of<dynamically_typed_actor_base, T>::value, constexpr int error_type_flag = 0x02;
bool IsStat = std::is_base_of<statically_typed_actor_base, T>::value> constexpr int dynamically_typed_actor_flag = 0x04;
struct implicit_actor_conversions { constexpr int statically_typed_actor_flag = 0x08;
template <bool, int>
struct conversion_flag {
static constexpr int value = 0;
};
template <int Value>
struct conversion_flag<true, Value> {
static constexpr int value = Value;
};
template <class T>
struct implicit_conversion_oracle {
static constexpr int value
= conversion_flag<std::is_integral<T>::value, integral_type_flag>::value
| conversion_flag<std::is_convertible<T, error>::value,
error_type_flag>::value
| conversion_flag<std::is_base_of<dynamically_typed_actor_base, T>::value,
dynamically_typed_actor_flag>::value
| conversion_flag<std::is_base_of<statically_typed_actor_base, T>::value,
statically_typed_actor_flag>::value;
};
template <class T, int = implicit_conversion_oracle<T>::value>
struct implicit_conversions;
template <class T>
struct implicit_conversions<T, 0> {
using type = T; using type = T;
}; };
template <>
struct implicit_conversions<bool, integral_type_flag> {
using type = bool;
};
template <class T> template <class T>
struct implicit_actor_conversions<T, true, false> { struct implicit_conversions<T, integral_type_flag> {
using type = squashed_int_t<T>;
};
template <class T>
struct implicit_conversions<T, dynamically_typed_actor_flag> {
using type = actor; using type = actor;
}; };
template <class T> template <class T>
struct implicit_actor_conversions<T, false, true> { struct implicit_conversions<T, statically_typed_actor_flag> {
using type = using type =
typename detail::tl_apply< typename detail::tl_apply<typename T::signatures, typed_actor>::type;
typename T::signatures,
typed_actor
>::type;
}; };
template <> template <>
struct implicit_actor_conversions<actor_control_block, false, false> { struct implicit_conversions<actor_control_block*, 0> {
using type = strong_actor_ptr; using type = strong_actor_ptr;
}; };
template <class T> template <class T>
struct implicit_conversions { struct implicit_conversions<T, error_type_flag> {
using type = using type = error;
typename std::conditional<
std::is_convertible<T, error>::value,
error,
T
>::type;
}; };
template <class T> template <class T>
struct implicit_conversions<T*> : implicit_actor_conversions<T> {}; struct implicit_conversions<T*, 0> {
static constexpr int oracle = implicit_conversion_oracle<T>::value;
static constexpr int is_actor_mask = dynamically_typed_actor_flag
| statically_typed_actor_flag;
static_assert((oracle & is_actor_mask) != 0,
"messages must not contain pointers");
using type = typename implicit_conversions<T, oracle>::type;
};
template <> template <>
struct implicit_conversions<char*> { struct implicit_conversions<char*, 0> {
using type = std::string; using type = std::string;
}; };
template <size_t N> template <size_t N>
struct implicit_conversions<char[N]> struct implicit_conversions<char[N], 0> : implicit_conversions<char*> {};
: implicit_conversions<char*> {};
template <> template <>
struct implicit_conversions<const char*> struct implicit_conversions<const char*, 0> : implicit_conversions<char*> {};
: implicit_conversions<char*> {};
template <size_t N> template <size_t N>
struct implicit_conversions<const char[N]> struct implicit_conversions<const char[N], 0> : implicit_conversions<char*> {};
: implicit_conversions<char*> {};
template <> template <>
struct implicit_conversions<char16_t*> { struct implicit_conversions<char16_t*, 0> {
using type = std::u16string; using type = std::u16string;
}; };
template <size_t N> template <size_t N>
struct implicit_conversions<char16_t[N]> struct implicit_conversions<char16_t[N], 0> : implicit_conversions<char16_t*> {
: implicit_conversions<char16_t*> {}; };
template <> template <>
struct implicit_conversions<const char16_t*> struct implicit_conversions<const char16_t*, 0>
: implicit_conversions<char16_t*> {}; : implicit_conversions<char16_t*> {};
template <size_t N> template <size_t N>
struct implicit_conversions<const char16_t[N]> struct implicit_conversions<const char16_t[N], 0>
: implicit_conversions<char16_t*> {}; : implicit_conversions<char16_t*> {};
template <> template <>
struct implicit_conversions<char32_t*> { struct implicit_conversions<char32_t*, 0> {
using type = std::u16string; using type = std::u16string;
}; };
template <size_t N> template <size_t N>
struct implicit_conversions<char32_t[N]> struct implicit_conversions<char32_t[N], 0> : implicit_conversions<char32_t*> {
: implicit_conversions<char32_t*> {}; };
template <> template <>
struct implicit_conversions<const char32_t*> struct implicit_conversions<const char32_t*, 0>
: implicit_conversions<char32_t*> {}; : implicit_conversions<char32_t*> {};
template <size_t N> template <size_t N>
struct implicit_conversions<const char32_t[N]> struct implicit_conversions<const char32_t[N], 0>
: implicit_conversions<char32_t*> {}; : implicit_conversions<char32_t*> {};
template <> template <>
struct implicit_conversions<scoped_actor> { struct implicit_conversions<scoped_actor, 0> {
using type = actor; using type = actor;
}; };
...@@ -130,14 +165,8 @@ using implicit_conversions_t = typename implicit_conversions<T>::type; ...@@ -130,14 +165,8 @@ using implicit_conversions_t = typename implicit_conversions<T>::type;
template <class T> template <class T>
struct strip_and_convert { struct strip_and_convert {
using type = using type = typename implicit_conversions<typename std::remove_const<
typename implicit_conversions< typename std::remove_reference<T>::type>::type>::type;
typename std::remove_const<
typename std::remove_reference<
T
>::type
>::type
>::type;
}; };
template <class T> template <class T>
...@@ -145,4 +174,3 @@ using strip_and_convert_t = typename strip_and_convert<T>::type; ...@@ -145,4 +174,3 @@ using strip_and_convert_t = typename strip_and_convert<T>::type;
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <type_traits>
namespace caf {
namespace detail {
template <class T, size_t = sizeof(T)>
std::true_type is_complete_impl(T*);
std::false_type is_complete_impl(...);
/// Checks whether T is a complete type. For example, passing a forward
/// declaration or undefined template specialization evaluates to `false`.
template <class T>
struct is_complete {
static constexpr bool value
= decltype(is_complete_impl(std::declval<T*>()))::value;
};
} // namespace detail
} // namespace caf
...@@ -220,8 +220,9 @@ public: ...@@ -220,8 +220,9 @@ public:
} }
template <class InputIterator> template <class InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last) { iterator insert(iterator pos, InputIterator first, InputIterator last) {
CAF_ASSERT(first <= last); if (first == last)
return pos;
auto num_elements = static_cast<size_t>(std::distance(first, last)); auto num_elements = static_cast<size_t>(std::distance(first, last));
if ((size() + num_elements) > MaxSize) { if ((size() + num_elements) > MaxSize) {
CAF_RAISE_ERROR("limited_vector::insert: too much elements"); CAF_RAISE_ERROR("limited_vector::insert: too much elements");
...@@ -238,6 +239,13 @@ public: ...@@ -238,6 +239,13 @@ public:
// insert new elements // insert new elements
std::copy(first, last, pos); std::copy(first, last, pos);
} }
// Iterator to the first element inserted.
return pos + 1;
}
iterator insert(iterator pos, value_type value) {
T tmp[] = {value};
return insert(pos, std::begin(tmp), std::end(tmp));
} }
private: private:
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "caf/config.hpp" #include "caf/config.hpp"
/// Evaluates to nothing /// Evaluates to nothing.
#define CAF_PP_EMPTY() #define CAF_PP_EMPTY()
/// Concatenates x and y into a single token. /// Concatenates x and y into a single token.
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
/// Evaluate x and y before concatenating into a single token. /// Evaluate x and y before concatenating into a single token.
#define CAF_PP_PASTE(x, y) CAF_PP_CAT(x, y) #define CAF_PP_PASTE(x, y) CAF_PP_CAT(x, y)
/// Evaluates to __COUNTER__. Allows delaying evaluation of __COUNTER__ in some
/// edge cases where it otherwise could increment the internal counter twice.
#define CAF_PP_COUNTER() __COUNTER__
#ifdef CAF_MSVC #ifdef CAF_MSVC
/// Computes the number of arguments of a variadic pack. /// Computes the number of arguments of a variadic pack.
...@@ -65,3 +69,24 @@ ...@@ -65,3 +69,24 @@
#define CAF_PP_OVERLOAD(PREFIX, ...) \ #define CAF_PP_OVERLOAD(PREFIX, ...) \
CAF_PP_PASTE(PREFIX, CAF_PP_SIZE(__VA_ARGS__)) CAF_PP_PASTE(PREFIX, CAF_PP_SIZE(__VA_ARGS__))
#define CAF_PP_EXPAND(...) __VA_ARGS__
// clang-format off
#define CAF_PP_STR_1(x1) #x1
#define CAF_PP_STR_2(x1, x2) #x1 ", " #x2
#define CAF_PP_STR_3(x1, x2, x3) #x1 ", " #x2 ", " #x3
#define CAF_PP_STR_4(x1, x2, x3, x4) #x1 ", " #x2 ", " #x3 ", " #x4
#define CAF_PP_STR_5(x1, x2, x3, x4, x5) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5
#define CAF_PP_STR_6(x1, x2, x3, x4, x5, x6) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6
#define CAF_PP_STR_7(x1, x2, x3, x4, x5, x6, x7) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7
#define CAF_PP_STR_8(x1, x2, x3, x4, x5, x6, x7, x8) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7 ", " #x8
#define CAF_PP_STR_9(x1, x2, x3, x4, x5, x6, x7, x8, x9) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7 ", " #x8 ", " #x9
// clang-format on
#ifdef CAF_MSVC
# define CAF_PP_STR(...) \
CAF_PP_CAT(CAF_PP_OVERLOAD(CAF_PP_STR_, __VA_ARGS__)(__VA_ARGS__), \
CAF_PP_EMPTY())
#else
# define CAF_PP_STR(...) CAF_PP_OVERLOAD(CAF_PP_STR_, __VA_ARGS__)(__VA_ARGS__)
#endif
...@@ -23,13 +23,11 @@ ...@@ -23,13 +23,11 @@
#include <utility> #include <utility>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/meta/omittable_if_empty.hpp" #include "caf/meta/omittable_if_empty.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
#include "caf/none.hpp"
#include "caf/detail/comparable.hpp"
namespace caf { namespace caf {
...@@ -53,10 +51,25 @@ public: ...@@ -53,10 +51,25 @@ public:
&& std::is_same<error, type>::value; && std::is_same<error, type>::value;
}; };
/// Convenience alias for `std::enable_if<has_make_error<T>::value, U>::type`. namespace detail {
// Enables `CAF_ERROR_CODE_ENUM` for forward compatibility with CAF 0.18.
template <class T>
struct error_factory {
static constexpr bool specialized = false;
};
} // namespace detail
/// Convenience alias to detect enums that provide `make_error` overloads.
template <class T, class U = void> template <class T, class U = void>
using enable_if_has_make_error_t = typename std::enable_if< using enable_if_has_make_error_t = typename std::enable_if<
has_make_error<T>::value, U>::type; !detail::error_factory<T>::specialized && has_make_error<T>::value, U>::type;
/// @private
template <class T, class U = void>
using enable_if_has_error_factory_t =
typename std::enable_if<detail::error_factory<T>::specialized, U>::type;
/// A serializable type for storing error codes with category and optional, /// A serializable type for storing error codes with category and optional,
/// human-readable context information. Unlike error handling classes from /// human-readable context information. Unlike error handling classes from
...@@ -115,13 +128,28 @@ public: ...@@ -115,13 +128,28 @@ public:
// nop // nop
} }
template <class E, class = enable_if_has_make_error_t<E>> template <class E>
error& operator=(E error_value) { enable_if_has_make_error_t<E, error&> operator=(E error_value) {
auto tmp = make_error(error_value); auto tmp = make_error(error_value);
std::swap(data_, tmp.data_); std::swap(data_, tmp.data_);
return *this; return *this;
} }
template <class E, class = enable_if_has_error_factory_t<E>, class... Ts>
error(E error_value, Ts&&... xs)
: error(static_cast<uint8_t>(error_value),
detail::error_factory<E>::category,
detail::error_factory<E>::context(std::forward<Ts>(xs)...)) {
// nop
}
template <class E>
enable_if_has_error_factory_t<E, error&> operator=(E error_value) {
auto tmp = error{error_value};
std::swap(data_, tmp.data_);
return *this;
}
~error(); ~error();
// -- observers -------------------------------------------------------------- // -- observers --------------------------------------------------------------
...@@ -249,3 +277,18 @@ bool operator!=(E x, const error& y) { ...@@ -249,3 +277,18 @@ bool operator!=(E x, const error& y) {
} }
} // namespace caf } // namespace caf
#define CAF_ERROR_CODE_ENUM(enum_name) \
namespace caf { \
namespace detail { \
template <> \
struct error_factory<enum_name> { \
static constexpr bool specialized = true; \
static constexpr atom_value category = atom(#enum_name); \
template <class... Ts> \
static message context(Ts&&... xs) { \
return make_message(std::forward<Ts>(xs)...); \
} \
}; \
} \
}
...@@ -176,9 +176,11 @@ config_option make_config_option(T& storage, string_view category, ...@@ -176,9 +176,11 @@ config_option make_config_option(T& storage, string_view category,
enum class atom_value : uint64_t; enum class atom_value : uint64_t;
enum class byte : uint8_t; enum class byte : uint8_t;
enum class exit_reason : uint8_t;
enum class invoke_message_result;
enum class pec : uint8_t;
enum class sec : uint8_t; enum class sec : uint8_t;
enum class stream_priority; enum class stream_priority;
enum class invoke_message_result;
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf {
/// Customization point for enabling conversion from an enum type to an
/// @ref error or @ref error_code.
template <class T>
struct is_error_code_enum {
static constexpr bool value = false;
};
/// @relates is_error_code_enum
template <class T>
constexpr bool is_error_code_enum_v = is_error_code_enum<T>::value;
} // namespace caf
#define CAF_ERROR_CODE_ENUM(type_name) \
namespace caf { \
template <> \
struct is_error_code_enum<type_name> { \
static constexpr bool value = true; \
}; \
}
...@@ -22,11 +22,13 @@ ...@@ -22,11 +22,13 @@
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
#include "caf/message.hpp"
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/build_config.hpp"
#include "caf/detail/is_complete.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/message.hpp"
#include "caf/type_id.hpp"
namespace caf { namespace caf {
...@@ -52,13 +54,21 @@ struct unbox_message_element<actor_control_block*, 0> { ...@@ -52,13 +54,21 @@ struct unbox_message_element<actor_control_block*, 0> {
using type = strong_actor_ptr; using type = strong_actor_ptr;
}; };
/// /// @private
template <class T> template <class T>
struct is_serializable_or_whitelisted { struct is_serializable_or_whitelisted {
static constexpr bool value = detail::is_serializable<T>::value static constexpr bool value = detail::is_serializable<T>::value
|| allowed_unsafe_message_type<T>::value; || allowed_unsafe_message_type<T>::value;
}; };
/// @private
template <class T>
struct has_type_id {
static constexpr bool value = detail::is_complete<type_id<T>>::value
|| is_atom_constant<T>::value
|| allowed_unsafe_message_type<T>::value;
};
/// Returns a new `message` containing the values `(x, xs...)`. /// Returns a new `message` containing the values `(x, xs...)`.
/// @relates message /// @relates message
template <class T, class... Ts> template <class T, class... Ts>
...@@ -86,6 +96,12 @@ make_message(T&& x, Ts&&... xs) { ...@@ -86,6 +96,12 @@ make_message(T&& x, Ts&&... xs) {
"you can whitelist individual types by " "you can whitelist individual types by "
"specializing `caf::allowed_unsafe_message_type<T>` " "specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE"); "or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
#ifdef CAF_ENABLE_TYPE_ID_CHECKS
static_assert(tl_forall<stored_types, has_type_id>::value,
"at least one type has no type ID: please assign type IDs "
"to all of your types via CAF_ADD_TYPE_ID (this check was "
"enabled via CAF_ENABLE_TYPE_ID_CHECKS)");
#endif
using storage = typename tl_apply<stored_types, tuple_vals>::type; using storage = typename tl_apply<stored_types, tuple_vals>::type;
auto ptr = make_counted<storage>(std::forward<T>(x), std::forward<Ts>(xs)...); auto ptr = make_counted<storage>(std::forward<T>(x), std::forward<Ts>(xs)...);
return message{detail::message_data::cow_ptr{std::move(ptr)}}; return message{detail::message_data::cow_ptr{std::move(ptr)}};
...@@ -118,4 +134,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) { ...@@ -118,4 +134,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) {
} }
} // namespace caf } // namespace caf
...@@ -53,8 +53,15 @@ public: ...@@ -53,8 +53,15 @@ public:
value = make_message(Ts{std::forward<Us>(xs)}...); value = make_message(Ts{std::forward<Us>(xs)}...);
} }
template <class E, class = enable_if_has_make_error_t<E>> template <class E>
result(E x) : flag(rt_error), err(make_error(x)) { result(E x, enable_if_has_make_error_t<E, void*> = nullptr)
: flag(rt_error), err(make_error(x)) {
// nop
}
template <class E>
result(E x, enable_if_has_error_factory_t<E, int> = 0)
: flag(rt_error), err(x) {
// nop // nop
} }
...@@ -185,5 +192,13 @@ struct is_result : std::false_type {}; ...@@ -185,5 +192,13 @@ struct is_result : std::false_type {};
template <class... Ts> template <class... Ts>
struct is_result<result<Ts...>> : std::true_type {}; struct is_result<result<Ts...>> : std::true_type {};
} // namespace caf // -- free functions -----------------------------------------------------------
/// Convenience function for wrapping the parameter pack `xs...` into a
/// `result`.
template <class... Ts>
result<detail::decay_t<Ts>...> make_result(Ts&&... xs) {
return {std::forward<Ts>(xs)...};
}
} // namespace caf
This diff is collapsed.
...@@ -16,10 +16,9 @@ ...@@ -16,10 +16,9 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE actor_pool #define CAF_SUITE actor_pool
#include "caf/test/unit_test.hpp"
#include "core-test.hpp"
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -42,13 +41,10 @@ public: ...@@ -42,13 +41,10 @@ public:
behavior make_behavior() override { behavior make_behavior() override {
auto nested = exit_handler_; auto nested = exit_handler_;
set_exit_handler([=](scheduled_actor* self, exit_msg& em) { set_exit_handler(
nested(self, em); [=](scheduled_actor* self, exit_msg& em) { nested(self, em); });
});
return { return {
[](int x, int y) { [](int32_t x, int32_t y) { return x + y; },
return x + y;
}
}; };
} }
}; };
...@@ -56,17 +52,20 @@ public: ...@@ -56,17 +52,20 @@ public:
struct fixture { struct fixture {
// allows us to check s_dtors after dtor of actor_system // allows us to check s_dtors after dtor of actor_system
actor_system_config cfg; actor_system_config cfg;
union { actor_system system; }; union {
union { scoped_execution_unit context; }; actor_system system;
};
union {
scoped_execution_unit context;
};
std::function<actor ()> spawn_worker; std::function<actor()> spawn_worker;
fixture() { fixture() {
cfg.add_message_types<id_block::core_test>();
new (&system) actor_system(cfg); new (&system) actor_system(cfg);
new (&context) scoped_execution_unit(&system); new (&context) scoped_execution_unit(&system);
spawn_worker = [&] { spawn_worker = [&] { return system.spawn<worker>(); };
return system.spawn<worker>();
};
} }
~fixture() { ~fixture() {
...@@ -91,28 +90,28 @@ CAF_TEST(round_robin_actor_pool) { ...@@ -91,28 +90,28 @@ CAF_TEST(round_robin_actor_pool) {
actor_pool::round_robin()); actor_pool::round_robin());
self->send(pool, sys_atom::value, put_atom::value, spawn_worker()); self->send(pool, sys_atom::value, put_atom::value, spawn_worker());
std::vector<actor> workers; std::vector<actor> workers;
for (int i = 0; i < 6; ++i) { for (int32_t i = 0; i < 6; ++i) {
self->request(pool, infinite, i, i).receive( self->request(pool, infinite, i, i)
[&](int res) { .receive(
[&](int32_t res) {
CAF_CHECK_EQUAL(res, i + i); CAF_CHECK_EQUAL(res, i + i);
auto sender = actor_cast<strong_actor_ptr>(self->current_sender()); auto sender = actor_cast<strong_actor_ptr>(self->current_sender());
CAF_REQUIRE(sender); CAF_REQUIRE(sender);
workers.push_back(actor_cast<actor>(std::move(sender))); workers.push_back(actor_cast<actor>(std::move(sender)));
}, },
handle_err handle_err);
);
} }
CAF_CHECK_EQUAL(workers.size(), 6u); CAF_CHECK_EQUAL(workers.size(), 6u);
CAF_CHECK(std::unique(workers.begin(), workers.end()) == workers.end()); CAF_CHECK(std::unique(workers.begin(), workers.end()) == workers.end());
self->request(pool, infinite, sys_atom::value, get_atom::value).receive( self->request(pool, infinite, sys_atom::value, get_atom::value)
.receive(
[&](std::vector<actor>& ws) { [&](std::vector<actor>& ws) {
std::sort(workers.begin(), workers.end()); std::sort(workers.begin(), workers.end());
std::sort(ws.begin(), ws.end()); std::sort(ws.begin(), ws.end());
CAF_REQUIRE_EQUAL(workers.size(), ws.size()); CAF_REQUIRE_EQUAL(workers.size(), ws.size());
CAF_CHECK(std::equal(workers.begin(), workers.end(), ws.begin())); CAF_CHECK(std::equal(workers.begin(), workers.end(), ws.begin()));
}, },
handle_err handle_err);
);
CAF_MESSAGE("await last worker"); CAF_MESSAGE("await last worker");
anon_send_exit(workers.back(), exit_reason::user_shutdown); anon_send_exit(workers.back(), exit_reason::user_shutdown);
self->wait_for(workers.back()); self->wait_for(workers.back());
...@@ -122,7 +121,8 @@ CAF_TEST(round_robin_actor_pool) { ...@@ -122,7 +121,8 @@ CAF_TEST(round_robin_actor_pool) {
bool success = false; bool success = false;
size_t i = 0; size_t i = 0;
while (!success && ++i <= 10) { while (!success && ++i <= 10) {
self->request(pool, infinite, sys_atom::value, get_atom::value).receive( self->request(pool, infinite, sys_atom::value, get_atom::value)
.receive(
[&](std::vector<actor>& ws) { [&](std::vector<actor>& ws) {
success = workers.size() == ws.size(); success = workers.size() == ws.size();
if (success) { if (success) {
...@@ -133,8 +133,7 @@ CAF_TEST(round_robin_actor_pool) { ...@@ -133,8 +133,7 @@ CAF_TEST(round_robin_actor_pool) {
std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
} }
}, },
handle_err handle_err);
);
} }
CAF_REQUIRE_EQUAL(success, true); CAF_REQUIRE_EQUAL(success, true);
CAF_MESSAGE("about to send exit to workers"); CAF_MESSAGE("about to send exit to workers");
...@@ -152,32 +151,23 @@ CAF_TEST(broadcast_actor_pool) { ...@@ -152,32 +151,23 @@ CAF_TEST(broadcast_actor_pool) {
auto pool = actor_pool::make(&context, 5, spawn5, actor_pool::broadcast()); auto pool = actor_pool::make(&context, 5, spawn5, actor_pool::broadcast());
CAF_CHECK_EQUAL(system.registry().running(), 32u); CAF_CHECK_EQUAL(system.registry().running(), 32u);
self->send(pool, 1, 2); self->send(pool, 1, 2);
std::vector<int> results; std::vector<int32_t> results;
int i = 0; int32_t i = 0;
self->receive_for(i, 25)( self->receive_for(i, 25)([&](int32_t res) { results.push_back(res); },
[&](int res) { after(std::chrono::milliseconds(250)) >>
results.push_back(res); [] { CAF_ERROR("didn't receive a result"); });
},
after(std::chrono::milliseconds(250)) >> [] {
CAF_ERROR("didn't receive a result");
}
);
CAF_CHECK_EQUAL(results.size(), 25u); CAF_CHECK_EQUAL(results.size(), 25u);
CAF_CHECK(std::all_of(results.begin(), results.end(), CAF_CHECK(std::all_of(results.begin(), results.end(),
[](int res) { return res == 3; })); [](int32_t res) { return res == 3; }));
self->send_exit(pool, exit_reason::user_shutdown); self->send_exit(pool, exit_reason::user_shutdown);
} }
CAF_TEST(random_actor_pool) { CAF_TEST(random_actor_pool) {
scoped_actor self{system}; scoped_actor self{system};
auto pool = actor_pool::make(&context, 5, spawn_worker, actor_pool::random()); auto pool = actor_pool::make(&context, 5, spawn_worker, actor_pool::random());
for (int i = 0; i < 5; ++i) { for (int32_t i = 0; i < 5; ++i) {
self->request(pool, std::chrono::milliseconds(250), 1, 2).receive( self->request(pool, std::chrono::milliseconds(250), 1, 2)
[&](int res) { .receive([&](int32_t res) { CAF_CHECK_EQUAL(res, 3); }, handle_err);
CAF_CHECK_EQUAL(res, 3);
},
handle_err
);
} }
self->send_exit(pool, exit_reason::user_shutdown); self->send_exit(pool, exit_reason::user_shutdown);
} }
...@@ -186,10 +176,7 @@ CAF_TEST(split_join_actor_pool) { ...@@ -186,10 +176,7 @@ CAF_TEST(split_join_actor_pool) {
auto spawn_split_worker = [&] { auto spawn_split_worker = [&] {
return system.spawn<lazy_init>([]() -> behavior { return system.spawn<lazy_init>([]() -> behavior {
return { return {
[](size_t pos, const std::vector<int>& xs) { [](size_t pos, const std::vector<int32_t>& xs) { return xs[pos]; }};
return xs[pos];
}
};
}); });
}; };
auto split_fun = [](std::vector<std::pair<actor, message>>& xs, message& y) { auto split_fun = [](std::vector<std::pair<actor, message>>& xs, message& y) {
...@@ -197,27 +184,18 @@ CAF_TEST(split_join_actor_pool) { ...@@ -197,27 +184,18 @@ CAF_TEST(split_join_actor_pool) {
xs[i].second = make_message(i) + y; xs[i].second = make_message(i) + y;
} }
}; };
auto join_fun = [](int& res, message& msg) { auto join_fun = [](int32_t& res, message& msg) {
msg.apply([&](int x) { msg.apply([&](int32_t x) { res += x; });
res += x;
});
}; };
scoped_actor self{system}; scoped_actor self{system};
CAF_MESSAGE("create actor pool"); CAF_MESSAGE("create actor pool");
auto pool = actor_pool::make(&context, 5, spawn_split_worker, auto pool
actor_pool::split_join<int>(join_fun, split_fun)); = actor_pool::make(&context, 5, spawn_split_worker,
self->request(pool, infinite, std::vector<int>{1, 2, 3, 4, 5}).receive( actor_pool::split_join<int32_t>(join_fun, split_fun));
[&](int res) { self->request(pool, infinite, std::vector<int32_t>{1, 2, 3, 4, 5})
CAF_CHECK_EQUAL(res, 15); .receive([&](int32_t res) { CAF_CHECK_EQUAL(res, 15); }, handle_err);
}, self->request(pool, infinite, std::vector<int32_t>{6, 7, 8, 9, 10})
handle_err .receive([&](int32_t res) { CAF_CHECK_EQUAL(res, 40); }, handle_err);
);
self->request(pool, infinite, std::vector<int>{6, 7, 8, 9, 10}).receive(
[&](int res) {
CAF_CHECK_EQUAL(res, 40);
},
handle_err
);
self->send_exit(pool, exit_reason::user_shutdown); self->send_exit(pool, exit_reason::user_shutdown);
} }
......
...@@ -19,23 +19,22 @@ ...@@ -19,23 +19,22 @@
#define CAF_SUITE broadcast_downstream_manager #define CAF_SUITE broadcast_downstream_manager
#include "core-test.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
#include "caf/broadcast_downstream_manager.hpp" #include "caf/broadcast_downstream_manager.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/mixin/sender.hpp" #include "caf/mixin/sender.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/test/unit_test.hpp"
using namespace caf; using namespace caf;
namespace { namespace {
using bcast_manager = broadcast_downstream_manager<int>; using bcast_manager = broadcast_downstream_manager<int32_t>;
// Mocks just enough of a stream manager to serve our entity. // Mocks just enough of a stream manager to serve our entity.
class mock_stream_manager : public stream_manager { class mock_stream_manager : public stream_manager {
...@@ -130,7 +129,7 @@ public: ...@@ -130,7 +129,7 @@ public:
return static_cast<size_t>((*i)->open_credit); return static_cast<size_t>((*i)->open_credit);
} }
void new_round(int num, bool force_emit) { void new_round(int32_t num, bool force_emit) {
for (auto& ptr : paths) for (auto& ptr : paths)
ptr->open_credit += num; ptr->open_credit += num;
if (force_emit) if (force_emit)
...@@ -165,9 +164,15 @@ struct not_empty_t {}; ...@@ -165,9 +164,15 @@ struct not_empty_t {};
constexpr auto some = not_empty_t{}; constexpr auto some = not_empty_t{};
struct config : actor_system_config {
config() {
add_message_types<id_block::core_test>();
}
};
// Provides the setup with alice, bob, and carl. // Provides the setup with alice, bob, and carl.
struct fixture { struct fixture {
actor_system_config cfg; config cfg;
actor_system sys; actor_system sys;
...@@ -205,7 +210,7 @@ struct fixture { ...@@ -205,7 +210,7 @@ struct fixture {
// nop // nop
} }
using batch_type = std::vector<int>; using batch_type = std::vector<int32_t>;
using batches_type = std::vector<batch_type>; using batches_type = std::vector<batch_type>;
...@@ -224,7 +229,7 @@ struct fixture { ...@@ -224,7 +229,7 @@ struct fixture {
return result; return result;
} }
batch_type make_batch(int first, int last) { batch_type make_batch(int32_t first, int32_t last) {
batch_type result; batch_type result;
result.resize(static_cast<size_t>((last + 1) - first)); result.resize(static_cast<size_t>((last + 1) - first));
std::iota(result.begin(), result.end(), first); std::iota(result.begin(), result.end(), first);
...@@ -351,7 +356,7 @@ CAF_TEST(one_path_force) { ...@@ -351,7 +356,7 @@ CAF_TEST(one_path_force) {
// Give alice 100 elements to send and a path to bob with desired batch size // Give alice 100 elements to send and a path to bob with desired batch size
// of 10. // of 10.
alice.add_path_to(bob, 10); alice.add_path_to(bob, 10);
for (int i = 1; i <= 100; ++i) for (int32_t i = 1; i <= 100; ++i)
alice.mgr.out().push(i); alice.mgr.out().push(i);
// Give 3 credit (less than 10). // Give 3 credit (less than 10).
AFTER ENTITY alice TRIED FORCE_SENDING 3 ELEMENTS { AFTER ENTITY alice TRIED FORCE_SENDING 3 ELEMENTS {
...@@ -390,7 +395,7 @@ CAF_TEST(one_path_without_force) { ...@@ -390,7 +395,7 @@ CAF_TEST(one_path_without_force) {
// Give alice 100 elements to send and a path to bob with desired batch size // Give alice 100 elements to send and a path to bob with desired batch size
// of 10. // of 10.
alice.add_path_to(bob, 10); alice.add_path_to(bob, 10);
for (int i = 1; i <= 100; ++i) for (int32_t i = 1; i <= 100; ++i)
alice.mgr.out().push(i); alice.mgr.out().push(i);
// Give 3 credit (less than 10). // Give 3 credit (less than 10).
AFTER ENTITY alice TRIED SENDING 3 ELEMENTS { AFTER ENTITY alice TRIED SENDING 3 ELEMENTS {
...@@ -430,7 +435,7 @@ CAF_TEST(two_paths_different_sizes_force) { ...@@ -430,7 +435,7 @@ CAF_TEST(two_paths_different_sizes_force) {
// 10, and a path to carl with desired batch size of 7. // 10, and a path to carl with desired batch size of 7.
alice.add_path_to(bob, 10); alice.add_path_to(bob, 10);
alice.add_path_to(carl, 7); alice.add_path_to(carl, 7);
for (int i = 1; i <= 100; ++i) for (int32_t i = 1; i <= 100; ++i)
alice.mgr.out().push(i); alice.mgr.out().push(i);
// Give 3 credit (less than 10). // Give 3 credit (less than 10).
AFTER ENTITY alice TRIED FORCE_SENDING 3 ELEMENTS { AFTER ENTITY alice TRIED FORCE_SENDING 3 ELEMENTS {
...@@ -481,7 +486,7 @@ CAF_TEST(two_paths_different_sizes_without_force) { ...@@ -481,7 +486,7 @@ CAF_TEST(two_paths_different_sizes_without_force) {
// 10, and a path to carl with desired batch size of 7. // 10, and a path to carl with desired batch size of 7.
alice.add_path_to(bob, 10); alice.add_path_to(bob, 10);
alice.add_path_to(carl, 7); alice.add_path_to(carl, 7);
for (int i = 1; i <= 100; ++i) for (int32_t i = 1; i <= 100; ++i)
alice.mgr.out().push(i); alice.mgr.out().push(i);
// Give 3 credit (less than 10). // Give 3 credit (less than 10).
AFTER ENTITY alice TRIED SENDING 3 ELEMENTS { AFTER ENTITY alice TRIED SENDING 3 ELEMENTS {
......
This diff is collapsed.
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define CAF_SUITE continuous_streaming #define CAF_SUITE continuous_streaming
#include "caf/test/dsl.hpp" #include "core-test.hpp"
#include <memory> #include <memory>
#include <numeric> #include <numeric>
...@@ -37,18 +37,19 @@ using namespace caf; ...@@ -37,18 +37,19 @@ using namespace caf;
namespace { namespace {
/// Returns the sum of natural numbers up until `n`, i.e., 1 + 2 + ... + n. /// Returns the sum of natural numbers up until `n`, i.e., 1 + 2 + ... + n.
int sum(int n) { int32_t sum(int32_t n) {
return (n * (n + 1)) / 2; return (n * (n + 1)) / 2;
} }
TESTEE_SETUP(); TESTEE_SETUP();
TESTEE_STATE(file_reader) { TESTEE_STATE(file_reader) {
std::vector<int> buf; std::vector<int32_t> buf;
}; };
VARARGS_TESTEE(file_reader, size_t buf_size) { VARARGS_TESTEE(file_reader, size_t buf_size) {
return {[=](string& fname) -> result<stream<int>, string> { return {
[=](string& fname) -> result<stream<int32_t>, string> {
CAF_CHECK_EQUAL(fname, "numbers.txt"); CAF_CHECK_EQUAL(fname, "numbers.txt");
CAF_CHECK_EQUAL(self->mailbox().empty(), true); CAF_CHECK_EQUAL(self->mailbox().empty(), true);
return attach_stream_source( return attach_stream_source(
...@@ -62,7 +63,7 @@ VARARGS_TESTEE(file_reader, size_t buf_size) { ...@@ -62,7 +63,7 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
std::iota(xs.begin(), xs.end(), 1); std::iota(xs.begin(), xs.end(), 1);
}, },
// get next element // get next element
[=](unit_t&, downstream<int>& out, size_t num) { [=](unit_t&, downstream<int32_t>& out, size_t num) {
auto& xs = self->state.buf; auto& xs = self->state.buf;
CAF_MESSAGE("push " << num << " messages downstream"); CAF_MESSAGE("push " << num << " messages downstream");
auto n = std::min(num, xs.size()); auto n = std::min(num, xs.size());
...@@ -78,17 +79,19 @@ VARARGS_TESTEE(file_reader, size_t buf_size) { ...@@ -78,17 +79,19 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
} }
return false; return false;
}); });
}}; },
};
} }
TESTEE_STATE(sum_up) { TESTEE_STATE(sum_up) {
int x = 0; int32_t x = 0;
}; };
TESTEE(sum_up) { TESTEE(sum_up) {
return {[=](stream<int>& in, const string& fname) { return {
[=](stream<int32_t>& in, const string& fname) {
CAF_CHECK_EQUAL(fname, "numbers.txt"); CAF_CHECK_EQUAL(fname, "numbers.txt");
using int_ptr = int*; using int_ptr = int32_t*;
return attach_stream_sink( return attach_stream_sink(
self, self,
// input stream // input stream
...@@ -96,7 +99,7 @@ TESTEE(sum_up) { ...@@ -96,7 +99,7 @@ TESTEE(sum_up) {
// initialize state // initialize state
[=](int_ptr& x) { x = &self->state.x; }, [=](int_ptr& x) { x = &self->state.x; },
// processing step // processing step
[](int_ptr& x, int y) { *x += y; }, [](int_ptr& x, int32_t y) { *x += y; },
// cleanup // cleanup
[=](int_ptr&, const error&) { [=](int_ptr&, const error&) {
CAF_MESSAGE(self->name() << " is done"); CAF_MESSAGE(self->name() << " is done");
...@@ -105,11 +108,12 @@ TESTEE(sum_up) { ...@@ -105,11 +108,12 @@ TESTEE(sum_up) {
[=](join_atom atm, actor src) { [=](join_atom atm, actor src) {
CAF_MESSAGE(self->name() << " joins a stream"); CAF_MESSAGE(self->name() << " joins a stream");
self->send(self * src, atm); self->send(self * src, atm);
}}; },
};
} }
TESTEE_STATE(stream_multiplexer) { TESTEE_STATE(stream_multiplexer) {
stream_stage_ptr<int, broadcast_downstream_manager<int>> stage; stream_stage_ptr<int32_t, broadcast_downstream_manager<int32_t>> stage;
}; };
TESTEE(stream_multiplexer) { TESTEE(stream_multiplexer) {
...@@ -120,7 +124,7 @@ TESTEE(stream_multiplexer) { ...@@ -120,7 +124,7 @@ TESTEE(stream_multiplexer) {
// nop // nop
}, },
// processing step // processing step
[](unit_t&, downstream<int>& out, int x) { out.push(x); }, [](unit_t&, downstream<int32_t>& out, int32_t x) { out.push(x); },
// cleanup // cleanup
[=](unit_t&, const error&) { CAF_MESSAGE(self->name() << " is done"); }); [=](unit_t&, const error&) { CAF_MESSAGE(self->name() << " is done"); });
return { return {
...@@ -129,18 +133,24 @@ TESTEE(stream_multiplexer) { ...@@ -129,18 +133,24 @@ TESTEE(stream_multiplexer) {
return self->state.stage->add_outbound_path( return self->state.stage->add_outbound_path(
std::make_tuple("numbers.txt")); std::make_tuple("numbers.txt"));
}, },
[=](const stream<int>& in, std::string& fname) { [=](const stream<int32_t>& in, std::string& fname) {
CAF_CHECK_EQUAL(fname, "numbers.txt"); CAF_CHECK_EQUAL(fname, "numbers.txt");
return self->state.stage->add_inbound_path(in); return self->state.stage->add_inbound_path(in);
}, },
[=](close_atom, int sink_index) { [=](close_atom, int32_t sink_index) {
auto& out = self->state.stage->out(); auto& out = self->state.stage->out();
out.close(out.path_slots().at(static_cast<size_t>(sink_index))); out.close(out.path_slots().at(static_cast<size_t>(sink_index)));
}, },
}; };
} }
using fixture = test_coordinator_fixture<>; struct config : actor_system_config {
config() {
add_message_types<id_block::core_test>();
}
};
using fixture = test_coordinator_fixture<config>;
} // namespace } // namespace
...@@ -224,7 +234,7 @@ CAF_TEST(closing_downstreams_before_end_of_stream) { ...@@ -224,7 +234,7 @@ CAF_TEST(closing_downstreams_before_end_of_stream) {
auto sink1_result = sum(next_pending - 1); auto sink1_result = sum(next_pending - 1);
CAF_MESSAGE("gracefully close sink 1, next pending: " << next_pending); CAF_MESSAGE("gracefully close sink 1, next pending: " << next_pending);
self->send(stg, close_atom::value, 0); self->send(stg, close_atom::value, 0);
expect((atom_value, int), from(self).to(stg)); expect((atom_value, int32_t), from(self).to(stg));
CAF_MESSAGE("ship remaining elements"); CAF_MESSAGE("ship remaining elements");
run(); run();
CAF_CHECK_EQUAL(st.stage->out().num_paths(), 1u); CAF_CHECK_EQUAL(st.stage->out().num_paths(), 1u);
......
#include "core-test.hpp"
#include "caf/test/unit_test_impl.hpp"
fail_on_copy::fail_on_copy(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy constructor called");
}
fail_on_copy& fail_on_copy::operator=(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy assign operator called");
}
size_t i32_wrapper::instances = 0;
size_t i64_wrapper::instances = 0;
void test_empty_non_pod::foo() {
// nop
}
test_empty_non_pod::~test_empty_non_pod() {
// nop
}
namespace {
const char* test_enum_strings[] = {
"a",
"b",
"c",
};
} // namespace
std::string to_string(test_enum x) {
return test_enum_strings[static_cast<uint32_t>(x)];
}
This diff is collapsed.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include "caf/all.hpp" #include "caf/all.hpp"
#define ERROR_HANDLER [&](error& err) { CAF_FAIL(system.render(err)); } #define ERROR_HANDLER [&](error& err) { CAF_FAIL(err); }
using namespace caf; using namespace caf;
...@@ -42,7 +42,9 @@ using second_stage = typed_actor<replies_to<double, double>::with<double>>; ...@@ -42,7 +42,9 @@ using second_stage = typed_actor<replies_to<double, double>::with<double>>;
first_stage::behavior_type typed_first_stage() { first_stage::behavior_type typed_first_stage() {
return { return {
[](int i) { return std::make_tuple(i * 2.0, i * 4.0); }, [](int i) -> result<double, double> {
return {i * 2.0, i * 4.0};
},
}; };
} }
......
...@@ -88,11 +88,11 @@ CAF_TEST(shutdown_delayed_send_loop) { ...@@ -88,11 +88,11 @@ CAF_TEST(shutdown_delayed_send_loop) {
CAF_MESSAGE("does sys shut down after spawning a detached actor that used " CAF_MESSAGE("does sys shut down after spawning a detached actor that used "
"a delayed send loop and was interrupted via exit message?"); "a delayed send loop and was interrupted via exit message?");
auto f = [](event_based_actor* self) -> behavior { auto f = [](event_based_actor* self) -> behavior {
self->send(self, std::chrono::milliseconds(1), ok_atom::value); self->delayed_send(self, std::chrono::milliseconds(1), ok_atom::value);
return { return {
[=](ok_atom) { [=](ok_atom) {
self->send(self, std::chrono::milliseconds(1), ok_atom::value); self->delayed_send(self, std::chrono::milliseconds(1), ok_atom::value);
} },
}; };
}; };
auto a = sys.spawn<detached>(f); auto a = sys.spawn<detached>(f);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "caf/fused_downstream_manager.hpp" #include "caf/fused_downstream_manager.hpp"
#include "caf/test/dsl.hpp" #include "core-test.hpp"
#include <memory> #include <memory>
#include <numeric> #include <numeric>
...@@ -250,11 +250,11 @@ TESTEE(stream_multiplexer) { ...@@ -250,11 +250,11 @@ TESTEE(stream_multiplexer) {
struct config : actor_system_config { struct config : actor_system_config {
config() { config() {
add_message_type<std::deque<std::string>>("deque<string>"); add_message_types<id_block::core_test>();
} }
}; };
using fixture = test_coordinator_fixture<>; using fixture = test_coordinator_fixture<config>;
} // namespace } // namespace
......
...@@ -16,26 +16,24 @@ ...@@ -16,26 +16,24 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#define CAF_SUITE message_operations
#include "core-test.hpp"
#include <iostream>
#include <map> #include <map>
#include <vector>
#include <string>
#include <numeric> #include <numeric>
#include <iostream>
#include <set> #include <set>
#include <string>
#include <unordered_set> #include <unordered_set>
#include <vector>
#include "caf/config.hpp"
#define CAF_SUITE message_operations
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp" #include "caf/all.hpp"
using std::map; using std::map;
using std::string; using std::string;
using std::tuple;
using std::vector; using std::vector;
using std::make_tuple;
using namespace caf; using namespace caf;
...@@ -185,45 +183,11 @@ CAF_TEST(concat) { ...@@ -185,45 +183,11 @@ CAF_TEST(concat) {
CAF_CHECK_EQUAL(to_string(message::concat(m3, message{}, m1, m2)), to_string(m4)); CAF_CHECK_EQUAL(to_string(message::concat(m3, message{}, m1, m2)), to_string(m4));
} }
namespace {
struct s1 {
int value[3] = {10, 20, 30};
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, s1& x) {
return f(x.value);
}
struct s2 {
int value[4][2] = {{1, 10}, {2, 20}, {3, 30}, {4, 40}};
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, s2& x) {
return f(x.value);
}
struct s3 {
std::array<int, 4> value;
s3() {
std::iota(value.begin(), value.end(), 1);
}
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, s3& x) {
return f(x.value);
}
template <class... Ts> template <class... Ts>
std::string msg_as_string(Ts&&... xs) { std::string msg_as_string(Ts&&... xs) {
return to_string(make_message(std::forward<Ts>(xs)...)); return to_string(make_message(std::forward<Ts>(xs)...));
} }
} // namespace
CAF_TEST(compare_custom_types) { CAF_TEST(compare_custom_types) {
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
...@@ -237,7 +201,7 @@ CAF_TEST(empty_to_string) { ...@@ -237,7 +201,7 @@ CAF_TEST(empty_to_string) {
} }
CAF_TEST(integers_to_string) { CAF_TEST(integers_to_string) {
using ivec = vector<int>; using ivec = vector<int32_t>;
CAF_CHECK_EQUAL(msg_as_string(1, 2, 3), "(1, 2, 3)"); CAF_CHECK_EQUAL(msg_as_string(1, 2, 3), "(1, 2, 3)");
CAF_CHECK_EQUAL(msg_as_string(ivec{1, 2, 3}), "([1, 2, 3])"); CAF_CHECK_EQUAL(msg_as_string(ivec{1, 2, 3}), "([1, 2, 3])");
CAF_CHECK_EQUAL(msg_as_string(ivec{1, 2}, 3, 4, ivec{5, 6, 7}), CAF_CHECK_EQUAL(msg_as_string(ivec{1, 2}, 3, 4, ivec{5, 6, 7}),
...@@ -259,15 +223,16 @@ CAF_TEST(strings_to_string) { ...@@ -259,15 +223,16 @@ CAF_TEST(strings_to_string) {
} }
CAF_TEST(maps_to_string) { CAF_TEST(maps_to_string) {
map<int, int> m1{{1, 10}, {2, 20}, {3, 30}}; map<int32_t, int32_t> m1{{1, 10}, {2, 20}, {3, 30}};
auto msg1 = make_message(move(m1)); auto msg1 = make_message(move(m1));
CAF_CHECK_EQUAL(to_string(msg1), "({1 = 10, 2 = 20, 3 = 30})"); CAF_CHECK_EQUAL(to_string(msg1), "({1 = 10, 2 = 20, 3 = 30})");
} }
CAF_TEST(tuples_to_string) { CAF_TEST(tuples_to_string) {
auto msg1 = make_message(make_tuple(1, 2, 3), 4, 5); auto msg1 = make_message(tuple<int32_t, int32_t, int32_t>{1, 2, 3}, 4, 5);
CAF_CHECK_EQUAL(to_string(msg1), "((1, 2, 3), 4, 5)"); CAF_CHECK_EQUAL(to_string(msg1), "((1, 2, 3), 4, 5)");
auto msg2 = make_message(make_tuple(string{"one"}, 2, uint32_t{3}), 4, true); using msg2_type = tuple<string, int32_t, uint32_t>;
auto msg2 = make_message(msg2_type{"one", 2, 3}, 4, true);
CAF_CHECK_EQUAL(to_string(msg2), "((\"one\", 2, 3), 4, true)"); CAF_CHECK_EQUAL(to_string(msg2), "((\"one\", 2, 3), 4, true)");
} }
......
...@@ -16,10 +16,9 @@ ...@@ -16,10 +16,9 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE message_lifetime #define CAF_SUITE message_lifetime
#include "caf/test/unit_test.hpp"
#include "core-test.hpp"
#include <atomic> #include <atomic>
#include <iostream> #include <iostream>
...@@ -81,35 +80,16 @@ private: ...@@ -81,35 +80,16 @@ private:
message msg_; message msg_;
}; };
struct fixture { struct config : actor_system_config {
actor_system_config cfg; config() {
actor_system system{cfg}; add_message_types<id_block::core_test>();
};
struct fail_on_copy {
int value;
fail_on_copy(int x = 0) : value(x) {
// nop
}
fail_on_copy(fail_on_copy&&) = default;
fail_on_copy& operator=(fail_on_copy&&) = default;
fail_on_copy(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy constructor called");
}
fail_on_copy& operator=(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy assign operator called");
} }
}; };
template <class Inspector> struct fixture {
typename Inspector::result_type inspect(Inspector& f, fail_on_copy& x) { config cfg;
return f(x.value); actor_system system{cfg};
} };
} // namespace } // namespace
......
This diff is collapsed.
...@@ -68,14 +68,14 @@ struct consumer { ...@@ -68,14 +68,14 @@ struct consumer {
intrusive::task_result operator()(const Key&, const Queue&, intrusive::task_result operator()(const Key&, const Queue&,
const mailbox_element& x) { const mailbox_element& x) {
if (!x.content().match_elements<int>()) if (!x.content().match_elements<int>())
CAF_FAIL("unexepected message: " << x.content()); CAF_FAIL("unexpected message: " << x.content());
ints.emplace_back(x.content().get_as<int>(0)); ints.emplace_back(x.content().get_as<int>(0));
return intrusive::task_result::resume; return intrusive::task_result::resume;
} }
template <class Key, class Queue, class... Ts> template <class Key, class Queue, class... Ts>
intrusive::task_result operator()(const Key&, const Queue&, const Ts&...) { intrusive::task_result operator()(const Key&, const Queue&, const Ts&...) {
CAF_FAIL("unexepected message type"); // << typeid(Ts).name()); CAF_FAIL("unexpected message type"); // << typeid(Ts).name());
return intrusive::task_result::resume; return intrusive::task_result::resume;
} }
}; };
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#include "io-test.hpp"
#include "caf/test/unit_test_impl.hpp"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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