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