Commit 72a77e03 authored by Dominik Charousset's avatar Dominik Charousset

Unify serialize and to_string API via inspect

Replace existing `to_string`/`serialize` function pair with a single `inspect`.
This change is backwards compatible, i.e., serializers and deserializers pick
up `serialize` functions via ADL if available. User-defined `to_string`
functions are preferred over `inspect`. Formatting hints and other meta
information that can be evaluated by inspectors are realized with annotations.
Close #470.
parent ce38a7c8
...@@ -26,16 +26,9 @@ struct foo { ...@@ -26,16 +26,9 @@ struct foo {
}; };
// foo needs to be serializable // foo needs to be serializable
template <class Processor> template <class Inspector>
void serialize(Processor& proc, foo& x, const unsigned int) { error inspect(Inspector& f, foo& x) {
proc & x.a; return f(meta::type_name("foo"), x.a, x.b);
proc & x.b;
}
// also, CAF gives us `deep_to_string` for implementing `to_string` easily
std::string to_string(const foo& x) {
// `to_string(foo{{1, 2, 3}, 4})` prints: "foo([1, 2, 3], 4)"
return "foo" + deep_to_string_as_tuple(x.a, x.b);
} }
// a pair of two ints // a pair of two ints
...@@ -51,15 +44,9 @@ struct foo2 { ...@@ -51,15 +44,9 @@ struct foo2 {
}; };
// foo2 also needs to be serializable // foo2 also needs to be serializable
template <class Processor> template <class Inspector>
void serialize(Processor& proc, foo2& x, const unsigned int) { error inspect(Inspector& f, foo2& x) {
proc & x.a; return f(meta::type_name("foo2"), x.a, x.b);
proc & x.b; // traversed automatically and recursively
}
// `deep_to_string` also traverses nested containers
std::string to_string(const foo2& x) {
return "foo" + deep_to_string_as_tuple(x.a, x.b);
} }
// receives our custom message types // receives our custom message types
...@@ -105,10 +92,20 @@ void caf_main(actor_system& system, const config&) { ...@@ -105,10 +92,20 @@ void caf_main(actor_system& system, const config&) {
vector<char> buf; vector<char> buf;
// write f1 to buffer // write f1 to buffer
binary_serializer bs{system, buf}; binary_serializer bs{system, buf};
bs << f1; auto e = bs(f1);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
return;
}
// read f2 back from buffer // read f2 back from buffer
binary_deserializer bd{system, buf}; binary_deserializer bd{system, buf};
bd >> f2; e = bd(f2);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
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
...@@ -118,7 +115,6 @@ void caf_main(actor_system& system, const config&) { ...@@ -118,7 +115,6 @@ void caf_main(actor_system& system, const config&) {
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});
self->await_all_other_actors_done();
} }
} // namespace <anonymous> } // namespace <anonymous>
......
...@@ -40,14 +40,9 @@ public: ...@@ -40,14 +40,9 @@ public:
b_ = val; b_ = val;
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, foo& x, const unsigned int) { friend error inspect(Inspector& f, foo& x) {
proc & x.a_; return f(meta::type_name("foo"), x.a_, x.b_);
proc & x.b_;
}
friend std::string to_string(const foo& x) {
return "foo" + deep_to_string_as_tuple(x.a_, x.b_);
} }
private: private:
......
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide
// friend access to the serialize() function. // friend access to the inspect() function.
// Manual refs: 57-59, 64-66 (ConfiguringActorApplications) // Manual refs: 57-59, 64-66 (ConfiguringActorApplications)
...@@ -48,27 +48,18 @@ private: ...@@ -48,27 +48,18 @@ private:
int b_; int b_;
}; };
// to_string is straightforward ... template <class Inspector>
std::string to_string(const foo& x) { error inspect(Inspector& f, foo& x) {
return "foo" + deep_to_string_as_tuple(x.a(), x.b()); // store current state into temporaries, then give the inspector references
} // to temporaries that are written back only when the inspector is saving
auto a = x.a();
// ... but we need to split serialization into a saving ... auto b = x.b();
template <class T> auto save = [&]() -> error {
typename std::enable_if<T::is_saving::value>::type x.set_a(a);
serialize(T& out, const foo& x, const unsigned int) { x.set_b(b);
out << x.a() << x.b(); return none;
} };
return f(meta::type_name("foo"), a, b, meta::save_callback(save));
// ... and a loading function
template <class T>
typename std::enable_if<T::is_loading::value>::type
serialize(T& in, foo& x, const unsigned int) {
int tmp;
in >> tmp;
x.set_a(tmp);
in >> tmp;
x.set_b(tmp);
} }
behavior testee(event_based_actor* self) { behavior testee(event_based_actor* self) {
......
...@@ -40,7 +40,6 @@ set (LIBCAF_CORE_SRCS ...@@ -40,7 +40,6 @@ set (LIBCAF_CORE_SRCS
src/continue_helper.cpp src/continue_helper.cpp
src/decorated_tuple.cpp src/decorated_tuple.cpp
src/default_invoke_result_visitor.cpp src/default_invoke_result_visitor.cpp
src/deep_to_string.cpp
src/default_attachable.cpp src/default_attachable.cpp
src/deserializer.cpp src/deserializer.cpp
src/duration.cpp src/duration.cpp
...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS ...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS
src/skip.cpp src/skip.cpp
src/splitter.cpp src/splitter.cpp
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
src/stringification_inspector.cpp
src/try_match.cpp src/try_match.cpp
src/type_erased_value.cpp src/type_erased_value.cpp
src/type_erased_tuple.cpp src/type_erased_tuple.cpp
......
...@@ -164,13 +164,9 @@ public: ...@@ -164,13 +164,9 @@ public:
actor(actor_control_block*, bool); actor(actor_control_block*, bool);
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, actor& x, const unsigned int v) { friend error inspect(Inspector& f, actor& x) {
serialize(proc, x.ptr_, v); return inspect(f, x.ptr_);
}
friend inline std::string to_string(const actor& x) {
return to_string(x.ptr_);
} }
/// @endcond /// @endcond
......
...@@ -109,13 +109,9 @@ public: ...@@ -109,13 +109,9 @@ public:
return compare(other.get()); return compare(other.get());
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, actor_addr& x, const unsigned int v) { friend error inspect(Inspector& f, actor_addr& x) {
serialize(proc, x.ptr_, v); return inspect(f, x.ptr_);
}
friend inline std::string to_string(const actor_addr& x) {
return to_string(x.ptr_);
} }
/// Releases the reference held by handle `x`. Using the /// Releases the reference held by handle `x`. Using the
......
...@@ -23,10 +23,16 @@ ...@@ -23,10 +23,16 @@
#include <atomic> #include <atomic>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/error.hpp"
#include "caf/node_id.hpp" #include "caf/node_id.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/weak_intrusive_ptr.hpp" #include "caf/weak_intrusive_ptr.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_none.hpp"
namespace caf { namespace caf {
/// Actors are always allocated with a control block that stores its identity /// Actors are always allocated with a control block that stores its identity
...@@ -179,23 +185,43 @@ inline bool operator!=(const abstract_actor* x, const strong_actor_ptr& y) { ...@@ -179,23 +185,43 @@ inline bool operator!=(const abstract_actor* x, const strong_actor_ptr& y) {
/// @relates actor_control_block /// @relates actor_control_block
using weak_actor_ptr = weak_intrusive_ptr<actor_control_block>; using weak_actor_ptr = weak_intrusive_ptr<actor_control_block>;
/// @relates strong_actor_ptr error load_actor(strong_actor_ptr& storage, execution_unit*,
void serialize(serializer&, strong_actor_ptr&, const unsigned int); actor_id aid, const node_id& nid);
/// @relates strong_actor_ptr error save_actor(strong_actor_ptr& storage, execution_unit*,
void serialize(deserializer&, strong_actor_ptr&, const unsigned int); actor_id aid, const node_id& nid);
/// @relates weak_actor_ptr template <class Inspector>
void serialize(serializer&, weak_actor_ptr&, const unsigned int); auto context_of(Inspector* f) -> decltype(f->context()) {
return f->context();
}
/// @relates weak_actor_ptr inline execution_unit* context_of(void*) {
void serialize(deserializer&, weak_actor_ptr&, const unsigned int); return nullptr;
}
/// @relates strong_actor_ptr template <class Inspector>
std::string to_string(const strong_actor_ptr&); error inspect(Inspector& f, strong_actor_ptr& x) {
actor_id aid = 0;
node_id nid;
if (x) {
aid = x->aid;
nid = x->nid;
}
auto load = [&] { return load_actor(x, context_of(&f), aid, nid); };
auto save = [&] { return save_actor(x, context_of(&f), aid, nid); };
return f(meta::type_name("actor"), aid,
meta::omittable_if_none(), nid,
meta::load_callback(load), meta::save_callback(save));
}
/// @relates weak_actor_ptr template <class Inspector>
std::string to_string(const weak_actor_ptr&); error inspect(Inspector& f, weak_actor_ptr& x) {
// inspect as strong pointer, then write back to weak pointer on save
auto tmp = x.lock();
auto load = [&]() -> error { x.reset(tmp.get()); return none; };
return f(tmp, meta::load_callback(load));
}
} // namespace caf } // namespace caf
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "caf/exec_main.hpp" #include "caf/exec_main.hpp"
#include "caf/resumable.hpp" #include "caf/resumable.hpp"
#include "caf/streambuf.hpp" #include "caf/streambuf.hpp"
#include "caf/to_string.hpp"
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/actor_pool.hpp" #include "caf/actor_pool.hpp"
#include "caf/attachable.hpp" #include "caf/attachable.hpp"
...@@ -99,6 +100,12 @@ ...@@ -99,6 +100,12 @@
#include "caf/decorator/adapter.hpp" #include "caf/decorator/adapter.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/scheduler/abstract_coordinator.hpp" #include "caf/scheduler/abstract_coordinator.hpp"
/// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de> /// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de>
......
...@@ -35,6 +35,7 @@ enum class atom_value : uint64_t { ...@@ -35,6 +35,7 @@ enum class atom_value : uint64_t {
/// @endcond /// @endcond
}; };
/// @relates atom_value
std::string to_string(const atom_value& x); std::string to_string(const atom_value& x);
atom_value atom_from_string(const std::string& x); atom_value atom_from_string(const std::string& x);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#ifndef CAF_NAMED_CALLBACK_HPP #ifndef CAF_NAMED_CALLBACK_HPP
#define CAF_NAMED_CALLBACK_HPP #define CAF_NAMED_CALLBACK_HPP
#include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
...@@ -40,7 +41,7 @@ namespace caf { ...@@ -40,7 +41,7 @@ namespace caf {
template <class... Ts> template <class... Ts>
class callback { class callback {
public: public:
virtual void operator()(Ts...) = 0; virtual error operator()(Ts...) = 0;
}; };
/// Utility class for wrapping a function object of type `Base`. /// Utility class for wrapping a function object of type `Base`.
...@@ -54,8 +55,8 @@ public: ...@@ -54,8 +55,8 @@ public:
callback_impl(callback_impl&&) = default; callback_impl(callback_impl&&) = default;
callback_impl& operator=(callback_impl&&) = default; callback_impl& operator=(callback_impl&&) = default;
void operator()(Ts... xs) override { error operator()(Ts... xs) override {
f_(xs...); return f_(xs...);
} }
private: private:
......
...@@ -28,6 +28,12 @@ ...@@ -28,6 +28,12 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/error.hpp"
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
...@@ -57,18 +63,18 @@ public: ...@@ -57,18 +63,18 @@ public:
/// Begins processing of an object. Saves the type information /// Begins processing of an object. Saves the type information
/// to the underlying storage when in saving mode, otherwise /// to the underlying storage when in saving mode, otherwise
/// extracts them and sets both arguments accordingly. /// extracts them and sets both arguments accordingly.
virtual void begin_object(uint16_t& typenr, std::string& name) = 0; virtual error begin_object(uint16_t& typenr, std::string& name) = 0;
/// Ends processing of an object. /// Ends processing of an object.
virtual void end_object() = 0; virtual error end_object() = 0;
/// Begins processing of a sequence. Saves the size /// Begins processing of a sequence. Saves the size
/// to the underlying storage when in saving mode, otherwise /// to the underlying storage when in saving mode, otherwise
/// sets `num` accordingly. /// sets `num` accordingly.
virtual void begin_sequence(size_t& num) = 0; virtual error begin_sequence(size_t& num) = 0;
/// Ends processing of a sequence. /// Ends processing of a sequence.
virtual void end_sequence() = 0; virtual error end_sequence() = 0;
/// Returns the actor system associated to this data processor. /// Returns the actor system associated to this data processor.
execution_unit* context() { execution_unit* context() {
...@@ -113,17 +119,18 @@ public: ...@@ -113,17 +119,18 @@ public:
/// Applies this processor to an arithmetic type. /// Applies this processor to an arithmetic type.
template <class T> template <class T>
typename std::enable_if<std::is_floating_point<T>::value>::type typename std::enable_if<std::is_floating_point<T>::value, error>::type
apply(T& x) { apply(T& x) {
static constexpr auto tlindex = detail::tl_index_of<builtin_t, T>::value; static constexpr auto tlindex = detail::tl_index_of<builtin_t, T>::value;
static_assert(tlindex >= 0, "T not recognized as builtiln type"); static_assert(tlindex >= 0, "T not recognized as builtiln type");
apply_builtin(static_cast<builtin>(tlindex), &x); return apply_builtin(static_cast<builtin>(tlindex), &x);
} }
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_integral<T>::value std::is_integral<T>::value
&& ! std::is_same<bool, T>::value && ! std::is_same<bool, T>::value,
error
>::type >::type
apply(T& x) { apply(T& x) {
using type = using type =
...@@ -132,31 +139,31 @@ public: ...@@ -132,31 +139,31 @@ public:
>::type; >::type;
static constexpr auto tlindex = detail::tl_index_of<builtin_t, type>::value; static constexpr auto tlindex = detail::tl_index_of<builtin_t, type>::value;
static_assert(tlindex >= 0, "T not recognized as builtiln type"); static_assert(tlindex >= 0, "T not recognized as builtiln type");
apply_builtin(static_cast<builtin>(tlindex), &x); return apply_builtin(static_cast<builtin>(tlindex), &x);
} }
void apply(std::string& x) { error apply(std::string& x) {
apply_builtin(string8_v, &x); return apply_builtin(string8_v, &x);
} }
void apply(std::u16string& x) { error apply(std::u16string& x) {
apply_builtin(string16_v, &x); return apply_builtin(string16_v, &x);
} }
void apply(std::u32string& x) { error apply(std::u32string& x) {
apply_builtin(string32_v, &x); return apply_builtin(string32_v, &x);
} }
template <class D, atom_value V> template <class D, atom_value V>
static void apply_atom_constant(D& self, atom_constant<V>&) { static error apply_atom_constant(D& self, atom_constant<V>&) {
static_assert(D::is_saving::value, "cannot deserialize an atom_constant"); static_assert(D::is_saving::value, "cannot deserialize an atom_constant");
auto x = V; auto x = V;
self.apply(x); return self.apply(x);
} }
template <atom_value V> template <atom_value V>
void apply(atom_constant<V>& x) { error apply(atom_constant<V>& x) {
apply_atom_constant(dref(), x); return apply_atom_constant(dref(), x);
} }
/// Serializes enums using the underlying type /// Serializes enums using the underlying type
...@@ -164,7 +171,8 @@ public: ...@@ -164,7 +171,8 @@ public:
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_enum<T>::value std::is_enum<T>::value
&& ! detail::has_serialize<T>::value && ! detail::has_serialize<T>::value,
error
>::type >::type
apply(T& x) { apply(T& x) {
using underlying = typename std::underlying_type<T>::type; using underlying = typename std::underlying_type<T>::type;
...@@ -177,17 +185,17 @@ public: ...@@ -177,17 +185,17 @@ public:
} }
} assign; } assign;
underlying tmp; underlying tmp;
convert_apply(dref(), x, tmp, assign); return convert_apply(dref(), x, tmp, assign);
} }
/// Applies this processor to an empty type. /// Applies this processor to an empty type.
template <class T> template <class T>
typename std::enable_if<std::is_empty<T>::value>::type typename std::enable_if<std::is_empty<T>::value, error>::type
apply(T&) { apply(T&) {
// nop return none;
} }
void apply(bool& x) { error apply(bool& x) {
struct { struct {
void operator()(bool& lhs, uint8_t& rhs) const { void operator()(bool& lhs, uint8_t& rhs) const {
lhs = rhs != 0; lhs = rhs != 0;
...@@ -197,63 +205,109 @@ public: ...@@ -197,63 +205,109 @@ public:
} }
} assign; } assign;
uint8_t tmp; uint8_t tmp;
convert_apply(dref(), x, tmp, assign); return convert_apply(dref(), x, tmp, assign);
} }
template <class T>
error consume_range(T& xs) {
for (auto& x : xs) {
using value_type = typename std::remove_const<
typename std::remove_reference<decltype(x)>::type
>::type;
auto e = apply(const_cast<value_type&>(x));
if (e)
return e;
}
return none;
}
/// Converts each element in `xs` to `U` before calling `apply`.
template <class U, class T>
error consume_range_c(T& xs) {
for (U x : xs) {
auto e = apply(x);
if (e)
return e;
}
return none;
}
template <class T>
error fill_range(T& xs, size_t num_elements) {
auto insert_iter = std::inserter(xs, xs.end());
for (size_t i = 0; i < num_elements; ++i) {
typename std::remove_const<typename T::value_type>::type x;
auto err = apply(x);
if (err)
return err;
*insert_iter++ = std::move(x);
}
return none;
}
/// Loads elements from type `U` before inserting to `xs`.
template <class U, class T>
error fill_range_c(T& xs, size_t num_elements) {
auto insert_iter = std::inserter(xs, xs.end());
for (size_t i = 0; i < num_elements; ++i) {
U x;
auto err = apply(x);
if (err)
return err;
*insert_iter++ = std::move(x);
}
return none;
}
// Applies this processor as Derived to `xs` in saving mode. // Applies this processor as Derived to `xs` in saving mode.
template <class D, class T> template <class D, class T>
static typename std::enable_if< static typename std::enable_if<
D::is_saving::value && ! detail::is_byte_sequence<T>::value D::is_saving::value && ! detail::is_byte_sequence<T>::value,
error
>::type >::type
apply_sequence(D& self, T& xs) { apply_sequence(D& self, T& xs) {
auto s = xs.size(); auto s = xs.size();
self.begin_sequence(s); return error::eval([&] { return self.begin_sequence(s); },
using value_type = typename std::remove_const<typename T::value_type>::type; [&] { return self.consume_range(xs); },
for (auto& x : xs) [&] { return self.end_sequence(); });
self.apply(const_cast<value_type&>(x));
self.end_sequence();
} }
// Applies this processor as Derived to `xs` in loading mode. // Applies this processor as Derived to `xs` in loading mode.
template <class D, class T> template <class D, class T>
static typename std::enable_if< static typename std::enable_if<
! D::is_saving::value && ! detail::is_byte_sequence<T>::value ! D::is_saving::value && ! detail::is_byte_sequence<T>::value,
error
>::type >::type
apply_sequence(D& self, T& xs) { apply_sequence(D& self, T& xs) {
size_t num_elements; size_t s;
self.begin_sequence(num_elements); return error::eval([&] { return self.begin_sequence(s); },
auto insert_iter = std::inserter(xs, xs.end()); [&] { return self.fill_range(xs, s); },
for (size_t i = 0; i < num_elements; ++i) { [&] { return self.end_sequence(); });
typename std::remove_const<typename T::value_type>::type x;
self.apply(x);
*insert_iter++ = std::move(x);
}
self.end_sequence();
} }
// Optimized saving for contiguous byte sequences. // Optimized saving for contiguous byte sequences.
template <class D, class T> template <class D, class T>
static typename std::enable_if< static typename std::enable_if<
D::is_saving::value && detail::is_byte_sequence<T>::value D::is_saving::value && detail::is_byte_sequence<T>::value,
error
>::type >::type
apply_sequence(D& self, T& xs) { apply_sequence(D& self, T& xs) {
auto s = xs.size(); auto s = xs.size();
self.begin_sequence(s); return error::eval([&] { return self.begin_sequence(s); },
self.apply_raw(xs.size(), &xs[0]); [&] { return self.apply_raw(xs.size(), &xs[0]); },
self.end_sequence(); [&] { return self.end_sequence(); });
} }
// Optimized loading for contiguous byte sequences. // Optimized loading for contiguous byte sequences.
template <class D, class T> template <class D, class T>
static typename std::enable_if< static typename std::enable_if<
! D::is_saving::value && detail::is_byte_sequence<T>::value ! D::is_saving::value && detail::is_byte_sequence<T>::value,
error
>::type >::type
apply_sequence(D& self, T& xs) { apply_sequence(D& self, T& xs) {
size_t num_elements; size_t s;
self.begin_sequence(num_elements); return error::eval([&] { return self.begin_sequence(s); },
xs.resize(num_elements); [&] { xs.resize(s); return self.apply_raw(s, &xs[0]); },
self.apply_raw(xs.size(), &xs[0]); [&] { return self.end_sequence(); });
self.end_sequence();
} }
/// Applies this processor to a sequence of values. /// Applies this processor to a sequence of values.
...@@ -261,82 +315,67 @@ public: ...@@ -261,82 +315,67 @@ public:
typename std::enable_if< typename std::enable_if<
detail::is_iterable<T>::value detail::is_iterable<T>::value
&& ! detail::has_serialize<T>::value && ! detail::has_serialize<T>::value
&& ! detail::is_inspectable<Derived, T>::value,
error
>::type >::type
apply(T& xs) { apply(T& xs) {
apply_sequence(dref(), xs); return apply_sequence(dref(), xs);
} }
/// Applies this processor to an array. /// Applies this processor to an array.
template <class T, size_t S> template <class T, size_t S>
typename std::enable_if<detail::is_serializable<T>::value>::type typename std::enable_if<detail::is_serializable<T>::value, error>::type
apply(std::array<T, S>& xs) { apply(std::array<T, S>& xs) {
for (auto& x : xs) return consume_range(xs);
apply(x);
} }
/// Applies this processor to an array. /// Applies this processor to an array.
template <class T, size_t S> template <class T, size_t S>
typename std::enable_if<detail::is_serializable<T>::value>::type typename std::enable_if<detail::is_serializable<T>::value, error>::type
apply(T (&xs) [S]) { apply(T (&xs) [S]) {
for (auto& x : xs) return consume_range(xs);
apply(x);
} }
template <class F, class S> template <class F, class S>
typename std::enable_if< typename std::enable_if<
detail::is_serializable<typename std::remove_const<F>::type>::value detail::is_serializable<typename std::remove_const<F>::type>::value
&& detail::is_serializable<S>::value && detail::is_serializable<S>::value,
error
>::type >::type
apply(std::pair<F, S>& xs) { apply(std::pair<F, S>& xs) {
using t0 = typename std::remove_const<F>::type;
// This cast allows the data processor to cope with // This cast allows the data processor to cope with
// `pair<const K, V>` value types used by `std::map`. // `pair<const K, V>` value types used by `std::map`.
apply(const_cast<typename std::remove_const<F>::type&>(xs.first)); return error::eval([&] { return apply(const_cast<t0&>(xs.first)); },
apply(xs.second); [&] { return apply(xs.second); });
}
class apply_helper {
public:
data_processor& parent;
apply_helper(data_processor& dp) : parent(dp) {
// nop
} }
inline void operator()() {
// end of recursion
}
template <class T, class... Ts>
void operator()(T& x, Ts&... xs) {
parent.apply(x);
(*this)(xs...);
}
};
template <class... Ts> template <class... Ts>
typename std::enable_if< typename std::enable_if<
detail::conjunction< detail::conjunction<
detail::is_serializable<Ts>::value... detail::is_serializable<Ts>::value...
>::value >::value,
error
>::type >::type
apply(std::tuple<Ts...>& xs) { apply(std::tuple<Ts...>& xs) {
apply_helper f{*this}; //apply_helper f{*this};
detail::apply_args(f, detail::get_indices(xs), xs); return detail::apply_args(*this, detail::get_indices(xs), xs);
} }
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
! std::is_empty<T>::value ! std::is_empty<T>::value
&& detail::has_serialize<T>::value && detail::has_serialize<T>::value,
error
>::type >::type
apply(T& x) { apply(T& x) {
// throws on error
detail::delegate_serialize(dref(), x); detail::delegate_serialize(dref(), x);
return none;
} }
template <class Rep, class Period> template <class Rep, class Period>
typename std::enable_if< typename std::enable_if<std::is_integral<Rep>::value, error>::type
std::is_integral<Rep>::value
>::type
apply(std::chrono::duration<Rep, Period>& x) { apply(std::chrono::duration<Rep, Period>& x) {
using duration_type = std::chrono::duration<Rep, Period>; using duration_type = std::chrono::duration<Rep, Period>;
// always save/store durations as int64_t to work around possibly // always save/store durations as int64_t to work around possibly
...@@ -351,13 +390,11 @@ public: ...@@ -351,13 +390,11 @@ public:
} }
} assign; } assign;
int64_t tmp; int64_t tmp;
convert_apply(dref(), x, tmp, assign); return convert_apply(dref(), x, tmp, assign);
} }
template <class Rep, class Period> template <class Rep, class Period>
typename std::enable_if< typename std::enable_if<std::is_floating_point<Rep>::value, error>::type
std::is_floating_point<Rep>::value
>::type
apply(std::chrono::duration<Rep, Period>& x) { apply(std::chrono::duration<Rep, Period>& x) {
using duration_type = std::chrono::duration<Rep, Period>; using duration_type = std::chrono::duration<Rep, Period>;
// always save/store floating point durations // always save/store floating point durations
...@@ -372,29 +409,82 @@ public: ...@@ -372,29 +409,82 @@ public:
} }
} assign; } assign;
double tmp; double tmp;
convert_apply(dref(), x, tmp, assign); return convert_apply(dref(), x, tmp, assign);
} }
/// Applies this processor to a raw block of data of size `num_bytes`. /// Applies this processor to a raw block of data of size `num_bytes`.
virtual void apply_raw(size_t num_bytes, void* data) = 0; virtual error apply_raw(size_t num_bytes, void* data) = 0;
template <class T>
auto apply(T& x) -> decltype(inspect(std::declval<Derived&>(), x)) {
return inspect(dref(), x);
}
inline error operator()() {
return none;
}
template <class F, class... Ts>
error operator()(meta::save_callback_t<F> x, Ts&&... xs) {
error e;
if (Derived::is_saving::value)
e = x.fun();
return e ? e : (*this)(std::forward<Ts>(xs)...);
}
template <class F, class... Ts>
error operator()(meta::load_callback_t<F> x, Ts&&... xs) {
error e;
if (Derived::is_loading::value)
e = x.fun();
return e ? e : (*this)(std::forward<Ts>(xs)...);
}
template <class... Ts>
error operator()(const meta::annotation&, Ts&&... xs) {
return (*this)(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
typename std::enable_if<
allowed_unsafe_message_type<T>::value,
error
>::type
operator()(T&, Ts&&... xs) {
return (*this)(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
typename std::enable_if<
! meta::is_annotation<T>::value
&& ! allowed_unsafe_message_type<T>::value,
error
>::type
operator()(T& x, Ts&&... xs) {
auto e = apply(x);
return e ? e : (*this)(std::forward<Ts>(xs)...);
}
protected: protected:
/// Applies this processor to a single builtin value. /// Applies this processor to a single builtin value.
virtual void apply_builtin(builtin in_out_type, void* in_out) = 0; virtual error apply_builtin(builtin in_out_type, void* in_out) = 0;
private: private:
template <class D, class T, class U, class F> template <class D, class T, class U, class F>
static typename std::enable_if<D::is_saving::value>::type static typename std::enable_if<D::is_saving::value, error>::type
convert_apply(D& self, T& x, U& storage, F assign) { convert_apply(D& self, T& x, U& storage, F assign) {
assign(storage, x); assign(storage, x);
self.apply(storage); return self.apply(storage);
} }
template <class D, class T, class U, class F> template <class D, class T, class U, class F>
static typename std::enable_if<! D::is_saving::value>::type static typename std::enable_if<! D::is_saving::value, error>::type
convert_apply(D& self, T& x, U& storage, F assign) { convert_apply(D& self, T& x, U& storage, F assign) {
self.apply(storage); auto e = self.apply(storage);
if (e)
return e;
assign(x, storage); assign(x, storage);
return none;
} }
// Returns a reference to the derived type. // Returns a reference to the derived type.
......
...@@ -30,166 +30,23 @@ ...@@ -30,166 +30,23 @@
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/detail/stringification_inspector.hpp"
namespace caf { namespace caf {
struct deep_to_string_append_t {};
constexpr deep_to_string_append_t deep_to_string_append = deep_to_string_append_t{};
class deep_to_string_t { class deep_to_string_t {
public: public:
constexpr deep_to_string_t() { constexpr deep_to_string_t() {
// nop // nop
} }
template <class T>
std::string operator()(const std::reference_wrapper<T>& x) const {
return (*this)(x.get());
}
std::string operator()(const char* cstr) const;
inline std::string operator()(char* cstr) const {
return (*this)(const_cast<const char*>(cstr));
}
inline std::string operator()(const std::string& str) const {
return (*this)(str.c_str());
}
std::string operator()(const atom_value& x) const;
constexpr const char* operator()(const bool& x) const {
return x ? "true" : "false";
}
template <class T>
typename std::enable_if<
! detail::is_iterable<T>::value && ! std::is_pointer<T>::value,
std::string
>::type
operator()(const T& x) const {
std::string res = impl(&x);
if (res == "<unprintable>") {
res = "<unprintable:";
res += typeid(T).name();
res += ">";
}
return res;
}
template <class F, class S>
std::string operator()(const std::pair<F, S>& x) const {
std::string result = "(";
result += (*this)(x.first);
result += ", ";
result += (*this)(x.second);
result += ")";
return result;
}
inline void operator()(const deep_to_string_append_t&, std::string&,
const char*) const {
// end of recursion
}
template <class T, class... Ts>
void operator()(const deep_to_string_append_t& tk, std::string& str,
const char* glue, const T& x, const Ts&... xs) const {
if (! str.empty())
str += glue;
str += (*this)(x);
(*this)(tk, str, glue, xs...);
}
template <class T, class U, class... Us>
std::string operator()(const T& x, const U& y, const Us&... ys) const {
std::string result;
(*this)(deep_to_string_append, result, ", ", x, y, ys...);
return result;
}
template <class... Ts> template <class... Ts>
std::string operator()(const std::tuple<Ts...>& xs) const { std::string operator()(Ts&&... xs) const {
std::string result = "("; std::string result;
result += detail::apply_args(*this, detail::get_indices(xs), xs); detail::stringification_inspector f{result};
result += ")"; f(std::forward<Ts>(xs)...);
return result;
}
template <class T>
typename std::enable_if<
detail::is_iterable<T>::value,
std::string
>::type
operator()(const T& x) const {
auto i = x.begin();
auto e = x.end();
if (i == e)
return "[]";
std::string result = "[";
result += (*this)(*i);
for (++i; i != e; ++i) {
result += ", ";
result += (*this)(*i);
}
result += "]";
return result;
}
template <class T>
std::string decompose_array(const T& xs, size_t n) const {
if (n == 0)
return "()";
std::string result = "(";
result += (*this)(xs[0]);
for (size_t i = 1; i < n; ++i) {
result += ", ";
result += (*this)(xs[i]);
}
result += ")";
return result; return result;
} }
template <class T, size_t S>
std::string operator()(const std::array<T, S>& xs) const {
return decompose_array(xs, S);
}
template <class T, size_t S>
std::string operator()(const T (&xs)[S]) const {
return decompose_array(xs, S);
}
template <class T>
typename std::enable_if<
std::is_pointer<T>::value,
std::string
>::type
operator()(const T ptr) const {
return ptr ? "*" + (*this)(*ptr) : "<nullptr>";
}
private:
template <class T>
auto impl(const T* x) const -> decltype(to_string(*x)) {
return to_string(*x);
}
template <class T>
typename std::enable_if<
std::is_arithmetic<T>::value,
std::string
>::type
impl(const T* x) const {
return std::to_string(*x);
}
constexpr const char* impl(const void*) const {
return "<unprintable>";
}
}; };
/// Unrolls collections such as vectors/maps, decomposes /// Unrolls collections such as vectors/maps, decomposes
...@@ -199,8 +56,7 @@ private: ...@@ -199,8 +56,7 @@ private:
/// provide a `to_string` is mapped to `<unprintable>`. /// provide a `to_string` is mapped to `<unprintable>`.
constexpr deep_to_string_t deep_to_string = deep_to_string_t{}; constexpr deep_to_string_t deep_to_string = deep_to_string_t{};
/// Convenience function returning /// Convenience function for `deep_to_string(std::forward_as_tuple(xs...))`.
/// `deep_to_string(std::forward_as_tuple(xs...))`.
template <class... Ts> template <class... Ts>
std::string deep_to_string_as_tuple(Ts&&... xs) { std::string deep_to_string_as_tuple(Ts&&... xs) {
return deep_to_string(std::forward_as_tuple(std::forward<Ts>(xs)...)); return deep_to_string(std::forward_as_tuple(std::forward<Ts>(xs)...));
......
...@@ -25,6 +25,12 @@ ...@@ -25,6 +25,12 @@
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include "caf/config.hpp"
#ifndef CAF_NO_EXCEPTIONS
#include <exception>
#endif // CAF_NO_EXCEPTIONS
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/data_processor.hpp" #include "caf/data_processor.hpp"
...@@ -47,25 +53,35 @@ public: ...@@ -47,25 +53,35 @@ public:
explicit deserializer(execution_unit* ctx = nullptr); explicit deserializer(execution_unit* ctx = nullptr);
}; };
/// Reads `x` from `source`. #ifndef CAF_NO_EXCEPTIONS
/// @relates serializer
template <class T>
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value
>::type
operator&(deserializer& source, T& x) {
auto e = source.apply(x);
if (e)
throw std::runtime_error(to_string(e));
}
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>())) decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value, >::value,
deserializer& deserializer&
>::type >::type
operator>>(deserializer& source, T& x) { operator>>(deserializer& source, T& x) {
source.apply(x); source & x;
return source; return source;
} }
template <class T> #endif // CAF_NO_EXCEPTIONS
auto operator&(deserializer& source, T& x) -> decltype(source.apply(x)) {
source.apply(x);
}
} // namespace caf } // namespace caf
......
...@@ -142,7 +142,7 @@ public: ...@@ -142,7 +142,7 @@ public:
inline bool visit(optional<skip_t>& x) { inline bool visit(optional<skip_t>& x) {
if (x) if (x)
return false; return false;
(*this)(x); (*this)();
return true; return true;
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
#define CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
#include <string>
#include <vector>
#include <functional>
#include <type_traits>
#include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/error.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/meta/omittable_if_none.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
class stringification_inspector {
public:
using is_saving = std::true_type;
stringification_inspector(std::string& result) : result_(result) {
// nop
}
template <class... Ts>
error operator()(Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
return none;
}
private:
/// Prints a separator to the result string.
void sep();
inline void traverse() noexcept {
// end of recursion
}
void consume(atom_value& x);
void consume(const char* cstr);
void consume_hex(const uint8_t* xs, size_t n);
inline void consume(bool& x) {
result_ += x ? "true" : "false";
}
inline void consume(char* cstr) {
consume(const_cast<const char*>(cstr));
}
inline void consume(std::string& str) {
consume(str.c_str());
}
template <class T>
enable_if_tt<std::is_arithmetic<T>> consume(T& x) {
result_ += std::to_string(x);
}
// unwrap std::ref
template <class T>
void consume(std::reference_wrapper<T>& x) {
return consume(x.get());
}
/// Picks up user-defined `to_string` functions.
template <class T>
enable_if_tt<has_to_string<T>> consume(T& x) {
result_ += to_string(x);
}
/// Delegates to `inspect(*this, x)` if available and `T`
/// does not provide a `to_string`.
template <class T>
enable_if_t<
is_inspectable<stringification_inspector, T>::value
&& ! has_to_string<T>::value>
consume(T& x) {
inspect(*this, x);
}
template <class F, class S>
void consume(std::pair<F, S>& x) {
result_ += '(';
traverse(deconst(x.first), deconst(x.second));
result_ += ')';
}
template <class... Ts>
void consume(std::tuple<Ts...>& x) {
result_ += '(';
apply_args(*this, get_indices(x), x);
result_ += ')';
}
template <class T>
enable_if_t<is_iterable<T>::value
&& ! is_inspectable<stringification_inspector, T>::value
&& ! has_to_string<T>::value>
consume(T& xs) {
result_ += '[';
for (auto& x : xs) {
sep();
consume(deconst(x));
}
result_ += ']';
}
template <class T>
void consume(T* xs, size_t n) {
result_ += '(';
for (size_t i = 0; i < n; ++i) {
sep();
consume(deconst(xs[i]));
}
result_ += ')';
}
template <class T, size_t S>
void consume(std::array<T, S>& xs) {
return consume(xs.data(), S);
}
template <class T, size_t S>
void consume(T (&xs)[S]) {
return consume(xs, S);
}
template <class T>
enable_if_tt<std::is_pointer<T>> consume(T ptr) {
if (ptr) {
result_ += '*';
consume(*ptr);
} else {
result_ + "<null>";
}
}
/// Fallback printing `<unprintable>`.
template <class T>
enable_if_t<
! is_iterable<T>::value
&& ! std::is_pointer<T>::value
&& ! is_inspectable<stringification_inspector, T>::value
&& ! std::is_arithmetic<T>::value
&& ! has_to_string<T>::value>
consume(T&) {
result_ += "<unprintable>";
}
template <class T, class... Ts>
void traverse(meta::hex_formatted_t, T& x, Ts&&... xs) {
sep();
consume_hex(reinterpret_cast<uint8_t*>(deconst(x).data()), x.size());
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_if_none_t, T& x, Ts&&... xs) {
if (x != none) {
sep();
consume(x);
}
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_if_empty_t, T& x, Ts&&... xs) {
if (! x.empty()) {
sep();
consume(x);
}
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_t, T&, Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
}
template <class... Ts>
void traverse(meta::type_name_t x, Ts&&... xs) {
sep();
result_ += x.value;
result_ += '(';
traverse(std::forward<Ts>(xs)...);
result_ += ')';
}
template <class... Ts>
void traverse(const meta::annotation&, Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
enable_if_t<! meta::is_annotation<T>::value> traverse(T&& x, Ts&&... xs) {
sep();
consume(deconst(x));
traverse(std::forward<Ts>(xs)...);
}
template <class T>
T& deconst(const T& x) {
return const_cast<T&>(x);
}
std::string& result_;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
...@@ -23,111 +23,60 @@ ...@@ -23,111 +23,60 @@
#include <tuple> #include <tuple>
#include <stdexcept> #include <stdexcept>
#include "caf/type_nr.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/make_type_erased_value.hpp"
#include "caf/type_nr.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/message_data.hpp" #include "caf/detail/message_data.hpp"
#include "caf/detail/try_serialize.hpp" #include "caf/detail/try_serialize.hpp"
#include "caf/make_type_erased_value.hpp" #include "caf/detail/stringification_inspector.hpp"
#define CAF_TUPLE_VALS_DISPATCH(x) \
case x: \
return tuple_inspect_delegate<x, sizeof...(Ts)-1>(data_, f)
namespace caf { namespace caf {
namespace detail { namespace detail {
template <size_t Pos, size_t Max, bool InRange = (Pos < Max)> // avoids triggering static asserts when using CAF_TUPLE_VALS_DISPATCH
struct tup_ptr_access { template <size_t X, size_t Max, class T, class F>
template <class T> auto tuple_inspect_delegate(T& data, F& f) -> decltype(f(std::get<Max>(data))) {
static typename std::conditional< return f(std::get<(X < Max ? X : Max)>(data));
std::is_const<T>::value, }
const void*,
void*
>::type
get(size_t pos, T& tup) {
if (pos == Pos) return &std::get<Pos>(tup);
return tup_ptr_access<Pos + 1, Max>::get(pos, tup);
}
template <class T>
static bool cmp(size_t pos, T& tup, const void* x) {
using type = typename std::tuple_element<Pos, T>::type;
if (pos == Pos)
return safe_equal(std::get<Pos>(tup), *reinterpret_cast<const type*>(x));
return tup_ptr_access<Pos + 1, Max>::cmp(pos, tup, x);
}
template <class T, class Processor>
static error serialize(size_t pos, T& tup, Processor& proc) {
if (pos == Pos)
try_serialize(proc, &std::get<Pos>(tup));
else
tup_ptr_access<Pos + 1, Max>::serialize(pos, tup, proc);
// TODO: refactor after visit API is in place (#470)
return {};
}
template <class T> template <size_t X, size_t N>
static std::string stringify(size_t pos, const T& tup) { struct tup_ptr_access_pos {
if (pos == Pos) constexpr tup_ptr_access_pos() {
return deep_to_string(std::get<Pos>(tup)); // nop
return tup_ptr_access<Pos + 1, Max>::stringify(pos, tup);
}
template <class T>
static type_erased_value_ptr copy(size_t pos, const T& tup) {
using value_type = typename std::tuple_element<Pos, T>::type;
if (pos == Pos)
return make_type_erased_value<value_type>(std::get<Pos>(tup));
return tup_ptr_access<Pos + 1, Max>::copy(pos, tup);;
} }
}; };
template <size_t Pos, size_t Max> template <size_t X, size_t N>
struct tup_ptr_access<Pos, Max, false> { constexpr tup_ptr_access_pos<X + 1, N> next(tup_ptr_access_pos<X, N>) {
template <class T> return {};
static typename std::conditional< }
std::is_const<T>::value,
const void*,
void*
>::type
get(size_t, T&) {
// end of recursion
return nullptr;
}
template <class T>
static bool cmp(size_t, T&, const void*) {
return false;
}
template <class T, class Processor>
static void serialize(size_t, T&, Processor&) {
// end of recursion
}
template <class T>
static std::string stringify(size_t, const T&) {
return "<unprintable>";
}
struct void_ptr_access {
template <class T> template <class T>
static type_erased_value_ptr copy(size_t, const T&) { void* operator()(T& x) const noexcept {
return nullptr; return &x;
} }
}; };
template <class T, uint16_t N = type_nr<T>::value> template <class T, uint16_t N = type_nr<T>::value>
struct tuple_vals_type_helper { struct tuple_vals_type_helper {
static typename message_data::rtti_pair get() { static typename message_data::rtti_pair get() noexcept {
return {N, nullptr}; return {N, nullptr};
} }
}; };
template <class T> template <class T>
struct tuple_vals_type_helper<T, 0> { struct tuple_vals_type_helper<T, 0> {
static typename message_data::rtti_pair get() { static typename message_data::rtti_pair get() noexcept {
return {0, &typeid(T)}; return {0, &typeid(T)};
} }
}; };
...@@ -135,14 +84,25 @@ struct tuple_vals_type_helper<T, 0> { ...@@ -135,14 +84,25 @@ struct tuple_vals_type_helper<T, 0> {
template <class Base, class... Ts> template <class Base, class... Ts>
class tuple_vals_impl : public Base { class tuple_vals_impl : public Base {
public: public:
// -- static invariants ------------------------------------------------------
static_assert(sizeof...(Ts) > 0, "tuple_vals is not allowed to be empty"); static_assert(sizeof...(Ts) > 0, "tuple_vals is not allowed to be empty");
// -- member types -----------------------------------------------------------
using super = message_data; using super = message_data;
using rtti_pair = typename message_data::rtti_pair; using rtti_pair = typename message_data::rtti_pair;
using data_type = std::tuple<Ts...>; using data_type = std::tuple<Ts...>;
// -- friend functions -------------------------------------------------------
template <class Inspector>
friend error inspect(Inspector& f, tuple_vals_impl& x) {
return apply_args(f, get_indices(x.data_), x.data_);
}
tuple_vals_impl(const tuple_vals_impl&) = default; tuple_vals_impl(const tuple_vals_impl&) = default;
template <class... Us> template <class... Us>
...@@ -166,26 +126,32 @@ public: ...@@ -166,26 +126,32 @@ public:
const void* get(size_t pos) const noexcept override { const void* get(size_t pos) const noexcept override {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
return tup_ptr_access<0, sizeof...(Ts)>::get(pos, data_); void_ptr_access f;
return mptr()->dispatch(pos, f);
} }
void* get_mutable(size_t pos) override { void* get_mutable(size_t pos) override {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
return const_cast<void*>(get(pos)); void_ptr_access f;
return this->dispatch(pos, f);
} }
std::string stringify(size_t pos) const override { std::string stringify(size_t pos) const override {
return tup_ptr_access<0, sizeof...(Ts)>::stringify(pos, data_); std::string result;
stringification_inspector f{result};
mptr()->dispatch(pos, f);
return result;
} }
using Base::copy; using Base::copy;
type_erased_value_ptr copy(size_t pos) const override { type_erased_value_ptr copy(size_t pos) const override {
return tup_ptr_access<0, sizeof...(Ts)>::copy(pos, data_); type_erased_value_factory f;
return mptr()->dispatch(pos, f);
} }
error load(size_t pos, deserializer& source) override { error load(size_t pos, deserializer& source) override {
return tup_ptr_access<0, sizeof...(Ts)>::serialize(pos, data_, source); return dispatch(pos, source);
} }
uint32_t type_token() const noexcept override { uint32_t type_token() const noexcept override {
...@@ -197,13 +163,49 @@ public: ...@@ -197,13 +163,49 @@ public:
} }
error save(size_t pos, serializer& sink) const override { error save(size_t pos, serializer& sink) const override {
// the serialization framework uses non-const arguments for deserialization, return mptr()->dispatch(pos, sink);
// but this cast is safe since the values are not actually changed
auto& nc_data = const_cast<data_type&>(data_);
return tup_ptr_access<0, sizeof...(Ts)>::serialize(pos, nc_data, sink);
} }
private: private:
template <class F>
auto dispatch(size_t pos, F& f) -> decltype(f(std::declval<int&>())) {
CAF_ASSERT(pos < sizeof...(Ts));
switch (pos) {
CAF_TUPLE_VALS_DISPATCH(0);
CAF_TUPLE_VALS_DISPATCH(1);
CAF_TUPLE_VALS_DISPATCH(2);
CAF_TUPLE_VALS_DISPATCH(3);
CAF_TUPLE_VALS_DISPATCH(4);
CAF_TUPLE_VALS_DISPATCH(5);
CAF_TUPLE_VALS_DISPATCH(6);
CAF_TUPLE_VALS_DISPATCH(7);
CAF_TUPLE_VALS_DISPATCH(8);
CAF_TUPLE_VALS_DISPATCH(9);
default:
// fall back to recursive dispatch function
static constexpr size_t max_pos = sizeof...(Ts) - 1;
tup_ptr_access_pos<(10 < max_pos ? 10 : max_pos), max_pos> first;
return rec_dispatch(pos, f, first);
}
}
template <class F, size_t N>
auto rec_dispatch(size_t, F& f, tup_ptr_access_pos<N, N>)
-> decltype(f(std::declval<int&>())) {
return tuple_inspect_delegate<N, N>(data_, f);
}
template <class F, size_t X, size_t N>
auto rec_dispatch(size_t pos, F& f, tup_ptr_access_pos<X, N> token)
-> decltype(f(std::declval<int&>())) {
return pos == X ? tuple_inspect_delegate<X, N>(data_, f)
: rec_dispatch(pos, f, next(token));
}
tuple_vals_impl* mptr() const {
return const_cast<tuple_vals_impl*>(this);
}
data_type data_; data_type data_;
std::array<rtti_pair, sizeof...(Ts)> types_; std::array<rtti_pair, sizeof...(Ts)> types_;
}; };
......
...@@ -68,9 +68,7 @@ public: ...@@ -68,9 +68,7 @@ public:
} }
error load(size_t pos, deserializer& source) override { error load(size_t pos, deserializer& source) override {
ptrs_[pos]->load(source); return ptrs_[pos]->load(source);
// TODO: refactor after visit API is in place (#470)
return {};
} }
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
......
...@@ -78,9 +78,7 @@ public: ...@@ -78,9 +78,7 @@ public:
} }
error load(deserializer& source) override { error load(deserializer& source) override {
detail::try_serialize(source, addr_of(x_)); return source(*addr_of(x_));
// TODO: refactor after visit API is in place (#470)
return {};
} }
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
...@@ -105,9 +103,7 @@ public: ...@@ -105,9 +103,7 @@ public:
} }
error save(serializer& sink) const override { error save(serializer& sink) const override {
detail::try_serialize(sink, addr_of(x_)); return sink(*addr_of(const_cast<T&>(x_)));
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::string stringify() const override { std::string stringify() const override {
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
#include <tuple> #include <tuple>
#include <string> #include <string>
#include <vector>
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <vector>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -34,6 +34,48 @@ ...@@ -34,6 +34,48 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class T>
using decay_t = typename std::decay<T>::type;
template <bool V, class T = void>
using enable_if_t = typename std::enable_if<V, T>::type;
template <class Trait, class T = void>
using enable_if_tt = typename std::enable_if<Trait::value, T>::type;
template <class Inspector, class T>
class is_inspectable {
private:
template <class U>
static auto sfinae(Inspector& x, U& y) -> decltype(inspect(x, y));
static std::false_type sfinae(Inspector&, ...);
using result_type = decltype(sfinae(std::declval<Inspector&>(),
std::declval<T&>()));
public:
static constexpr bool value = ! std::is_same<result_type, std::false_type>::value;
};
template <class T>
class has_to_string {
private:
template <class U>
static auto sfinae(const U& x) -> decltype(to_string(x));
static void sfinae(...);
using result = decltype(sfinae(std::declval<const T&>()));
public:
static constexpr bool value = std::is_convertible<result, std::string>::value;
};
// pointers are never inspectable
template <class Inspector, class T>
class is_inspectable<Inspector, T*> : std::false_type {};
template <bool X> template <bool X>
using bool_token = std::integral_constant<bool, X>; using bool_token = std::integral_constant<bool, X>;
...@@ -80,17 +122,17 @@ struct is_array_of { ...@@ -80,17 +122,17 @@ struct is_array_of {
/// Deduces the reference type of T0 and applies it to T1. /// Deduces the reference type of T0 and applies it to T1.
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type { struct deduce_ref_type {
using type = typename std::decay<T1>::type; using type = decay_t<T1>;
}; };
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type<T0&, T1> { struct deduce_ref_type<T0&, T1> {
using type = typename std::decay<T1>::type&; using type = decay_t<T1>&;
}; };
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type<const T0&, T1> { struct deduce_ref_type<const T0&, T1> {
using type = const typename std::decay<T1>::type&; using type = const decay_t<T1>&;
}; };
/// Checks wheter `X` is in the template parameter pack Ts. /// Checks wheter `X` is in the template parameter pack Ts.
...@@ -143,11 +185,10 @@ class is_comparable { ...@@ -143,11 +185,10 @@ class is_comparable {
// candidate and thus decltype(cmp_help_fun(...)) is void. // candidate and thus decltype(cmp_help_fun(...)) is void.
template <class A, typename B> template <class A, typename B>
static bool cmp_help_fun(const A* arg0, const B* arg1, static bool cmp_help_fun(const A* arg0, const B* arg1,
decltype(*arg0 == *arg1)* = nullptr) { decltype(*arg0 == *arg1)* = nullptr);
return true;
}
template <class A, typename B> template <class A, typename B>
static void cmp_help_fun(const A*, const B*, void* = nullptr) { } static void cmp_help_fun(const A*, const B*, void* = nullptr);
using result_type = decltype(cmp_help_fun(static_cast<T1*>(nullptr), using result_type = decltype(cmp_help_fun(static_cast<T1*>(nullptr),
static_cast<T2*>(nullptr), static_cast<T2*>(nullptr),
...@@ -160,25 +201,19 @@ public: ...@@ -160,25 +201,19 @@ public:
template <class T> template <class T>
class is_forward_iterator { class is_forward_iterator {
template <class C> template <class C>
static bool sfinae_fun( static bool sfinae(C& x, C& y,
C* iter, // check for operator*
// check for 'C::value_type C::operator*()' returning a non-void type decay_t<decltype(*x)>* = 0,
typename std::decay<decltype(*(*iter))>::type* = 0, // check for operator++ returning an iterator
// check for 'C& C::operator++()' decay_t<decltype(x = ++y)>* = 0,
typename std::enable_if< // check for operator==
std::is_same<C&, decltype(++(*iter))>::value>::type* = 0, decay_t<decltype(x == y)>* = 0,
// check for 'bool C::operator==()' // check for operator!=
typename std::enable_if< decay_t<decltype(x != y)>* = 0);
std::is_same<bool, decltype(*iter == *iter)>::value>::type* = 0,
// check for 'bool C::operator!=()'
typename std::enable_if<
std::is_same<bool, decltype(*iter != *iter)>::value>::type* = 0) {
return true;
}
static void sfinae_fun(void*) {} static void sfinae(...);
using result_type = decltype(sfinae_fun(static_cast<T*>(nullptr))); using result_type = decltype(sfinae(std::declval<T&>(), std::declval<T&>()));
public: public:
static constexpr bool value = std::is_same<bool, result_type>::value; static constexpr bool value = std::is_same<bool, result_type>::value;
...@@ -189,21 +224,15 @@ public: ...@@ -189,21 +224,15 @@ public:
template <class T> template <class T>
class has_char_insert { class has_char_insert {
template <class C> template <class C>
static bool sfinae_fun(C* cc, static bool sfinae(C* cc,
const char* first = nullptr, const char* first = nullptr,
const char* last = nullptr, const char* last = nullptr,
decltype(cc->insert(cc->end(), first, last))* = 0) { decltype(cc->insert(cc->end(), first, last))* = 0);
return true;
}
// SFNINAE default // SFNINAE default
static void sfinae_fun(const void*) { static void sfinae(const void*);
// nop
}
using decayed = typename std::decay<T>::type;
using result_type = decltype(sfinae_fun(static_cast<decayed*>(nullptr))); using result_type = decltype(sfinae(static_cast<decay_t<T>>(nullptr)));
public: public:
static constexpr bool value = is_primitive<T>::value == false && static constexpr bool value = is_primitive<T>::value == false &&
...@@ -216,30 +245,20 @@ template <class T> ...@@ -216,30 +245,20 @@ template <class T>
class is_iterable { class is_iterable {
// this horrible code would just disappear if we had concepts // this horrible code would just disappear if we had concepts
template <class C> template <class C>
static bool sfinae_fun( static bool sfinae(C* cc,
const C* cc, // check if 'C::begin()' returns a forward iterator
// check for 'C::begin()' returning a forward iterator enable_if_tt<is_forward_iterator<decltype(cc->begin())>>* = 0,
typename std::enable_if<is_forward_iterator<decltype(cc->begin())>::value>:: // check if begin() and end() both exist and are comparable
type* = 0, decltype(cc->begin() != cc->end())* = 0);
// check for 'C::end()' returning the same kind of forward iterator
typename std::enable_if<std::is_same<decltype(cc->begin()),
decltype(cc->end())>::value>::type
* = 0) {
return true;
}
// SFNINAE default // SFNINAE default
static void sfinae_fun(const void*) { static void sfinae(void*);
// nop
}
using decayed = typename std::decay<T>::type;
using result_type = decltype(sfinae_fun(static_cast<const decayed*>(nullptr))); using result_type = decltype(sfinae(static_cast<decay_t<T>*>(nullptr)));
public: public:
static constexpr bool value = is_primitive<T>::value == false && static constexpr bool value = is_primitive<T>::value == false
std::is_same<bool, result_type>::value; && std::is_same<bool, result_type>::value;
}; };
template <class T> template <class T>
...@@ -320,6 +339,7 @@ struct has_serialize<T, true> { ...@@ -320,6 +339,7 @@ struct has_serialize<T, true> {
static constexpr bool value = false; static constexpr bool value = false;
}; };
/// Any inspectable type is considered to be serializable.
template <class T> template <class T>
struct is_serializable; struct is_serializable;
...@@ -329,11 +349,13 @@ template <class T, ...@@ -329,11 +349,13 @@ template <class T,
|| std::is_function<T>::value> || std::is_function<T>::value>
struct is_serializable_impl; struct is_serializable_impl;
/// Checks whether `T` is builtin or provides a `serialize` /// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function. /// (free or member) function.
template <class T> template <class T>
struct is_serializable_impl<T, false, false> { struct is_serializable_impl<T, false, false> {
static constexpr bool value = has_serialize<T>::value static constexpr bool value = has_serialize<T>::value
|| is_inspectable<serializer, T>::value
|| is_builtin<T>::value; || is_builtin<T>::value;
}; };
...@@ -371,6 +393,7 @@ struct is_serializable_impl<T, IsIterable, true> { ...@@ -371,6 +393,7 @@ struct is_serializable_impl<T, IsIterable, true> {
template <class T> template <class T>
struct is_serializable { struct is_serializable {
static constexpr bool value = is_serializable_impl<T>::value static constexpr bool value = is_serializable_impl<T>::value
|| is_inspectable<serializer, T>::value
|| std::is_empty<T>::value || std::is_empty<T>::value
|| std::is_enum<T>::value; || std::is_enum<T>::value;
}; };
...@@ -413,11 +436,13 @@ private: ...@@ -413,11 +436,13 @@ private:
class = typename std::enable_if< class = typename std::enable_if<
! std::is_member_pointer<decltype(&U::static_type_name)>::value ! std::is_member_pointer<decltype(&U::static_type_name)>::value
>::type> >::type>
static std::true_type sfinae_fun(int); static std::true_type sfinae(int);
template <class> template <class>
static std::false_type sfinae_fun(...); static std::false_type sfinae(...);
public: public:
static constexpr bool value = decltype(sfinae_fun<T>(0))::value; static constexpr bool value = decltype(sfinae<T>(0))::value;
}; };
/// Checks whether `T::memory_cache_flag` exists. /// Checks whether `T::memory_cache_flag` exists.
...@@ -494,7 +519,7 @@ struct get_callable_trait_helper<false, false, C> { ...@@ -494,7 +519,7 @@ struct get_callable_trait_helper<false, false, C> {
template <class T> template <class T>
struct get_callable_trait { struct get_callable_trait {
// type without cv qualifiers // type without cv qualifiers
using bare_type = typename std::decay<T>::type; using bare_type = decay_t<T>;
// if T is a function pointer, this type identifies the function // if T is a function pointer, this type identifies the function
using signature_type = typename std::remove_pointer<bare_type>::type; using signature_type = typename std::remove_pointer<bare_type>::type;
using type = using type =
...@@ -527,9 +552,7 @@ struct is_callable { ...@@ -527,9 +552,7 @@ struct is_callable {
static void _fun(void*) { } static void _fun(void*) { }
using pointer = typename std::decay<T>::type*; using result_type = decltype(_fun(static_cast<decay_t<T>*>(nullptr)));
using result_type = decltype(_fun(static_cast<pointer>(nullptr)));
public: public:
static constexpr bool value = std::is_same<bool, result_type>::value; static constexpr bool value = std::is_same<bool, result_type>::value;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <cstdint> #include <cstdint>
#include <stdexcept> #include <stdexcept>
#include "caf/error.hpp"
namespace caf { namespace caf {
/// SI time units to specify timeouts. /// SI time units to specify timeouts.
...@@ -138,15 +140,15 @@ private: ...@@ -138,15 +140,15 @@ private:
} }
}; };
std::string to_string(const duration& x);
/// @relates duration /// @relates duration
template <class Processor> template <class Inspector>
void serialize(Processor& proc, duration& x, const unsigned int) { error inspect(Inspector& f, duration& x) {
proc & x.unit; return f(x.unit, x.count);
proc & x.count;
} }
std::string to_string(const duration& x);
/// @relates duration /// @relates duration
bool operator==(const duration& lhs, const duration& rhs); bool operator==(const duration& lhs, const duration& rhs);
......
...@@ -21,10 +21,16 @@ ...@@ -21,10 +21,16 @@
#define CAF_ERROR_HPP #define CAF_ERROR_HPP
#include <cstdint> #include <cstdint>
#include <utility>
#include <functional>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/message.hpp" #include "caf/none.hpp"
#include "caf/atom.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
...@@ -87,70 +93,132 @@ using enable_if_has_make_error_t ...@@ -87,70 +93,132 @@ using enable_if_has_make_error_t
/// rendering error messages via `actor_system::render(const error&)`. /// rendering error messages via `actor_system::render(const error&)`.
class error : detail::comparable<error> { class error : detail::comparable<error> {
public: public:
// -- member types -----------------------------------------------------------
using inspect_fun = std::function<error (meta::type_name_t,
uint8_t&, atom_value&,
meta::omittable_if_empty_t,
message&)>;
// -- constructors, destructors, and assignment operators --------------------
error() noexcept; error() noexcept;
error(error&&) noexcept = default; error(none_t) noexcept;
error(const error&) noexcept = default;
error& operator=(error&&) noexcept = default; error(error&&) noexcept;
error& operator=(const error&) noexcept = default; error& operator=(error&&) noexcept;
error(const error&);
error& operator=(const error&);
error(uint8_t code, atom_value category) noexcept; error(uint8_t code, atom_value category);
error(uint8_t code, atom_value category, message msg) noexcept; error(uint8_t code, atom_value category, message msg);
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
error(E error_value) : error(make_error(error_value)) { error(E error_value) : error(make_error(error_value)) {
// nop // nop
} }
/// Returns the category-specific error code, whereas `0` means "no error". template <class E, class = enable_if_has_make_error_t<E>>
inline uint8_t code() const noexcept { error& operator=(E error_value) {
return code_; auto tmp = make_error(error_value);
std::swap(data_, tmp.data_);
return *this;
} }
/// Returns the category of this error. ~error();
inline atom_value category() const noexcept {
return category_;
}
/// Returns optional context information to this error. // -- observers --------------------------------------------------------------
inline message& context() noexcept {
return context_;
}
/// Returns optional context information to this error. /// Returns the category-specific error code, whereas `0` means "no error".
inline const message& context() const noexcept { /// @pre `*this != none`
return context_; uint8_t code() const noexcept;
}
/// Returns `code() != 0`. /// Returns the category of this error.
/// @pre `*this != none`
atom_value category() const noexcept;
/// Returns context information to this error.
/// @pre `*this != none`
const message& context() const noexcept;
/// Returns `*this != none`.
inline explicit operator bool() const noexcept { inline explicit operator bool() const noexcept {
return code_ != 0; return data_ != nullptr;
} }
/// Returns `code() == 0`. /// Returns `*this == none`.
inline bool operator!() const noexcept { inline bool operator!() const noexcept {
return code_ == 0; return data_ == nullptr;
} }
int compare(const error&) const noexcept;
int compare(uint8_t code, atom_value category) const noexcept;
// -- modifiers --------------------------------------------------------------
/// Returns context information to this error.
/// @pre `*this != none`
message& context() noexcept;
/// Sets the error code to 0. /// Sets the error code to 0.
inline void clear() noexcept { void clear() noexcept;
code_ = 0;
context_.reset(); // -- static convenience functions -------------------------------------------
/// @cond PRIVATE
static inline error eval() {
return none;
} }
friend void serialize(serializer& sink, error& x, const unsigned int); template <class F, class... Fs>
static error eval(F&& f, Fs&&... fs) {
auto x = f();
return x ? x : eval(std::forward<Fs>(fs)...);
}
friend void serialize(deserializer& source, error& x, const unsigned int); /// @endcond
int compare(const error&) const noexcept; // -- friend functions -------------------------------------------------------
int compare(uint8_t code, atom_value category) const noexcept; template <class Inspector>
friend error inspect(Inspector& f, error& x) {
auto fun = [&](meta::type_name_t x0, uint8_t& x1, atom_value& x2,
meta::omittable_if_empty_t x3, message& x4) -> error{
return f(x0, x1, x2, x3, x4);
};
return x.apply(fun);
}
private: private:
uint8_t code_; // -- inspection support -----------------------------------------------------
atom_value category_;
message context_; error apply(inspect_fun f);
// -- nested classes ---------------------------------------------------------
struct data;
// -- member variables -------------------------------------------------------
data* data_;
}; };
/// @relates error
std::string to_string(const error& x);
/// @relates error
inline bool operator==(const error& x, none_t) {
return ! x;
}
/// @relates error
inline bool operator==(none_t, const error& x) {
return ! x;
}
/// @relates error /// @relates error
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
bool operator==(const error& x, E y) { bool operator==(const error& x, E y) {
...@@ -163,6 +231,16 @@ bool operator==(E x, const error& y) { ...@@ -163,6 +231,16 @@ bool operator==(E x, const error& y) {
return make_error(x) == y; return make_error(x) == y;
} }
/// @relates error
inline bool operator!=(const error& x, none_t) {
return static_cast<bool>(x);
}
/// @relates error
inline bool operator!=(none_t, const error& x) {
return static_cast<bool>(x);
}
/// @relates error /// @relates error
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
bool operator!=(const error& x, E y) { bool operator!=(const error& x, E y) {
...@@ -175,9 +253,6 @@ bool operator!=(E x, const error& y) { ...@@ -175,9 +253,6 @@ bool operator!=(E x, const error& y) {
return ! (x == y); return ! (x == y);
} }
/// @relates error
std::string to_string(const error& x);
} // namespace caf } // namespace caf
#endif // CAF_ERROR_HPP #endif // CAF_ERROR_HPP
...@@ -81,7 +81,6 @@ class continue_helper; ...@@ -81,7 +81,6 @@ class continue_helper;
class mailbox_element; class mailbox_element;
class message_handler; class message_handler;
class scheduled_actor; class scheduled_actor;
class sync_timeout_msg;
class response_promise; class response_promise;
class event_based_actor; class event_based_actor;
class type_erased_tuple; class type_erased_tuple;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/group_module.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/abstract_group.hpp" #include "caf/abstract_group.hpp"
...@@ -84,9 +85,23 @@ public: ...@@ -84,9 +85,23 @@ public:
return ptr_ ? 1 : 0; return ptr_ ? 1 : 0;
} }
friend void serialize(serializer& sink, const group& x, const unsigned int); template <class Inspector>
friend error inspect(Inspector& f, group& x) {
std::string x_id;
std::string x_mod;
auto ptr = x.get();
if (ptr) {
x_id = ptr->identifier();
x_mod = ptr->module().name();
}
return f(meta::type_name("group"),
meta::omittable_if_empty(), x_id,
meta::omittable_if_empty(), x_mod);
}
friend error inspect(serializer&, group&);
friend void serialize(deserializer& sink, group& x, const unsigned int); friend error inspect(deserializer&, group&);
inline abstract_group* get() const noexcept { inline abstract_group* get() const noexcept {
return ptr_.get(); return ptr_.get();
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <string> #include <string>
#include <functional> #include <functional>
#include "caf/deep_to_string.hpp" #include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
...@@ -49,13 +49,10 @@ inline bool operator==(const index_mapping& x, const index_mapping& y) { ...@@ -49,13 +49,10 @@ inline bool operator==(const index_mapping& x, const index_mapping& y) {
return x.value == y.value; return x.value == y.value;
} }
template <class Processor> template <class Inspector>
void serialize(Processor& proc, index_mapping& x, const unsigned int) { auto inspect(Inspector& f, index_mapping& x)
proc & x.value; -> decltype(f(meta::type_name("index_mapping"), x.value)) {
} return f(meta::type_name("index_mapping"), x.value);
inline std::string to_string(const index_mapping& x) {
return "idx" + deep_to_string_as_tuple(x.value);
} }
} // namespace caf } // namespace caf
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#include "caf/type_erased_tuple.hpp" #include "caf/type_erased_tuple.hpp"
#include "caf/actor_control_block.hpp" #include "caf/actor_control_block.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/disposer.hpp" #include "caf/detail/disposer.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_erased_tuple_view.hpp" #include "caf/detail/type_erased_tuple_view.hpp"
...@@ -87,6 +90,13 @@ protected: ...@@ -87,6 +90,13 @@ protected:
empty_type_erased_tuple dummy_; empty_type_erased_tuple dummy_;
}; };
/// @relates mailbox_element
template <class Inspector>
void inspect(Inspector& f, mailbox_element& x) {
return f(meta::type_name("mailbox_element"), x.sender, x.mid,
meta::omittable_if_empty(), x.stages, x.content());
}
/// Encapsulates arbitrary data in a message element. /// Encapsulates arbitrary data in a message element.
template <class... Ts> template <class... Ts>
class mailbox_element_vals class mailbox_element_vals
...@@ -171,8 +181,6 @@ make_mailbox_element(strong_actor_ptr sender, message_id id, ...@@ -171,8 +181,6 @@ make_mailbox_element(strong_actor_ptr sender, message_id id,
return mailbox_element_ptr{ptr}; return mailbox_element_ptr{ptr};
} }
std::string to_string(const mailbox_element&);
} // namespace caf } // namespace caf
#endif // CAF_MAILBOX_ELEMENT_HPP #endif // CAF_MAILBOX_ELEMENT_HPP
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
...@@ -75,10 +76,11 @@ make_message(T&& x, Ts&&... xs) { ...@@ -75,10 +76,11 @@ make_message(T&& x, Ts&&... xs) {
>::type... >::type...
>; >;
static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value, static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value,
"at least one type is not serializable via free " "at least one type is neither inspectable via "
"inspect(Inspector&, T&) nor serializable via "
"'serialize(Processor&, T&, const unsigned int)' or " "'serialize(Processor&, T&, const unsigned int)' or "
"`T::serialize(Processor&, const unsigned int)` " "`T::serialize(Processor&, const unsigned int)`; "
"member function; 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");
using storage = typename tl_apply<stored_types, tuple_vals>::type; using storage = typename tl_apply<stored_types, tuple_vals>::type;
......
...@@ -39,6 +39,15 @@ type_erased_value_ptr make_type_erased_value(Ts&&... xs) { ...@@ -39,6 +39,15 @@ type_erased_value_ptr make_type_erased_value(Ts&&... xs) {
return result; return result;
} }
/// @relates type_erased_value
/// Converts values to type-erased values.
struct type_erased_value_factory {
template <class T>
type_erased_value_ptr operator()(T&& x) const {
return make_type_erased_value<typename std::decay<T>::type>(std::forward<T>(x));
}
};
} // namespace caf } // namespace caf
#endif // CAF_MAKE_TYPE_ERASED_VALUE_HPP #endif // CAF_MAKE_TYPE_ERASED_VALUE_HPP
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/skip.hpp" #include "caf/skip.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/make_counted.hpp" #include "caf/make_counted.hpp"
...@@ -67,6 +68,7 @@ public: ...@@ -67,6 +68,7 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
message() noexcept = default; message() noexcept = default;
message(none_t) noexcept;
message(const message&) noexcept = default; message(const message&) noexcept = default;
message& operator=(const message&) noexcept = default; message& operator=(const message&) noexcept = default;
...@@ -322,10 +324,10 @@ private: ...@@ -322,10 +324,10 @@ private:
}; };
/// @relates message /// @relates message
void serialize(serializer& sink, const message& msg, const unsigned int); error inspect(serializer& sink, message& msg);
/// @relates message /// @relates message
void serialize(deserializer& sink, message& msg, const unsigned int); error inspect(deserializer& sink, message& msg);
/// @relates message /// @relates message
std::string to_string(const message& msg); std::string to_string(const message& msg);
......
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include "caf/config.hpp" #include "caf/error.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
namespace caf { namespace caf {
...@@ -132,9 +135,9 @@ public: ...@@ -132,9 +135,9 @@ public:
: (value_ < other.value_ ? -1 : 1); : (value_ < other.value_ ? -1 : 1);
} }
template <class T> template <class Inspector>
friend void serialize(T& in_or_out, message_id& mid, const unsigned int) { friend error inspect(Inspector& f, message_id& x) {
in_or_out & mid.value_; return f(meta::type_name("message_id"), x.value_);
} }
private: private:
...@@ -145,11 +148,6 @@ private: ...@@ -145,11 +148,6 @@ private:
uint64_t value_; uint64_t value_;
}; };
/// @relates message_id
inline std::string to_string(const message_id& x) {
return std::to_string(x.integer_value());
}
} // namespace caf } // namespace caf
namespace std { namespace std {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_ANNOTATION_HPP
#define CAF_META_ANNOTATION_HPP
#include <type_traits>
namespace caf {
namespace meta {
/// Type tag for all meta annotations in CAF.
struct annotation {
constexpr annotation() {
// nop
}
};
template <class T>
struct is_annotation {
static constexpr bool value = std::is_base_of<annotation, T>::value;
};
} // namespace meta
} // namespace caf
#endif // CAF_META_ANNOTATION_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_HEX_FORMATTED_HPP
#define CAF_META_HEX_FORMATTED_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct hex_formatted_t : annotation {
constexpr hex_formatted_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr hex_formatted_t hex_formatted() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_HEX_FORMATTED_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_LOAD_CALLBACK_HPP
#define CAF_META_LOAD_CALLBACK_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
template <class F>
struct load_callback_t : annotation {
load_callback_t(F&& f) : fun(f) {
// nop
}
load_callback_t(load_callback_t&&) = default;
F fun;
};
/// Returns an annotation that allows inspectors to call
/// user-defined code after performing load operations.
template <class F>
load_callback_t<F> load_callback(F fun) {
return {std::move(fun)};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_LOAD_CALLBACK_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_HPP
#define CAF_META_OMITTABLE_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_t : annotation {
constexpr omittable_t() {
// nop
}
};
/// Allows an inspector to omit the following data field
/// unconditionally when producing human-friendly output.
constexpr omittable_t omittable() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_IF_EMPTY_HPP
#define CAF_META_OMITTABLE_IF_EMPTY_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_if_empty_t : annotation {
constexpr omittable_if_empty_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr omittable_if_empty_t omittable_if_empty() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_IF_EMPTY_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_IF_NONE_HPP
#define CAF_META_OMITTABLE_IF_NONE_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_if_none_t : annotation {
constexpr omittable_if_none_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr omittable_if_none_t omittable_if_none() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_IF_NONE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_SAVE_CALLBACK_HPP
#define CAF_META_SAVE_CALLBACK_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
template <class F>
struct save_callback_t : annotation {
save_callback_t(F&& f) : fun(f) {
// nop
}
save_callback_t(save_callback_t&&) = default;
F fun;
};
/// Returns an annotation that allows inspectors to call
/// user-defined code after performing save operations.
template <class F>
save_callback_t<F> save_callback(F fun) {
return {std::move(fun)};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_SAVE_CALLBACK_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_META_TYPE_NAME_HPP
#define CAF_META_TYPE_NAME_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct type_name_t : annotation {
constexpr type_name_t(const char* cstr) : value(cstr) {
// nop
}
const char* value;
};
/// Returns a type name annotation.
type_name_t constexpr type_name(const char* cstr) {
return {cstr};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_TYPE_NAME_HPP
...@@ -26,26 +26,21 @@ ...@@ -26,26 +26,21 @@
#include <functional> #include <functional>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
#include "caf/make_counted.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
/// Objects of this type identify an invalid `node_id`.
/// @relates node_id
struct invalid_node_id_t {
constexpr invalid_node_id_t() {
// nop
}
};
/// Identifies an invalid `node_id`.
/// @relates node_id
constexpr invalid_node_id_t invalid_node_id = invalid_node_id_t{};
/// A node ID consists of a host ID and process ID. The host ID identifies /// A node ID consists of a host ID and process ID. The host ID identifies
/// the physical machine in the network, whereas the process ID identifies /// the physical machine in the network, whereas the process ID identifies
/// the running system-level process on that machine. /// the running system-level process on that machine.
...@@ -57,11 +52,11 @@ public: ...@@ -57,11 +52,11 @@ public:
node_id(const node_id&) = default; node_id(const node_id&) = default;
node_id(const invalid_node_id_t&); node_id(const none_t&);
node_id& operator=(const node_id&) = default; node_id& operator=(const node_id&) = default;
node_id& operator=(const invalid_node_id_t&); node_id& operator=(const none_t&);
/// A 160 bit hash (20 bytes). /// A 160 bit hash (20 bytes).
static constexpr size_t host_id_size = 20; static constexpr size_t host_id_size = 20;
...@@ -101,19 +96,6 @@ public: ...@@ -101,19 +96,6 @@ public:
// A reference counted container for host ID and process ID. // A reference counted container for host ID and process ID.
class data : public ref_counted { class data : public ref_counted {
public: public:
// for singleton API
void stop();
// for singleton API
inline void dispose() {
deref();
}
// for singleton API
inline void initialize() {
// nop
}
static intrusive_ptr<data> create_singleton(); static intrusive_ptr<data> create_singleton();
int compare(const node_id& other) const; int compare(const node_id& other) const;
...@@ -126,6 +108,10 @@ public: ...@@ -126,6 +108,10 @@ public:
data(uint32_t procid, const std::string& hash); data(uint32_t procid, const std::string& hash);
data& operator=(const data&) = default;
bool valid() const;
uint32_t pid_; uint32_t pid_;
host_id_type host_; host_id_type host_;
...@@ -135,15 +121,35 @@ public: ...@@ -135,15 +121,35 @@ public:
int compare(const node_id& other) const; int compare(const node_id& other) const;
// "inherited" from comparable<node_id, invalid_node_id_t> // "inherited" from comparable<node_id, invalid_node_id_t>
int compare(const invalid_node_id_t&) const; int compare(const none_t&) const;
explicit node_id(intrusive_ptr<data> dataptr); explicit node_id(intrusive_ptr<data> dataptr);
friend void serialize(serializer& sink, node_id& x, const unsigned int); template <class Inspector>
friend detail::enable_if_t<Inspector::is_saving::value, error>
friend void serialize(deserializer& source, node_id& x, const unsigned int); inspect(Inspector& f, node_id& x) {
data tmp;
data* ptr = x ? x.data_.get() : &tmp;
return f(meta::type_name("node_id"), ptr->pid_,
meta::hex_formatted(), ptr->host_);
}
void from_string(const std::string& str); template <class Inspector>
friend detail::enable_if_t<Inspector::is_loading::value, error>
inspect(Inspector& f, node_id& x) {
data tmp;
auto err = f(meta::type_name("node_id"), tmp.pid_,
meta::hex_formatted(), tmp.host_);
if (! err) {
if (! tmp.valid())
x.data_.reset();
else if (! x || ! x.data_->unique())
x.data_.reset(new data(tmp));
else
*x.data_ = tmp;
}
return err;
}
/// @endcond /// @endcond
...@@ -151,27 +157,38 @@ private: ...@@ -151,27 +157,38 @@ private:
intrusive_ptr<data> data_; intrusive_ptr<data> data_;
}; };
inline bool operator==(const node_id& lhs, const node_id& rhs) { /// @relates node_id
return lhs.compare(rhs) == 0; inline bool operator==(const node_id& x, none_t) {
return ! x;
} }
inline bool operator==(const node_id& lhs, invalid_node_id_t) { /// @relates node_id
return ! static_cast<bool>(lhs); inline bool operator==(none_t, const node_id& x) {
return ! x;
} }
inline bool operator!=(const node_id& lhs, const node_id& rhs) { /// @relates node_id
return ! (lhs == rhs); inline bool operator!=(const node_id& x, none_t) {
return static_cast<bool>(x);
} }
inline bool operator!=(const node_id& lhs, invalid_node_id_t) { /// @relates node_id
return static_cast<bool>(lhs); inline bool operator!=(none_t, const node_id& x) {
return static_cast<bool>(x);
}
inline bool operator==(const node_id& lhs, const node_id& rhs) {
return lhs.compare(rhs) == 0;
}
inline bool operator!=(const node_id& lhs, const node_id& rhs) {
return ! (lhs == rhs);
} }
inline bool operator<(const node_id& lhs, const node_id& rhs) { inline bool operator<(const node_id& lhs, const node_id& rhs) {
return lhs.compare(rhs) < 0; return lhs.compare(rhs) < 0;
} }
/// @relates node_id
std::string to_string(const node_id& x); std::string to_string(const node_id& x);
} // namespace caf } // namespace caf
...@@ -181,7 +198,7 @@ namespace std{ ...@@ -181,7 +198,7 @@ namespace std{
template<> template<>
struct hash<caf::node_id> { struct hash<caf::node_id> {
size_t operator()(const caf::node_id& nid) const { size_t operator()(const caf::node_id& nid) const {
if (nid == caf::invalid_node_id) if (nid == caf::none)
return 0; return 0;
// xor the first few bytes from the node ID and the process ID // xor the first few bytes from the node ID and the process ID
auto x = static_cast<size_t>(nid.process_id()); auto x = static_cast<size_t>(nid.process_id());
......
...@@ -42,12 +42,6 @@ struct none_t : detail::comparable<none_t> { ...@@ -42,12 +42,6 @@ struct none_t : detail::comparable<none_t> {
static constexpr none_t none = none_t{}; static constexpr none_t none = none_t{};
/// @relates none_t
template <class Processor>
void serialize(Processor&, const none_t&, const unsigned int) {
// nop
}
/// @relates none_t /// @relates none_t
template <class T> template <class T>
std::string to_string(const none_t&) { std::string to_string(const none_t&) {
......
...@@ -26,13 +26,12 @@ ...@@ -26,13 +26,12 @@
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/unit.hpp" #include "caf/unit.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
namespace caf { namespace caf {
/// A since C++17 compatible `optional` implementation. /// A C++17 compatible `optional` implementation.
template <class T> template <class T>
class optional { class optional {
public: public:
...@@ -255,35 +254,10 @@ class optional<void> { ...@@ -255,35 +254,10 @@ class optional<void> {
bool m_value; bool m_value;
}; };
/// @relates optional /// @relates optional
template <class T> template <class T>
std::string to_string(const optional<T>& x) { auto to_string(const optional<T>& x) -> decltype(to_string(*x)) {
return x ? "!" + deep_to_string(*x) : "<none>"; return x ? "*" + to_string(*x) : "<null>";
}
/// @relates optional
template <class Processor, class T>
typename std::enable_if<Processor::is_saving::value>::type
serialize(Processor& sink, optional<T>& x, const unsigned int) {
uint8_t flag = x ? 1 : 0;
sink & flag;
if (flag)
sink & *x;
}
/// @relates optional
template <class Processor, class T>
typename std::enable_if<Processor::is_loading::value>::type
serialize(Processor& source, optional<T>& x, const unsigned int) {
uint8_t flag;
source & flag;
if (flag) {
T value;
source & value;
x = std::move(value);
}
x = none;
} }
// -- [X.Y.8] comparison with optional ---------------------------------------- // -- [X.Y.8] comparison with optional ----------------------------------------
......
...@@ -79,6 +79,14 @@ enum class sec : uint8_t { ...@@ -79,6 +79,14 @@ enum class sec : uint8_t {
cannot_publish_invalid_actor, cannot_publish_invalid_actor,
/// A remote spawn failed because the provided types did not match. /// A remote spawn failed because the provided types did not match.
cannot_spawn_actor_from_arguments, cannot_spawn_actor_from_arguments,
/// Serialization failed because there was not enough data to read.
end_of_stream,
/// Serialization failed because no CAF context is available.
no_context,
/// Serialization failed because CAF misses run-time type information.
unknown_type,
/// Serialization of actors failed because no proxy registry is available.
no_proxy_registry,
/// A function view was called without assigning an actor first. /// A function view was called without assigning an actor first.
bad_function_call bad_function_call
}; };
......
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
#include <cstddef> // size_t #include <cstddef> // size_t
#include <type_traits> #include <type_traits>
#include "caf/config.hpp"
#ifndef CAF_NO_EXCEPTIONS
#include <exception>
#endif // CAF_NO_EXCEPTIONS
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/data_processor.hpp" #include "caf/data_processor.hpp"
...@@ -46,37 +52,36 @@ public: ...@@ -46,37 +52,36 @@ public:
virtual ~serializer(); virtual ~serializer();
}; };
#ifndef CAF_NO_EXCEPTIONS
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<serializer&>().apply(std::declval<T&>())) decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value, >::value
serializer&
>::type >::type
operator<<(serializer& sink, const T& x) { operator&(serializer& sink, const T& x) {
// implementations are required to not change an object while serializing // implementations are required to never modify `x` while saving
sink.apply(const_cast<T&>(x)); auto e = sink.apply(const_cast<T&>(x));
return sink; if (e)
throw std::runtime_error(to_string(e));
} }
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<serializer&>().apply(std::declval<T&>())) decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value, >::value,
serializer& serializer&
>::type >::type
operator<<(serializer& sink, T& x) { operator<<(serializer& sink, const T& x) {
sink.apply(x); sink & x;
return sink; return sink;
} }
template <class T> #endif // CAF_NO_EXCEPTIONS
auto operator&(serializer& sink, T& x) -> decltype(sink.apply(x)) {
sink.apply(x);
}
} // namespace caf } // namespace caf
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <type_traits> #include <type_traits>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
...@@ -63,15 +64,11 @@ public: ...@@ -63,15 +64,11 @@ public:
} }
error save_state(serializer& sink, const unsigned int version) override { error save_state(serializer& sink, const unsigned int version) override {
serialize_state(sink, state, version); return serialize_state(&sink, state, version);
// TODO: refactor after visit API is in place (#470)
return {};
} }
error load_state(deserializer& source, const unsigned int version) override { error load_state(deserializer& source, const unsigned int version) override {
serialize_state(source, state, version); return serialize_state(&source, state, version);
// TODO: refactor after visit API is in place (#470)
return {};
} }
/// A reference to the actor's state. /// A reference to the actor's state.
...@@ -87,16 +84,15 @@ public: ...@@ -87,16 +84,15 @@ public:
/// @endcond /// @endcond
private: private:
template <class Archive, class U> template <class Inspector, class T>
typename std::enable_if<detail::is_serializable<U>::value>::type auto serialize_state(Inspector* f, T& x, const unsigned int)
serialize_state(Archive& ar, U& st, const unsigned int) { -> decltype(inspect(*f, x)) {
ar & st; return inspect(*f, x);
} }
template <class Archive, class U> template <class T>
typename std::enable_if<! detail::is_serializable<U>::value>::type error serialize_state(void*, T&, const unsigned int) {
serialize_state(Archive&, U&, const unsigned int) { return sec::invalid_argument;
CAF_RAISE_ERROR("serialize_state with unserializable type called");
} }
template <class T> template <class T>
......
...@@ -20,19 +20,19 @@ ...@@ -20,19 +20,19 @@
#ifndef CAF_STREAM_DESERIALIZER_HPP #ifndef CAF_STREAM_DESERIALIZER_HPP
#define CAF_STREAM_DESERIALIZER_HPP #define CAF_STREAM_DESERIALIZER_HPP
#include <limits>
#include <string>
#include <sstream>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <exception>
#include <iomanip> #include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <type_traits> #include <type_traits>
#include "caf/deserializer.hpp" #include "caf/sec.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/ieee_754.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp" #include "caf/detail/network_order.hpp"
...@@ -45,6 +45,8 @@ class stream_deserializer : public deserializer { ...@@ -45,6 +45,8 @@ class stream_deserializer : public deserializer {
using streambuf_type = typename std::remove_reference<Streambuf>::type; using streambuf_type = typename std::remove_reference<Streambuf>::type;
using char_type = typename streambuf_type::char_type; using char_type = typename streambuf_type::char_type;
using streambuf_base = std::basic_streambuf<char_type>; using streambuf_base = std::basic_streambuf<char_type>;
using streamsize = std::streamsize;
static_assert(std::is_base_of<streambuf_base, streambuf_type>::value, static_assert(std::is_base_of<streambuf_base, streambuf_type>::value,
"Streambuf must inherit from std::streambuf"); "Streambuf must inherit from std::streambuf");
...@@ -75,152 +77,136 @@ public: ...@@ -75,152 +77,136 @@ public:
streambuf_(std::forward<S>(sb)) { streambuf_(std::forward<S>(sb)) {
} }
void begin_object(uint16_t& typenr, std::string& name) override { error begin_object(uint16_t& typenr, std::string& name) override {
apply_int(typenr); return error::eval([&] { return apply_int(typenr); },
if (typenr == 0) [&] { return typenr == 0 ? apply(name) : error{}; });
apply(name);
} }
void end_object() override { error end_object() override {
// nop return none;
} }
void begin_sequence(size_t& num_elements) override { error begin_sequence(size_t& num_elements) override {
varbyte_decode(num_elements); return varbyte_decode(num_elements);
} }
void end_sequence() override { error end_sequence() override {
// nop return none;
} }
void apply_raw(size_t num_bytes, void* data) override { error apply_raw(size_t num_bytes, void* data) override {
auto n = streambuf_.sgetn(reinterpret_cast<char_type*>(data), return range_check(streambuf_.sgetn(reinterpret_cast<char_type*>(data),
static_cast<std::streamsize>(num_bytes)); static_cast<streamsize>(num_bytes)),
CAF_ASSERT(n >= 0); num_bytes);
range_check(static_cast<size_t>(n), num_bytes);
} }
protected: protected:
// Decode an unsigned integral type as variable-byte-encoded byte sequence. // Decode an unsigned integral type as variable-byte-encoded byte sequence.
template <class T> template <class T>
size_t varbyte_decode(T& x) { error varbyte_decode(T& x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type"); static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
auto n = T{0}; T n = 0;
x = 0; x = 0;
uint8_t low7; uint8_t low7;
do { do {
auto c = streambuf_.sbumpc(); auto c = streambuf_.sbumpc();
using traits = typename streambuf_type::traits_type; using traits = typename streambuf_type::traits_type;
if (traits::eq_int_type(c, traits::eof())) if (traits::eq_int_type(c, traits::eof()))
CAF_RAISE_ERROR("stream_deserializer<T>::begin_sequence"); return sec::end_of_stream;
low7 = static_cast<uint8_t>(traits::to_char_type(c)); low7 = static_cast<uint8_t>(traits::to_char_type(c));
x |= static_cast<T>((low7 & 0x7F) << (7 * n)); x |= static_cast<T>((low7 & 0x7F) << (7 * n));
++n; ++n;
} while (low7 & 0x80); } while (low7 & 0x80);
return n; return none;
} }
void apply_builtin(builtin type, void* val) override { error apply_builtin(builtin type, void* val) override {
CAF_ASSERT(val != nullptr); CAF_ASSERT(val != nullptr);
switch (type) { switch (type) {
case i8_v: case i8_v:
case u8_v: case u8_v:
apply_raw(sizeof(uint8_t), val); return apply_raw(sizeof(uint8_t), val);
break;
case i16_v: case i16_v:
case u16_v: case u16_v:
apply_int(*reinterpret_cast<uint16_t*>(val)); return apply_int(*reinterpret_cast<uint16_t*>(val));
break;
case i32_v: case i32_v:
case u32_v: case u32_v:
apply_int(*reinterpret_cast<uint32_t*>(val)); return apply_int(*reinterpret_cast<uint32_t*>(val));
break;
case i64_v: case i64_v:
case u64_v: case u64_v:
apply_int(*reinterpret_cast<uint64_t*>(val)); return apply_int(*reinterpret_cast<uint64_t*>(val));
break;
case float_v: case float_v:
apply_float(*reinterpret_cast<float*>(val)); return apply_float(*reinterpret_cast<float*>(val));
break;
case double_v: case double_v:
apply_float(*reinterpret_cast<double*>(val)); return apply_float(*reinterpret_cast<double*>(val));
break;
case ldouble_v: { case ldouble_v: {
// the IEEE-754 conversion does not work for long double // the IEEE-754 conversion does not work for long double
// => fall back to string serialization (even though it sucks) // => fall back to string serialization (even though it sucks)
std::string tmp; std::string tmp;
apply(tmp); auto e = apply(tmp);
if (e)
return e;
std::istringstream iss{std::move(tmp)}; std::istringstream iss{std::move(tmp)};
iss >> *reinterpret_cast<long double*>(val); iss >> *reinterpret_cast<long double*>(val);
break; return none;
} }
case string8_v: { case string8_v: {
auto& str = *reinterpret_cast<std::string*>(val); auto& str = *reinterpret_cast<std::string*>(val);
size_t str_size; size_t str_size;
begin_sequence(str_size); return error::eval([&] { return begin_sequence(str_size); },
str.resize(str_size); [&] { str.resize(str_size);
// TODO: When using C++14, switch to str.data(), which then has a auto p = &str[0];
// non-const overload. auto data = reinterpret_cast<char_type*>(p);
auto data = reinterpret_cast<char_type*>(&str[0]); auto s = static_cast<streamsize>(str_size);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(str_size)); return range_check(streambuf_.sgetn(data, s),
CAF_ASSERT(n >= 0); str_size); },
range_check(static_cast<size_t>(n), str_size); [&] { return end_sequence(); });
end_sequence();
break;
} }
case string16_v: { case string16_v: {
auto& str = *reinterpret_cast<std::u16string*>(val); auto& str = *reinterpret_cast<std::u16string*>(val);
size_t str_size; str.clear();
begin_sequence(str_size); size_t ns;
auto bytes = str_size * sizeof(std::u16string::value_type); return error::eval([&] { return begin_sequence(ns); },
str.resize(str_size); [&] { return fill_range_c<uint16_t>(str, ns); },
// TODO: When using C++14, switch to str.data(), which then has a [&] { return end_sequence(); });
// non-const overload.
auto data = reinterpret_cast<char_type*>(&str[0]);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(bytes));
CAF_ASSERT(n >= 0);
range_check(static_cast<size_t>(n), str_size);
end_sequence();
break;
} }
case string32_v: { case string32_v: {
auto& str = *reinterpret_cast<std::u32string*>(val); auto& str = *reinterpret_cast<std::u32string*>(val);
size_t str_size; str.clear();
begin_sequence(str_size); size_t ns;
auto bytes = str_size * sizeof(std::u32string::value_type); return error::eval([&] { return begin_sequence(ns); },
str.resize(str_size); [&] { return fill_range_c<uint32_t>(str, ns); },
// TODO: When using C++14, switch to str.data(), which then has a [&] { return end_sequence(); });
// non-const overload.
auto data = reinterpret_cast<char_type*>(&str[0]);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(bytes));
CAF_ASSERT(n >= 0);
range_check(static_cast<size_t>(n), str_size);
end_sequence();
break;
} }
} }
} }
private: private:
void range_check(size_t got, size_t need) { error range_check(size_t got, size_t need) {
if (got != need) { if (got == need)
return none;
CAF_LOG_ERROR("range_check failed"); CAF_LOG_ERROR("range_check failed");
CAF_RAISE_ERROR("stream_deserializer<T>::range_check()"); return sec::end_of_stream;
}
} }
template <class T> template <class T>
void apply_int(T& x) { error apply_int(T& x) {
T tmp; T tmp;
apply_raw(sizeof(T), &tmp); auto e = apply_raw(sizeof(T), &tmp);
if (e)
return e;
x = detail::from_network_order(tmp); x = detail::from_network_order(tmp);
return none;
} }
template <class T> template <class T>
void apply_float(T& x) { error apply_float(T& x) {
typename detail::ieee_754_trait<T>::packed_type tmp; typename detail::ieee_754_trait<T>::packed_type tmp;
apply_int(tmp); auto e = apply_int(tmp);
if (e)
return e;
x = detail::unpack754(tmp); x = detail::unpack754(tmp);
return none;
} }
Streambuf streambuf_; Streambuf streambuf_;
......
...@@ -20,17 +20,18 @@ ...@@ -20,17 +20,18 @@
#ifndef CAF_STREAM_SERIALIZER_HPP #ifndef CAF_STREAM_SERIALIZER_HPP
#define CAF_STREAM_SERIALIZER_HPP #define CAF_STREAM_SERIALIZER_HPP
#include <string>
#include <limits>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <iomanip> #include <iomanip>
#include <limits>
#include <streambuf>
#include <sstream> #include <sstream>
#include <string> #include <streambuf>
#include <type_traits> #include <type_traits>
#include "caf/serializer.hpp" #include "caf/sec.hpp"
#include "caf/streambuf.hpp" #include "caf/streambuf.hpp"
#include "caf/serializer.hpp"
#include "caf/detail/ieee_754.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp" #include "caf/detail/network_order.hpp"
...@@ -73,33 +74,36 @@ public: ...@@ -73,33 +74,36 @@ public:
streambuf_(std::forward<S>(sb)) { streambuf_(std::forward<S>(sb)) {
} }
void begin_object(uint16_t& typenr, std::string& name) override { error begin_object(uint16_t& typenr, std::string& name) override {
apply(typenr); return error::eval([&] { return apply(typenr); },
if (typenr == 0) [&] { return typenr == 0 ? apply(name) : error{}; });
apply(name);
} }
void end_object() override { error end_object() override {
// nop return none;
} }
void begin_sequence(size_t& list_size) override { error begin_sequence(size_t& list_size) override {
varbyte_encode(list_size); return varbyte_encode(list_size);
} }
void end_sequence() override { error end_sequence() override {
// nop return none;
} }
void apply_raw(size_t num_bytes, void* data) override { error apply_raw(size_t num_bytes, void* data) override {
streambuf_.sputn(reinterpret_cast<char_type*>(data), auto ssize = static_cast<std::streamsize>(num_bytes);
static_cast<std::streamsize>(num_bytes)); auto n = streambuf_.sputn(reinterpret_cast<char_type*>(data), ssize);
if (n != ssize)
return sec::end_of_stream;
return none;
} }
protected: protected:
// Encode an unsigned integral type as variable-byte sequence. // Encode an unsigned integral type as variable-byte sequence.
template <class T> template <class T>
size_t varbyte_encode(T x) { error varbyte_encode(T x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type"); static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
// For 64-bit values, the encoded representation cannot get larger than 10 // For 64-bit values, the encoded representation cannot get larger than 10
// bytes. A scratch space of 16 bytes suffices as upper bound. // bytes. A scratch space of 16 bytes suffices as upper bound.
...@@ -112,35 +116,30 @@ protected: ...@@ -112,35 +116,30 @@ protected:
*i++ = static_cast<uint8_t>(x) & 0x7f; *i++ = static_cast<uint8_t>(x) & 0x7f;
auto res = streambuf_.sputn(reinterpret_cast<char_type*>(buf), auto res = streambuf_.sputn(reinterpret_cast<char_type*>(buf),
static_cast<std::streamsize>(i - buf)); static_cast<std::streamsize>(i - buf));
CAF_ASSERT(res >= 0); if (res != (i - buf))
return static_cast<size_t>(res); return sec::end_of_stream;
return none;
} }
void apply_builtin(builtin type, void* val) override { error apply_builtin(builtin type, void* val) override {
CAF_ASSERT(val != nullptr); CAF_ASSERT(val != nullptr);
switch (type) { switch (type) {
case i8_v: case i8_v:
case u8_v: case u8_v:
apply_raw(sizeof(uint8_t), val); return apply_raw(sizeof(uint8_t), val);
break;
case i16_v: case i16_v:
case u16_v: case u16_v:
apply_int(*reinterpret_cast<uint16_t*>(val)); return apply_int(*reinterpret_cast<uint16_t*>(val));
break;
case i32_v: case i32_v:
case u32_v: case u32_v:
apply_int(*reinterpret_cast<uint32_t*>(val)); return apply_int(*reinterpret_cast<uint32_t*>(val));
break;
case i64_v: case i64_v:
case u64_v: case u64_v:
apply_int(*reinterpret_cast<uint64_t*>(val)); return apply_int(*reinterpret_cast<uint64_t*>(val));
break;
case float_v: case float_v:
apply_int(detail::pack754(*reinterpret_cast<float*>(val))); return apply_int(detail::pack754(*reinterpret_cast<float*>(val)));
break;
case double_v: case double_v:
apply_int(detail::pack754(*reinterpret_cast<double*>(val))); return apply_int(detail::pack754(*reinterpret_cast<double*>(val)));
break;
case ldouble_v: { case ldouble_v: {
// the IEEE-754 conversion does not work for long double // the IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks) // => fall back to string serialization (event though it sucks)
...@@ -148,50 +147,41 @@ protected: ...@@ -148,50 +147,41 @@ protected:
oss << std::setprecision(std::numeric_limits<long double>::digits) oss << std::setprecision(std::numeric_limits<long double>::digits)
<< *reinterpret_cast<long double*>(val); << *reinterpret_cast<long double*>(val);
auto tmp = oss.str(); auto tmp = oss.str();
apply(tmp); return apply(tmp);
break;
} }
case string8_v: { case string8_v: {
auto str = reinterpret_cast<std::string*>(val); auto str = reinterpret_cast<std::string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s); auto data = reinterpret_cast<char_type*>(
auto data = const_cast<std::string::value_type*>(str->data()); const_cast<std::string::value_type*>(str->data()));
apply_raw(str->size(), reinterpret_cast<char_type*>(data)); return error::eval([&] { return begin_sequence(s); },
end_sequence(); [&] { return apply_raw(str->size(), data); },
break; [&] { return end_sequence(); });
} }
case string16_v: { case string16_v: {
auto str = reinterpret_cast<std::u16string*>(val); auto str = reinterpret_cast<std::u16string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s);
for (auto c : *str) {
// the standard does not guarantee that char16_t is exactly 16 bits... // the standard does not guarantee that char16_t is exactly 16 bits...
auto tmp = static_cast<uint16_t>(c); return error::eval([&] { return begin_sequence(s); },
apply_raw(sizeof(tmp), &tmp); [&] { return consume_range_c<uint16_t>(*str); },
} [&] { return end_sequence(); });
end_sequence();
break;
} }
case string32_v: { case string32_v: {
auto str = reinterpret_cast<std::u32string*>(val); auto str = reinterpret_cast<std::u32string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s);
for (auto c : *str) {
// the standard does not guarantee that char32_t is exactly 32 bits... // the standard does not guarantee that char32_t is exactly 32 bits...
auto tmp = static_cast<uint32_t>(c); return error::eval([&] { return begin_sequence(s); },
apply_raw(sizeof(tmp), &tmp); [&] { return consume_range_c<uint32_t>(*str); },
} [&] { return end_sequence(); });
end_sequence();
break;
} }
} }
} }
private: private:
template <class T> template <class T>
void apply_int(T x) { error apply_int(T x) {
auto y = detail::to_network_order(x); auto y = detail::to_network_order(x);
apply_raw(sizeof(T), &y); return apply_raw(sizeof(T), &y);
} }
Streambuf streambuf_; Streambuf streambuf_;
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/detail/tbind.hpp" #include "caf/detail/tbind.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
...@@ -67,14 +69,10 @@ private: ...@@ -67,14 +69,10 @@ private:
exit_msg() = default; exit_msg() = default;
}; };
inline std::string to_string(const exit_msg& x) { /// @relates exit_msg
return "exit_msg" + deep_to_string(std::tie(x.source, x.reason)); template <class Inspector>
} error inspect(Inspector& f, exit_msg& x) {
return f(meta::type_name("exit_msg"), x.source, x.reason);
template <class Processor>
void serialize(Processor& proc, exit_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
} }
/// Sent to all actors monitoring an actor when it is terminated. /// Sent to all actors monitoring an actor when it is terminated.
...@@ -108,14 +106,10 @@ private: ...@@ -108,14 +106,10 @@ private:
down_msg() = default; down_msg() = default;
}; };
inline std::string to_string(const down_msg& x) { /// @relates down_msg
return "down_msg" + deep_to_string(std::tie(x.source, x.reason)); template <class Inspector>
} error inspect(Inspector& f, down_msg& x) {
return f(meta::type_name("down_msg"), x.source, x.reason);
template <class Processor>
void serialize(Processor& proc, down_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
} }
/// Sent to all members of a group when it goes offline. /// Sent to all members of a group when it goes offline.
...@@ -124,29 +118,10 @@ struct group_down_msg { ...@@ -124,29 +118,10 @@ struct group_down_msg {
group source; group source;
}; };
inline std::string to_string(const group_down_msg& x) {
return "group_down" + deep_to_string(std::tie(x.source));
}
/// @relates group_down_msg
template <class Processor>
void serialize(Processor& proc, group_down_msg& x, const unsigned int) {
proc & x.source;
}
/// Sent whenever a timeout occurs during a synchronous send.
/// This system message does not have any fields, because the message ID
/// sent alongside this message identifies the matching request that timed out.
class sync_timeout_msg { };
inline std::string to_string(const sync_timeout_msg&) {
return "sync_timeout";
}
/// @relates group_down_msg /// @relates group_down_msg
template <class Processor> template <class Inspector>
void serialize(Processor&, sync_timeout_msg&, const unsigned int) { error inspect(Inspector& f, group_down_msg& x) {
// nop return f(meta::type_name("group_down_msg"), x.source);
} }
/// Signalizes a timeout event. /// Signalizes a timeout event.
...@@ -156,14 +131,10 @@ struct timeout_msg { ...@@ -156,14 +131,10 @@ struct timeout_msg {
uint32_t timeout_id; uint32_t timeout_id;
}; };
inline std::string to_string(const timeout_msg& x) {
return "timeout" + deep_to_string(std::tie(x.timeout_id));
}
/// @relates timeout_msg /// @relates timeout_msg
template <class Processor> template <class Inspector>
void serialize(Processor& proc, timeout_msg& x, const unsigned int) { error inspect(Inspector& f, timeout_msg& x) {
proc & x.timeout_id; return f(meta::type_name("timeout_msg"), x.timeout_id);
} }
} // namespace caf } // namespace caf
......
...@@ -17,52 +17,33 @@ ...@@ -17,52 +17,33 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/deep_to_string.hpp" #ifndef CAF_TO_STRING_HPP
#define CAF_TO_STRING_HPP
namespace caf {
namespace { #include <string>
bool is_escaped(const char* cstr) { #include "caf/error.hpp"
if (*cstr != '"') #include "caf/deep_to_string.hpp"
return false;
char last_char = *cstr++;
for (auto c = *cstr; c != '\0'; ++cstr)
if (c == '"' && last_char != '\\')
return false;
return last_char == '"';
}
} // namespace <anonymous> #include "caf/detail/type_traits.hpp"
#include "caf/detail/stringification_inspector.hpp"
std::string deep_to_string_t::operator()(const char* cstr) const { namespace caf {
if (! cstr || *cstr == '\0')
return "\"\"";
if (is_escaped(cstr))
return cstr;
std::string result = "\"";
char c;
while ((c = *cstr++) != '\0') {
switch (c) {
case '\\':
result += "\\\\";
break;
case '"':
result += "\\\"";
break;
default:
result += c;
}
}
result += "\"";
return result;
}
std::string deep_to_string_t::operator()(const atom_value& x) const { template <class T,
std::string result = "'"; class E = typename std::enable_if<
result += to_string(x); detail::is_inspectable<
result += "'"; detail::stringification_inspector,
return result; T
>::value
>::type>
std::string to_string(const T& x) {
std::string res;
detail::stringification_inspector f{res};
inspect(f, const_cast<T&>(x));
return res;
} }
} // namespace caf } // namespace caf
#endif // CAF_TO_STRING_HPP
...@@ -130,6 +130,15 @@ public: ...@@ -130,6 +130,15 @@ public:
return *reinterpret_cast<T*>(get_mutable(pos)); return *reinterpret_cast<T*>(get_mutable(pos));
} }
/// Convenience function for moving a value out of the tuple if it is
/// unshared. Returns a copy otherwise.
template <class T>
T move_if_unshared(size_t pos) {
if (shared())
return get_as<T>(pos);
return std::move(get_mutable_as<T>(pos));
}
/// Returns `true` if the element at `pos` matches `T`. /// Returns `true` if the element at `pos` matches `T`.
template <class T> template <class T>
bool match_element(size_t pos) const noexcept { bool match_element(size_t pos) const noexcept {
......
...@@ -97,17 +97,17 @@ public: ...@@ -97,17 +97,17 @@ public:
}; };
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
template <class Processor> template <class Inspector>
typename std::enable_if<Processor::is_saving::value>::type typename std::enable_if<Inspector::is_saving::value, error>::type
serialize(Processor& proc, type_erased_value& x) { inspect(Inspector& f, type_erased_value& x) {
x.save(proc); return x.save(f);
} }
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
template <class Processor> template <class Inspector>
typename std::enable_if<Processor::is_loading::value>::type typename std::enable_if<Inspector::is_loading::value, error>::type
serialize(Processor& proc, type_erased_value& x) { inspect(Inspector& f, type_erased_value& x) {
x.load(proc); return x.load(f);
} }
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
......
...@@ -62,7 +62,6 @@ using sorted_builtin_types = ...@@ -62,7 +62,6 @@ using sorted_builtin_types =
strong_actor_ptr, // @strong_actor_ptr strong_actor_ptr, // @strong_actor_ptr
std::set<std::string>, // @strset std::set<std::string>, // @strset
std::vector<std::string>, // @strvec std::vector<std::string>, // @strvec
sync_timeout_msg, // @sync_timeout_msg
timeout_msg, // @timeout timeout_msg, // @timeout
uint16_t, // @u16 uint16_t, // @u16
std::u16string, // @u16_str std::u16string, // @u16_str
......
...@@ -241,13 +241,9 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>, ...@@ -241,13 +241,9 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
// nop // nop
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, typed_actor& x, const unsigned int v) { friend error inspect(Inspector& f, typed_actor& x) {
serialize(proc, x.ptr_, v); return f(x.ptr_);
}
friend inline std::string to_string(const typed_actor& x) {
return to_string(x.ptr_);
} }
/// Releases the reference held by handle `x`. Using the /// Releases the reference held by handle `x`. Using the
......
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include "caf/actor_control_block.hpp" #include "caf/actor_control_block.hpp"
#include "caf/to_string.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/proxy_registry.hpp" #include "caf/proxy_registry.hpp"
...@@ -76,86 +81,34 @@ bool operator==(const abstract_actor* x, const strong_actor_ptr& y) { ...@@ -76,86 +81,34 @@ bool operator==(const abstract_actor* x, const strong_actor_ptr& y) {
return actor_control_block::from(x) == y.get(); return actor_control_block::from(x) == y.get();
} }
template <class T> error load_actor(strong_actor_ptr& storage, execution_unit* ctx,
void safe_actor(serializer& sink, T& storage) { actor_id aid, const node_id& nid) {
CAF_LOG_TRACE(CAF_ARG(storage)); if (! ctx)
if (! sink.context()) { return sec::no_context;
CAF_LOG_ERROR("Cannot serialize actors without context."); auto& sys = ctx->system();
CAF_RAISE_ERROR("Cannot serialize actors without context.");
}
auto& sys = sink.context()->system();
auto ptr = storage.get();
actor_id aid = invalid_actor_id;
node_id nid = invalid_node_id;
if (ptr) {
aid = ptr->aid;
nid = ptr->nid;
// register locally running actors to be able to deserialize them later
if (nid == sys.node())
sys.registry().put(aid, actor_cast<strong_actor_ptr>(storage));
}
sink << aid << nid;
}
template <class T>
void load_actor(deserializer& source, T& storage) {
CAF_LOG_TRACE("");
storage.reset();
if (! source.context())
CAF_RAISE_ERROR("Cannot deserialize actor_addr without context.");
auto& sys = source.context()->system();
actor_id aid;
node_id nid;
source >> aid >> nid;
CAF_LOG_DEBUG(CAF_ARG(aid) << CAF_ARG(nid));
// deal with local actors
if (sys.node() == nid) { if (sys.node() == nid) {
storage = actor_cast<T>(sys.registry().get(aid)); storage = sys.registry().get(aid);
CAF_LOG_DEBUG("fetch actor handle from local actor registry: " CAF_LOG_DEBUG("fetch actor handle from local actor registry: "
<< (storage ? "found" : "not found")); << (storage ? "found" : "not found"));
return; return none;
} }
auto prp = source.context()->proxy_registry_ptr(); auto prp = ctx->proxy_registry_ptr();
if (! prp) if (! prp)
CAF_RAISE_ERROR("Cannot deserialize remote actors without proxy registry."); return sec::no_proxy_registry;
// deal with (proxies for) remote actors // deal with (proxies for) remote actors
storage = actor_cast<T>(prp->get_or_put(nid, aid)); storage = prp->get_or_put(nid, aid);
} return none;
void serialize(serializer& sink, strong_actor_ptr& x, const unsigned int) {
CAF_LOG_TRACE("");
safe_actor(sink, x);
} }
void serialize(deserializer& source, strong_actor_ptr& x, const unsigned int) { error save_actor(strong_actor_ptr& storage, execution_unit* ctx,
CAF_LOG_TRACE(""); actor_id aid, const node_id& nid) {
load_actor(source, x); if (! ctx)
} return sec::no_context;
auto& sys = ctx->system();
void serialize(serializer& sink, weak_actor_ptr& x, const unsigned int) { // register locally running actors to be able to deserialize them later
CAF_LOG_TRACE(""); if (nid == sys.node())
safe_actor(sink, x); sys.registry().put(aid, storage);
} return none;
void serialize(deserializer& source, weak_actor_ptr& x, const unsigned int) {
CAF_LOG_TRACE("");
load_actor(source, x);
}
std::string to_string(const actor_control_block* x) {
if (! x)
return "<invalid-actor>";
std::string result = std::to_string(x->id());
result += "@";
result += to_string(x->node());
return result;
}
std::string to_string(const strong_actor_ptr& x) {
return to_string(x.get());
}
std::string to_string(const weak_actor_ptr& x) {
return to_string(x.get());
} }
} // namespace caf } // namespace caf
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <unordered_set> #include <unordered_set>
#include "caf/send.hpp" #include "caf/send.hpp"
#include "caf/to_string.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
......
...@@ -318,14 +318,16 @@ actor_system_config& actor_system_config::set(const char* cn, config_value cv) { ...@@ -318,14 +318,16 @@ actor_system_config& actor_system_config::set(const char* cn, config_value cv) {
std::string actor_system_config::render_sec(uint8_t x, atom_value, std::string actor_system_config::render_sec(uint8_t x, atom_value,
const message& xs) { const message& xs) {
return "system_error" auto tmp = static_cast<sec>(x);
+ (xs.empty() ? deep_to_string_as_tuple(static_cast<sec>(x)) return deep_to_string(meta::type_name("system_error"), tmp,
: deep_to_string_as_tuple(static_cast<sec>(x), xs)); meta::omittable_if_empty(), xs);
} }
std::string actor_system_config::render_exit_reason(uint8_t x, atom_value, std::string actor_system_config::render_exit_reason(uint8_t x, atom_value,
const message&) { const message& xs) {
return "exit_reason" + deep_to_string_as_tuple(static_cast<exit_reason>(x)); auto tmp = static_cast<sec>(x);
return deep_to_string(meta::type_name("exit_reason"), tmp,
meta::omittable_if_empty(), xs);
} }
} // namespace caf } // namespace caf
...@@ -66,9 +66,7 @@ void* concatenated_tuple::get_mutable(size_t pos) { ...@@ -66,9 +66,7 @@ void* concatenated_tuple::get_mutable(size_t pos) {
error concatenated_tuple::load(size_t pos, deserializer& source) { error concatenated_tuple::load(size_t pos, deserializer& source) {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
auto selected = select(pos); auto selected = select(pos);
selected.first->load(selected.second, source); return selected.first->load(selected.second, source);
// TODO: refactor after visit API is in place (#470)
return {};
} }
size_t concatenated_tuple::size() const noexcept { size_t concatenated_tuple::size() const noexcept {
...@@ -106,9 +104,7 @@ type_erased_value_ptr concatenated_tuple::copy(size_t pos) const { ...@@ -106,9 +104,7 @@ type_erased_value_ptr concatenated_tuple::copy(size_t pos) const {
error concatenated_tuple::save(size_t pos, serializer& sink) const { error concatenated_tuple::save(size_t pos, serializer& sink) const {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
auto selected = select(pos); auto selected = select(pos);
selected.first->save(selected.second, sink); return selected.first->save(selected.second, sink);
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) const { std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) const {
......
...@@ -20,62 +20,154 @@ ...@@ -20,62 +20,154 @@
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp"
namespace caf { namespace caf {
error::error() noexcept : code_(0), category_(atom("")) { // -- nested classes -----------------------------------------------------------
struct error::data {
uint8_t code;
atom_value category;
message context;
};
// -- constructors, destructors, and assignment operators ----------------------
error::error() noexcept : data_(nullptr) {
// nop
}
error::error(none_t) noexcept : data_(nullptr) {
// nop
}
error::error(error&& x) noexcept : data_(x.data_) {
if (data_)
x.data_ = nullptr;
}
error& error::operator=(error&& x) noexcept {
std::swap(data_, x.data_);
return *this;
}
error::error(const error& x) : data_(x ? new data(*x.data_) : nullptr) {
// nop // nop
} }
error::error(uint8_t x, atom_value y) noexcept : code_(x), category_(y) { error& error::operator=(const error& x) {
if (x) {
if (! data_)
data_ = new data(*x.data_);
else
*data_ = *x.data_;
} else {
clear();
}
return *this;
}
error::error(uint8_t x, atom_value y)
: data_(x != 0 ? new data{x, y, none} : nullptr) {
// nop // nop
} }
error::error(uint8_t x, atom_value y, message z) noexcept error::error(uint8_t x, atom_value y, message z)
: code_(x), : data_(x != 0 ? new data{x, y, std::move(z)} : nullptr) {
category_(y),
context_(std::move(z)) {
// nop // nop
} }
void serialize(serializer& sink, error& x, const unsigned int) { error::~error() {
sink << x.code_ << x.category_ << x.context_; delete data_;
} }
void serialize(deserializer& source, error& x, const unsigned int) { // -- observers ----------------------------------------------------------------
source >> x.code_ >> x.category_ >> x.context_;
uint8_t error::code() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->code;
}
atom_value error::category() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->category;
}
const message& error::context() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->context;
} }
int error::compare(const error& x) const noexcept { int error::compare(const error& x) const noexcept {
return compare(x.code(), x.category()); uint8_t x_code;
atom_value x_category;
if (x) {
x_code = x.data_->code;
x_category = x.data_->category;
} else {
x_code = 0;
x_category = atom("");
}
return compare(x_code, x_category);
} }
int error::compare(uint8_t x, atom_value y) const noexcept { int error::compare(uint8_t x, atom_value y) const noexcept {
// exception: all errors with default value are considered no error -> equal uint8_t mx;
if (code_ == 0 && x == 0) atom_value my;
if (data_) {
mx = data_->code;
my = data_->category;
} else {
mx = 0;
my = atom("");
}
// all errors with default value are considered no error -> equal
if (mx == x && x == 0)
return 0; return 0;
if (category_ < y) if (my < y)
return -1; return -1;
if (category_ > y) if (my > y)
return 1; return 1;
return static_cast<int>(code_) - x; return static_cast<int>(mx) - x;
} }
std::string to_string(const error& x) { // -- modifiers --------------------------------------------------------------
if (! x)
return "no-error"; message& error::context() noexcept {
std::string result = "error("; CAF_ASSERT(data_ != nullptr);
result += to_string(x.category()); return data_->context;
result += ", "; }
result += std::to_string(static_cast<int>(x.code()));
if (! x.context().empty()) { void error::clear() noexcept {
result += ", "; if (data_) {
result += to_string(x.context()); delete data_;
data_ = nullptr;
} }
result += ")"; }
// -- inspection support -----------------------------------------------------
error error::apply(inspect_fun f) {
data tmp{0, atom(""), message{}};
data& ref = data_ ? *data_ : tmp;
auto result = f(meta::type_name("error"), ref.code, ref.category,
meta::omittable_if_empty(), ref.context);
if (ref.code == 0)
clear();
else if (&tmp == &ref)
data_ = new data(std::move(tmp));
return result; return result;
} }
std::string to_string(const error& x) {
if (! x)
return "none";
return deep_to_string(meta::type_name("error"), x.code(), x.category(),
meta::omittable_if_empty(), x.context());
}
} // namespace caf } // namespace caf
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "caf/exit_reason.hpp" #include "caf/exit_reason.hpp"
#include "caf/message.hpp"
namespace caf { namespace caf {
namespace { namespace {
......
...@@ -55,36 +55,34 @@ intptr_t group::compare(const group& other) const noexcept { ...@@ -55,36 +55,34 @@ intptr_t group::compare(const group& other) const noexcept {
return compare(ptr_.get(), other.ptr_.get()); return compare(ptr_.get(), other.ptr_.get());
} }
void serialize(serializer& sink, const group& x, const unsigned int) { error inspect(serializer& f, group& x) {
std::string mod_name;
auto ptr = x.get(); auto ptr = x.get();
if (! ptr) { if (! ptr)
std::string dummy; return f(mod_name);
sink << dummy; mod_name = ptr->module().name();
} else { auto e = f(mod_name);
sink << ptr->module().name(); return e ? e : ptr->save(f);
ptr->save(sink);
}
} }
void serialize(deserializer& source, group& x, const unsigned int) { error inspect(deserializer& f, group& x) {
std::string module_name; std::string module_name;
source >> module_name; f(module_name);
if (module_name.empty()) { if (module_name.empty()) {
x = invalid_group; x = invalid_group;
return; return none;
} }
if (! source.context()) if (! f.context())
CAF_RAISE_ERROR("Cannot serialize group without context."); return sec::no_context;
auto& sys = source.context()->system(); auto& sys = f.context()->system();
auto mod = sys.groups().get_module(module_name); auto mod = sys.groups().get_module(module_name);
if (! mod) if (! mod)
CAF_RAISE_ERROR("Cannot deserialize a group for unknown module: " return sec::no_such_group_module;
+ module_name); return mod->load(f, x);
mod->load(source, x);
} }
std::string to_string(const group& x) { std::string to_string(const group& x) {
if (! x) if (x == invalid_group)
return "<invalid-group>"; return "<invalid-group>";
std::string result = x.get()->module().name(); std::string result = x.get()->module().name();
result += "/"; result += "/";
......
...@@ -350,22 +350,24 @@ public: ...@@ -350,22 +350,24 @@ public:
// deserialize identifier and broker // deserialize identifier and broker
std::string identifier; std::string identifier;
strong_actor_ptr broker_ptr; strong_actor_ptr broker_ptr;
source >> identifier >> broker_ptr; auto e = source(identifier, broker_ptr);
if (e)
return e;
CAF_LOG_DEBUG(CAF_ARG(identifier) << CAF_ARG(broker_ptr)); CAF_LOG_DEBUG(CAF_ARG(identifier) << CAF_ARG(broker_ptr));
if (! broker_ptr) { if (! broker_ptr) {
storage = invalid_group; storage = invalid_group;
return {}; return none;
} }
auto broker = actor_cast<actor>(broker_ptr); auto broker = actor_cast<actor>(broker_ptr);
if (broker->node() == system().node()) { if (broker->node() == system().node()) {
storage = *this->get(identifier); storage = *this->get(identifier);
return {}; return none;
} }
upgrade_guard guard(proxies_mtx_); upgrade_guard guard(proxies_mtx_);
auto i = proxies_.find(broker); auto i = proxies_.find(broker);
if (i != proxies_.end()) { if (i != proxies_.end()) {
storage = group{i->second}; storage = group{i->second};
return {}; return none;
} }
local_group_ptr tmp = make_counted<local_group_proxy>(system(), broker, local_group_ptr tmp = make_counted<local_group_proxy>(system(), broker,
*this, identifier, *this, identifier,
...@@ -374,15 +376,15 @@ public: ...@@ -374,15 +376,15 @@ public:
auto p = proxies_.emplace(broker, tmp); auto p = proxies_.emplace(broker, tmp);
// someone might preempt us // someone might preempt us
storage = group{p.first->second}; storage = group{p.first->second};
return {}; return none;
} }
error save(const local_group* ptr, serializer& sink) const { error save(const local_group* ptr, serializer& sink) const {
CAF_ASSERT(ptr != nullptr); CAF_ASSERT(ptr != nullptr);
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
sink << ptr->identifier() << actor_cast<strong_actor_ptr>(ptr->broker()); auto bro = actor_cast<strong_actor_ptr>(ptr->broker());
// TODO: refactor after visit API is in place (#470) auto& id = const_cast<std::string&>(ptr->identifier());
return {}; return sink(id, bro);
} }
void stop() override { void stop() override {
...@@ -423,9 +425,7 @@ error local_group::save(serializer& sink) const { ...@@ -423,9 +425,7 @@ error local_group::save(serializer& sink) const {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
// this cast is safe, because the only available constructor accepts // this cast is safe, because the only available constructor accepts
// local_group_module* as module pointer // local_group_module* as module pointer
static_cast<local_group_module&>(parent_).save(this, sink); return static_cast<local_group_module&>(parent_).save(this, sink);
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::atomic<size_t> s_ad_hoc_id; std::atomic<size_t> s_ad_hoc_id;
......
...@@ -93,17 +93,4 @@ mailbox_element_ptr make_mailbox_element(strong_actor_ptr sender, message_id id, ...@@ -93,17 +93,4 @@ mailbox_element_ptr make_mailbox_element(strong_actor_ptr sender, message_id id,
return mailbox_element_ptr{ptr}; return mailbox_element_ptr{ptr};
} }
std::string to_string(const mailbox_element& x) {
std::string result = "mailbox_element(";
result += to_string(x.sender);
result += ", ";
result += to_string(x.mid);
result += ", ";
result += deep_to_string(x.stages);
result += ", ";
result += to_string(x.content());
result += ")";
return result;
}
} // namespace caf } // namespace caf
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
namespace caf { namespace caf {
message::message(none_t) noexcept {
// nop
}
message::message(message&& other) noexcept : vals_(std::move(other.vals_)) { message::message(message&& other) noexcept : vals_(std::move(other.vals_)) {
// nop // nop
} }
...@@ -378,17 +382,15 @@ message message::concat_impl(std::initializer_list<data_ptr> xs) { ...@@ -378,17 +382,15 @@ message message::concat_impl(std::initializer_list<data_ptr> xs) {
} }
} }
void serialize(serializer& sink, const message& msg, const unsigned int) { error inspect(serializer& sink, message& msg) {
if (! sink.context()) if (! sink.context())
CAF_RAISE_ERROR("Cannot serialize message without context."); return sec::no_context;
// build type name // build type name
uint16_t zero = 0; uint16_t zero = 0;
std::string tname = "@<>"; std::string tname = "@<>";
if (msg.empty()) { if (msg.empty())
sink.begin_object(zero, tname); return error::eval([&] { return sink.begin_object(zero, tname); },
sink.end_object(); [&] { return sink.end_object(); });
return;
}
auto& types = sink.context()->system().types(); auto& types = sink.context()->system().types();
auto n = msg.size(); auto n = msg.size();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
...@@ -399,31 +401,42 @@ void serialize(serializer& sink, const message& msg, const unsigned int) { ...@@ -399,31 +401,42 @@ void serialize(serializer& sink, const message& msg, const unsigned int) {
"not added to the types list, typeid name: " "not added to the types list, typeid name: "
<< (rtti.second ? rtti.second->name() : "-not-available-") << (rtti.second ? rtti.second->name() : "-not-available-")
<< std::endl; << std::endl;
CAF_RAISE_ERROR("unknown type while serializing"); return make_error(sec::unknown_type,
rtti.second ? rtti.second->name() : "-not-available-");
} }
tname += '+'; tname += '+';
tname += *ptr; tname += *ptr;
} }
sink.begin_object(zero, tname); auto save_loop = [&]() -> error {
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i) {
msg.cvals()->save(i, sink); auto e = msg.cvals()->save(i, sink);
sink.end_object(); if (e)
return e;
}
return none;
};
return error::eval([&] { return sink.begin_object(zero, tname); },
[&] { return save_loop(); },
[&] { return sink.end_object(); });
} }
void serialize(deserializer& source, message& msg, const unsigned int) { error inspect(deserializer& source, message& msg) {
if (! source.context()) if (! source.context())
CAF_RAISE_ERROR("Cannot deserialize message without context."); return sec::no_context;
uint16_t zero; uint16_t zero;
std::string tname; std::string tname;
source.begin_object(zero, tname); error err;
err = source.begin_object(zero, tname);
if (err)
return err;
if (zero != 0) if (zero != 0)
CAF_RAISE_ERROR("unexpected builtin type found in message"); return sec::unknown_type;
if (tname == "@<>") { if (tname == "@<>") {
msg = message{}; msg = message{};
return; return none;
} }
if (tname.compare(0, 4, "@<>+") != 0) if (tname.compare(0, 4, "@<>+") != 0)
CAF_RAISE_ERROR("type name does not start with @<>+: " + tname); return sec::unknown_type;
// iterate over concatenated type names // iterate over concatenated type names
auto eos = tname.end(); auto eos = tname.end();
auto next = [&](std::string::iterator iter) { auto next = [&](std::string::iterator iter) {
...@@ -439,17 +452,22 @@ void serialize(deserializer& source, message& msg, const unsigned int) { ...@@ -439,17 +452,22 @@ void serialize(deserializer& source, message& msg, const unsigned int) {
tmp.assign(i, n); tmp.assign(i, n);
auto ptr = types.make_value(tmp); auto ptr = types.make_value(tmp);
if (! ptr) if (! ptr)
CAF_RAISE_ERROR("cannot serialize a value of type " + tmp); return make_error(sec::unknown_type, tmp);
ptr->load(source); err = ptr->load(source);
if (err)
return err;
dmd->append(std::move(ptr)); dmd->append(std::move(ptr));
if (n != eos) if (n != eos)
i = n + 1; i = n + 1;
else else
i = eos; i = eos;
} while (i != eos); } while (i != eos);
source.end_object(); err = source.end_object();
if (err)
return err;
message result{std::move(dmd)}; message result{std::move(dmd)};
msg.swap(result); msg.swap(result);
return none;
} }
std::string to_string(const message& msg) { std::string to_string(const message& msg) {
......
...@@ -50,11 +50,11 @@ node_id::~node_id() { ...@@ -50,11 +50,11 @@ node_id::~node_id() {
// nop // nop
} }
node_id::node_id(const invalid_node_id_t&) { node_id::node_id(const none_t&) {
// nop // nop
} }
node_id& node_id::operator=(const invalid_node_id_t&) { node_id& node_id::operator=(const none_t&) {
data_.reset(); data_.reset();
return *this; return *this;
} }
...@@ -73,7 +73,7 @@ node_id::node_id(uint32_t procid, const host_id_type& hid) ...@@ -73,7 +73,7 @@ node_id::node_id(uint32_t procid, const host_id_type& hid)
// nop // nop
} }
int node_id::compare(const invalid_node_id_t&) const { int node_id::compare(const none_t&) const {
return data_ ? 1 : 0; // invalid instances are always smaller return data_ ? 1 : 0; // invalid instances are always smaller
} }
...@@ -95,7 +95,7 @@ int node_id::compare(const node_id& other) const { ...@@ -95,7 +95,7 @@ int node_id::compare(const node_id& other) const {
} }
node_id::data::data() : pid_(0) { node_id::data::data() : pid_(0) {
// nop memset(host_.data(), 0, host_.size());
} }
node_id::data::data(uint32_t procid, host_id_type hid) node_id::data::data(uint32_t procid, host_id_type hid)
...@@ -130,8 +130,9 @@ node_id::data::~data() { ...@@ -130,8 +130,9 @@ node_id::data::~data() {
// nop // nop
} }
void node_id::data::stop() { bool node_id::data::valid() const {
// nop auto is_zero = [](uint8_t x) { return x == 0; };
return pid_ != 0 && ! std::all_of(host_.begin(), host_.end(), is_zero);
} }
namespace { namespace {
...@@ -178,104 +179,13 @@ void node_id::swap(node_id& x) { ...@@ -178,104 +179,13 @@ void node_id::swap(node_id& x) {
data_.swap(x.data_); data_.swap(x.data_);
} }
void serialize(serializer& sink, node_id& x, const unsigned int) {
if (! x.data_) {
node_id::host_id_type zero_id;
std::fill(zero_id.begin(), zero_id.end(), 0);
uint32_t zero_pid = 0;
sink.apply_raw(zero_id.size(), zero_id.data());
sink << zero_pid;
} else {
sink.apply_raw(x.data_->host_.size(), x.data_->host_.data());
sink.apply(x.data_->pid_);
}
}
void serialize(deserializer& source, node_id& x, const unsigned int) {
node_id::host_id_type tmp_hid;
uint32_t tmp_pid;
source.apply_raw(tmp_hid.size(), tmp_hid.data());
source >> tmp_pid;
auto is_zero = [](uint8_t value) { return value == 0; };
// no need to keep the data if it is invalid
if (tmp_pid == 0 && std::all_of(tmp_hid.begin(), tmp_hid.end(), is_zero)) {
x = invalid_node_id;
return;
}
if (! x.data_ || ! x.data_->unique()) {
x.data_ = make_counted<node_id::data>(tmp_pid, tmp_hid);
} else {
x.data_->pid_ = tmp_pid;
x.data_->host_ = tmp_hid;
}
}
namespace {
inline uint8_t hex_nibble(char c) {
return static_cast<uint8_t>(c >= '0' && c <= '9'
? c - '0'
: (c >= 'a' && c <= 'f' ? (c - 'a') + 10
: (c - 'A') + 10));
}
} // namespace <anonymous>
void node_id::from_string(const std::string& str) {
data_.reset();
if (str == "<invalid-node>")
return;
static constexpr size_t hexstr_size = host_id_size * 2;
// node id format is: "[0-9a-zA-Z]{40}:[0-9]+"
if (str.size() < hexstr_size + 2)
return;
auto beg = str.begin();
auto sep = beg + hexstr_size; // separator ':' / end of hex-string
auto eos = str.end(); // end-of-string
if (*sep != ':')
return;
if (! std::all_of(beg, sep, ::isxdigit))
return;
if (! std::all_of(sep + 1, eos, ::isdigit))
return;
// iterate two digits in the input string as one byte in hex format
struct hex_byte_iter : std::iterator<std::input_iterator_tag, uint8_t> {
using const_iterator = std::string::const_iterator;
const_iterator i;
hex_byte_iter(const_iterator x) : i(x) {
// nop
}
uint8_t operator*() const {
return static_cast<uint8_t>(hex_nibble(*i) << 4) | hex_nibble(*(i + 1));
}
hex_byte_iter& operator++() {
i += 2;
return *this;
}
bool operator!=(const hex_byte_iter& x) const {
return i != x.i;
}
};
hex_byte_iter first{beg};
hex_byte_iter last{sep};
data_.reset(new data);
std::copy(first, last, data_->host_.begin());
data_->pid_ = static_cast<uint32_t>(atoll(&*(sep + 1)));
}
std::string to_string(const node_id& x) { std::string to_string(const node_id& x) {
if (! x) if (! x)
return "<invalid-node>"; return "none";
std::ostringstream oss; return deep_to_string(meta::type_name("node_id"),
oss << std::hex; x.process_id(),
oss.fill('0'); meta::hex_formatted(),
auto& hid = x.host_id(); x.host_id());
for (auto val : hid) {
oss.width(2);
oss << static_cast<uint32_t>(val);
}
oss << ":" << std::dec << x.process_id();
return oss.str();
} }
} // namespace caf } // namespace caf
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "caf/scheduled_actor.hpp" #include "caf/scheduled_actor.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/to_string.hpp"
#include "caf/actor_ostream.hpp" #include "caf/actor_ostream.hpp"
#include "caf/detail/private_thread.hpp" #include "caf/detail/private_thread.hpp"
...@@ -320,7 +321,7 @@ scheduled_actor::categorize(mailbox_element& x) { ...@@ -320,7 +321,7 @@ scheduled_actor::categorize(mailbox_element& x) {
: message_category::expired_timeout; : message_category::expired_timeout;
} }
case make_type_token<exit_msg>(): { case make_type_token<exit_msg>(): {
auto& em = content.get_mutable_as<exit_msg>(0); auto em = content.move_if_unshared<exit_msg>(0);
// make sure to get rid of attachables if they're no longer needed // make sure to get rid of attachables if they're no longer needed
unlink_from(em.source); unlink_from(em.source);
// exit_reason::kill is always fatal // exit_reason::kill is always fatal
...@@ -333,12 +334,12 @@ scheduled_actor::categorize(mailbox_element& x) { ...@@ -333,12 +334,12 @@ scheduled_actor::categorize(mailbox_element& x) {
return message_category::internal; return message_category::internal;
} }
case make_type_token<down_msg>(): { case make_type_token<down_msg>(): {
auto& dm = content.get_mutable_as<down_msg>(0); auto dm = content.move_if_unshared<down_msg>(0);
down_handler_(this, dm); down_handler_(this, dm);
return message_category::internal; return message_category::internal;
} }
case make_type_token<error>(): { case make_type_token<error>(): {
auto& err = content.get_mutable_as<error>(0); auto err = content.move_if_unshared<error>(0);
error_handler_(this, err); error_handler_(this, err);
return message_category::internal; return message_category::internal;
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "caf/scoped_actor.hpp" #include "caf/scoped_actor.hpp"
#include "caf/to_string.hpp"
#include "caf/spawn_options.hpp" #include "caf/spawn_options.hpp"
#include "caf/actor_registry.hpp" #include "caf/actor_registry.hpp"
#include "caf/scoped_execution_unit.hpp" #include "caf/scoped_execution_unit.hpp"
......
...@@ -24,7 +24,7 @@ namespace caf { ...@@ -24,7 +24,7 @@ namespace caf {
namespace { namespace {
const char* sec_strings[] = { const char* sec_strings[] = {
"<no-error>", "none",
"unexpected_message", "unexpected_message",
"unexpected_response", "unexpected_response",
"request_receiver_down", "request_receiver_down",
...@@ -48,6 +48,10 @@ const char* sec_strings[] = { ...@@ -48,6 +48,10 @@ const char* sec_strings[] = {
"invalid_protocol_family", "invalid_protocol_family",
"cannot_publish_invalid_actor", "cannot_publish_invalid_actor",
"cannot_spawn_actor_from_arguments", "cannot_spawn_actor_from_arguments",
"end_of_stream",
"no_context",
"unknown_type",
"no_proxy_registry",
"bad_function_call" "bad_function_call"
}; };
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include "caf/detail/stringification_inspector.hpp"
namespace caf {
namespace detail {
void stringification_inspector::sep() {
if (! result_.empty())
switch (result_.back()) {
case '(':
case '[':
case ' ': // only at back if we've printed ", " before
break;
default:
result_ += ", ";
}
}
void stringification_inspector::consume(atom_value& x) {
result_ += '\'';
result_ += to_string(x);
result_ += '\'';
}
void stringification_inspector::consume(const char* cstr) {
if (! cstr || *cstr == '\0') {
result_ += "\"\"";
return;
}
if (*cstr == '"') {
// assume an already escaped string
result_ += cstr;
return;
}
result_ += '"';
char c;
for(;;) {
switch (c = *cstr++) {
default:
result_ += c;
break;
case '\\':
result_ += "\\\\";
break;
case '"':
result_ += "\\\"";
break;
case '\0':
goto end_of_string;
}
}
end_of_string:
result_ += '"';
}
void stringification_inspector::consume_hex(const uint8_t* xs, size_t n) {
if (n == 0) {
result_ += "00";
return;
}
auto tbl = "0123456789ABCDEF";
char buf[3] = {0, 0, 0};
for (size_t i = 0; i < n; ++i) {
auto c = xs[i];
buf[0] = tbl[c >> 4];
buf[1] = tbl[c & 0x0F];
result_ += buf;
}
}
} // namespace detail
} // namespace caf
...@@ -29,10 +29,12 @@ type_erased_tuple::~type_erased_tuple() { ...@@ -29,10 +29,12 @@ type_erased_tuple::~type_erased_tuple() {
} }
error type_erased_tuple::load(deserializer& source) { error type_erased_tuple::load(deserializer& source) {
for (size_t i = 0; i < size(); ++i) for (size_t i = 0; i < size(); ++i) {
load(i, source); auto e = load(i, source);
// TODO: refactor after visit API is in place (#470) if (e)
return {}; return e;
}
return none;
} }
bool type_erased_tuple::shared() const noexcept { bool type_erased_tuple::shared() const noexcept {
...@@ -57,10 +59,12 @@ std::string type_erased_tuple::stringify() const { ...@@ -57,10 +59,12 @@ std::string type_erased_tuple::stringify() const {
} }
error type_erased_tuple::save(serializer& sink) const { error type_erased_tuple::save(serializer& sink) const {
for (size_t i = 0; i < size(); ++i) for (size_t i = 0; i < size(); ++i) {
save(i, sink); auto e = save(i, sink);
// TODO: refactor after visit API is in place (#470) if (e)
return {}; return e;
}
return none;
} }
bool type_erased_tuple::matches(size_t pos, uint16_t nr, bool type_erased_tuple::matches(size_t pos, uint16_t nr,
......
...@@ -77,7 +77,6 @@ const char* numbered_type_names[] = { ...@@ -77,7 +77,6 @@ const char* numbered_type_names[] = {
"@strong_actor_ptr", "@strong_actor_ptr",
"@strset", "@strset",
"@strvec", "@strvec",
"@sync_timeout_msg",
"@timeout", "@timeout",
"@u16", "@u16",
"@u16str", "@u16str",
......
...@@ -42,6 +42,10 @@ public: ...@@ -42,6 +42,10 @@ public:
} }
behavior make_behavior() override { behavior make_behavior() override {
auto nested = exit_handler_;
set_exit_handler([=](scheduled_actor* self, exit_msg& em) {
nested(self, em);
});
return { return {
[](int x, int y) { [](int x, int y) {
return x + y; return x + y;
......
...@@ -120,9 +120,9 @@ public: ...@@ -120,9 +120,9 @@ public:
return str_; return str_;
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, counting_string& x) { friend error inspect(Inspector& f, counting_string& x) {
proc & x.str_; return f(x.str_);
} }
private: private:
......
...@@ -39,9 +39,12 @@ namespace { ...@@ -39,9 +39,12 @@ namespace {
using rtti_pair = std::pair<uint16_t, const std::type_info*>; using rtti_pair = std::pair<uint16_t, const std::type_info*>;
std::string to_string(const rtti_pair& x) { std::string to_string(const rtti_pair& x) {
if (x.second) std::string result = "(";
return deep_to_string_as_tuple(x.first, x.second->name()); result += std::to_string(x.first);
return deep_to_string_as_tuple(x.first, "<null>"); result += ", ";
result += x.second ? x.second->name() : "<null>";
result += ")";
return result;
} }
struct fixture { struct fixture {
......
...@@ -170,26 +170,18 @@ struct s1 { ...@@ -170,26 +170,18 @@ struct s1 {
int value[3] = {10, 20, 30}; int value[3] = {10, 20, 30};
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s1& x, const unsigned int) { error inspect(Inspector& f, s1& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s1& x) {
return deep_to_string(x.value);
} }
struct s2 { struct s2 {
int value[4][2] = {{1, 10}, {2, 20}, {3, 30}, {4, 40}}; int value[4][2] = {{1, 10}, {2, 20}, {3, 30}, {4, 40}};
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s2& x, const unsigned int) { error inspect(Inspector& f, s2& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s2& x) {
return deep_to_string(x.value);
} }
struct s3 { struct s3 {
...@@ -199,13 +191,9 @@ struct s3 { ...@@ -199,13 +191,9 @@ struct s3 {
} }
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s3& x, const unsigned int) { error inspect(Inspector& f, s3& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s3& x) {
return deep_to_string(x.value);
} }
template <class... Ts> template <class... Ts>
...@@ -218,7 +206,8 @@ std::string msg_as_string(Ts&&... xs) { ...@@ -218,7 +206,8 @@ std::string msg_as_string(Ts&&... xs) {
CAF_TEST(compare_custom_types) { CAF_TEST(compare_custom_types) {
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
CAF_CHECK(to_string(make_message(s2{})) != to_string(make_message(tmp))); CAF_CHECK_NOT_EQUAL(to_string(make_message(s2{})),
to_string(make_message(tmp)));
} }
CAF_TEST(empty_to_string) { CAF_TEST(empty_to_string) {
......
...@@ -79,9 +79,9 @@ struct raw_struct { ...@@ -79,9 +79,9 @@ struct raw_struct {
string str; string str;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, raw_struct& x) { error inspect(Inspector& f, raw_struct& x) {
proc & x.str; return f(x.str);
} }
bool operator==(const raw_struct& lhs, const raw_struct& rhs) { bool operator==(const raw_struct& lhs, const raw_struct& rhs) {
...@@ -108,10 +108,9 @@ struct test_array { ...@@ -108,10 +108,9 @@ struct test_array {
int value2[2][4]; int value2[2][4];
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, test_array& x, const unsigned int) { error inspect(Inspector& f, test_array& x) {
proc & x.value; return f(x.value, x.value2);
proc & x.value2;
} }
struct test_empty_non_pod { struct test_empty_non_pod {
...@@ -125,9 +124,9 @@ struct test_empty_non_pod { ...@@ -125,9 +124,9 @@ struct test_empty_non_pod {
} }
}; };
template <class Processor> template <class Inspector>
void serialize(Processor&, test_empty_non_pod&, const unsigned int) { error inspect(Inspector&, test_empty_non_pod&) {
// nop return none;
} }
class config : public actor_system_config { class config : public actor_system_config {
...@@ -161,29 +160,18 @@ struct fixture { ...@@ -161,29 +160,18 @@ struct fixture {
scoped_execution_unit context; scoped_execution_unit context;
message msg; message msg;
template <class Processor>
void apply(Processor&) {
// end of recursion
}
template <class Processor, class T, class... Ts>
void apply(Processor& proc, T& x, Ts&... xs) {
proc & x;
apply(proc, xs...);
}
template <class T, class... Ts> template <class T, class... Ts>
vector<char> serialize(T& x, Ts&... xs) { vector<char> serialize(T& x, Ts&... xs) {
vector<char> buf; vector<char> buf;
binary_serializer bs{&context, buf}; binary_serializer bs{&context, buf};
apply(bs, x, xs...); bs(x, xs...);
return buf; return buf;
} }
template <class T, class... Ts> template <class T, class... Ts>
void deserialize(const vector<char>& buf, T& x, Ts&... xs) { void deserialize(const vector<char>& buf, T& x, Ts&... xs) {
binary_deserializer bd{&context, buf}; binary_deserializer bd{&context, buf};
apply(bd, x, xs...); bd(x, xs...);
} }
// serializes `x` and then deserializes and returns the serialized value // serializes `x` and then deserializes and returns the serialized value
...@@ -429,21 +417,25 @@ CAF_TEST(streambuf_serialization) { ...@@ -429,21 +417,25 @@ CAF_TEST(streambuf_serialization) {
// First, we check the standard use case in CAF where stream serializers own // First, we check the standard use case in CAF where stream serializers own
// their stream buffers. // their stream buffers.
stream_serializer<vectorbuf> bs{vectorbuf{buf}}; stream_serializer<vectorbuf> bs{vectorbuf{buf}};
bs << data; auto e = bs(data);
CAF_REQUIRE_EQUAL(e, none);
stream_deserializer<charbuf> bd{charbuf{buf}}; stream_deserializer<charbuf> bd{charbuf{buf}};
std::string target; std::string target;
bd >> target; e = bd(target);
CAF_CHECK(data == target); CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK_EQUAL(data, target);
// Second, we test another use case where the serializers only keep // Second, we test another use case where the serializers only keep
// references of the stream buffers. // references of the stream buffers.
buf.clear(); buf.clear();
target.clear(); target.clear();
vectorbuf vb{buf}; vectorbuf vb{buf};
stream_serializer<vectorbuf&> vs{vb}; stream_serializer<vectorbuf&> vs{vb};
vs << data; e = vs(data);
CAF_REQUIRE_EQUAL(e, none);
charbuf cb{buf}; charbuf cb{buf};
stream_deserializer<charbuf&> vd{cb}; stream_deserializer<charbuf&> vd{cb};
vd >> target; e = vd(target);
CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK(data == target); CAF_CHECK(data == target);
} }
...@@ -454,11 +446,13 @@ CAF_TEST(byte_sequence_optimization) { ...@@ -454,11 +446,13 @@ CAF_TEST(byte_sequence_optimization) {
using streambuf_type = containerbuf<std::vector<uint8_t>>; using streambuf_type = containerbuf<std::vector<uint8_t>>;
streambuf_type cb{buf}; streambuf_type cb{buf};
stream_serializer<streambuf_type&> bs{cb}; stream_serializer<streambuf_type&> bs{cb};
bs << data; auto e = bs(data);
CAF_REQUIRE(! e);
data.clear(); data.clear();
streambuf_type cb2{buf}; streambuf_type cb2{buf};
stream_deserializer<streambuf_type&> bd{cb2}; stream_deserializer<streambuf_type&> bd{cb2};
bd >> data; e = bd(data);
CAF_REQUIRE(! e);
CAF_CHECK_EQUAL(data.size(), 42u); CAF_CHECK_EQUAL(data.size(), 42u);
CAF_CHECK(std::all_of(data.begin(), data.end(), CAF_CHECK(std::all_of(data.begin(), data.end(),
[](uint8_t c) { return c == 0x2a; })); [](uint8_t c) { return c == 0x2a; }));
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE to_string
#include "caf/test/unit_test.hpp"
#include "caf/to_string.hpp"
using namespace std;
using namespace caf;
CAF_TEST(buffer) {
std::vector<char> buf;
CAF_CHECK_EQUAL(deep_to_string(buf), "[]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "00");
buf.push_back(-1);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF");
buf.push_back(0);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF00");
buf.push_back(127);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F");
buf.push_back(10);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127, 10]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F0A");
buf.push_back(16);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127, 10, 16]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F0A10");
}
...@@ -84,10 +84,9 @@ struct my_request { ...@@ -84,10 +84,9 @@ struct my_request {
int b; int b;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, my_request& x, const unsigned int) { error inspect(Inspector& f, my_request& x) {
proc & x.a; return f(x.a, x.b);
proc & x.b;
} }
using server_type = typed_actor<replies_to<my_request>::with<bool>>; using server_type = typed_actor<replies_to<my_request>::with<bool>>;
......
...@@ -182,8 +182,12 @@ public: ...@@ -182,8 +182,12 @@ public:
/// Closes the connection or acceptor identified by `handle`. /// Closes the connection or acceptor identified by `handle`.
/// Unwritten data will still be send. /// Unwritten data will still be send.
template <class Handle> template <class Handle>
void close(Handle hdl) { bool close(Handle hdl) {
by_id(hdl).stop_reading(); auto x = by_id(hdl);
if (! x)
return false;
x->stop_reading();
return true;
} }
/// Checks whether `hdl` is assigned to broker. /// Checks whether `hdl` is assigned to broker.
...@@ -255,11 +259,11 @@ protected: ...@@ -255,11 +259,11 @@ protected:
/// Returns a `scribe` or `doorman` identified by `hdl`. /// Returns a `scribe` or `doorman` identified by `hdl`.
template <class Handle> template <class Handle>
auto by_id(Handle hdl) -> decltype(*ptr_of(hdl)) { auto by_id(Handle hdl) -> optional<decltype(*ptr_of(hdl))> {
auto& elements = get_map(hdl); auto& elements = get_map(hdl);
auto i = elements.find(hdl); auto i = elements.find(hdl);
if (i == elements.end()) if (i == elements.end())
CAF_RAISE_ERROR("invalid handle"); return none;
return *(i->second); return *(i->second);
} }
...@@ -272,7 +276,7 @@ protected: ...@@ -272,7 +276,7 @@ protected:
decltype(ptr_of(hdl)) result; decltype(ptr_of(hdl)) result;
auto i = elements.find(hdl); auto i = elements.find(hdl);
if (i == elements.end()) if (i == elements.end())
CAF_RAISE_ERROR("invalid handle"); return nullptr;
swap(result, i->second); swap(result, i->second);
elements.erase(i); elements.erase(i);
return result; return result;
...@@ -282,6 +286,7 @@ private: ...@@ -282,6 +286,7 @@ private:
scribe_map scribes_; scribe_map scribes_;
doorman_map doormen_; doorman_map doormen_;
detail::intrusive_partitioned_list<mailbox_element, detail::disposer> cache_; detail::intrusive_partitioned_list<mailbox_element, detail::disposer> cache_;
std::vector<char> dummy_wr_buf_;
}; };
} // namespace io } // namespace io
......
...@@ -20,8 +20,14 @@ ...@@ -20,8 +20,14 @@
#ifndef CAF_IO_ACCEPT_HANDLE_HPP #ifndef CAF_IO_ACCEPT_HANDLE_HPP
#define CAF_IO_ACCEPT_HANDLE_HPP #define CAF_IO_ACCEPT_HANDLE_HPP
#include <functional>
#include "caf/error.hpp"
#include "caf/io/handle.hpp" #include "caf/io/handle.hpp"
#include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
namespace io { namespace io {
...@@ -49,9 +55,9 @@ public: ...@@ -49,9 +55,9 @@ public:
// nop // nop
} }
template <class Procesor> template <class Inspector>
friend void serialize(Procesor& proc, accept_handle& x, const unsigned int) { friend error inspect(Inspector& f, accept_handle& x) {
proc & x.id_; return f(meta::type_name("accept_handle"), x.id_);
} }
private: private:
......
...@@ -23,8 +23,12 @@ ...@@ -23,8 +23,12 @@
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#include "caf/error.hpp"
#include "caf/node_id.hpp" #include "caf/node_id.hpp"
#include "caf/meta/omittable.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/io/basp/message_type.hpp" #include "caf/io/basp/message_type.hpp"
namespace caf { namespace caf {
...@@ -76,24 +80,17 @@ struct header { ...@@ -76,24 +80,17 @@ struct header {
}; };
/// @relates header /// @relates header
template <class Processor> template <class Inspector>
void serialize(Processor& proc, header& hdr, const unsigned int) { error inspect(Inspector& f, header& hdr) {
uint8_t pad = 0; uint8_t pad = 0;
proc & hdr.operation; return f(meta::type_name("header"),
proc & pad; hdr.operation,
proc & pad; meta::omittable(), pad,
proc & hdr.flags; meta::omittable(), pad,
proc & hdr.payload_len; hdr.flags, hdr.payload_len, hdr.operation_data, hdr.source_node,
proc & hdr.operation_data; hdr.dest_node, hdr.source_actor, hdr.dest_actor);
proc & hdr.source_node;
proc & hdr.dest_node;
proc & hdr.source_actor;
proc & hdr.dest_actor;
} }
/// @relates header
std::string to_string(const header& hdr);
/// @relates header /// @relates header
bool operator==(const header& lhs, const header& rhs); bool operator==(const header& lhs, const header& rhs);
......
...@@ -59,7 +59,7 @@ public: ...@@ -59,7 +59,7 @@ public:
optional<route> lookup(const node_id& target); optional<route> lookup(const node_id& target);
/// Returns the ID of the peer connected via `hdl` or /// Returns the ID of the peer connected via `hdl` or
/// `invalid_node_id` if `hdl` is unknown. /// `none` if `hdl` is unknown.
node_id lookup_direct(const connection_handle& hdl) const; node_id lookup_direct(const connection_handle& hdl) const;
/// Returns the handle offering a direct connection to `nid` or /// Returns the handle offering a direct connection to `nid` or
...@@ -67,14 +67,14 @@ public: ...@@ -67,14 +67,14 @@ public:
connection_handle lookup_direct(const node_id& nid) const; connection_handle lookup_direct(const node_id& nid) const;
/// Returns the next hop that would be chosen for `nid` /// Returns the next hop that would be chosen for `nid`
/// or `invalid_node_id` if there's no indirect route to `nid`. /// or `none` if there's no indirect route to `nid`.
node_id lookup_indirect(const node_id& nid) const; node_id lookup_indirect(const node_id& nid) const;
/// Flush output buffer for `r`. /// Flush output buffer for `r`.
void flush(const route& r); void flush(const route& r);
/// Adds a new direct route to the table. /// Adds a new direct route to the table.
/// @pre `hdl != invalid_connection_handle && nid != invalid_node_id` /// @pre `hdl != invalid_connection_handle && nid != none`
void add_direct(const connection_handle& hdl, const node_id& dest); void add_direct(const connection_handle& hdl, const node_id& dest);
/// Adds a new indirect route to the table. /// Adds a new indirect route to the table.
......
...@@ -22,8 +22,12 @@ ...@@ -22,8 +22,12 @@
#include <functional> #include <functional>
#include "caf/error.hpp"
#include "caf/io/handle.hpp" #include "caf/io/handle.hpp"
#include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
namespace io { namespace io {
...@@ -52,10 +56,9 @@ public: ...@@ -52,10 +56,9 @@ public:
// nop // nop
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, connection_handle& x, friend error inspect(Inspector& f, connection_handle& x) {
const unsigned int) { return f(meta::type_name("connection_handle"), x.id_);
proc & x.id_;
} }
private: private:
......
...@@ -76,8 +76,10 @@ error ip_bind(asio_tcp_socket_acceptor& fd, uint16_t port, ...@@ -76,8 +76,10 @@ error ip_bind(asio_tcp_socket_acceptor& fd, uint16_t port,
fd.bind(ep, ec); fd.bind(ep, ec);
if (ec) if (ec)
return sec::cannot_open_port; return sec::cannot_open_port;
fd.listen(); fd.listen(ec);
return {}; if (ec)
return sec::cannot_open_port;
return none;
}; };
if (addr) { if (addr) {
CAF_LOG_DEBUG(CAF_ARG(addr)); CAF_LOG_DEBUG(CAF_ARG(addr));
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include "caf/deep_to_string.hpp" #include "caf/meta/type_name.hpp"
#include "caf/io/handle.hpp" #include "caf/io/handle.hpp"
#include "caf/io/accept_handle.hpp" #include "caf/io/accept_handle.hpp"
...@@ -42,8 +42,9 @@ struct new_connection_msg { ...@@ -42,8 +42,9 @@ struct new_connection_msg {
connection_handle handle; connection_handle handle;
}; };
inline std::string to_string(const new_connection_msg& x) { template <class Inspector>
return "new_connection" + deep_to_string(std::tie(x.source, x.handle)); error inspect(Inspector& f, new_connection_msg& x) {
return f(meta::type_name("new_connection_msg"), x.source, x.handle);
} }
/// @relates new_connection_msg /// @relates new_connection_msg
...@@ -58,13 +59,6 @@ inline bool operator!=(const new_connection_msg& lhs, ...@@ -58,13 +59,6 @@ inline bool operator!=(const new_connection_msg& lhs,
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates new_connection_msg
template <class Processor>
void serialize(Processor& proc, new_connection_msg& x, const unsigned int) {
proc & x.source;
proc & x.handle;
}
/// Signalizes newly arrived data for a {@link broker}. /// Signalizes newly arrived data for a {@link broker}.
struct new_data_msg { struct new_data_msg {
/// Handle to the related connection. /// Handle to the related connection.
...@@ -74,12 +68,9 @@ struct new_data_msg { ...@@ -74,12 +68,9 @@ struct new_data_msg {
}; };
/// @relates new_data_msg /// @relates new_data_msg
inline std::string to_string(const new_data_msg& x) { template <class Inspector>
std::ostringstream os; error inspect(Inspector& f, new_data_msg& x) {
os << std::setfill('0') << std::hex << std::right; return f(meta::type_name("new_data_msg"), x.handle, x.buf);
for (auto c : x.buf)
os << std::setw(2) << static_cast<int>(c);
return "new_data" + deep_to_string_as_tuple(x.handle, os.str());
} }
/// @relates new_data_msg /// @relates new_data_msg
...@@ -92,13 +83,6 @@ inline bool operator!=(const new_data_msg& lhs, const new_data_msg& rhs) { ...@@ -92,13 +83,6 @@ inline bool operator!=(const new_data_msg& lhs, const new_data_msg& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates new_data_msg
template <class Processor>
void serialize(Processor& proc, new_data_msg& x, const unsigned int) {
proc & x.handle;
proc & x.buf;
}
/// Signalizes that a certain amount of bytes has been written. /// Signalizes that a certain amount of bytes has been written.
struct data_transferred_msg { struct data_transferred_msg {
/// Handle to the related connection. /// Handle to the related connection.
...@@ -110,9 +94,10 @@ struct data_transferred_msg { ...@@ -110,9 +94,10 @@ struct data_transferred_msg {
}; };
/// @relates data_transferred_msg /// @relates data_transferred_msg
inline std::string to_string(const data_transferred_msg& x) { template <class Inspector>
return "data_transferred_msg" error inspect(Inspector& f, data_transferred_msg& x) {
+ deep_to_string_as_tuple(x.handle, x.written, x.remaining); return f(meta::type_name("data_transferred_msg"),
x.handle, x.written, x.remaining);
} }
/// @relates data_transferred_msg /// @relates data_transferred_msg
...@@ -129,13 +114,6 @@ inline bool operator!=(const data_transferred_msg& x, ...@@ -129,13 +114,6 @@ inline bool operator!=(const data_transferred_msg& x,
return ! (x == y); return ! (x == y);
} }
/// @relates new_data_msg
template <class Processor>
void serialize(Processor& proc, data_transferred_msg& x, const unsigned int) {
proc & x.handle;
proc & x.written;
proc & x.remaining;
}
/// Signalizes that a {@link broker} connection has been closed. /// Signalizes that a {@link broker} connection has been closed.
struct connection_closed_msg { struct connection_closed_msg {
...@@ -143,8 +121,10 @@ struct connection_closed_msg { ...@@ -143,8 +121,10 @@ struct connection_closed_msg {
connection_handle handle; connection_handle handle;
}; };
inline std::string to_string(const connection_closed_msg& x) { /// @relates connection_closed_msg
return "connection_closed" + deep_to_string(std::tie(x.handle)); template <class Inspector>
error inspect(Inspector& f, connection_closed_msg& x) {
return f(meta::type_name("connection_closed_msg"), x.handle);
} }
/// @relates connection_closed_msg /// @relates connection_closed_msg
...@@ -159,18 +139,18 @@ inline bool operator!=(const connection_closed_msg& lhs, ...@@ -159,18 +139,18 @@ inline bool operator!=(const connection_closed_msg& lhs,
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates connection_closed_msg
template <class Processor>
void serialize(Processor& proc, connection_closed_msg& x, const unsigned int) {
proc & x.handle;
}
/// Signalizes that a {@link broker} acceptor has been closed. /// Signalizes that a {@link broker} acceptor has been closed.
struct acceptor_closed_msg { struct acceptor_closed_msg {
/// Handle to the closed connection. /// Handle to the closed connection.
accept_handle handle; accept_handle handle;
}; };
/// @relates connection_closed_msg
template <class Inspector>
error inspect(Inspector& f, acceptor_closed_msg& x) {
return f(meta::type_name("acceptor_closed_msg"), x.handle);
}
/// @relates acceptor_closed_msg /// @relates acceptor_closed_msg
inline bool operator==(const acceptor_closed_msg& lhs, inline bool operator==(const acceptor_closed_msg& lhs,
const acceptor_closed_msg& rhs) { const acceptor_closed_msg& rhs) {
...@@ -183,12 +163,6 @@ inline bool operator!=(const acceptor_closed_msg& lhs, ...@@ -183,12 +163,6 @@ inline bool operator!=(const acceptor_closed_msg& lhs,
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates acceptor_closed_msg
template <class Processor>
void serialize(Processor& proc, acceptor_closed_msg& x, const unsigned int) {
proc & x.handle;
}
} // namespace io } // namespace io
} // namespace caf } // namespace caf
......
...@@ -75,16 +75,25 @@ abstract_broker::~abstract_broker() { ...@@ -75,16 +75,25 @@ abstract_broker::~abstract_broker() {
void abstract_broker::configure_read(connection_handle hdl, void abstract_broker::configure_read(connection_handle hdl,
receive_policy::config cfg) { receive_policy::config cfg) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(cfg)); CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(cfg));
by_id(hdl).configure_read(cfg); auto x = by_id(hdl);
if (x)
x->configure_read(cfg);
} }
void abstract_broker::ack_writes(connection_handle hdl, bool enable) { void abstract_broker::ack_writes(connection_handle hdl, bool enable) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(enable)); CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(enable));
by_id(hdl).ack_writes(enable); auto x = by_id(hdl);
if (x)
x->ack_writes(enable);
} }
std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) { std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) {
return by_id(hdl).wr_buf(); auto x = by_id(hdl);
if (! x) {
CAF_LOG_ERROR("tried to access wr_buf() of an unknown connection_handle");
return dummy_wr_buf_;
}
return x->wr_buf();
} }
void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) { void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) {
...@@ -95,7 +104,9 @@ void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) { ...@@ -95,7 +104,9 @@ void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) {
} }
void abstract_broker::flush(connection_handle hdl) { void abstract_broker::flush(connection_handle hdl) {
by_id(hdl).flush(); auto x = by_id(hdl);
if (x)
x->flush();
} }
std::vector<connection_handle> abstract_broker::connections() const { std::vector<connection_handle> abstract_broker::connections() const {
...@@ -175,7 +186,7 @@ accept_handle abstract_broker::hdl_by_port(uint16_t port) { ...@@ -175,7 +186,7 @@ accept_handle abstract_broker::hdl_by_port(uint16_t port) {
for (auto& kvp : doormen_) for (auto& kvp : doormen_)
if (kvp.second->port() == port) if (kvp.second->port() == port)
return kvp.first; return kvp.first;
CAF_RAISE_ERROR("no such port"); return invalid_accept_handle;
} }
void abstract_broker::close_all() { void abstract_broker::close_all() {
......
...@@ -52,7 +52,7 @@ basp_broker_state::basp_broker_state(broker* selfptr) ...@@ -52,7 +52,7 @@ basp_broker_state::basp_broker_state(broker* selfptr)
static_cast<proxy_registry::backend&>(*this)), static_cast<proxy_registry::backend&>(*this)),
self(selfptr), self(selfptr),
instance(selfptr, *this) { instance(selfptr, *this) {
CAF_ASSERT(this_node() != invalid_node_id); CAF_ASSERT(this_node() != none);
} }
basp_broker_state::~basp_broker_state() { basp_broker_state::~basp_broker_state() {
...@@ -64,7 +64,7 @@ basp_broker_state::~basp_broker_state() { ...@@ -64,7 +64,7 @@ basp_broker_state::~basp_broker_state() {
strong_actor_ptr basp_broker_state::make_proxy(node_id nid, actor_id aid) { strong_actor_ptr basp_broker_state::make_proxy(node_id nid, actor_id aid) {
CAF_LOG_TRACE(CAF_ARG(nid) << CAF_ARG(aid)); CAF_LOG_TRACE(CAF_ARG(nid) << CAF_ARG(aid));
CAF_ASSERT(nid != this_node()); CAF_ASSERT(nid != this_node());
if (nid == invalid_node_id || aid == invalid_actor_id) if (nid == none || aid == invalid_actor_id)
return nullptr; return nullptr;
// this member function is being called whenever we deserialize a // this member function is being called whenever we deserialize a
// payload received from a remote node; if a remote node A sends // payload received from a remote node; if a remote node A sends
...@@ -295,11 +295,11 @@ void basp_broker_state::learned_new_node(const node_id& nid) { ...@@ -295,11 +295,11 @@ void basp_broker_state::learned_new_node(const node_id& nid) {
spawn_servers.emplace(nid, tmp); spawn_servers.emplace(nid, tmp);
using namespace detail; using namespace detail;
system().registry().put(tmp.id(), actor_cast<strong_actor_ptr>(tmp)); system().registry().put(tmp.id(), actor_cast<strong_actor_ptr>(tmp));
auto writer = make_callback([](serializer& sink) { auto writer = make_callback([](serializer& sink) -> error {
auto name_atm = atom("SpawnServ"); auto name_atm = atom("SpawnServ");
std::vector<actor_id> stages; std::vector<actor_id> stages;
auto msg = make_message(sys_atom::value, get_atom::value, "info"); auto msg = make_message(sys_atom::value, get_atom::value, "info");
sink << name_atm << stages << msg; return sink(name_atm, stages, msg);
}); });
auto path = instance.tbl().lookup(nid); auto path = instance.tbl().lookup(nid);
if (! path) { if (! path) {
...@@ -396,11 +396,11 @@ void basp_broker_state::learned_new_node_indirectly(const node_id& nid) { ...@@ -396,11 +396,11 @@ void basp_broker_state::learned_new_node_indirectly(const node_id& nid) {
using namespace detail; using namespace detail;
auto tmp = system().spawn<detached + hidden>(connection_helper, self); auto tmp = system().spawn<detached + hidden>(connection_helper, self);
system().registry().put(tmp.id(), actor_cast<strong_actor_ptr>(tmp)); system().registry().put(tmp.id(), actor_cast<strong_actor_ptr>(tmp));
auto writer = make_callback([](serializer& sink) { auto writer = make_callback([](serializer& sink) -> error {
auto name_atm = atom("ConfigServ"); auto name_atm = atom("ConfigServ");
std::vector<actor_id> stages; std::vector<actor_id> stages;
auto msg = make_message(get_atom::value, "basp.default-connectivity"); auto msg = make_message(get_atom::value, "basp.default-connectivity");
sink << name_atm << stages << msg; return sink(name_atm, stages, msg);
}); });
basp::header hdr{basp::message_type::dispatch_message, basp::header hdr{basp::message_type::dispatch_message,
basp::header::named_receiver_flag, basp::header::named_receiver_flag,
...@@ -418,10 +418,10 @@ void basp_broker_state::set_context(connection_handle hdl) { ...@@ -418,10 +418,10 @@ void basp_broker_state::set_context(connection_handle hdl) {
connection_context{ connection_context{
basp::await_header, basp::await_header,
basp::header{basp::message_type::server_handshake, 0, basp::header{basp::message_type::server_handshake, 0,
0, 0, invalid_node_id, invalid_node_id, 0, 0, none, none,
invalid_actor_id, invalid_actor_id}, invalid_actor_id, invalid_actor_id},
hdl, hdl,
invalid_node_id, none,
0, 0,
none}).first; none}).first;
} }
...@@ -473,7 +473,7 @@ behavior basp_broker::make_behavior() { ...@@ -473,7 +473,7 @@ behavior basp_broker::make_behavior() {
CAF_LOG_WARNING("failed to handshake with remote node" CAF_LOG_WARNING("failed to handshake with remote node"
<< CAF_ARG(msg.handle)); << CAF_ARG(msg.handle));
ctx.callback->deliver(ok_atom::value, ctx.callback->deliver(ok_atom::value,
node_id{invalid_node_id}, node_id{none},
strong_actor_ptr{nullptr}, strong_actor_ptr{nullptr},
std::set<std::string>{}); std::set<std::string>{});
} }
...@@ -524,9 +524,9 @@ behavior basp_broker::make_behavior() { ...@@ -524,9 +524,9 @@ behavior basp_broker::make_behavior() {
} }
if (system().node() == src->node()) if (system().node() == src->node())
system().registry().put(src->id(), src); system().registry().put(src->id(), src);
auto writer = make_callback([&](serializer& sink) { auto writer = make_callback([&](serializer& sink) -> error {
std::vector<actor_addr> stages; std::vector<actor_addr> stages;
sink << dest_name << stages << msg; return sink(dest_name, stages, const_cast<message&>(msg));
}); });
basp::header hdr{basp::message_type::dispatch_message, basp::header hdr{basp::message_type::dispatch_message,
basp::header::named_receiver_flag, basp::header::named_receiver_flag,
...@@ -555,7 +555,7 @@ behavior basp_broker::make_behavior() { ...@@ -555,7 +555,7 @@ behavior basp_broker::make_behavior() {
auto nid = state.instance.tbl().lookup_direct(msg.handle); auto nid = state.instance.tbl().lookup_direct(msg.handle);
// tell BASP instance we've lost connection // tell BASP instance we've lost connection
state.instance.handle_node_shutdown(nid); state.instance.handle_node_shutdown(nid);
CAF_ASSERT(nid == invalid_node_id CAF_ASSERT(nid == none
|| ! state.instance.tbl().reachable(nid)); || ! state.instance.tbl().reachable(nid));
}, },
// received from underlying broker implementation // received from underlying broker implementation
...@@ -610,9 +610,9 @@ behavior basp_broker::make_behavior() { ...@@ -610,9 +610,9 @@ behavior basp_broker::make_behavior() {
}, },
[=](unpublish_atom, const actor_addr& whom, uint16_t port) -> result<void> { [=](unpublish_atom, const actor_addr& whom, uint16_t port) -> result<void> {
CAF_LOG_TRACE(CAF_ARG(whom) << CAF_ARG(port)); CAF_LOG_TRACE(CAF_ARG(whom) << CAF_ARG(port));
auto cb = make_callback([&](const strong_actor_ptr&, uint16_t x) { auto cb = make_callback([&](const strong_actor_ptr&, uint16_t x) -> error {
try { close(hdl_by_port(x)); } close(hdl_by_port(x));
catch (std::exception&) { } return none;
}); });
if (state.instance.remove_published_actor(whom, port, &cb) == 0) if (state.instance.remove_published_actor(whom, port, &cb) == 0)
return sec::no_actor_published_at_port; return sec::no_actor_published_at_port;
......
...@@ -63,7 +63,7 @@ bool operator==(const header& lhs, const header& rhs) { ...@@ -63,7 +63,7 @@ bool operator==(const header& lhs, const header& rhs) {
namespace { namespace {
bool valid(const node_id& val) { bool valid(const node_id& val) {
return val != invalid_node_id; return val != none;
} }
template <class T> template <class T>
......
...@@ -43,7 +43,7 @@ instance::instance(abstract_broker* parent, callee& lstnr) ...@@ -43,7 +43,7 @@ instance::instance(abstract_broker* parent, callee& lstnr)
: tbl_(parent), : tbl_(parent),
this_node_(parent->system().node()), this_node_(parent->system().node()),
callee_(lstnr) { callee_(lstnr) {
CAF_ASSERT(this_node_ != invalid_node_id); CAF_ASSERT(this_node_ != none);
} }
connection_state instance::handle(execution_unit* ctx, connection_state instance::handle(execution_unit* ctx,
...@@ -52,8 +52,9 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -52,8 +52,9 @@ connection_state instance::handle(execution_unit* ctx,
CAF_LOG_TRACE(CAF_ARG(dm) << CAF_ARG(is_payload)); CAF_LOG_TRACE(CAF_ARG(dm) << CAF_ARG(is_payload));
// function object providing cleanup code on errors // function object providing cleanup code on errors
auto err = [&]() -> connection_state { auto err = [&]() -> connection_state {
auto cb = make_callback([&](const node_id& nid){ auto cb = make_callback([&](const node_id& nid) -> error {
callee_.purge_state(nid); callee_.purge_state(nid);
return none;
}); });
tbl_.erase_direct(dm.handle, cb); tbl_.erase_direct(dm.handle, cb);
return close_connection; return close_connection;
...@@ -67,8 +68,8 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -67,8 +68,8 @@ connection_state instance::handle(execution_unit* ctx,
} }
} else { } else {
binary_deserializer bd{ctx, dm.buf}; binary_deserializer bd{ctx, dm.buf};
bd >> hdr; auto e = bd(hdr);
if (! valid(hdr)) { if (e || ! valid(hdr)) {
CAF_LOG_WARNING("received invalid header:" << CAF_ARG(hdr)); CAF_LOG_WARNING("received invalid header:" << CAF_ARG(hdr));
return err(); return err();
} }
...@@ -84,7 +85,9 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -84,7 +85,9 @@ connection_state instance::handle(execution_unit* ctx,
auto path = lookup(hdr.dest_node); auto path = lookup(hdr.dest_node);
if (path) { if (path) {
binary_serializer bs{ctx, path->wr_buf}; binary_serializer bs{ctx, path->wr_buf};
bs << hdr; auto e = bs(hdr);
if (e)
return err();
if (payload) if (payload)
bs.apply_raw(payload->size(), payload->data()); bs.apply_raw(payload->size(), payload->data());
tbl_.flush(*path); tbl_.flush(*path);
...@@ -118,12 +121,16 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -118,12 +121,16 @@ connection_state instance::handle(execution_unit* ctx,
if (payload_valid()) { if (payload_valid()) {
binary_deserializer bd{ctx, *payload}; binary_deserializer bd{ctx, *payload};
std::string remote_appid; std::string remote_appid;
bd >> remote_appid; auto e = bd(remote_appid);
if (e)
return err();
if (remote_appid != callee_.system().config().middleman_app_identifier) { if (remote_appid != callee_.system().config().middleman_app_identifier) {
CAF_LOG_ERROR("app identifier mismatch"); CAF_LOG_ERROR("app identifier mismatch");
return err(); return err();
} }
bd >> aid >> sigs; e = bd(aid, sigs);
if (e)
return err();
} else { } else {
CAF_LOG_ERROR("fail to receive the app identifier"); CAF_LOG_ERROR("fail to receive the app identifier");
return err(); return err();
...@@ -166,7 +173,9 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -166,7 +173,9 @@ connection_state instance::handle(execution_unit* ctx,
if (payload_valid()) { if (payload_valid()) {
binary_deserializer bd{ctx, *payload}; binary_deserializer bd{ctx, *payload};
std::string remote_appid; std::string remote_appid;
bd >> remote_appid; auto e = bd(remote_appid);
if (e)
return err();
if (remote_appid != callee_.system().config().middleman_app_identifier) { if (remote_appid != callee_.system().config().middleman_app_identifier) {
CAF_LOG_ERROR("app identifier mismatch"); CAF_LOG_ERROR("app identifier mismatch");
return err(); return err();
...@@ -188,7 +197,7 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -188,7 +197,7 @@ connection_state instance::handle(execution_unit* ctx,
// in case the sender of this message was received via a third node, // in case the sender of this message was received via a third node,
// we assume that that node to offers a route to the original source // we assume that that node to offers a route to the original source
auto last_hop = tbl_.lookup_direct(dm.handle); auto last_hop = tbl_.lookup_direct(dm.handle);
if (hdr.source_node != invalid_node_id if (hdr.source_node != none
&& hdr.source_node != this_node_ && hdr.source_node != this_node_
&& last_hop != hdr.source_node && last_hop != hdr.source_node
&& tbl_.lookup_direct(hdr.source_node) == invalid_connection_handle && tbl_.lookup_direct(hdr.source_node) == invalid_connection_handle
...@@ -198,9 +207,14 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -198,9 +207,14 @@ connection_state instance::handle(execution_unit* ctx,
auto receiver_name = static_cast<atom_value>(0); auto receiver_name = static_cast<atom_value>(0);
std::vector<strong_actor_ptr> forwarding_stack; std::vector<strong_actor_ptr> forwarding_stack;
message msg; message msg;
if (hdr.has(header::named_receiver_flag)) if (hdr.has(header::named_receiver_flag)) {
bd >> receiver_name; auto e = bd(receiver_name);
bd >> forwarding_stack >> msg; if (e)
return err();
}
auto e = bd(forwarding_stack, msg);
if (e)
return err();
CAF_LOG_DEBUG(CAF_ARG(forwarding_stack) << CAF_ARG(msg)); CAF_LOG_DEBUG(CAF_ARG(forwarding_stack) << CAF_ARG(msg));
if (hdr.has(header::named_receiver_flag)) if (hdr.has(header::named_receiver_flag))
callee_.deliver(hdr.source_node, hdr.source_actor, receiver_name, callee_.deliver(hdr.source_node, hdr.source_actor, receiver_name,
...@@ -220,7 +234,9 @@ connection_state instance::handle(execution_unit* ctx, ...@@ -220,7 +234,9 @@ connection_state instance::handle(execution_unit* ctx,
return err(); return err();
binary_deserializer bd{ctx, *payload}; binary_deserializer bd{ctx, *payload};
error fail_state; error fail_state;
bd >> fail_state; auto e = bd(fail_state);
if (e)
return err();
callee_.kill_proxy(hdr.source_node, hdr.source_actor, fail_state); callee_.kill_proxy(hdr.source_node, hdr.source_actor, fail_state);
break; break;
} }
...@@ -247,11 +263,12 @@ void instance::handle_heartbeat(execution_unit* ctx) { ...@@ -247,11 +263,12 @@ void instance::handle_heartbeat(execution_unit* ctx) {
void instance::handle_node_shutdown(const node_id& affected_node) { void instance::handle_node_shutdown(const node_id& affected_node) {
CAF_LOG_TRACE(CAF_ARG(affected_node)); CAF_LOG_TRACE(CAF_ARG(affected_node));
if (affected_node == invalid_node_id) if (affected_node == none)
return; return;
CAF_LOG_INFO("lost direct connection:" << CAF_ARG(affected_node)); CAF_LOG_INFO("lost direct connection:" << CAF_ARG(affected_node));
auto cb = make_callback([&](const node_id& nid){ auto cb = make_callback([&](const node_id& nid) -> error {
callee_.purge_state(nid); callee_.purge_state(nid);
return none;
}); });
tbl_.erase(affected_node, cb); tbl_.erase(affected_node, cb);
} }
...@@ -337,8 +354,9 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender, ...@@ -337,8 +354,9 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
notify<hook::message_sending_failed>(sender, receiver, mid, msg); notify<hook::message_sending_failed>(sender, receiver, mid, msg);
return false; return false;
} }
auto writer = make_callback([&](serializer& sink) { auto writer = make_callback([&](serializer& sink) -> error {
sink << forwarding_stack << msg; return sink(const_cast<std::vector<strong_actor_ptr>&>(forwarding_stack),
const_cast<message&>(msg));
}); });
header hdr{message_type::dispatch_message, 0, 0, mid.integer_value(), header hdr{message_type::dispatch_message, 0, 0, mid.integer_value(),
sender ? sender->node() : this_node(), receiver->node(), sender ? sender->node() : this_node(), receiver->node(),
...@@ -352,7 +370,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender, ...@@ -352,7 +370,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
void instance::write(execution_unit* ctx, buffer_type& buf, void instance::write(execution_unit* ctx, buffer_type& buf,
header& hdr, payload_writer* pw) { header& hdr, payload_writer* pw) {
CAF_LOG_TRACE(CAF_ARG(hdr)); CAF_LOG_TRACE(CAF_ARG(hdr));
try { error err;
if (pw) { if (pw) {
auto pos = buf.size(); auto pos = buf.size();
// write payload first (skip first 72 bytes and write header later) // write payload first (skip first 72 bytes and write header later)
...@@ -364,15 +382,13 @@ void instance::write(execution_unit* ctx, buffer_type& buf, ...@@ -364,15 +382,13 @@ void instance::write(execution_unit* ctx, buffer_type& buf,
CAF_ASSERT(plen <= std::numeric_limits<uint32_t>::max()); CAF_ASSERT(plen <= std::numeric_limits<uint32_t>::max());
hdr.payload_len = static_cast<uint32_t>(plen); hdr.payload_len = static_cast<uint32_t>(plen);
stream_serializer<charbuf> out{ctx, buf.data() + pos, basp::header_size}; stream_serializer<charbuf> out{ctx, buf.data() + pos, basp::header_size};
out << hdr; err = out(hdr);
} else { } else {
binary_serializer bs{ctx, buf}; binary_serializer bs{ctx, buf};
bs << hdr; err = bs(hdr);
}
}
catch (std::exception& e) {
CAF_LOG_ERROR(CAF_ARG(e.what()));
} }
if (err)
CAF_LOG_ERROR(CAF_ARG(err));
} }
void instance::write_server_handshake(execution_unit* ctx, void instance::write_server_handshake(execution_unit* ctx,
...@@ -387,17 +403,22 @@ void instance::write_server_handshake(execution_unit* ctx, ...@@ -387,17 +403,22 @@ void instance::write_server_handshake(execution_unit* ctx,
pa = &i->second; pa = &i->second;
} }
CAF_LOG_DEBUG_IF(! pa && port, "no actor published"); CAF_LOG_DEBUG_IF(! pa && port, "no actor published");
auto writer = make_callback([&](serializer& sink) { auto writer = make_callback([&](serializer& sink) -> error {
sink << callee_.system().config().middleman_app_identifier; auto& ref = callee_.system().config().middleman_app_identifier;
auto e = sink(const_cast<std::string&>(ref));
if (e)
return e;
if (pa) { if (pa) {
auto i = pa->first ? pa->first->id() : invalid_actor_id; auto i = pa->first ? pa->first->id() : invalid_actor_id;
sink << i << pa->second; return sink(i, pa->second);
} else { } else {
sink << invalid_actor_id << std::set<std::string>{}; auto aid = invalid_actor_id;
std::set<std::string> tmp;
return sink(aid, tmp);
} }
}); });
header hdr{message_type::server_handshake, 0, 0, version, header hdr{message_type::server_handshake, 0, 0, version,
this_node_, invalid_node_id, this_node_, none,
pa && pa->first ? pa->first->id() : invalid_actor_id, pa && pa->first ? pa->first->id() : invalid_actor_id,
invalid_actor_id}; invalid_actor_id};
write(ctx, out_buf, hdr, &writer); write(ctx, out_buf, hdr, &writer);
...@@ -407,8 +428,9 @@ void instance::write_client_handshake(execution_unit* ctx, ...@@ -407,8 +428,9 @@ void instance::write_client_handshake(execution_unit* ctx,
buffer_type& buf, buffer_type& buf,
const node_id& remote_side) { const node_id& remote_side) {
CAF_LOG_TRACE(CAF_ARG(remote_side)); CAF_LOG_TRACE(CAF_ARG(remote_side));
auto writer = make_callback([&](serializer& sink) { auto writer = make_callback([&](serializer& sink) -> error {
sink << callee_.system().config().middleman_app_identifier; auto& str = callee_.system().config().middleman_app_identifier;
return sink(const_cast<std::string&>(str));
}); });
header hdr{message_type::client_handshake, 0, 0, 0, header hdr{message_type::client_handshake, 0, 0, 0,
this_node_, remote_side, invalid_actor_id, invalid_actor_id}; this_node_, remote_side, invalid_actor_id, invalid_actor_id};
...@@ -427,8 +449,8 @@ void instance::write_kill_proxy(execution_unit* ctx, buffer_type& buf, ...@@ -427,8 +449,8 @@ void instance::write_kill_proxy(execution_unit* ctx, buffer_type& buf,
const node_id& dest_node, actor_id aid, const node_id& dest_node, actor_id aid,
const error& rsn) { const error& rsn) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn)); CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn));
auto writer = make_callback([&](serializer& sink) { auto writer = make_callback([&](serializer& sink) -> error {
sink << rsn; return sink(const_cast<error&>(rsn));
}); });
header hdr{message_type::kill_proxy, 0, 0, 0, header hdr{message_type::kill_proxy, 0, 0, 0,
this_node_, dest_node, aid, invalid_actor_id}; this_node_, dest_node, aid, invalid_actor_id};
......
...@@ -58,7 +58,7 @@ void routing_table::flush(const route& r) { ...@@ -58,7 +58,7 @@ void routing_table::flush(const route& r) {
} }
node_id routing_table::lookup_direct(const connection_handle& hdl) const { node_id routing_table::lookup_direct(const connection_handle& hdl) const {
return get_opt(direct_by_hdl_, hdl, invalid_node_id); return get_opt(direct_by_hdl_, hdl, none);
} }
connection_handle routing_table::lookup_direct(const node_id& nid) const { connection_handle routing_table::lookup_direct(const node_id& nid) const {
...@@ -68,9 +68,9 @@ connection_handle routing_table::lookup_direct(const node_id& nid) const { ...@@ -68,9 +68,9 @@ connection_handle routing_table::lookup_direct(const node_id& nid) const {
node_id routing_table::lookup_indirect(const node_id& nid) const { node_id routing_table::lookup_indirect(const node_id& nid) const {
auto i = indirect_.find(nid); auto i = indirect_.find(nid);
if (i == indirect_.end()) if (i == indirect_.end())
return invalid_node_id; return none;
if (i->second.empty()) if (i->second.empty())
return invalid_node_id; return none;
return *i->second.begin(); return *i->second.begin();
} }
......
...@@ -103,17 +103,25 @@ static constexpr uint32_t num_remote_nodes = 2; ...@@ -103,17 +103,25 @@ static constexpr uint32_t num_remote_nodes = 2;
using buffer = std::vector<char>; using buffer = std::vector<char>;
string hexstr(const buffer& buf) { std::string hexstr(const buffer& buf) {
ostringstream oss; return deep_to_string(meta::hex_formatted(), buf);
oss << hex;
oss.fill('0');
for (auto& c : buf) {
oss.width(2);
oss << int{static_cast<uint8_t>(c)};
}
return oss.str();
} }
struct node {
std::string name;
node_id id;
connection_handle connection;
union { scoped_actor dummy_actor; };
node() {
// nop
}
~node() {
// nop
}
};
class fixture { class fixture {
public: public:
fixture(bool autoconn = false) fixture(bool autoconn = false)
...@@ -126,7 +134,6 @@ public: ...@@ -126,7 +134,6 @@ public:
auto hdl = mm.named_broker<basp_broker>(basp_atom); auto hdl = mm.named_broker<basp_broker>(basp_atom);
aut_ = static_cast<basp_broker*>(actor_cast<abstract_actor*>(hdl)); aut_ = static_cast<basp_broker*>(actor_cast<abstract_actor*>(hdl));
this_node_ = system.node(); this_node_ = system.node();
CAF_MESSAGE("this node: " << to_string(this_node_));
self_.reset(new scoped_actor{system}); self_.reset(new scoped_actor{system});
ahdl_ = accept_handle::from_int(1); ahdl_ = accept_handle::from_int(1);
mpx_->assign_tcp_doorman(aut_, ahdl_); mpx_->assign_tcp_doorman(aut_, ahdl_);
...@@ -134,34 +141,53 @@ public: ...@@ -134,34 +141,53 @@ public:
registry_->put((*self_)->id(), actor_cast<strong_actor_ptr>(*self_)); registry_->put((*self_)->id(), actor_cast<strong_actor_ptr>(*self_));
// first remote node is everything of this_node + 1, then +2, etc. // first remote node is everything of this_node + 1, then +2, etc.
for (uint32_t i = 0; i < num_remote_nodes; ++i) { for (uint32_t i = 0; i < num_remote_nodes; ++i) {
auto& n = nodes_[i];
node_id::host_id_type tmp = this_node_.host_id(); node_id::host_id_type tmp = this_node_.host_id();
for (auto& c : tmp) for (auto& c : tmp)
c = static_cast<uint8_t>(c + i + 1); c = static_cast<uint8_t>(c + i + 1);
remote_node_[i] = node_id{this_node_.process_id() + i + 1, tmp}; n.id = node_id{this_node_.process_id() + i + 1, tmp};
remote_hdl_[i] = connection_handle::from_int(i + 1); n.connection = connection_handle::from_int(i + 1);
auto& ptr = pseudo_remote_[i]; new (&n.dummy_actor) scoped_actor(system);
ptr.reset(new scoped_actor{system});
// register all pseudo remote actors in the registry // register all pseudo remote actors in the registry
registry_->put((*ptr)->id(), actor_cast<strong_actor_ptr>(*ptr)); registry_->put(n.dummy_actor->id(),
actor_cast<strong_actor_ptr>(n.dummy_actor));
} }
// make sure all init messages are handled properly // make sure all init messages are handled properly
mpx_->flush_runnables(); mpx_->flush_runnables();
nodes_[0].name = "Jupiter";
nodes_[1].name = "Mars";
CAF_REQUIRE_NOT_EQUAL(jupiter().connection, mars().connection);
CAF_REQUIRE(! (jupiter().id == mars().id));
CAF_REQUIRE(jupiter().id != mars().id);
CAF_REQUIRE(jupiter().id < mars().id);
CAF_MESSAGE("Earth: " << to_string(this_node_));
CAF_MESSAGE("Jupiter: " << to_string(jupiter().id));
CAF_MESSAGE("Mars: " << to_string(mars().id));
}
~fixture() {
this_node_ = none;
self_.reset();
for (auto& n : nodes_) {
n.id = none;
n.dummy_actor.~scoped_actor();
}
} }
uint32_t serialized_size(const message& msg) { uint32_t serialized_size(const message& msg) {
buffer buf; buffer buf;
binary_serializer bs{mpx_, buf}; binary_serializer bs{mpx_, buf};
bs << msg; auto e = bs(const_cast<message&>(msg));
CAF_REQUIRE(! e);
return static_cast<uint32_t>(buf.size()); return static_cast<uint32_t>(buf.size());
} }
~fixture() { node& jupiter() {
this_node_ = invalid_node_id; return nodes_[0];
self_.reset(); }
for (auto& nid : remote_node_)
nid = invalid_node_id; node& mars() {
for (auto& ptr : pseudo_remote_) return nodes_[1];
ptr.reset();
} }
// our "virtual communication backend" // our "virtual communication backend"
...@@ -184,31 +210,6 @@ public: ...@@ -184,31 +210,6 @@ public:
return *self_; return *self_;
} }
// dummy remote node ID
node_id& remote_node(size_t i) {
return remote_node_[i];
}
// dummy remote node ID by connection
node_id& remote_node(connection_handle hdl) {
return remote_node_[static_cast<size_t>(hdl.id() - 1)];
}
// handle to a virtual connection
connection_handle remote_hdl(size_t i) {
return remote_hdl_[i];
}
// an actor reference representing a remote actor
scoped_actor& pseudo_remote(size_t i) {
return *pseudo_remote_[i];
}
// an actor reference representing a remote actor (by connection)
scoped_actor& pseudo_remote(connection_handle hdl) {
return *pseudo_remote_[static_cast<size_t>(hdl.id() - 1)];
}
// implementation of the Binary Actor System Protocol // implementation of the Binary Actor System Protocol
basp::instance& instance() { basp::instance& instance() {
return aut()->state.instance; return aut()->state.instance;
...@@ -231,14 +232,9 @@ public: ...@@ -231,14 +232,9 @@ public:
using payload_writer = basp::instance::payload_writer; using payload_writer = basp::instance::payload_writer;
void to_payload(binary_serializer&) { template <class... Ts>
// end of recursion void to_payload(binary_serializer& bs, const Ts&... xs) {
} bs(const_cast<Ts&>(xs)...);
template <class T, class... Ts>
void to_payload(binary_serializer& bs, const T& x, const Ts&... xs) {
bs << x;
to_payload(bs, xs...);
} }
template <class... Ts> template <class... Ts>
...@@ -254,10 +250,11 @@ public: ...@@ -254,10 +250,11 @@ public:
template <class T, class... Ts> template <class T, class... Ts>
void to_buf(buffer& buf, basp::header& hdr, payload_writer* writer, void to_buf(buffer& buf, basp::header& hdr, payload_writer* writer,
const T& x, const Ts&... xs) { const T& x, const Ts&... xs) {
auto pw = make_callback([&](serializer& sink) { auto pw = make_callback([&](serializer& sink) -> error {
if (writer) if (writer)
(*writer)(sink); return error::eval([&] { return (*writer)(sink); },
sink << x; [&] { return sink(const_cast<T&>(x)); });
return sink(const_cast<T&>(x));
}); });
to_buf(buf, hdr, &pw, xs...); to_buf(buf, hdr, &pw, xs...);
} }
...@@ -265,7 +262,8 @@ public: ...@@ -265,7 +262,8 @@ public:
std::pair<basp::header, buffer> from_buf(const buffer& buf) { std::pair<basp::header, buffer> from_buf(const buffer& buf) {
basp::header hdr; basp::header hdr;
binary_deserializer bd{mpx_, buf}; binary_deserializer bd{mpx_, buf};
bd >> hdr; auto e = bd(hdr);
CAF_REQUIRE(! e);
buffer payload; buffer payload;
if (hdr.payload_len > 0) { if (hdr.payload_len > 0) {
std::copy(buf.begin() + basp::header_size, buf.end(), std::copy(buf.begin() + basp::header_size, buf.end(),
...@@ -274,14 +272,15 @@ public: ...@@ -274,14 +272,15 @@ public:
return {hdr, std::move(payload)}; return {hdr, std::move(payload)};
} }
void connect_node(size_t i, void connect_node(node& n,
optional<accept_handle> ax = none, optional<accept_handle> ax = none,
actor_id published_actor_id = invalid_actor_id, actor_id published_actor_id = invalid_actor_id,
set<string> published_actor_ifs = std::set<std::string>{}) { set<string> published_actor_ifs = std::set<std::string>{}) {
auto src = ax ? *ax : ahdl_; auto src = ax ? *ax : ahdl_;
CAF_MESSAGE("connect remote node " << i << ", connection ID = " << (i + 1) CAF_MESSAGE("connect remote node " << n.name
<< ", connection ID = " << n.connection.id()
<< ", acceptor ID = " << src.id()); << ", acceptor ID = " << src.id());
auto hdl = remote_hdl(i); auto hdl = n.connection;
mpx_->add_pending_connect(src, hdl); mpx_->add_pending_connect(src, hdl);
mpx_->assign_tcp_scribe(aut(), hdl); mpx_->assign_tcp_scribe(aut(), hdl);
mpx_->accept_connection(src); mpx_->accept_connection(src);
...@@ -289,11 +288,11 @@ public: ...@@ -289,11 +288,11 @@ public:
// before we send the client handshake // before we send the client handshake
mock(hdl, mock(hdl,
{basp::message_type::client_handshake, 0, 0, 0, {basp::message_type::client_handshake, 0, 0, 0,
remote_node(i), this_node(), n.id, this_node(),
invalid_actor_id, invalid_actor_id}, std::string{}) invalid_actor_id, invalid_actor_id}, std::string{})
.expect(hdl, .expect(hdl,
basp::message_type::server_handshake, no_flags, basp::message_type::server_handshake, no_flags,
any_vals, basp::version, this_node(), node_id{invalid_node_id}, any_vals, basp::version, this_node(), node_id{none},
published_actor_id, invalid_actor_id, std::string{}, published_actor_id, invalid_actor_id, std::string{},
published_actor_id, published_actor_id,
published_actor_ifs) published_actor_ifs)
...@@ -303,17 +302,17 @@ public: ...@@ -303,17 +302,17 @@ public:
basp::message_type::dispatch_message, basp::message_type::dispatch_message,
basp::header::named_receiver_flag, any_vals, basp::header::named_receiver_flag, any_vals,
no_operation_data, no_operation_data,
this_node(), remote_node(i), this_node(), n.id,
any_vals, invalid_actor_id, any_vals, invalid_actor_id,
spawn_serv_atom, spawn_serv_atom,
std::vector<actor_addr>{}, std::vector<actor_addr>{},
make_message(sys_atom::value, get_atom::value, "info")); make_message(sys_atom::value, get_atom::value, "info"));
// test whether basp instance correctly updates the // test whether basp instance correctly updates the
// routing table upon receiving client handshakes // routing table upon receiving client handshakes
auto path = tbl().lookup(remote_node(i)); auto path = tbl().lookup(n.id);
CAF_REQUIRE(path); CAF_REQUIRE(path);
CAF_CHECK_EQUAL(path->hdl, remote_hdl(i)); CAF_CHECK_EQUAL(path->hdl, n.connection);
CAF_CHECK_EQUAL(path->next_hop, remote_node(i)); CAF_CHECK_EQUAL(path->next_hop, n.id);
} }
std::pair<basp::header, buffer> read_from_out_buf(connection_handle hdl) { std::pair<basp::header, buffer> read_from_out_buf(connection_handle hdl) {
...@@ -336,7 +335,8 @@ public: ...@@ -336,7 +335,8 @@ public:
binary_deserializer source{mpx_, buf}; binary_deserializer source{mpx_, buf};
std::vector<strong_actor_ptr> stages; std::vector<strong_actor_ptr> stages;
message msg; message msg;
source >> stages >> msg; auto e = source(stages, msg);
CAF_REQUIRE(! e);
auto src = actor_cast<strong_actor_ptr>(registry_->get(hdr.source_actor)); auto src = actor_cast<strong_actor_ptr>(registry_->get(hdr.source_actor));
auto dest = registry_->get(hdr.dest_actor); auto dest = registry_->get(hdr.dest_actor);
CAF_REQUIRE(dest); CAF_REQUIRE(dest);
...@@ -380,7 +380,8 @@ public: ...@@ -380,7 +380,8 @@ public:
basp::header hdr; basp::header hdr;
{ // lifetime scope of source { // lifetime scope of source
binary_deserializer source{this_->mpx(), ob}; binary_deserializer source{this_->mpx(), ob};
source >> hdr; auto e = source(hdr);
CAF_REQUIRE_EQUAL(e, none);
} }
buffer payload; buffer payload;
if (hdr.payload_len > 0) { if (hdr.payload_len > 0) {
...@@ -437,9 +438,12 @@ private: ...@@ -437,9 +438,12 @@ private:
network::test_multiplexer* mpx_; network::test_multiplexer* mpx_;
node_id this_node_; node_id this_node_;
unique_ptr<scoped_actor> self_; unique_ptr<scoped_actor> self_;
array<node, num_remote_nodes> nodes_;
/*
array<node_id, num_remote_nodes> remote_node_; array<node_id, num_remote_nodes> remote_node_;
array<connection_handle, num_remote_nodes> remote_hdl_; array<connection_handle, num_remote_nodes> remote_hdl_;
array<unique_ptr<scoped_actor>, num_remote_nodes> pseudo_remote_; array<unique_ptr<scoped_actor>, num_remote_nodes> pseudo_remote_;
*/
actor_registry* registry_; actor_registry* registry_;
}; };
...@@ -465,7 +469,7 @@ CAF_TEST(empty_server_handshake) { ...@@ -465,7 +469,7 @@ CAF_TEST(empty_server_handshake) {
basp::header expected{basp::message_type::server_handshake, 0, basp::header expected{basp::message_type::server_handshake, 0,
static_cast<uint32_t>(payload.size()), static_cast<uint32_t>(payload.size()),
basp::version, basp::version,
this_node(), invalid_node_id, this_node(), none,
invalid_actor_id, invalid_actor_id}; invalid_actor_id, invalid_actor_id};
CAF_CHECK(basp::valid(hdr)); CAF_CHECK(basp::valid(hdr));
CAF_CHECK(basp::is_handshake(hdr)); CAF_CHECK(basp::is_handshake(hdr));
...@@ -481,7 +485,7 @@ CAF_TEST(non_empty_server_handshake) { ...@@ -481,7 +485,7 @@ CAF_TEST(non_empty_server_handshake) {
instance().write_server_handshake(mpx(), buf, uint16_t{4242}); instance().write_server_handshake(mpx(), buf, uint16_t{4242});
buffer expected_buf; buffer expected_buf;
basp::header expected{basp::message_type::server_handshake, 0, 0, basp::header expected{basp::message_type::server_handshake, 0, 0,
basp::version, this_node(), invalid_node_id, basp::version, this_node(), none,
self()->id(), invalid_actor_id}; self()->id(), invalid_actor_id};
to_buf(expected_buf, expected, nullptr, std::string{}, to_buf(expected_buf, expected, nullptr, std::string{},
self()->id(), set<string>{"caf::replies_to<@u16>::with<@u16>"}); self()->id(), set<string>{"caf::replies_to<@u16>::with<@u16>"});
...@@ -489,39 +493,40 @@ CAF_TEST(non_empty_server_handshake) { ...@@ -489,39 +493,40 @@ CAF_TEST(non_empty_server_handshake) {
} }
CAF_TEST(remote_address_and_port) { CAF_TEST(remote_address_and_port) {
CAF_MESSAGE("connect node 1"); CAF_MESSAGE("connect to Mars");
connect_node(1); connect_node(mars());
auto mm = system.middleman().actor_handle(); auto mm = system.middleman().actor_handle();
CAF_MESSAGE("ask MM about node 1"); CAF_MESSAGE("ask MM about node ID of Mars");
self()->send(mm, get_atom::value, remote_node(1)); self()->send(mm, get_atom::value, mars().id);
do { do {
mpx()->exec_runnable(); mpx()->exec_runnable();
} while (! self()->has_next_message()); } while (! self()->has_next_message());
CAF_MESSAGE("receive result of MM"); CAF_MESSAGE("receive result of MM");
self()->receive( self()->receive(
[&](const node_id& nid, const std::string& addr, uint16_t port) { [&](const node_id& nid, const std::string& addr, uint16_t port) {
CAF_CHECK_EQUAL(nid, remote_node(1)); CAF_CHECK_EQUAL(nid, mars().id);
// all test nodes have address "test" and connection handle ID as port // all test nodes have address "test" and connection handle ID as port
CAF_CHECK_EQUAL(addr, "test"); CAF_CHECK_EQUAL(addr, "test");
CAF_CHECK_EQUAL(port, remote_hdl(1).id()); CAF_CHECK_EQUAL(port, mars().connection.id());
} }
); );
} }
CAF_TEST(client_handshake_and_dispatch) { CAF_TEST(client_handshake_and_dispatch) {
connect_node(0); CAF_MESSAGE("connect to Jupiter");
connect_node(jupiter());
// send a message via `dispatch` from node 0 // send a message via `dispatch` from node 0
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
remote_node(0), this_node(), pseudo_remote(0)->id(), self()->id()}, jupiter().id, this_node(), jupiter().dummy_actor->id(), self()->id()},
std::vector<actor_addr>{}, std::vector<actor_addr>{},
make_message(1, 2, 3)) make_message(1, 2, 3))
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::announce_proxy, no_flags, no_payload, basp::message_type::announce_proxy, no_flags, no_payload,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, pseudo_remote(0)->id()); invalid_actor_id, jupiter().dummy_actor->id());
// must've created a proxy for our remote actor // must've created a proxy for our remote actor
CAF_REQUIRE(proxies().count_proxies(remote_node(0)) == 1); CAF_REQUIRE(proxies().count_proxies(jupiter().id) == 1);
// must've send remote node a message that this proxy is monitored now // must've send remote node a message that this proxy is monitored now
// receive the message // receive the message
self()->receive( self()->receive(
...@@ -534,8 +539,8 @@ CAF_TEST(client_handshake_and_dispatch) { ...@@ -534,8 +539,8 @@ CAF_TEST(client_handshake_and_dispatch) {
); );
CAF_MESSAGE("exec message of forwarding proxy"); CAF_MESSAGE("exec message of forwarding proxy");
mpx()->exec_runnable(); mpx()->exec_runnable();
dispatch_out_buf(remote_hdl(0)); // deserialize and send message from out buf dispatch_out_buf(jupiter().connection); // deserialize and send message from out buf
pseudo_remote(0)->receive( jupiter().dummy_actor->receive(
[](int i) { [](int i) {
CAF_CHECK_EQUAL(i, 6); CAF_CHECK_EQUAL(i, 6);
} }
...@@ -544,19 +549,19 @@ CAF_TEST(client_handshake_and_dispatch) { ...@@ -544,19 +549,19 @@ CAF_TEST(client_handshake_and_dispatch) {
CAF_TEST(message_forwarding) { CAF_TEST(message_forwarding) {
// connect two remote nodes // connect two remote nodes
connect_node(0); connect_node(jupiter());
connect_node(1); connect_node(mars());
auto msg = make_message(1, 2, 3); auto msg = make_message(1, 2, 3);
// send a message from node 0 to node 1, forwarded by this node // send a message from node 0 to node 1, forwarded by this node
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
remote_node(0), remote_node(1), jupiter().id, mars().id,
invalid_actor_id, pseudo_remote(1)->id()}, invalid_actor_id, mars().dummy_actor->id()},
msg) msg)
.expect(remote_hdl(1), .expect(mars().connection,
basp::message_type::dispatch_message, no_flags, any_vals, basp::message_type::dispatch_message, no_flags, any_vals,
no_operation_data, remote_node(0), remote_node(1), no_operation_data, jupiter().id, mars().id,
invalid_actor_id, pseudo_remote(1)->id(), invalid_actor_id, mars().dummy_actor->id(),
msg); msg);
} }
...@@ -566,60 +571,60 @@ CAF_TEST(publish_and_connect) { ...@@ -566,60 +571,60 @@ CAF_TEST(publish_and_connect) {
auto res = system.middleman().publish(self(), 4242); auto res = system.middleman().publish(self(), 4242);
CAF_REQUIRE(res == 4242); CAF_REQUIRE(res == 4242);
mpx()->flush_runnables(); // process publish message in basp_broker mpx()->flush_runnables(); // process publish message in basp_broker
connect_node(0, ax, self()->id()); connect_node(jupiter(), ax, self()->id());
} }
CAF_TEST(remote_actor_and_send) { CAF_TEST(remote_actor_and_send) {
constexpr const char* lo = "localhost"; constexpr const char* lo = "localhost";
CAF_MESSAGE("self: " << to_string(self()->address())); CAF_MESSAGE("self: " << to_string(self()->address()));
mpx()->provide_scribe(lo, 4242, remote_hdl(0)); mpx()->provide_scribe(lo, 4242, jupiter().connection);
CAF_REQUIRE(mpx()->has_pending_scribe(lo, 4242)); CAF_REQUIRE(mpx()->has_pending_scribe(lo, 4242));
auto mm1 = system.middleman().actor_handle(); auto mm1 = system.middleman().actor_handle();
actor result{unsafe_actor_handle_init}; actor result{unsafe_actor_handle_init};
auto f = self()->request(mm1, infinite, auto f = self()->request(mm1, infinite,
connect_atom::value, lo, uint16_t{4242}); connect_atom::value, lo, uint16_t{4242});
// wait until BASP broker has received and processed the connect message // wait until BASP broker has received and processed the connect message
while (! aut()->valid(remote_hdl(0))) while (! aut()->valid(jupiter().connection))
mpx()->exec_runnable(); mpx()->exec_runnable();
CAF_REQUIRE(! mpx()->has_pending_scribe(lo, 4242)); CAF_REQUIRE(! mpx()->has_pending_scribe(lo, 4242));
// build a fake server handshake containing the id of our first pseudo actor // build a fake server handshake containing the id of our first pseudo actor
CAF_MESSAGE("server handshake => client handshake + proxy announcement"); CAF_MESSAGE("server handshake => client handshake + proxy announcement");
auto na = registry()->named_actors(); auto na = registry()->named_actors();
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::server_handshake, 0, 0, basp::version, {basp::message_type::server_handshake, 0, 0, basp::version,
remote_node(0), invalid_node_id, jupiter().id, none,
pseudo_remote(0)->id(), invalid_actor_id}, jupiter().dummy_actor->id(), invalid_actor_id},
std::string{}, std::string{},
pseudo_remote(0)->id(), jupiter().dummy_actor->id(),
uint32_t{0}) uint32_t{0})
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::client_handshake, no_flags, 1u, basp::message_type::client_handshake, no_flags, 1u,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, invalid_actor_id, std::string{}) invalid_actor_id, invalid_actor_id, std::string{})
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::dispatch_message, basp::message_type::dispatch_message,
basp::header::named_receiver_flag, any_vals, basp::header::named_receiver_flag, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
any_vals, invalid_actor_id, any_vals, invalid_actor_id,
spawn_serv_atom, spawn_serv_atom,
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message(sys_atom::value, get_atom::value, "info")) make_message(sys_atom::value, get_atom::value, "info"))
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::announce_proxy, no_flags, no_payload, basp::message_type::announce_proxy, no_flags, no_payload,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, pseudo_remote(0)->id()); invalid_actor_id, jupiter().dummy_actor->id());
CAF_MESSAGE("BASP broker should've send the proxy"); CAF_MESSAGE("BASP broker should've send the proxy");
f.receive( f.receive(
[&](ok_atom, node_id nid, strong_actor_ptr res, std::set<std::string> ifs) { [&](ok_atom, node_id nid, strong_actor_ptr res, std::set<std::string> ifs) {
CAF_REQUIRE(res); CAF_REQUIRE(res);
auto aptr = actor_cast<abstract_actor*>(res); auto aptr = actor_cast<abstract_actor*>(res);
CAF_REQUIRE(dynamic_cast<forwarding_actor_proxy*>(aptr) != nullptr); CAF_REQUIRE(dynamic_cast<forwarding_actor_proxy*>(aptr) != nullptr);
CAF_CHECK_EQUAL(proxies().count_proxies(remote_node(0)), 1u); CAF_CHECK_EQUAL(proxies().count_proxies(jupiter().id), 1u);
CAF_CHECK_EQUAL(nid, remote_node(0)); CAF_CHECK_EQUAL(nid, jupiter().id);
CAF_CHECK_EQUAL(res->node(), remote_node(0)); CAF_CHECK_EQUAL(res->node(), jupiter().id);
CAF_CHECK_EQUAL(res->id(), pseudo_remote(0)->id()); CAF_CHECK_EQUAL(res->id(), jupiter().dummy_actor->id());
CAF_CHECK(ifs.empty()); CAF_CHECK(ifs.empty());
auto proxy = proxies().get(remote_node(0), pseudo_remote(0)->id()); auto proxy = proxies().get(jupiter().id, jupiter().dummy_actor->id());
CAF_REQUIRE(proxy != nullptr); CAF_REQUIRE(proxy != nullptr);
CAF_REQUIRE(proxy == res); CAF_REQUIRE(proxy == res);
result = actor_cast<actor>(res); result = actor_cast<actor>(res);
...@@ -633,18 +638,18 @@ CAF_TEST(remote_actor_and_send) { ...@@ -633,18 +638,18 @@ CAF_TEST(remote_actor_and_send) {
mpx()->flush_runnables(); mpx()->flush_runnables();
// mpx()->exec_runnable(); // process forwarded message in basp_broker // mpx()->exec_runnable(); // process forwarded message in basp_broker
mock() mock()
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::dispatch_message, no_flags, any_vals, basp::message_type::dispatch_message, no_flags, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, pseudo_remote(0)->id(), invalid_actor_id, jupiter().dummy_actor->id(),
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message(42)); make_message(42));
auto msg = make_message("hi there!"); auto msg = make_message("hi there!");
CAF_MESSAGE("send message via BASP (from proxy)"); CAF_MESSAGE("send message via BASP (from proxy)");
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
remote_node(0), this_node(), jupiter().id, this_node(),
pseudo_remote(0)->id(), self()->id()}, jupiter().dummy_actor->id(), self()->id()},
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message("hi there!")); make_message("hi there!"));
self()->receive( self()->receive(
...@@ -665,20 +670,20 @@ CAF_TEST(actor_serialize_and_deserialize) { ...@@ -665,20 +670,20 @@ CAF_TEST(actor_serialize_and_deserialize) {
} }
}; };
}; };
connect_node(0); connect_node(jupiter());
auto prx = proxies().get_or_put(remote_node(0), pseudo_remote(0)->id()); auto prx = proxies().get_or_put(jupiter().id, jupiter().dummy_actor->id());
mock() mock()
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::announce_proxy, no_flags, no_payload, basp::message_type::announce_proxy, no_flags, no_payload,
no_operation_data, this_node(), prx->node(), no_operation_data, this_node(), prx->node(),
invalid_actor_id, prx->id()); invalid_actor_id, prx->id());
CAF_CHECK_EQUAL(prx->node(), remote_node(0)); CAF_CHECK_EQUAL(prx->node(), jupiter().id);
CAF_CHECK_EQUAL(prx->id(), pseudo_remote(0)->id()); CAF_CHECK_EQUAL(prx->id(), jupiter().dummy_actor->id());
auto testee = system.spawn(testee_impl); auto testee = system.spawn(testee_impl);
registry()->put(testee->id(), actor_cast<strong_actor_ptr>(testee)); registry()->put(testee->id(), actor_cast<strong_actor_ptr>(testee));
CAF_MESSAGE("send message via BASP (from proxy)"); CAF_MESSAGE("send message via BASP (from proxy)");
auto msg = make_message(actor_cast<actor_addr>(prx)); auto msg = make_message(actor_cast<actor_addr>(prx));
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
prx->node(), this_node(), prx->node(), this_node(),
prx->id(), testee->id()}, prx->id(), testee->id()},
...@@ -686,48 +691,49 @@ CAF_TEST(actor_serialize_and_deserialize) { ...@@ -686,48 +691,49 @@ CAF_TEST(actor_serialize_and_deserialize) {
msg); msg);
// testee must've responded (process forwarded message in BASP broker) // testee must've responded (process forwarded message in BASP broker)
CAF_MESSAGE("wait until BASP broker writes to its output buffer"); CAF_MESSAGE("wait until BASP broker writes to its output buffer");
while (mpx()->output_buffer(remote_hdl(0)).empty()) while (mpx()->output_buffer(jupiter().connection).empty())
mpx()->exec_runnable(); // process forwarded message in basp_broker mpx()->exec_runnable(); // process forwarded message in basp_broker
// output buffer must contain the reflected message // output buffer must contain the reflected message
mock() mock()
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::dispatch_message, no_flags, any_vals, basp::message_type::dispatch_message, no_flags, any_vals,
no_operation_data, this_node(), prx->node(), testee->id(), prx->id(), no_operation_data, this_node(), prx->node(), testee->id(), prx->id(),
std::vector<actor_id>{}, msg); std::vector<actor_id>{}, msg);
} }
CAF_TEST(indirect_connections) { CAF_TEST(indirect_connections) {
// jupiter [remote hdl 0] -> mars [remote hdl 1] -> earth [this_node];
// this node receives a message from jupiter via mars and responds via mars // this node receives a message from jupiter via mars and responds via mars
// and any ad-hoc automatic connection requests are ignored // and any ad-hoc automatic connection requests are ignored
CAF_MESSAGE("self: " << to_string(self()->address())); CAF_MESSAGE("self: " << to_string(self()->address()));
CAF_MESSAGE("publish self at port 4242");
auto ax = accept_handle::from_int(4242); auto ax = accept_handle::from_int(4242);
mpx()->provide_acceptor(4242, ax); mpx()->provide_acceptor(4242, ax);
system.middleman().publish(self(), 4242); system.middleman().publish(self(), 4242);
mpx()->flush_runnables(); // process publish message in basp_broker mpx()->flush_runnables(); // process publish message in basp_broker
// connect to mars CAF_MESSAGE("connect to Mars");
connect_node(1, ax, self()->id()); connect_node(mars(), ax, self()->id());
// now, an actor from jupiter sends a message to us via mars CAF_MESSAGE("actor from Jupiter sends a message to us via Mars");
mock(remote_hdl(1), auto mx = mock(mars().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
remote_node(0), this_node(), jupiter().id, this_node(),
pseudo_remote(0)->id(), self()->id()}, jupiter().dummy_actor->id(), self()->id()},
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message("hello from jupiter!")) make_message("hello from jupiter!"));
CAF_MESSAGE("expect ('sys', 'get', \"info\") from Earth to Jupiter at Mars");
// this asks Jupiter if it has a 'SpawnServ' // this asks Jupiter if it has a 'SpawnServ'
.expect(remote_hdl(1), mx.expect(mars().connection,
basp::message_type::dispatch_message, basp::message_type::dispatch_message,
basp::header::named_receiver_flag, any_vals, basp::header::named_receiver_flag, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
any_vals, invalid_actor_id, any_vals, invalid_actor_id,
spawn_serv_atom, spawn_serv_atom,
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message(sys_atom::value, get_atom::value, "info")) make_message(sys_atom::value, get_atom::value, "info"));
// this tells Jupiter that Earth learned the address of one its actors CAF_MESSAGE("expect announce_proxy message at Mars from Earth to Jupiter");
.expect(remote_hdl(1), mx.expect(mars().connection,
basp::message_type::announce_proxy, no_flags, no_payload, basp::message_type::announce_proxy, no_flags, no_payload,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, pseudo_remote(0)->id()); invalid_actor_id, jupiter().dummy_actor->id());
CAF_MESSAGE("receive message from jupiter"); CAF_MESSAGE("receive message from jupiter");
self()->receive( self()->receive(
[](const std::string& str) -> std::string { [](const std::string& str) -> std::string {
...@@ -737,10 +743,10 @@ CAF_TEST(indirect_connections) { ...@@ -737,10 +743,10 @@ CAF_TEST(indirect_connections) {
); );
mpx()->exec_runnable(); // process forwarded message in basp_broker mpx()->exec_runnable(); // process forwarded message in basp_broker
mock() mock()
.expect(remote_hdl(1), .expect(mars().connection,
basp::message_type::dispatch_message, no_flags, any_vals, basp::message_type::dispatch_message, no_flags, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
self()->id(), pseudo_remote(0)->id(), self()->id(), jupiter().dummy_actor->id(),
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message("hello from earth!")); make_message("hello from earth!"));
} }
...@@ -757,7 +763,7 @@ CAF_TEST(automatic_connection) { ...@@ -757,7 +763,7 @@ CAF_TEST(automatic_connection) {
// jupiter [remote hdl 0] -> mars [remote hdl 1] -> earth [this_node] // jupiter [remote hdl 0] -> mars [remote hdl 1] -> earth [this_node]
// (this node receives a message from jupiter via mars and responds via mars, // (this node receives a message from jupiter via mars and responds via mars,
// but then also establishes a connection to jupiter directly) // but then also establishes a connection to jupiter directly)
mpx()->provide_scribe("jupiter", 8080, remote_hdl(0)); mpx()->provide_scribe("jupiter", 8080, jupiter().connection);
CAF_CHECK(mpx()->has_pending_scribe("jupiter", 8080)); CAF_CHECK(mpx()->has_pending_scribe("jupiter", 8080));
CAF_MESSAGE("self: " << to_string(self()->address())); CAF_MESSAGE("self: " << to_string(self()->address()));
auto ax = accept_handle::from_int(4242); auto ax = accept_handle::from_int(4242);
...@@ -765,46 +771,46 @@ CAF_TEST(automatic_connection) { ...@@ -765,46 +771,46 @@ CAF_TEST(automatic_connection) {
system.middleman().publish(self(), 4242); system.middleman().publish(self(), 4242);
mpx()->flush_runnables(); // process publish message in basp_broker mpx()->flush_runnables(); // process publish message in basp_broker
CAF_MESSAGE("connect to mars"); CAF_MESSAGE("connect to mars");
connect_node(1, ax, self()->id()); connect_node(mars(), ax, self()->id());
CAF_CHECK_EQUAL(tbl().lookup_direct(remote_node(1)).id(), remote_hdl(1).id()); CAF_CHECK_EQUAL(tbl().lookup_direct(mars().id).id(), mars().connection.id());
CAF_MESSAGE("simulate that an actor from jupiter " CAF_MESSAGE("simulate that an actor from jupiter "
"sends a message to us via mars"); "sends a message to us via mars");
mock(remote_hdl(1), mock(mars().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
remote_node(0), this_node(), jupiter().id, this_node(),
pseudo_remote(0)->id(), self()->id()}, jupiter().dummy_actor->id(), self()->id()},
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message("hello from jupiter!")) make_message("hello from jupiter!"))
.expect(remote_hdl(1), .expect(mars().connection,
basp::message_type::dispatch_message, basp::message_type::dispatch_message,
basp::header::named_receiver_flag, any_vals, no_operation_data, basp::header::named_receiver_flag, any_vals, no_operation_data,
this_node(), remote_node(0), any_vals, invalid_actor_id, this_node(), jupiter().id, any_vals, invalid_actor_id,
spawn_serv_atom, spawn_serv_atom,
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message(sys_atom::value, get_atom::value, "info")) make_message(sys_atom::value, get_atom::value, "info"))
.expect(remote_hdl(1), .expect(mars().connection,
basp::message_type::dispatch_message, basp::message_type::dispatch_message,
basp::header::named_receiver_flag, any_vals, basp::header::named_receiver_flag, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
any_vals, // actor ID of an actor spawned by the BASP broker any_vals, // actor ID of an actor spawned by the BASP broker
invalid_actor_id, invalid_actor_id,
config_serv_atom, config_serv_atom,
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message(get_atom::value, "basp.default-connectivity")) make_message(get_atom::value, "basp.default-connectivity"))
.expect(remote_hdl(1), .expect(mars().connection,
basp::message_type::announce_proxy, no_flags, no_payload, basp::message_type::announce_proxy, no_flags, no_payload,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, pseudo_remote(0)->id()); invalid_actor_id, jupiter().dummy_actor->id());
CAF_CHECK_EQUAL(mpx()->output_buffer(remote_hdl(1)).size(), 0u); CAF_CHECK_EQUAL(mpx()->output_buffer(mars().connection).size(), 0u);
CAF_CHECK_EQUAL(tbl().lookup_indirect(remote_node(0)), remote_node(1)); CAF_CHECK_EQUAL(tbl().lookup_indirect(jupiter().id), mars().id);
CAF_CHECK_EQUAL(tbl().lookup_indirect(remote_node(1)), invalid_node_id); CAF_CHECK_EQUAL(tbl().lookup_indirect(mars().id), none);
auto connection_helper = system.latest_actor_id(); auto connection_helper = system.latest_actor_id();
CAF_CHECK_EQUAL(mpx()->output_buffer(remote_hdl(1)).size(), 0u); CAF_CHECK_EQUAL(mpx()->output_buffer(mars().connection).size(), 0u);
// create a dummy config server and respond to the name lookup // create a dummy config server and respond to the name lookup
CAF_MESSAGE("receive ConfigServ of jupiter"); CAF_MESSAGE("receive ConfigServ of jupiter");
network::address_listing res; network::address_listing res;
res[network::protocol::ipv4].push_back("jupiter"); res[network::protocol::ipv4].push_back("jupiter");
mock(remote_hdl(1), mock(mars().connection,
{basp::message_type::dispatch_message, 0, 0, 0, {basp::message_type::dispatch_message, 0, 0, 0,
this_node(), this_node(), this_node(), this_node(),
invalid_actor_id, connection_helper}, invalid_actor_id, connection_helper},
...@@ -815,23 +821,23 @@ CAF_TEST(automatic_connection) { ...@@ -815,23 +821,23 @@ CAF_TEST(automatic_connection) {
// send the scribe handle over to the BASP broker // send the scribe handle over to the BASP broker
while (mpx()->has_pending_scribe("jupiter", 8080)) while (mpx()->has_pending_scribe("jupiter", 8080))
mpx()->flush_runnables(); mpx()->flush_runnables();
CAF_REQUIRE(mpx()->output_buffer(remote_hdl(1)).size() == 0); CAF_REQUIRE(mpx()->output_buffer(mars().connection).size() == 0);
// send handshake from jupiter // send handshake from jupiter
mock(remote_hdl(0), mock(jupiter().connection,
{basp::message_type::server_handshake, 0, 0, basp::version, {basp::message_type::server_handshake, 0, 0, basp::version,
remote_node(0), invalid_node_id, jupiter().id, none,
pseudo_remote(0)->id(), invalid_actor_id}, jupiter().dummy_actor->id(), invalid_actor_id},
std::string{}, std::string{},
pseudo_remote(0)->id(), jupiter().dummy_actor->id(),
uint32_t{0}) uint32_t{0})
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::client_handshake, no_flags, 1u, basp::message_type::client_handshake, no_flags, 1u,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
invalid_actor_id, invalid_actor_id, std::string{}); invalid_actor_id, invalid_actor_id, std::string{});
CAF_CHECK_EQUAL(tbl().lookup_indirect(remote_node(0)), invalid_node_id); CAF_CHECK_EQUAL(tbl().lookup_indirect(jupiter().id), none);
CAF_CHECK_EQUAL(tbl().lookup_indirect(remote_node(1)), invalid_node_id); CAF_CHECK_EQUAL(tbl().lookup_indirect(mars().id), none);
CAF_CHECK_EQUAL(tbl().lookup_direct(remote_node(0)).id(), remote_hdl(0).id()); CAF_CHECK_EQUAL(tbl().lookup_direct(jupiter().id).id(), jupiter().connection.id());
CAF_CHECK_EQUAL(tbl().lookup_direct(remote_node(1)).id(), remote_hdl(1).id()); CAF_CHECK_EQUAL(tbl().lookup_direct(mars().id).id(), mars().connection.id());
CAF_MESSAGE("receive message from jupiter"); CAF_MESSAGE("receive message from jupiter");
self()->receive( self()->receive(
[](const std::string& str) -> std::string { [](const std::string& str) -> std::string {
...@@ -842,13 +848,13 @@ CAF_TEST(automatic_connection) { ...@@ -842,13 +848,13 @@ CAF_TEST(automatic_connection) {
mpx()->exec_runnable(); // process forwarded message in basp_broker mpx()->exec_runnable(); // process forwarded message in basp_broker
CAF_MESSAGE("response message must take direct route now"); CAF_MESSAGE("response message must take direct route now");
mock() mock()
.expect(remote_hdl(0), .expect(jupiter().connection,
basp::message_type::dispatch_message, no_flags, any_vals, basp::message_type::dispatch_message, no_flags, any_vals,
no_operation_data, this_node(), remote_node(0), no_operation_data, this_node(), jupiter().id,
self()->id(), pseudo_remote(0)->id(), self()->id(), jupiter().dummy_actor->id(),
std::vector<actor_id>{}, std::vector<actor_id>{},
make_message("hello from earth!")); make_message("hello from earth!"));
CAF_CHECK_EQUAL(mpx()->output_buffer(remote_hdl(1)).size(), 0u); CAF_CHECK_EQUAL(mpx()->output_buffer(mars().connection).size(), 0u);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -105,7 +105,8 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl, ...@@ -105,7 +105,8 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl,
auto write = [=](atom_value x, int y) { auto write = [=](atom_value x, int y) {
auto& buf = self->wr_buf(hdl); auto& buf = self->wr_buf(hdl);
binary_serializer sink{self->system(), buf}; binary_serializer sink{self->system(), buf};
sink << x << y; auto e = sink(x, y);
CAF_REQUIRE(! e);
self->flush(hdl); self->flush(hdl);
}; };
self->set_down_handler([=](down_msg& dm) { self->set_down_handler([=](down_msg& dm) {
...@@ -123,7 +124,8 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl, ...@@ -123,7 +124,8 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl,
atom_value x; atom_value x;
int y; int y;
binary_deserializer source{self->system(), msg.buf}; binary_deserializer source{self->system(), msg.buf};
source >> x >> y; auto e = source(x, y);
CAF_REQUIRE(! e);
if (x == pong_atom::value) if (x == pong_atom::value)
self->send(actor_cast<ping_actor>(buddy), pong_atom::value, y); self->send(actor_cast<ping_actor>(buddy), pong_atom::value, y);
else else
......
...@@ -39,9 +39,9 @@ struct ping { ...@@ -39,9 +39,9 @@ struct ping {
int32_t value; int32_t value;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, ping& x, const unsigned int) { error inspect(Inspector& f, ping& x) {
proc & x.value; return f(meta::type_name("ping"), x.value);
} }
bool operator==(const ping& lhs, const ping& rhs) { bool operator==(const ping& lhs, const ping& rhs) {
...@@ -52,9 +52,9 @@ struct pong { ...@@ -52,9 +52,9 @@ struct pong {
int32_t value; int32_t value;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, pong& x, const unsigned int) { error inspect(Inspector& f, pong& x) {
proc & x.value; return f(meta::type_name("pong"), x.value);
} }
bool operator==(const pong& lhs, const pong& rhs) { bool operator==(const pong& lhs, const pong& rhs) {
......
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