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);
......
This diff is collapsed.
...@@ -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>
......
This diff is collapsed.
...@@ -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> template <class Inspector>
typename std::enable_if<Inspector::reads_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();
return f(meta::type_name("foo"), x.a(), x.b()); auto load = meta::load_callback([&]() -> error {
} // Write back to x when loading values from the inspector.
template <class Inspector>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
int a;
int b;
// 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,71 +95,56 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, ...@@ -94,71 +95,56 @@ 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, : event_based_actor(cfg),
chopstick l, name_(std::move(n)),
chopstick r) left_(std::move(l)),
: event_based_actor(cfg), right_(std::move(r)) {
name_(std::move(n)),
left_(std::move(l)),
right_(std::move(r)) {
// 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_v);
send(left_, take_atom::value); send(right_, take_atom_v);
send(right_, take_atom::value); });
}
);
// 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_ << " has picked up chopsticks with IDs "
aout(this) << name_ << left_->id() << " and " << right_->id()
<< " has picked up chopsticks with IDs " << " and starts to eat\n";
<< left_->id() << " and " << right_->id() // eat some time
<< " and starts to eat\n"; delayed_send(this, seconds(5), think_atom_v);
// eat some time become(eating_);
delayed_send(this, seconds(5), think_atom::value); } else {
become(eating_); send(current_sender() == left_ ? right_ : left_, put_atom_v);
} else { send(this, eat_atom_v);
send(current_sender() == left_ ? right_ : left_, put_atom::value);
send(this, eat_atom::value);
become(thinking_);
}
}
);
// philosopher was *not* able to obtain the first chopstick
denied_.assign(
[=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom::value);
send(this, eat_atom::value);
become(thinking_); become(thinking_);
} }
); });
// philosopher was *not* able to obtain the first chopstick
denied_.assign([=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom_v);
send(this, eat_atom_v);
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_ become(thinking_);
<< " puts down his chopsticks and starts to think\n"; });
become(thinking_);
}
);
} }
const char* name() const override { const char* name() const override {
...@@ -168,26 +154,26 @@ public: ...@@ -168,26 +154,26 @@ 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:
std::string name_; // the name of this philosopher std::string name_; // the name of this philosopher
chopstick left_; // left chopstick chopstick left_; // left chopstick
chopstick right_; // right chopstick chopstick right_; // right chopstick
behavior thinking_; // initial behavior behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again behavior eating_; // wait for some time, then go thinking again
}; };
} // namespace } // namespace
...@@ -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, self->delegate(worker, atm);
[=](request_atom atm) { self->unbecome();
self->delegate(worker, atm); });
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(
aout(self) << "received response from " [&](pong_atom) {
<< (self->current_sender() == worker ? "worker\n" aout(self) << "received response from "
: "server\n"); << (self->current_sender() == worker ? "worker\n"
}, : "server\n");
[&](error& err) { },
aout(self) << "received error " [&](error& err) {
<< system.render(err) aout(self) << "received error " << to_string(err) << " from "
<< " 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(
aout(self) << x << " + " << y << " = " << res1 << endl; [&](int32_t res1) {
// second test: x - y = z aout(self) << x << " + " << y << " = " << res1 << endl;
self->request(hdl, infinite, sub_atom::value, x, y).receive( // second test: x - y = z
[&](int res2) { self->request(hdl, infinite, sub_atom_v, x, y)
aout(self) << x << " - " << y << " = " << res2 << endl; .receive(
}, [&](int32_t res2) {
handle_err aout(self) << x << " - " << y << " = " << res2 << endl;
); },
}, 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,22 +58,20 @@ void draw_kirby(const animation_step& animation) { ...@@ -56,22 +58,20 @@ 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; self->quit();
self->quit(); return;
return;
}
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150),
step_atom::value, step + 1);
} }
); // print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v,
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 << " => " << to_string(err) << endl;
<< system.render(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(
aout(self) << "cell #" << x.id() << " -> " << y << endl; [&](int32_t y) {
}, aout(self) << "cell #" << x.id() << " -> " << y << endl;
[&](error& err) { },
aout(self) << "cell #" << x.id() [&](error& err) {
<< " -> " << self->system().render(err) << endl; aout(self) << "cell #" << x.id() << " -> " << to_string(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(
// second test: 2 - 1 = 1 [=](int32_t r1) {
self->request(testee, infinite, minus_atom::value, 2, 1).then( // second test: 2 - 1 = 1
[=](result_atom, int r2) { self->request(testee, infinite, sub_atom_v, 2, 1).then([=](int32_t 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: " << to_string(err)
aout(self) << "AUT (actor under test) failed: " << endl;
<< self->system().render(err) << 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);
......
...@@ -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;
} },
}; };
} }
...@@ -71,81 +75,81 @@ public: ...@@ -71,81 +75,81 @@ public:
config() { config() {
opt_group{custom_options_, "global"} opt_group{custom_options_, "global"}
.add(name, "name,n", "set name") .add(name, "name,n", "set name")
.add(group_uris, "group,g", "join group") .add(group_uris, "group,g", "join group")
.add(server_mode, "server,s", "run in server mode") .add(server_mode, "server,s", "run in server mode")
.add(port, "port,p", "set port (ignored in client mode)"); .add(port, "port,p", "set port (ignored in client mode)");
} }
}; };
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";
" /help print this text\n" << flush; } 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;
} },
}; };
} }
...@@ -62,10 +63,11 @@ string trim(string s) { ...@@ -62,10 +63,11 @@ string trim(string s) {
// implements our main loop for reading user input // implements our main loop for reading user input
void client_repl(function_view<calculator> f) { 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";
} }
} }
...@@ -100,9 +103,9 @@ struct config : actor_system_config { ...@@ -100,9 +103,9 @@ struct config : actor_system_config {
config() { config() {
add_actor_type("calculator", calculator_fun); add_actor_type("calculator", calculator_fun);
opt_group{custom_options_, "global"} opt_group{custom_options_, "global"}
.add(port, "port,p", "set port") .add(port, "port,p", "set port")
.add(host, "host,H", "set node (ignored in server mode)") .add(host, "host,H", "set node (ignored in server mode)")
.add(server_mode, "server-mode,s", "enable server mode"); .add(server_mode, "server-mode,s", "enable server mode");
} }
uint16_t port = 0; uint16_t port = 0;
string host = "localhost"; string host = "localhost";
...@@ -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,109 +2,127 @@ ...@@ -2,109 +2,127 @@
* 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 {
// Produce at least one value. [=](open_atom, int32_t n) {
if (n <= 0) // Produce at least one value.
n = 1; if (n <= 0)
// Create a stream manager for implementing a stream source. The n = 1;
// streaming logic requires three functions: initializer, generator, and // Create a stream manager for implementing a stream source. The
// predicate. // streaming logic requires three functions: initializer, generator, and
return attach_stream_source( // predicate.
self, return attach_stream_source(
// Initializer. The type of the first argument (state) is freely self,
// chosen. If no state is required, `caf::unit_t` can be used here. // Initializer. The type of the first argument (state) is freely
[](int& x) { x = 0; }, // chosen. If no state is required, `caf::unit_t` can be used here.
// Generator. This function is called by CAF to produce new stream [](int32_t& x) { x = 0; },
// elements for downstream actors. The `x` argument is our state again // Generator. This function is called by CAF to produce new stream
// (with our freely chosen type). The second argument `out` points to // elements for downstream actors. The `x` argument is our state again
// the output buffer. The template argument (here: int) determines what // (with our freely chosen type). The second argument `out` points to
// elements downstream actors receive in this stream. Finally, `num` is // the output buffer. The template argument (here: int) determines what
// a hint from CAF how many elements we should ideally insert into // elements downstream actors receive in this stream. Finally, `num` is
// `out`. We can always insert fewer or more items. // a hint from CAF how many elements we should ideally insert into
[n](int& x, downstream<int>& out, size_t num) { // `out`. We can always insert fewer or more items.
auto max_x = std::min(x + static_cast<int>(num), n); [n](int32_t& x, downstream<int32_t>& out, size_t num) {
for (; x < max_x; ++x) auto max_x = std::min(x + static_cast<int>(num), n);
out.push(x); for (; x < max_x; ++x)
}, out.push(x);
// Predicate. This function tells CAF when we reached the end. },
[n](const int& x) { return x == n; }); // Predicate. This function tells CAF when we reached the end.
}}; [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 {
// Create a stream manager for implementing a stream stage. Similar to [=](stream<int32_t> in) {
// `make_source`, we need three functions: initialzer, processor, and // Create a stream manager for implementing a stream stage. Similar to
// finalizer. // `make_source`, we need three functions: initialzer, processor, and
return attach_stream_stage( // finalizer.
self, return attach_stream_stage(
// Our input source. self,
in, // Our input source.
// Initializer. Here, we don't need any state and simply use unit_t. in,
[](unit_t&) { // Initializer. Here, we don't need any state and simply use unit_t.
// nop [](unit_t&) {
}, // nop
// Processor. This function takes individual input elements as `val` },
// and forwards even integers to `out`. // Processor. This function takes individual input elements as `val`
[](unit_t&, downstream<int>& out, int val) { // and forwards even integers to `out`.
if (val % 2 == 0) [](unit_t&, downstream<int32_t>& out, int32_t val) {
out.push(val); if (val % 2 == 0)
}, out.push(val);
// Finalizer. Allows us to run cleanup code once the stream terminates. },
[=](unit_t&, const error& err) { // Finalizer. Allows us to run cleanup code once the stream terminates.
if (err) { [=](unit_t&, const error& err) {
aout(self) << "int_selector aborted with error: " << err << std::endl; if (err) {
} else { aout(self) << "int_selector aborted with error: " << err
aout(self) << "int_selector finalized" << std::endl; << std::endl;
} } else {
// else: regular stream shutdown aout(self) << "int_selector finalized" << std::endl;
}); }
}}; // 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 {
// Create a stream manager for implementing a stream sink. Once more, we [=](stream<int32_t> in) {
// have to provide three functions: Initializer, Consumer, Finalizer. // Create a stream manager for implementing a stream sink. Once more, we
return attach_stream_sink( // have to provide three functions: Initializer, Consumer, Finalizer.
self, return attach_stream_sink(
// Our input source. self,
in, // Our input source.
// Initializer. Here, we store all values we receive. Note that streams in,
// are potentially unbound, so this is usually a bad idea outside small // Initializer. Here, we store all values we receive. Note that streams
// examples like this one. // are potentially unbound, so this is usually a bad idea outside small
[](std::vector<int>&) { // examples like this one.
// nop [](std::vector<int>&) {
}, // nop
// Consumer. Takes individual input elements as `val` and stores them },
// in our history. // Consumer. Takes individual input elements as `val` and stores them
[](std::vector<int>& xs, int val) { xs.emplace_back(val); }, // in our history.
// Finalizer. Allows us to run cleanup code once the stream terminates. [](std::vector<int32_t>& xs, int32_t val) { xs.emplace_back(val); },
[=](std::vector<int>& xs, const error& err) { // Finalizer. Allows us to run cleanup code once the stream terminates.
if (err) { [=](std::vector<int32_t>& xs, const error& err) {
aout(self) << "int_sink aborted with error: " << err << std::endl; if (err) {
} else { aout(self) << "int_sink aborted with error: " << err << std::endl;
aout(self) << "int_sink finalized after receiving: " << xs } else {
<< std::endl; aout(self) << "int_sink finalized after receiving: " << xs
} << 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(
CAF_CHECK_EQUAL(res, i + i); [&](int32_t res) {
auto sender = actor_cast<strong_actor_ptr>(self->current_sender()); CAF_CHECK_EQUAL(res, i + i);
CAF_REQUIRE(sender); auto sender = actor_cast<strong_actor_ptr>(self->current_sender());
workers.push_back(actor_cast<actor>(std::move(sender))); CAF_REQUIRE(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)
[&](std::vector<actor>& ws) { .receive(
std::sort(workers.begin(), workers.end()); [&](std::vector<actor>& ws) {
std::sort(ws.begin(), ws.end()); std::sort(workers.begin(), workers.end());
CAF_REQUIRE_EQUAL(workers.size(), ws.size()); std::sort(ws.begin(), ws.end());
CAF_CHECK(std::equal(workers.begin(), workers.end(), ws.begin())); CAF_REQUIRE_EQUAL(workers.size(), ws.size());
}, 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,19 +121,19 @@ CAF_TEST(round_robin_actor_pool) { ...@@ -122,19 +121,19 @@ 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)
[&](std::vector<actor>& ws) { .receive(
success = workers.size() == ws.size(); [&](std::vector<actor>& ws) {
if (success) { success = workers.size() == ws.size();
std::sort(ws.begin(), ws.end()); if (success) {
CAF_CHECK_EQUAL(workers, ws); std::sort(ws.begin(), ws.end());
} else { CAF_CHECK_EQUAL(workers, ws);
// wait a bit until polling again } else {
std::this_thread::sleep_for(std::chrono::milliseconds(5)); // wait a bit until polling again
} 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,79 +37,83 @@ using namespace caf; ...@@ -37,79 +37,83 @@ 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 {
CAF_CHECK_EQUAL(fname, "numbers.txt"); [=](string& fname) -> result<stream<int32_t>, string> {
CAF_CHECK_EQUAL(self->mailbox().empty(), true); CAF_CHECK_EQUAL(fname, "numbers.txt");
return attach_stream_source( CAF_CHECK_EQUAL(self->mailbox().empty(), true);
self, return attach_stream_source(
// forward file name in handshake to next stage self,
std::forward_as_tuple(std::move(fname)), // forward file name in handshake to next stage
// initialize state std::forward_as_tuple(std::move(fname)),
[=](unit_t&) { // initialize state
auto& xs = self->state.buf; [=](unit_t&) {
xs.resize(buf_size); auto& xs = self->state.buf;
std::iota(xs.begin(), xs.end(), 1); xs.resize(buf_size);
}, std::iota(xs.begin(), xs.end(), 1);
// get next element },
[=](unit_t&, downstream<int>& out, size_t num) { // get next element
auto& xs = self->state.buf; [=](unit_t&, downstream<int32_t>& out, size_t num) {
CAF_MESSAGE("push " << num << " messages downstream"); auto& xs = self->state.buf;
auto n = std::min(num, xs.size()); CAF_MESSAGE("push " << num << " messages downstream");
for (size_t i = 0; i < n; ++i) auto n = std::min(num, xs.size());
out.push(xs[i]); for (size_t i = 0; i < n; ++i)
xs.erase(xs.begin(), xs.begin() + static_cast<ptrdiff_t>(n)); out.push(xs[i]);
}, xs.erase(xs.begin(), xs.begin() + static_cast<ptrdiff_t>(n));
// check whether we reached the end },
[=](const unit_t&) { // check whether we reached the end
if (self->state.buf.empty()) { [=](const unit_t&) {
CAF_MESSAGE(self->name() << " is done"); if (self->state.buf.empty()) {
return true; CAF_MESSAGE(self->name() << " is done");
} return true;
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 {
CAF_CHECK_EQUAL(fname, "numbers.txt"); [=](stream<int32_t>& in, const string& fname) {
using int_ptr = int*; CAF_CHECK_EQUAL(fname, "numbers.txt");
return attach_stream_sink( using int_ptr = int32_t*;
self, return attach_stream_sink(
// input stream self,
in, // input stream
// initialize state in,
[=](int_ptr& x) { x = &self->state.x; }, // initialize state
// processing step [=](int_ptr& x) { x = &self->state.x; },
[](int_ptr& x, int y) { *x += y; }, // processing step
// cleanup [](int_ptr& x, int32_t y) { *x += y; },
[=](int_ptr&, const error&) { // cleanup
CAF_MESSAGE(self->name() << " is done"); [=](int_ptr&, const error&) {
}); CAF_MESSAGE(self->name() << " is done");
}, });
[=](join_atom atm, actor src) { },
CAF_MESSAGE(self->name() << " joins a stream"); [=](join_atom atm, actor src) {
self->send(self * src, atm); CAF_MESSAGE(self->name() << " joins a stream");
}}; 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)");
} }
......
This diff is collapsed.
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