Commit 71c043cc authored by Dominik Charousset's avatar Dominik Charousset

Migrate to the new messaging API

parent 77569942
......@@ -403,50 +403,29 @@ if(NOT APPLE AND NOT WIN32)
endif()
# -fPIC generates warnings on MinGW and Cygwin plus extra setup steps needed on MinGW
if(MINGW)
add_definitions(-D_WIN32_WINNT=0x0600)
add_definitions(-DWIN32)
add_definitions(-D_WIN32_WINNT=0x0600 -DWIN32)
list(APPEND CAF_EXTRA_LDFLAGS -lws2_32 -liphlpapi -lpsapi)
# build static to avoid runtime dependencies to GCC libraries
build_string("EXTRA_FLAGS" "-static")
add_compile_options(-static)
elseif(CYGWIN)
build_string("EXTRA_FLAGS" "-U__STRICT_ANSI__")
add_compile_options(-U__STRICT_ANSI__)
endif()
if (WIN32)
list(APPEND CAF_EXTRA_LDFLAGS ws2_32 iphlpapi)
endif()
# iOS support
if(CAF_OSX_SYSROOT)
set(CMAKE_OSX_SYSROOT "${CAF_OSX_SYSROOT}")
endif()
if(CAF_IOS_DEPLOYMENT_TARGET)
if(CAF_OSX_SYSROOT STREQUAL "iphonesimulator")
build_string("EXTRA_FLAGS"
"-mios-simulator-version-min=${CAF_IOS_DEPLOYMENT_TARGET}")
add_compile_options("-mios-simulator-version-min=${CAF_IOS_DEPLOYMENT_TARGET}")
else()
build_string("EXTRA_FLAGS"
"-miphoneos-version-min=${CAF_IOS_DEPLOYMENT_TARGET}")
add_compile_options("-miphoneos-version-min=${CAF_IOS_DEPLOYMENT_TARGET}")
endif()
endif()
# check if the user provided CXXFLAGS, set defaults otherwise
if(NOT CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "-Wextra -Wall -pedantic ${EXTRA_FLAGS}")
endif()
if(NOT CMAKE_CXX_FLAGS_DEBUG)
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
endif()
if(NOT CMAKE_CXX_FLAGS_MINSIZEREL)
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os")
endif()
if(NOT CMAKE_CXX_FLAGS_RELEASE)
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
endif()
if(NOT CMAKE_CXX_FLAGS_RELWITHDEBINFO)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -fno-omit-frame-pointer")
endif()
# set build default build type to RelWithDebInfo if not set
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
# needed by subprojects
if (DEFINED CMAKE_LD_LIBS)
list(APPEND ${CMAKE_LD_LIBS})
......@@ -616,17 +595,6 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/CAFConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/CAFConfigVersion.cmake"
DESTINATION "${INSTALL_CAF_CMAKEDIR}")
# -- Fetch branch name and SHA if available ------------------------------------
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/release.txt")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/release.txt" CAF_RELEASE)
string(REGEX REPLACE "\n" "" CAF_RELEASE "${CAF_RELEASE}")
else()
set(CAF_RELEASE "${CAF_VERSION}")
endif()
message(STATUS "Set release version for all documentation to ${CAF_RELEASE}.")
# -- Setup for building manual and API documentation ---------------------------
if(NOT WIN32)
......@@ -661,11 +629,7 @@ invertYesNo(CAF_NO_EXCEPTIONS CAF_BUILD_WITH_EXCEPTIONS)
invertYesNo(CAF_NO_MEM_MANAGEMENT CAF_BUILD_MEM_MANAGEMENT)
invertYesNo(CAF_NO_OPENSSL CAF_BUILD_OPENSSL)
invertYesNo(CAF_NO_PYTHON CAF_BUILD_PYTHON)
# collect all compiler flags
string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPER_BUILD_TYPE)
set(ALL_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${UPPER_BUILD_TYPE}}")
set(ALL_LD_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CAF_EXTRA_LDFLAGS}")
string(STRIP "${ALL_LD_FLAGS}" ALL_LD_FLAGS)
get_property(all_cxx_flags DIRECTORY PROPERTY COMPILE_OPTIONS)
# done
if(NOT CAF_NO_SUMMARY)
message(STATUS
......@@ -689,7 +653,7 @@ if(NOT CAF_NO_SUMMARY)
"\nBuild Python: ${CAF_BUILD_PYTHON}"
"\n"
"\nCXX: ${CMAKE_CXX_COMPILER}"
"\nCXXFLAGS: ${ALL_CXX_FLAGS}"
"\nCXXFLAGS: ${all_cxx_flags}"
"\nLINKER_FLAGS (shared) ${ALL_LD_FLAGS}"
"\n"
"\nSource directory: ${CMAKE_CURRENT_SOURCE_DIR}"
......
......@@ -56,16 +56,22 @@ CAF_POP_WARNINGS
# pragma clang diagnostic ignored "-Wunused-const-variable"
#endif // __clang__
CAF_BEGIN_TYPE_ID_BLOCK(curl_fuse, first_custom_type_id)
CAF_ADD_TYPE_ID(curl_fuse, std::vector<char>)
CAF_ADD_ATOM(curl_fuse, read_atom);
CAF_ADD_ATOM(curl_fuse, fail_atom);
CAF_ADD_ATOM(curl_fuse, next_atom);
CAF_ADD_ATOM(curl_fuse, reply_atom);
CAF_ADD_ATOM(curl_fuse, finished_atom);
CAF_END_TYPE_ID_BLOCK(curl_fuse)
using namespace caf;
using buffer_type = std::vector<char>;
CAF_MSG_TYPE_ADD_ATOM(read_atom);
CAF_MSG_TYPE_ADD_ATOM(fail_atom);
CAF_MSG_TYPE_ADD_ATOM(next_atom);
CAF_MSG_TYPE_ADD_ATOM(reply_atom);
CAF_MSG_TYPE_ADD_ATOM(finished_atom);
namespace color {
// UNIX terminal color codes
......@@ -320,11 +326,7 @@ std::atomic<bool> shutdown_flag{false};
struct config : actor_system_config {
config() {
add_message_type<read_atom>("read_atom");
add_message_type<fail_atom>("fail_atom");
add_message_type<next_atom>("next_atom");
add_message_type<reply_atom>("reply_atom");
add_message_type<finished_atom>("finished_atom");
init_global_meta_objects<curl_fuse_type_ids>();
}
};
......
......@@ -11,6 +11,20 @@
#include "caf/all.hpp"
// --(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 CAF_PP_COMMA int32_t>)
CAF_END_TYPE_ID_BLOCK(custom_types_1)
// --(rst-type-id-block-end)--
using std::cout;
using std::cerr;
using std::endl;
......@@ -18,19 +32,17 @@ using std::vector;
using namespace caf;
namespace {
// POD struct foo
// --(rst-foo-begin)--
struct foo {
std::vector<int> a;
int b;
};
// foo needs to be serializable
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a, x.b);
}
// --(rst-foo-end)--
// a pair of two ints
using foo_pair = std::pair<int, int>;
......@@ -72,14 +84,14 @@ void testee(event_based_actor* self, size_t remaining) {
);
}
// --(rst-config-begin)--
class config : public actor_system_config {
public:
config() {
add_message_type<foo>("foo");
add_message_type<foo2>("foo2");
add_message_type<foo_pair>("foo_pair");
init_global_meta_objects<custom_types_1_type_ids>();
}
};
// --(rst-config-end)--
void caf_main(actor_system& system, const config&) {
// two variables for testing serialization
......@@ -118,6 +130,4 @@ void caf_main(actor_system& system, const config&) {
self->send(t, foo_pair2{3, 4});
}
} // namespace
CAF_MAIN()
......@@ -6,14 +6,20 @@
#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::endl;
using std::make_pair;
using namespace caf;
namespace {
// a simple class using getter and setter member functions
class foo {
public:
......@@ -61,7 +67,7 @@ behavior testee(event_based_actor* self) {
class config : public actor_system_config {
public:
config() {
add_message_type<foo>("foo");
init_global_meta_objects<custom_types_2_type_ids>();
}
};
......@@ -69,6 +75,4 @@ void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
}
} // namespace
CAF_MAIN()
// Showcases custom message types that cannot provide
// friend access to the inspect() function.
// Manual refs: 20-49, 76-103 (TypeInspection)
#include <utility>
#include <iostream>
#include <utility>
#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::endl;
using std::make_pair;
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 no friend access for
// `inspect`.
// --(rst-foo-begin)--
class foo {
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
......@@ -47,19 +52,22 @@ private:
int a_;
int b_;
};
// --(rst-foo-end)--
// A lightweight scope guard implementation.
template <class Fun>
class scope_guard {
public:
scope_guard(Fun f) : fun_(std::move(f)), enabled_(true) { }
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_();
if (enabled_)
fun_();
}
private:
......@@ -73,17 +81,12 @@ scope_guard<Fun> make_scope_guard(Fun f) {
return {std::move(f)};
}
// --(rst-inspect-begin)--
template <class Inspector>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
typename Inspector::result_type inspect(Inspector& f, foo& x) {
if constexpr (Inspector::reads_state) {
return f(meta::type_name("foo"), x.a(), x.b());
}
template <class Inspector>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
} else {
int a;
int b;
// write back to x at scope exit
......@@ -92,20 +95,20 @@ inspect(Inspector& f, foo& x) {
x.set_b(b);
});
return f(meta::type_name("foo"), a, b);
}
}
// --(rst-inspect-end)--
behavior testee(event_based_actor* self) {
return {
[=](const foo& x) {
aout(self) << to_string(x) << endl;
}
[=](const foo& x) { aout(self) << to_string(x) << endl; },
};
}
class config : public actor_system_config {
public:
config() {
add_message_type<foo>("foo");
init_global_meta_objects<custom_types_3_type_ids>();
}
};
......@@ -113,6 +116,4 @@ void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
}
} // namespace
CAF_MAIN()
......@@ -13,6 +13,15 @@
#include "caf/all.hpp"
CAF_BEGIN_TYPE_ID_BLOCK(dining_philosophers, first_custom_type_id)
CAF_ADD_ATOM(dining_philosophers, take_atom);
CAF_ADD_ATOM(dining_philosophers, taken_atom);
CAF_ADD_ATOM(dining_philosophers, eat_atom);
CAF_ADD_ATOM(dining_philosophers, think_atom);
CAF_END_TYPE_ID_BLOCK(dining_philosophers)
using std::cerr;
using std::cout;
using std::endl;
......@@ -23,10 +32,6 @@ using namespace caf;
namespace {
// atoms for chopstick and philosopher interfaces
CAF_MSG_TYPE_ADD_ATOM(take_atom);
CAF_MSG_TYPE_ADD_ATOM(taken_atom);
CAF_MSG_TYPE_ADD_ATOM(eat_atom);
CAF_MSG_TYPE_ADD_ATOM(think_atom);
// a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
......@@ -172,10 +177,7 @@ private:
struct config : actor_system_config {
config() {
add_message_type<take_atom>("take_atom");
add_message_type<taken_atom>("taken_atom");
add_message_type<eat_atom>("eat_atom");
add_message_type<think_atom>("think_atom");
init_global_meta_objects<dining_philosophers_type_ids>();
}
};
......
......@@ -14,19 +14,25 @@
#include "caf/event_based_actor.hpp"
#include "caf/exec_main.hpp"
#include "caf/function_view.hpp"
#include "caf/init_global_meta_objects.hpp"
#include "caf/policy/select_all.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/stateful_actor.hpp"
#include "caf/typed_actor.hpp"
#include "caf/typed_event_based_actor.hpp"
CAF_BEGIN_TYPE_ID_BLOCK(fan_out_request, first_custom_type_id)
CAF_ADD_ATOM(fan_out_request, row_atom);
CAF_ADD_ATOM(fan_out_request, column_atom);
CAF_ADD_ATOM(fan_out_request, average_atom);
CAF_END_TYPE_ID_BLOCK(fan_out_request)
using std::endl;
using std::chrono::seconds;
using namespace caf;
CAF_MSG_TYPE_ADD_ATOM(row_atom);
CAF_MSG_TYPE_ADD_ATOM(column_atom);
CAF_MSG_TYPE_ADD_ATOM(average_atom);
/// A simple actor for storing an integer value.
using cell = typed_actor<
......@@ -124,9 +130,7 @@ std::ostream& operator<<(std::ostream& out, const expected<int>& x) {
struct config : actor_system_config {
config() {
add_message_type<row_atom>("row_atom");
add_message_type<column_atom>("column_atom");
add_message_type<average_atom>("average_atom");
init_global_meta_objects<fan_out_request_type_ids>();
}
};
......
......@@ -3,21 +3,21 @@
#include <cstdint>
#include <iostream>
using std::endl;
using namespace caf;
CAF_BEGIN_TYPE_ID_BLOCK(fixed_stack, first_custom_type_id)
CAF_ADD_ATOM(fixed_stack, pop_atom);
CAF_ADD_ATOM(fixed_stack, push_atom);
namespace {
CAF_END_TYPE_ID_BLOCK(fixed_stack)
CAF_MSG_TYPE_ADD_ATOM(pop_atom);
CAF_MSG_TYPE_ADD_ATOM(push_atom);
using std::endl;
using namespace caf;
enum class fixed_stack_errc : uint8_t {
push_to_full = 1,
pop_from_empty,
};
} // namespace
namespace caf {
template <>
......@@ -27,8 +27,6 @@ struct error_category<fixed_stack_errc> {
} // namespace caf
namespace {
class fixed_stack : public event_based_actor {
public:
fixed_stack(actor_config& cfg, size_t stack_size)
......@@ -75,6 +73,12 @@ private:
behavior empty_;
};
struct config : actor_system_config {
config() {
init_global_meta_objects<fixed_stack_type_ids>();
}
};
void caf_main(actor_system& system) {
scoped_actor self{system};
auto st = self->spawn<fixed_stack>(5u);
......@@ -93,6 +97,4 @@ void caf_main(actor_system& system) {
self->send_exit(st, exit_reason::user_shutdown);
}
} // namespace
CAF_MAIN()
......@@ -274,7 +274,8 @@ void client_repl(actor_system& system, const config& cfg) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces
std::vector<string> words;
split(words, line, is_any_of(" "), token_compress_on);
if (!message_builder(words.begin(), words.end()).apply(eval))
auto msg = message_builder(words.begin(), words.end()).move_to_message();
if (!eval(msg))
usage();
}
}
......
......@@ -20,46 +20,47 @@
#include "caf/string_algorithms.hpp"
using namespace std;
using namespace caf;
CAF_BEGIN_TYPE_ID_BLOCK(group_chat, first_custom_type_id)
CAF_ADD_ATOM(group_chat, broadcast_atom);
namespace {
CAF_END_TYPE_ID_BLOCK(group_chat)
CAF_MSG_TYPE_ADD_ATOM(broadcast_atom);
using namespace caf;
struct line {
string str;
std::string str;
};
istream& operator>>(istream& is, line& l) {
getline(is, l.str);
std::istream& operator>>(std::istream& is, line& l) {
std::getline(is, l.str);
return is;
}
behavior client(event_based_actor* self, const string& name) {
behavior client(event_based_actor* self, const std::string& name) {
return {
[=](broadcast_atom, const string& message) {
[=](broadcast_atom, const std::string& message) {
for (auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message);
}
},
[=](join_atom, const group& what) {
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->leave(g);
}
cout << "*** join " << to_string(what) << endl;
std::cout << "*** join " << to_string(what) << std::endl;
self->join(what);
self->send(what, name + " has entered the chatroom");
},
[=](const string& txt) {
[=](const std::string& txt) {
// don't print own messages
if (self->current_sender() != self)
cout << txt << endl;
std::cout << txt << std::endl;
},
[=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl;
std::cout << "*** chatroom offline: " << to_string(g.source) << std::endl;
},
};
}
......@@ -72,6 +73,7 @@ public:
bool server_mode = false;
config() {
init_global_meta_objects<group_chat_type_ids>();
opt_group{custom_options_, "global"}
.add(name, "name,n", "set name")
.add(group_uris, "group,g", "join group")
......@@ -84,48 +86,47 @@ void run_server(actor_system& system, const config& cfg) {
auto res = system.middleman().publish_local_groups(cfg.port);
if (!res) {
std::cerr << "*** publishing local groups failed: "
<< system.render(res.error()) << endl;
<< system.render(res.error()) << std::endl;
return;
}
cout << "*** listening at port " << *res << endl
<< "*** press [enter] to quit" << endl;
string dummy;
std::cout << "*** listening at port " << *res << std::endl
<< "*** press [enter] to quit" << std::endl;
std::string dummy;
std::getline(std::cin, dummy);
cout << "... cya" << endl;
std::cout << "... cya" << std::endl;
}
void run_client(actor_system& system, const config& cfg) {
auto name = cfg.name;
while (name.empty()) {
cout << "please enter your name: " << flush;
if (!getline(cin, name)) {
cerr << "*** no name given... terminating" << endl;
std::cout << "please enter your name: " << std::flush;
if (!std::getline(std::cin, name)) {
std::cerr << "*** no name given... terminating" << std::endl;
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);
for (auto& uri : cfg.group_uris) {
auto tmp = system.groups().get(uri);
if (tmp)
anon_send(client_actor, join_atom_v, std::move(*tmp));
else
cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< system.render(tmp.error()) << endl;
std::cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< system.render(tmp.error()) << std::endl;
}
istream_iterator<line> eof;
vector<string> words;
for (istream_iterator<line> i(cin); i != eof; ++i) {
std::istream_iterator<line> eof;
std::vector<std::string> words;
for (std::istream_iterator<line> i{std::cin}; i != eof; ++i) {
auto send_input = [&] {
if (!i->str.empty())
anon_send(client_actor, broadcast_atom_v, i->str);
};
words.clear();
split(words, i->str, is_any_of(" "));
auto res
= message_builder(words.begin(), words.end())
.apply(
{[&](const string& cmd, const string& mod, const string& id) {
message_handler f{
[&](const std::string& cmd, const std::string& mod,
const std::string& id) {
if (cmd == "/join") {
auto grp = system.groups().get(mod, id);
if (grp)
......@@ -134,19 +135,21 @@ void run_client(actor_system& system, const config& cfg) {
send_input();
}
},
[&](const string& cmd) {
[&](const std::string& cmd) {
if (cmd == "/quit") {
cin.setstate(ios_base::eofbit);
std::cin.setstate(std::ios_base::eofbit);
} else if (cmd[0] == '/') {
cout << "*** available commands:\n"
std::cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n"
" /quit quit the program\n"
" /help print this text\n"
<< flush;
" /help print this text\n";
} else {
send_input();
}
}});
},
};
auto msg = message_builder(words.begin(), words.end()).move_to_message();
auto res = f(msg);
if (!res)
send_input();
}
......@@ -159,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) {
f(system, cfg);
}
} // namespace
CAF_MAIN(io::middleman)
......@@ -7,9 +7,6 @@
// Run client at the same host:
// - remote_spawn -H localhost -p 4242
// Manual refs: 33-34, 98-109 (ConfiguringActorApplications)
// 123-137 (RemoteSpawn)
#include <array>
#include <cassert>
#include <functional>
......@@ -21,6 +18,18 @@
#include "caf/all.hpp"
#include "caf/io/all.hpp"
// --(rst-calculator-begin)--
using calculator
= caf::typed_actor<caf::replies_to<caf::add_atom, int, int>::with<int>,
caf::replies_to<caf::sub_atom, int, int>::with<int>>;
// --(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::cout;
using std::endl;
......@@ -28,11 +37,6 @@ using std::string;
using namespace caf;
namespace {
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) {
return {
[=](add_atom, int a, int b) -> int {
......@@ -98,6 +102,7 @@ void client_repl(function_view<calculator> f) {
struct config : actor_system_config {
config() {
add_actor_type("calculator", calculator_fun);
init_global_meta_objects<remote_spawn_type_ids>();
opt_group{custom_options_, "global"}
.add(port, "port,p", "set port")
.add(host, "host,H", "set node (ignored in server mode)")
......@@ -119,6 +124,7 @@ void server(actor_system& system, const config& cfg) {
getchar();
}
// --(rst-client-begin)--
void client(actor_system& system, const config& cfg) {
auto node = system.middleman().connect(cfg.host, cfg.port);
if (!node) {
......@@ -140,12 +146,11 @@ void client(actor_system& system, const config& cfg) {
// be a good citizen and terminate remotely spawned actor before exiting
anon_send_exit(*worker, exit_reason::kill);
}
// --(rst-client-end)--
void caf_main(actor_system& system, const config& cfg) {
auto f = cfg.server_mode ? server : client;
f(system, cfg);
}
} // namespace
CAF_MAIN(io::middleman)
......@@ -2,22 +2,28 @@
* Basic, non-interactive streaming example for processing integers. *
******************************************************************************/
// Manual refs: lines 17-48, 50-83, 85-114, 128-132 (Streaming)
#include <iostream>
#include <vector>
#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 namespace caf;
namespace {
// --(rst-source-begin)--
// Simple source for generating a stream of integers from [0, n).
behavior int_source(event_based_actor* self) {
return {
[=](open_atom, int n) {
[=](open_atom, int32_t n) {
// Produce at least one value.
if (n <= 0)
n = 1;
......@@ -28,7 +34,7 @@ behavior int_source(event_based_actor* self) {
self,
// Initializer. The type of the first argument (state) is freely
// chosen. If no state is required, `caf::unit_t` can be used here.
[](int& x) { x = 0; },
[](int32_t& x) { x = 0; },
// Generator. This function is called by CAF to produce new stream
// elements for downstream actors. The `x` argument is our state again
// (with our freely chosen type). The second argument `out` points to
......@@ -36,21 +42,23 @@ behavior int_source(event_based_actor* self) {
// elements downstream actors receive in this stream. Finally, `num` is
// a hint from CAF how many elements we should ideally insert into
// `out`. We can always insert fewer or more items.
[n](int& x, downstream<int>& out, size_t num) {
[n](int32_t& x, downstream<int32_t>& out, size_t num) {
auto max_x = std::min(x + static_cast<int>(num), n);
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; });
[n](const int32_t& x) { return x == n; });
},
};
}
// --(rst-source-end)--
// --(rst-stage-begin)--
// Simple stage that only selects even numbers.
behavior int_selector(event_based_actor* self) {
return {
[=](stream<int> in) {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream stage. Similar to
// `make_source`, we need three functions: initialzer, processor, and
// finalizer.
......@@ -64,7 +72,7 @@ behavior int_selector(event_based_actor* self) {
},
// Processor. This function takes individual input elements as `val`
// and forwards even integers to `out`.
[](unit_t&, downstream<int>& out, int val) {
[](unit_t&, downstream<int32_t>& out, int32_t val) {
if (val % 2 == 0)
out.push(val);
},
......@@ -81,10 +89,12 @@ behavior int_selector(event_based_actor* self) {
},
};
}
// --(rst-stage-end)--
// --(rst-sink-begin)--
behavior int_sink(event_based_actor* self) {
return {
[=](stream<int> in) {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream sink. Once more, we
// have to provide three functions: Initializer, Consumer, Finalizer.
return attach_stream_sink(
......@@ -99,9 +109,9 @@ behavior int_sink(event_based_actor* self) {
},
// Consumer. Takes individual input elements as `val` and stores them
// in our history.
[](std::vector<int>& xs, int val) { xs.emplace_back(val); },
[](std::vector<int32_t>& xs, int32_t val) { xs.emplace_back(val); },
// Finalizer. Allows us to run cleanup code once the stream terminates.
[=](std::vector<int>& xs, const error& err) {
[=](std::vector<int32_t>& xs, const error& err) {
if (err) {
aout(self) << "int_sink aborted with error: " << err << std::endl;
} else {
......@@ -112,18 +122,21 @@ behavior int_sink(event_based_actor* self) {
},
};
}
// --(rst-sink-end)--
struct config : actor_system_config {
config() {
init_global_meta_objects<integer_stream_type_ids>();
opt_group{custom_options_, "global"}
.add(with_stage, "with-stage,s", "use a stage for filtering odd numbers")
.add(n, "num-values,n", "number of values produced by the source");
}
bool with_stage = false;
int n = 100;
int32_t n = 100;
};
// --(rst-main-begin)--
void caf_main(actor_system& sys, const config& cfg) {
auto src = sys.spawn(int_source);
auto snk = sys.spawn(int_sink);
......@@ -131,6 +144,7 @@ void caf_main(actor_system& sys, const config& cfg) {
: snk * src;
anon_send(pipeline, open_atom_v, cfg.n);
}
// --(rst-main-end)--
} // namespace
......
......@@ -69,6 +69,7 @@ set(CAF_CORE_SOURCES
src/detail/get_root_uuid.cpp
src/detail/ini_consumer.cpp
src/detail/invoke_result_visitor.cpp
src/detail/message_builder_element.cpp
src/detail/message_data.cpp
src/detail/meta_object.cpp
src/detail/parse.cpp
......
......@@ -104,7 +104,7 @@ public:
// -- overridden member functions --------------------------------------------
result_type begin_object(uint16_t& typenr, std::string& name);
result_type begin_object(type_id_t& type);
result_type end_object() noexcept;
......
......@@ -95,7 +95,7 @@ public:
// -- interface functions ----------------------------------------------------
error_code<sec> begin_object(uint16_t nr, string_view name);
error_code<sec> begin_object(type_id_t type);
error_code<sec> end_object();
......
......@@ -57,8 +57,7 @@ public:
// -- interface functions ----------------------------------------------------
/// Begins processing of an object.
virtual result_type begin_object(uint16_t& typenr, std::string& type_name)
= 0;
virtual result_type begin_object(type_id_t& type) = 0;
/// Ends processing of an object.
virtual result_type end_object() = 0;
......
......@@ -21,25 +21,25 @@
#include <string>
#include <type_traits>
#include "caf/byte.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf::detail {
CAF_CORE_EXPORT void
append_hex(std::string& result, const uint8_t* xs, size_t n);
append_hex(std::string& result, const byte* xs, size_t n);
template <class T>
enable_if_t<has_data_member<T>::value>
append_hex(std::string& result, const T& x) {
return append_hex(result, reinterpret_cast<const uint8_t*>(x.data()),
x.size());
return append_hex(result, reinterpret_cast<const byte*>(x.data()), x.size());
}
template <class T>
enable_if_t<std::is_integral<T>::value>
append_hex(std::string& result, const T& x) {
return append_hex(result, reinterpret_cast<const uint8_t*>(&x), sizeof(T));
return append_hex(result, reinterpret_cast<const byte*>(&x), sizeof(T));
}
} // namespace caf::detail
......@@ -27,6 +27,7 @@
#include "caf/deserializer.hpp"
#include "caf/detail/meta_object.hpp"
#include "caf/detail/padded_size.hpp"
#include "caf/detail/stringification_inspector.hpp"
#include "caf/error.hpp"
#include "caf/serializer.hpp"
......@@ -54,6 +55,10 @@ meta_object make_meta_object(const char* type_name) {
[](caf::deserializer& source, void* ptr) {
return source(*reinterpret_cast<T*>(ptr));
},
[](std::string& buf, const void* ptr) {
stringification_inspector f{buf};
f(*reinterpret_cast<const T*>(ptr));
},
};
}
......
......@@ -31,7 +31,7 @@ namespace caf::detail {
/// later.
class CAF_CORE_EXPORT message_builder_element {
public:
virtual ~message_builder_element() noexcept;
virtual ~message_builder_element();
/// Uses placement new to create a copy of the wrapped value at given memory
/// region.
......
......@@ -147,7 +147,7 @@ void message_data_init(byte* storage, T&& x, Ts&&... xs) {
// TODO: exception safety: if any constructor throws, we need to unwind the
// stack here and call destructors.
using type = strip_and_convert_t<T>;
new (storage) strip_and_convert_t<T>(std::forward<T>(x));
new (storage) type(std::forward<T>(x));
message_data_init(storage + padded_size_v<type>, std::forward<Ts>(xs)...);
}
......
......@@ -58,6 +58,9 @@ struct meta_object {
/// Applies an object to a generic deserializer.
caf::error (*load)(caf::deserializer&, void*);
/// Appends a string representation of an object to a buffer.
void (*stringify)(std::string&, const void*);
};
/// Convenience function for calling `meta.save(sink, obj)`.
......@@ -81,7 +84,7 @@ load(const meta_object& meta, caf::binary_deserializer& source, void* obj);
CAF_CORE_EXPORT span<const meta_object> global_meta_objects();
/// Returns the global meta object for given type ID.
CAF_CORE_EXPORT meta_object& global_meta_object(type_id_t id);
CAF_CORE_EXPORT const meta_object* global_meta_object(type_id_t id);
/// Clears the array for storing global meta objects.
/// @warning intended for unit testing only!
......
......@@ -33,7 +33,7 @@ public:
return result_;
}
result_type begin_object(uint16_t typenr, string_view type_name) override;
result_type begin_object(type_id_t) override;
result_type end_object() override;
......
......@@ -60,7 +60,7 @@ public:
// -- serializer interface ---------------------------------------------------
void begin_object(uint16_t, string_view) {
void begin_object(type_id_t) {
// nop
}
......
......@@ -20,12 +20,13 @@
#include <cstdlib>
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/type_id.hpp"
namespace caf::detail {
class type_id_list_builder {
class CAF_CORE_EXPORT type_id_list_builder {
public:
static constexpr size_t block_size = 8;
......
......@@ -268,7 +268,7 @@ template <class T>
std::string to_string(const intrusive_ptr<T>& x) {
std::string result;
auto v = reinterpret_cast<uintptr_t>(x.get());
detail::append_hex(result, reinterpret_cast<uint8_t*>(&v), sizeof(v));
detail::append_hex(result, reinterpret_cast<byte*>(&v), sizeof(v));
return result;
}
......
......@@ -182,14 +182,15 @@ template <class... Ts>
message make_message(Ts&&... xs) {
using namespace detail;
static_assert((!std::is_pointer<strip_and_convert_t<Ts>>::value && ...));
static constexpr size_t data_size = sizeof(message_data)
+ (padded_size_v<std::decay_t<Ts>> + ...);
static_assert((is_complete<type_id<strip_and_convert_t<Ts>>> && ...));
static constexpr size_t data_size
= sizeof(message_data) + (padded_size_v<strip_and_convert_t<Ts>> + ...);
auto types = make_type_id_list<strip_and_convert_t<Ts>...>();
auto vptr = malloc(data_size);
if (vptr == nullptr)
throw std::bad_alloc();
auto raw_ptr = new (vptr) message_data(types);
intrusive_cow_ptr<message_data> ptr{};
intrusive_cow_ptr<message_data> ptr{raw_ptr, false};
message_data_init(raw_ptr->storage(), std::forward<Ts>(xs)...);
return message{std::move(ptr)};
}
......
......@@ -58,8 +58,9 @@ public:
/// Adds `x` to the elements of the buffer.
template <class T>
message_builder& append(T&& x) {
using value_type = detail::strip_and_convert<T>;
static_assert(detail::sendable<value_type>);
using namespace detail;
using value_type = strip_and_convert_t<T>;
static_assert(sendable<value_type>);
types_.push_back(type_id_v<value_type>);
elements_.emplace_back(make_message_builder_element(std::forward<T>(x)));
return *this;
......
......@@ -62,7 +62,7 @@ public:
/// Begins processing of an object. Saves the type information
/// to the underlying storage.
virtual result_type begin_object(uint16_t typenr, string_view type_name) = 0;
virtual result_type begin_object(type_id_t type) = 0;
/// Ends processing of an object.
virtual result_type end_object() = 0;
......
......@@ -24,6 +24,7 @@
#include <utility>
#include "caf/detail/core_export.hpp"
#include "caf/detail/pp.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/type_name.hpp"
......@@ -120,8 +121,25 @@ constexpr type_id_t first_custom_type_id = 200;
: type_name<fully_qualified_name> {}; \
}
/// Creates a new tag type (atom) in the global namespace and assigns the next
/// free type ID to it.
#define CAF_ADD_ATOM_2(project_name, atom_name) \
struct atom_name {}; \
static constexpr atom_name atom_name##_v = atom_name{}; \
[[maybe_unused]] constexpr bool operator==(atom_name, atom_name) { \
return true; \
} \
[[maybe_unused]] constexpr bool operator!=(atom_name, atom_name) { \
return false; \
} \
template <class Inspector> \
auto inspect(Inspector& f, atom_name&) { \
return f(caf::meta::type_name(#atom_name)); \
} \
CAF_ADD_TYPE_ID(project_name, atom_name)
/// Creates a new tag type (atom) and assigns the next free type ID to it.
#define CAF_ADD_ATOM(project_name, atom_namespace, atom_name) \
#define CAF_ADD_ATOM_3(project_name, atom_namespace, atom_name) \
namespace atom_namespace { \
struct atom_name {}; \
static constexpr atom_name atom_name##_v = atom_name{}; \
......@@ -138,6 +156,15 @@ constexpr type_id_t first_custom_type_id = 200;
} \
CAF_ADD_TYPE_ID(project_name, atom_namespace ::atom_name)
#ifdef CAF_MSVC
# define CAF_ADD_ATOM(...) \
CAF_PP_CAT(CAF_PP_OVERLOAD(CAF_ADD_ATOM_, __VA_ARGS__)(__VA_ARGS__), \
CAF_PP_EMPTY())
#else
# define CAF_ADD_ATOM(...) \
CAF_PP_OVERLOAD(CAF_ADD_ATOM_, __VA_ARGS__)(__VA_ARGS__)
#endif
/// Finalizes a code block for registering custom types to CAF. Stores the last
/// type ID used by the project as `caf::${project_name}_last_type_id`.
#define CAF_END_TYPE_ID_BLOCK(project_name) \
......
......@@ -53,13 +53,16 @@ public:
}
/// Returns the number of elements in the list.
/// @pre `data() != nullptr`
constexpr size_t size() const noexcept {
return data_[0];
}
/// Returns `size() == 0`.
constexpr bool empty() const noexcept {
return size() == 0;
}
/// Returns the type ID at `index`.
/// @pre `data() != nullptr`
constexpr type_id_t operator[](size_t index) const noexcept {
return data_[index + 1];
}
......@@ -86,14 +89,14 @@ private:
/// @private
template <class... Ts>
struct make_type_id_list_helper {
static constexpr inline type_id_t data[]
= {static_cast<type_id_t>(sizeof...(Ts)), type_id_v<Ts>...};
static inline type_id_t data[] = {static_cast<type_id_t>(sizeof...(Ts)),
type_id_v<Ts>...};
};
/// Constructs a ::type_id_list from the template parameter pack `Ts`.
/// @relates type_id_list
template <class... Ts>
constexpr type_id_list make_type_id_list() {
type_id_list make_type_id_list() {
return type_id_list{make_type_id_list_helper<Ts...>::data};
}
......
......@@ -68,14 +68,8 @@ binary_deserializer::binary_deserializer(actor_system& sys) noexcept
// nop
}
result_type binary_deserializer::begin_object(uint16_t& nr, std::string& name) {
name.clear();
if (auto err = apply(nr))
return err;
if (nr == 0)
if (auto err = apply(name))
return err;
return none;
result_type binary_deserializer::begin_object(type_id_t& type) {
return apply(type);
}
result_type binary_deserializer::end_object() noexcept {
......
......@@ -49,10 +49,8 @@ void binary_serializer::skip(size_t num_bytes) {
write_pos_ += num_bytes;
}
error_code<sec> binary_serializer::begin_object(uint16_t nr, string_view name) {
apply(nr);
if (nr == 0)
apply(name);
error_code<sec> binary_serializer::begin_object(type_id_t type) {
apply(type);
return none;
}
......
......@@ -20,15 +20,15 @@
namespace caf::detail {
void append_hex(std::string& result, const uint8_t* xs, size_t n) {
void append_hex(std::string& result, const byte* xs, size_t n) {
if (n == 0) {
result += "00";
result += "<empty>";
return;
}
auto tbl = "0123456789ABCDEF";
char buf[3] = {0, 0, 0};
for (size_t i = 0; i < n; ++i) {
auto c = xs[i];
auto c = to_integer<uint8_t>(xs[i]);
buf[0] = tbl[c >> 4];
buf[1] = tbl[c & 0x0F];
result += buf;
......
......@@ -18,6 +18,7 @@
#include "caf/detail/append_percent_encoded.hpp"
#include "caf/byte.hpp"
#include "caf/config.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/string_view.hpp"
......@@ -33,7 +34,7 @@ void append_percent_encoded(std::string& str, string_view x, bool is_path) {
str += ch;
break;
}
CAF_ANNOTATE_FALLTHROUGH;
[[fallthrough]];
case ' ':
case '?':
case '#':
......@@ -53,7 +54,7 @@ void append_percent_encoded(std::string& str, string_view x, bool is_path) {
case ';':
case '=':
str += '%';
append_hex(str, reinterpret_cast<uint8_t*>(&ch), 1);
append_hex(str, reinterpret_cast<byte*>(&ch), 1);
break;
default:
str += ch;
......
......@@ -76,9 +76,10 @@ span<const meta_object> global_meta_objects() {
return {meta_objects, meta_objects_size};
}
meta_object& global_meta_object(type_id_t id) {
const meta_object* global_meta_object(type_id_t id) {
CAF_ASSERT(id < meta_objects_size);
return meta_objects[id];
auto& meta = meta_objects[id];
return meta.type_name != nullptr ? &meta : nullptr;
}
void clear_global_meta_objects() {
......
......@@ -26,11 +26,8 @@
namespace caf::detail {
error serialized_size_inspector::begin_object(uint16_t nr, string_view name) {
if (nr != 0)
return apply(nr);
apply(nr);
return apply(name);
error serialized_size_inspector::begin_object(type_id_t type) {
return apply(type);
}
error serialized_size_inspector::end_object() {
......
......@@ -75,7 +75,7 @@ inbound_path::inbound_path(stream_manager_ptr mgr_ptr, stream_slots id,
mgr->register_input_path(this);
CAF_STREAM_LOG_DEBUG(mgr->self()->name()
<< "opens input stream with element type"
<< detail::global_meta_object(in_type).type_name
<< detail::global_meta_object(in_type)->type_name
<< "at slot" << id.receiver << "from" << hdl);
if (auto str = get_if<std::string>(&self()->system().config(),
"stream.credit-policy")) {
......
......@@ -154,9 +154,28 @@ error_code<sec> inspect(binary_deserializer& source, message& msg) {
return msg.load(source);
}
std::string to_string(const message& ) {
// TODO: implement me
return "";
std::string to_string(const message& msg) {
if (msg.empty())
return "<empty-message>";
std::string result;
result += '(';
auto types = msg.types();
if (!types.empty()) {
auto ptr = msg.cdata().storage();
auto meta = detail::global_meta_object(types[0]);
CAF_ASSERT(meta != nullptr);
meta->stringify(result, ptr);
ptr += meta->padded_size;
for (size_t index = 1; index < types.size(); ++index) {
result += ", ";
meta = detail::global_meta_object(types[index]);
CAF_ASSERT(meta != nullptr);
meta->stringify(result, ptr);
ptr += meta->padded_size;
}
}
result += ')';
return result;
}
} // namespace caf
......@@ -27,10 +27,10 @@ std::string to_string(type_id_list xs) {
return "[]";
std::string result;
result += '[';
result += detail::global_meta_object(xs[0]).type_name;
result += detail::global_meta_object(xs[0])->type_name;
for (size_t index = 1; index < xs.size(); ++index) {
result += ", ";
result += detail::global_meta_object(xs[index]).type_name;
result += detail::global_meta_object(xs[index])->type_name;
}
result += ']';
return result;
......
......@@ -160,7 +160,7 @@ CAF_TEST(resizing the global meta objects keeps entries) {
CAF_CHECK_EQUAL(xs2.size(), 2u);
CAF_CHECK(eq(xs2[0], meta_i32));
resize_global_meta_objects(3);
CAF_CHECK(eq(global_meta_object(0), meta_i32));
CAF_CHECK(eq(*global_meta_object(0), meta_i32));
}
CAF_TEST(init_global_meta_objects takes care of creating a meta object table) {
......@@ -180,11 +180,11 @@ CAF_TEST(init_global_meta_objects takes care of creating a meta object table) {
};
CAF_CHECK(std::equal(xs.begin(), xs.end(), ys.begin(), ys.end(), same));
CAF_MESSAGE("init_global_meta_objects can initialize allocated chunks");
CAF_CHECK_EQUAL(global_meta_object(type_id_v<float>).type_name, nullptr);
CAF_CHECK_EQUAL(global_meta_object(type_id_v<float>)->type_name, nullptr);
init_global_meta_objects<builtin_type_ids>();
CAF_MESSAGE("again, calling init_global_meta_objects again is a no-op");
init_global_meta_objects<builtin_type_ids>();
CAF_CHECK_EQUAL(global_meta_object(type_id_v<float>).type_name, "float"s);
CAF_CHECK_EQUAL(global_meta_object(type_id_v<float>)->type_name, "float"s);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -30,6 +30,7 @@
#include "caf/init_global_meta_objects.hpp"
#include "caf/message_handler.hpp"
#include "caf/type_id.hpp"
#include "caf/type_id_list.hpp"
namespace {
......@@ -118,6 +119,17 @@ struct config : actor_system_config {
CAF_TEST_FIXTURE_SCOPE(message_tests, test_coordinator_fixture<config>)
CAF_TEST(messages allow index-based access) {
auto msg = make_message("abc", uint32_t{10}, 20.0);
CAF_CHECK_EQUAL(msg.size(), 3u);
CAF_CHECK_EQUAL(msg.types(),
(make_type_id_list<std::string, uint32_t, double>()));
CAF_CHECK_EQUAL(msg.get_as<std::string>(0), "abc");
CAF_CHECK_EQUAL(msg.get_as<uint32_t>(1), 10u);
CAF_CHECK_EQUAL(msg.get_as<double>(2), 20.0);
CAF_CHECK_EQUAL(msg.cdata().get_reference_count(), 1u);
}
CAF_TEST(compare_custom_types) {
s2 tmp;
tmp.value[0][1] = 100;
......@@ -127,15 +139,22 @@ CAF_TEST(compare_custom_types) {
CAF_TEST(empty_to_string) {
message msg;
CAF_CHECK(to_string(msg), "<empty-message>");
CAF_CHECK_EQUAL(to_string(msg), "<empty-message>");
}
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(ivec{1, 2, 3}), "([1, 2, 3])");
CAF_CHECK_EQUAL(msg_as_string(ivec{1, 2}, 3, 4, ivec{5, 6, 7}),
"([1, 2], 3, 4, [5, 6, 7])");
auto msg = make_message(ivec{1, 2, 3});
CAF_MESSAGE("s1: " << type_id_v<s1>);
CAF_MESSAGE("ivec: " << type_id_v<ivec>);
CAF_MESSAGE("msg.types: " << msg.types());
CAF_MESSAGE("types #1: " << make_type_id_list<s1>());
CAF_MESSAGE("types #2: " << make_type_id_list<ivec>());
CAF_CHECK_EQUAL(msg.get_as<ivec>(0), ivec({1, 2, 3}));
}
CAF_TEST(strings_to_string) {
......
......@@ -187,29 +187,40 @@ Adding Custom Message Types
---------------------------
CAF requires serialization support for all of its message types (see
:ref:`type-inspection`). However, CAF also needs a mapping of unique type names
to user-defined types at runtime. This is required to deserialize arbitrary
:ref:`type-inspection`). However, CAF also needs a mapping of unique type IDs to
user-defined types at runtime. This is required to deserialize arbitrary
messages from the network.
As an introductory example, we (again) use the following POD type
``foo``.
The type IDs are assigned by listing all custom types in a *type ID block*. CAF
assigns ascending IDs to each type by in the block as well as storing the type
name. In the following example, we forward-declare the types ``foo`` and
``foo2`` and register them to CAF in a *type ID block*. The name of the type ID
block is arbitrary, but it must be a valid C++ identifier.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 24-27
:start-after: --(rst-type-id-block-begin)--
:end-before: --(rst-type-id-block-end)--
To make ``foo`` serializable, we make it inspectable:
Aside from a type ID, CAF also requires an ``inspect`` overload in order to be
able to serialize objects. As an introductory example, we (again) use the
following POD type ``foo``.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 30-34
:start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
Finally, we give ``foo`` a platform-neutral name and add it to the list
of serializable types by using a custom config class.
By assigning type IDs and providing ``inspect`` overloads, we provide static and
compile-time information for all our types. However, CAF also needs some
information at run-time for deserializing received data. The function
``init_global_meta_objects`` takes care fo registering all the state we need at
run-time.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++
:lines: 75-78,81-84
:start-after: --(rst-config-begin)--
:end-before: --(rst-config-end)--
Adding Custom Error Types
-------------------------
......@@ -231,18 +242,14 @@ simple ``calculator`` actor.
.. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 33-34
:start-after: --(rst-calculator-begin)--
:end-before: --(rst-calculator-end)--
Adding the calculator actor type to our config is achieved by calling
``add_actor_type<T>``. Note that adding an actor type in this way
implicitly calls ``add_message_type<T>`` for typed actors
add-custom-message-type_. This makes our ``calculator`` actor type
serializable and also enables remote nodes to spawn calculators anywhere in the
distributed actor system (assuming all nodes use the same config).
.. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 98-109
``add_actor_type``. After calling this in our config, we can spawn the
``calculator`` anywhere in the distributed actor system (assuming all nodes use
the same config). Note that the handle type still requires a type ID (see
add-custom-message-type_).
Our final example illustrates how to spawn a ``calculator`` locally by
using its type name. Because the dynamic type name lookup can fail and the
......
......@@ -10,7 +10,8 @@ named "calculator".
.. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++
:lines: 123-137
:start-after: --(rst-client-begin)--
:end-before: --(rst-client-end)--
We first connect to a CAF node with ``middleman().connect(...)``. On success,
``connect`` returns the node ID we need for ``remote_spawn``. This requires the
......
......@@ -69,7 +69,8 @@ Defining Sources
.. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 17-48
:start-after: --(rst-source-begin)--
:end-before: --(rst-source-end)--
The simplest way to defining a source is to use the
``attach_stream_source`` function and pass it four arguments: a pointer
......@@ -81,7 +82,8 @@ Defining Stages
.. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 50-83
:start-after: --(rst-stage-begin)--
:end-before: --(rst-stage-end)--
The function ``make_stage`` also takes three lambdas but additionally
the received input stream handshake as first argument. Instead of a predicate,
......@@ -93,7 +95,8 @@ Defining Sinks
.. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 85-114
:start-after: --(rst-sink-begin)--
:end-before: --(rst-sink-end)--
The function ``make_sink`` is similar to ``make_stage``, except
that is does not produce outputs.
......@@ -103,7 +106,8 @@ Initiating Streams
.. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++
:lines: 128-132
:start-after: --(rst-main-begin)--
:end-before: --(rst-main-end)--
In our example, we always have a source ``int_source`` and a sink
``int_sink`` with an optional stage ``int_selector``. Sending
......
......@@ -49,10 +49,9 @@ implementing a hash function.
Inspector {
using result_type = T;
if (inspector only requires read access to the state of T)
static constexpr bool reads_state = true;
else
static constexpr bool writes_state = true;
static constexpr bool reads_state = ...;
static constexpr bool writes_state = ...;
template <class... Ts>
result_type operator()(Ts&&...);
......@@ -116,7 +115,8 @@ access to its members.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++
:lines: 20-49
:start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
Since there is no access to the data fields ``a_`` and ``b_``
(and assuming no changes to ``foo`` are possible), we need to split our
......@@ -124,7 +124,8 @@ implementation of ``inspect`` as shown below.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++
:lines: 76-103
:start-after: --(rst-inspect-begin)--
:end-before: --(rst-inspect-end)--
The purpose of the scope guard in the example above is to write the content of
the temporaries back to ``foo`` at scope exit automatically. Storing
......
......@@ -8,6 +8,17 @@
#include "caf/all.hpp"
struct entity;
struct se_event;
CAF_BEGIN_TYPE_ID_BLOCK(caf_vec, first_custom_type_id)
CAF_ADD_TYPE_ID(caf_vec, entity);
CAF_ADD_TYPE_ID(caf_vec, se_event);
CAF_END_TYPE_ID_BLOCK(caf_vec)
using std::string;
using namespace caf;
......@@ -739,6 +750,7 @@ struct config : public actor_system_config {
bool include_hidden_actors = false;
size_t verbosity = 0;
config() {
init_global_meta_objects<caf_vec_type_ids>();
opt_group{custom_options_, "global"}
.add(output_file, "output-file,o", "Path for the output file")
.add(include_hidden_actors, "include-hidden-actors,i",
......
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