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:
......
This diff is collapsed.
......@@ -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));
......
This diff is collapsed.
......@@ -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() {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment