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

Unify serialize and to_string API via inspect

Replace existing `to_string`/`serialize` function pair with a single `inspect`.
This change is backwards compatible, i.e., serializers and deserializers pick
up `serialize` functions via ADL if available. User-defined `to_string`
functions are preferred over `inspect`. Formatting hints and other meta
information that can be evaluated by inspectors are realized with annotations.
Close #470.
parent ce38a7c8
...@@ -26,16 +26,9 @@ struct foo { ...@@ -26,16 +26,9 @@ struct foo {
}; };
// foo needs to be serializable // foo needs to be serializable
template <class Processor> template <class Inspector>
void serialize(Processor& proc, foo& x, const unsigned int) { error inspect(Inspector& f, foo& x) {
proc & x.a; return f(meta::type_name("foo"), x.a, x.b);
proc & x.b;
}
// also, CAF gives us `deep_to_string` for implementing `to_string` easily
std::string to_string(const foo& x) {
// `to_string(foo{{1, 2, 3}, 4})` prints: "foo([1, 2, 3], 4)"
return "foo" + deep_to_string_as_tuple(x.a, x.b);
} }
// a pair of two ints // a pair of two ints
...@@ -51,15 +44,9 @@ struct foo2 { ...@@ -51,15 +44,9 @@ struct foo2 {
}; };
// foo2 also needs to be serializable // foo2 also needs to be serializable
template <class Processor> template <class Inspector>
void serialize(Processor& proc, foo2& x, const unsigned int) { error inspect(Inspector& f, foo2& x) {
proc & x.a; return f(meta::type_name("foo2"), x.a, x.b);
proc & x.b; // traversed automatically and recursively
}
// `deep_to_string` also traverses nested containers
std::string to_string(const foo2& x) {
return "foo" + deep_to_string_as_tuple(x.a, x.b);
} }
// receives our custom message types // receives our custom message types
...@@ -105,10 +92,20 @@ void caf_main(actor_system& system, const config&) { ...@@ -105,10 +92,20 @@ void caf_main(actor_system& system, const config&) {
vector<char> buf; vector<char> buf;
// write f1 to buffer // write f1 to buffer
binary_serializer bs{system, buf}; binary_serializer bs{system, buf};
bs << f1; auto e = bs(f1);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
return;
}
// read f2 back from buffer // read f2 back from buffer
binary_deserializer bd{system, buf}; binary_deserializer bd{system, buf};
bd >> f2; e = bd(f2);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
return;
}
// must be equal // must be equal
assert(to_string(f1) == to_string(f2)); assert(to_string(f1) == to_string(f2));
// spawn a testee that receives two messages of user-defined type // spawn a testee that receives two messages of user-defined type
...@@ -118,7 +115,6 @@ void caf_main(actor_system& system, const config&) { ...@@ -118,7 +115,6 @@ void caf_main(actor_system& system, const config&) {
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5}); self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2 // send t a foo_pair2
self->send(t, foo_pair2{3, 4}); self->send(t, foo_pair2{3, 4});
self->await_all_other_actors_done();
} }
} // namespace <anonymous> } // namespace <anonymous>
......
...@@ -40,14 +40,9 @@ public: ...@@ -40,14 +40,9 @@ public:
b_ = val; b_ = val;
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, foo& x, const unsigned int) { friend error inspect(Inspector& f, foo& x) {
proc & x.a_; return f(meta::type_name("foo"), x.a_, x.b_);
proc & x.b_;
}
friend std::string to_string(const foo& x) {
return "foo" + deep_to_string_as_tuple(x.a_, x.b_);
} }
private: private:
......
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide
// friend access to the serialize() function. // friend access to the inspect() function.
// Manual refs: 57-59, 64-66 (ConfiguringActorApplications) // Manual refs: 57-59, 64-66 (ConfiguringActorApplications)
...@@ -48,27 +48,18 @@ private: ...@@ -48,27 +48,18 @@ private:
int b_; int b_;
}; };
// to_string is straightforward ... template <class Inspector>
std::string to_string(const foo& x) { error inspect(Inspector& f, foo& x) {
return "foo" + deep_to_string_as_tuple(x.a(), x.b()); // store current state into temporaries, then give the inspector references
} // to temporaries that are written back only when the inspector is saving
auto a = x.a();
// ... but we need to split serialization into a saving ... auto b = x.b();
template <class T> auto save = [&]() -> error {
typename std::enable_if<T::is_saving::value>::type x.set_a(a);
serialize(T& out, const foo& x, const unsigned int) { x.set_b(b);
out << x.a() << x.b(); return none;
} };
return f(meta::type_name("foo"), a, b, meta::save_callback(save));
// ... and a loading function
template <class T>
typename std::enable_if<T::is_loading::value>::type
serialize(T& in, foo& x, const unsigned int) {
int tmp;
in >> tmp;
x.set_a(tmp);
in >> tmp;
x.set_b(tmp);
} }
behavior testee(event_based_actor* self) { behavior testee(event_based_actor* self) {
......
...@@ -40,7 +40,6 @@ set (LIBCAF_CORE_SRCS ...@@ -40,7 +40,6 @@ set (LIBCAF_CORE_SRCS
src/continue_helper.cpp src/continue_helper.cpp
src/decorated_tuple.cpp src/decorated_tuple.cpp
src/default_invoke_result_visitor.cpp src/default_invoke_result_visitor.cpp
src/deep_to_string.cpp
src/default_attachable.cpp src/default_attachable.cpp
src/deserializer.cpp src/deserializer.cpp
src/duration.cpp src/duration.cpp
...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS ...@@ -89,6 +88,7 @@ set (LIBCAF_CORE_SRCS
src/skip.cpp src/skip.cpp
src/splitter.cpp src/splitter.cpp
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
src/stringification_inspector.cpp
src/try_match.cpp src/try_match.cpp
src/type_erased_value.cpp src/type_erased_value.cpp
src/type_erased_tuple.cpp src/type_erased_tuple.cpp
......
...@@ -164,13 +164,9 @@ public: ...@@ -164,13 +164,9 @@ public:
actor(actor_control_block*, bool); actor(actor_control_block*, bool);
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, actor& x, const unsigned int v) { friend error inspect(Inspector& f, actor& x) {
serialize(proc, x.ptr_, v); return inspect(f, x.ptr_);
}
friend inline std::string to_string(const actor& x) {
return to_string(x.ptr_);
} }
/// @endcond /// @endcond
......
...@@ -109,13 +109,9 @@ public: ...@@ -109,13 +109,9 @@ public:
return compare(other.get()); return compare(other.get());
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, actor_addr& x, const unsigned int v) { friend error inspect(Inspector& f, actor_addr& x) {
serialize(proc, x.ptr_, v); return inspect(f, x.ptr_);
}
friend inline std::string to_string(const actor_addr& x) {
return to_string(x.ptr_);
} }
/// Releases the reference held by handle `x`. Using the /// Releases the reference held by handle `x`. Using the
......
...@@ -23,10 +23,16 @@ ...@@ -23,10 +23,16 @@
#include <atomic> #include <atomic>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/error.hpp"
#include "caf/node_id.hpp" #include "caf/node_id.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/weak_intrusive_ptr.hpp" #include "caf/weak_intrusive_ptr.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_none.hpp"
namespace caf { namespace caf {
/// Actors are always allocated with a control block that stores its identity /// Actors are always allocated with a control block that stores its identity
...@@ -179,23 +185,43 @@ inline bool operator!=(const abstract_actor* x, const strong_actor_ptr& y) { ...@@ -179,23 +185,43 @@ inline bool operator!=(const abstract_actor* x, const strong_actor_ptr& y) {
/// @relates actor_control_block /// @relates actor_control_block
using weak_actor_ptr = weak_intrusive_ptr<actor_control_block>; using weak_actor_ptr = weak_intrusive_ptr<actor_control_block>;
/// @relates strong_actor_ptr error load_actor(strong_actor_ptr& storage, execution_unit*,
void serialize(serializer&, strong_actor_ptr&, const unsigned int); actor_id aid, const node_id& nid);
/// @relates strong_actor_ptr error save_actor(strong_actor_ptr& storage, execution_unit*,
void serialize(deserializer&, strong_actor_ptr&, const unsigned int); actor_id aid, const node_id& nid);
/// @relates weak_actor_ptr template <class Inspector>
void serialize(serializer&, weak_actor_ptr&, const unsigned int); auto context_of(Inspector* f) -> decltype(f->context()) {
return f->context();
}
/// @relates weak_actor_ptr inline execution_unit* context_of(void*) {
void serialize(deserializer&, weak_actor_ptr&, const unsigned int); return nullptr;
}
/// @relates strong_actor_ptr template <class Inspector>
std::string to_string(const strong_actor_ptr&); error inspect(Inspector& f, strong_actor_ptr& x) {
actor_id aid = 0;
node_id nid;
if (x) {
aid = x->aid;
nid = x->nid;
}
auto load = [&] { return load_actor(x, context_of(&f), aid, nid); };
auto save = [&] { return save_actor(x, context_of(&f), aid, nid); };
return f(meta::type_name("actor"), aid,
meta::omittable_if_none(), nid,
meta::load_callback(load), meta::save_callback(save));
}
/// @relates weak_actor_ptr template <class Inspector>
std::string to_string(const weak_actor_ptr&); error inspect(Inspector& f, weak_actor_ptr& x) {
// inspect as strong pointer, then write back to weak pointer on save
auto tmp = x.lock();
auto load = [&]() -> error { x.reset(tmp.get()); return none; };
return f(tmp, meta::load_callback(load));
}
} // namespace caf } // namespace caf
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "caf/exec_main.hpp" #include "caf/exec_main.hpp"
#include "caf/resumable.hpp" #include "caf/resumable.hpp"
#include "caf/streambuf.hpp" #include "caf/streambuf.hpp"
#include "caf/to_string.hpp"
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/actor_pool.hpp" #include "caf/actor_pool.hpp"
#include "caf/attachable.hpp" #include "caf/attachable.hpp"
...@@ -99,6 +100,12 @@ ...@@ -99,6 +100,12 @@
#include "caf/decorator/adapter.hpp" #include "caf/decorator/adapter.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/scheduler/abstract_coordinator.hpp" #include "caf/scheduler/abstract_coordinator.hpp"
/// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de> /// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de>
......
...@@ -35,6 +35,7 @@ enum class atom_value : uint64_t { ...@@ -35,6 +35,7 @@ enum class atom_value : uint64_t {
/// @endcond /// @endcond
}; };
/// @relates atom_value
std::string to_string(const atom_value& x); std::string to_string(const atom_value& x);
atom_value atom_from_string(const std::string& x); atom_value atom_from_string(const std::string& x);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#ifndef CAF_NAMED_CALLBACK_HPP #ifndef CAF_NAMED_CALLBACK_HPP
#define CAF_NAMED_CALLBACK_HPP #define CAF_NAMED_CALLBACK_HPP
#include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
...@@ -40,7 +41,7 @@ namespace caf { ...@@ -40,7 +41,7 @@ namespace caf {
template <class... Ts> template <class... Ts>
class callback { class callback {
public: public:
virtual void operator()(Ts...) = 0; virtual error operator()(Ts...) = 0;
}; };
/// Utility class for wrapping a function object of type `Base`. /// Utility class for wrapping a function object of type `Base`.
...@@ -54,8 +55,8 @@ public: ...@@ -54,8 +55,8 @@ public:
callback_impl(callback_impl&&) = default; callback_impl(callback_impl&&) = default;
callback_impl& operator=(callback_impl&&) = default; callback_impl& operator=(callback_impl&&) = default;
void operator()(Ts... xs) override { error operator()(Ts... xs) override {
f_(xs...); return f_(xs...);
} }
private: private:
......
This diff is collapsed.
...@@ -30,166 +30,23 @@ ...@@ -30,166 +30,23 @@
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/detail/stringification_inspector.hpp"
namespace caf { namespace caf {
struct deep_to_string_append_t {};
constexpr deep_to_string_append_t deep_to_string_append = deep_to_string_append_t{};
class deep_to_string_t { class deep_to_string_t {
public: public:
constexpr deep_to_string_t() { constexpr deep_to_string_t() {
// nop // nop
} }
template <class T>
std::string operator()(const std::reference_wrapper<T>& x) const {
return (*this)(x.get());
}
std::string operator()(const char* cstr) const;
inline std::string operator()(char* cstr) const {
return (*this)(const_cast<const char*>(cstr));
}
inline std::string operator()(const std::string& str) const {
return (*this)(str.c_str());
}
std::string operator()(const atom_value& x) const;
constexpr const char* operator()(const bool& x) const {
return x ? "true" : "false";
}
template <class T>
typename std::enable_if<
! detail::is_iterable<T>::value && ! std::is_pointer<T>::value,
std::string
>::type
operator()(const T& x) const {
std::string res = impl(&x);
if (res == "<unprintable>") {
res = "<unprintable:";
res += typeid(T).name();
res += ">";
}
return res;
}
template <class F, class S>
std::string operator()(const std::pair<F, S>& x) const {
std::string result = "(";
result += (*this)(x.first);
result += ", ";
result += (*this)(x.second);
result += ")";
return result;
}
inline void operator()(const deep_to_string_append_t&, std::string&,
const char*) const {
// end of recursion
}
template <class T, class... Ts>
void operator()(const deep_to_string_append_t& tk, std::string& str,
const char* glue, const T& x, const Ts&... xs) const {
if (! str.empty())
str += glue;
str += (*this)(x);
(*this)(tk, str, glue, xs...);
}
template <class T, class U, class... Us>
std::string operator()(const T& x, const U& y, const Us&... ys) const {
std::string result;
(*this)(deep_to_string_append, result, ", ", x, y, ys...);
return result;
}
template <class... Ts> template <class... Ts>
std::string operator()(const std::tuple<Ts...>& xs) const { std::string operator()(Ts&&... xs) const {
std::string result = "("; std::string result;
result += detail::apply_args(*this, detail::get_indices(xs), xs); detail::stringification_inspector f{result};
result += ")"; f(std::forward<Ts>(xs)...);
return result;
}
template <class T>
typename std::enable_if<
detail::is_iterable<T>::value,
std::string
>::type
operator()(const T& x) const {
auto i = x.begin();
auto e = x.end();
if (i == e)
return "[]";
std::string result = "[";
result += (*this)(*i);
for (++i; i != e; ++i) {
result += ", ";
result += (*this)(*i);
}
result += "]";
return result;
}
template <class T>
std::string decompose_array(const T& xs, size_t n) const {
if (n == 0)
return "()";
std::string result = "(";
result += (*this)(xs[0]);
for (size_t i = 1; i < n; ++i) {
result += ", ";
result += (*this)(xs[i]);
}
result += ")";
return result; return result;
} }
template <class T, size_t S>
std::string operator()(const std::array<T, S>& xs) const {
return decompose_array(xs, S);
}
template <class T, size_t S>
std::string operator()(const T (&xs)[S]) const {
return decompose_array(xs, S);
}
template <class T>
typename std::enable_if<
std::is_pointer<T>::value,
std::string
>::type
operator()(const T ptr) const {
return ptr ? "*" + (*this)(*ptr) : "<nullptr>";
}
private:
template <class T>
auto impl(const T* x) const -> decltype(to_string(*x)) {
return to_string(*x);
}
template <class T>
typename std::enable_if<
std::is_arithmetic<T>::value,
std::string
>::type
impl(const T* x) const {
return std::to_string(*x);
}
constexpr const char* impl(const void*) const {
return "<unprintable>";
}
}; };
/// Unrolls collections such as vectors/maps, decomposes /// Unrolls collections such as vectors/maps, decomposes
...@@ -199,8 +56,7 @@ private: ...@@ -199,8 +56,7 @@ private:
/// provide a `to_string` is mapped to `<unprintable>`. /// provide a `to_string` is mapped to `<unprintable>`.
constexpr deep_to_string_t deep_to_string = deep_to_string_t{}; constexpr deep_to_string_t deep_to_string = deep_to_string_t{};
/// Convenience function returning /// Convenience function for `deep_to_string(std::forward_as_tuple(xs...))`.
/// `deep_to_string(std::forward_as_tuple(xs...))`.
template <class... Ts> template <class... Ts>
std::string deep_to_string_as_tuple(Ts&&... xs) { std::string deep_to_string_as_tuple(Ts&&... xs) {
return deep_to_string(std::forward_as_tuple(std::forward<Ts>(xs)...)); return deep_to_string(std::forward_as_tuple(std::forward<Ts>(xs)...));
......
...@@ -25,6 +25,12 @@ ...@@ -25,6 +25,12 @@
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include "caf/config.hpp"
#ifndef CAF_NO_EXCEPTIONS
#include <exception>
#endif // CAF_NO_EXCEPTIONS
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/data_processor.hpp" #include "caf/data_processor.hpp"
...@@ -47,25 +53,35 @@ public: ...@@ -47,25 +53,35 @@ public:
explicit deserializer(execution_unit* ctx = nullptr); explicit deserializer(execution_unit* ctx = nullptr);
}; };
/// Reads `x` from `source`. #ifndef CAF_NO_EXCEPTIONS
/// @relates serializer
template <class T>
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value
>::type
operator&(deserializer& source, T& x) {
auto e = source.apply(x);
if (e)
throw std::runtime_error(to_string(e));
}
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>())) decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value, >::value,
deserializer& deserializer&
>::type >::type
operator>>(deserializer& source, T& x) { operator>>(deserializer& source, T& x) {
source.apply(x); source & x;
return source; return source;
} }
template <class T> #endif // CAF_NO_EXCEPTIONS
auto operator&(deserializer& source, T& x) -> decltype(source.apply(x)) {
source.apply(x);
}
} // namespace caf } // namespace caf
......
...@@ -142,7 +142,7 @@ public: ...@@ -142,7 +142,7 @@ public:
inline bool visit(optional<skip_t>& x) { inline bool visit(optional<skip_t>& x) {
if (x) if (x)
return false; return false;
(*this)(x); (*this)();
return true; return true;
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
#define CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
#include <string>
#include <vector>
#include <functional>
#include <type_traits>
#include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/error.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/meta/omittable_if_none.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
class stringification_inspector {
public:
using is_saving = std::true_type;
stringification_inspector(std::string& result) : result_(result) {
// nop
}
template <class... Ts>
error operator()(Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
return none;
}
private:
/// Prints a separator to the result string.
void sep();
inline void traverse() noexcept {
// end of recursion
}
void consume(atom_value& x);
void consume(const char* cstr);
void consume_hex(const uint8_t* xs, size_t n);
inline void consume(bool& x) {
result_ += x ? "true" : "false";
}
inline void consume(char* cstr) {
consume(const_cast<const char*>(cstr));
}
inline void consume(std::string& str) {
consume(str.c_str());
}
template <class T>
enable_if_tt<std::is_arithmetic<T>> consume(T& x) {
result_ += std::to_string(x);
}
// unwrap std::ref
template <class T>
void consume(std::reference_wrapper<T>& x) {
return consume(x.get());
}
/// Picks up user-defined `to_string` functions.
template <class T>
enable_if_tt<has_to_string<T>> consume(T& x) {
result_ += to_string(x);
}
/// Delegates to `inspect(*this, x)` if available and `T`
/// does not provide a `to_string`.
template <class T>
enable_if_t<
is_inspectable<stringification_inspector, T>::value
&& ! has_to_string<T>::value>
consume(T& x) {
inspect(*this, x);
}
template <class F, class S>
void consume(std::pair<F, S>& x) {
result_ += '(';
traverse(deconst(x.first), deconst(x.second));
result_ += ')';
}
template <class... Ts>
void consume(std::tuple<Ts...>& x) {
result_ += '(';
apply_args(*this, get_indices(x), x);
result_ += ')';
}
template <class T>
enable_if_t<is_iterable<T>::value
&& ! is_inspectable<stringification_inspector, T>::value
&& ! has_to_string<T>::value>
consume(T& xs) {
result_ += '[';
for (auto& x : xs) {
sep();
consume(deconst(x));
}
result_ += ']';
}
template <class T>
void consume(T* xs, size_t n) {
result_ += '(';
for (size_t i = 0; i < n; ++i) {
sep();
consume(deconst(xs[i]));
}
result_ += ')';
}
template <class T, size_t S>
void consume(std::array<T, S>& xs) {
return consume(xs.data(), S);
}
template <class T, size_t S>
void consume(T (&xs)[S]) {
return consume(xs, S);
}
template <class T>
enable_if_tt<std::is_pointer<T>> consume(T ptr) {
if (ptr) {
result_ += '*';
consume(*ptr);
} else {
result_ + "<null>";
}
}
/// Fallback printing `<unprintable>`.
template <class T>
enable_if_t<
! is_iterable<T>::value
&& ! std::is_pointer<T>::value
&& ! is_inspectable<stringification_inspector, T>::value
&& ! std::is_arithmetic<T>::value
&& ! has_to_string<T>::value>
consume(T&) {
result_ += "<unprintable>";
}
template <class T, class... Ts>
void traverse(meta::hex_formatted_t, T& x, Ts&&... xs) {
sep();
consume_hex(reinterpret_cast<uint8_t*>(deconst(x).data()), x.size());
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_if_none_t, T& x, Ts&&... xs) {
if (x != none) {
sep();
consume(x);
}
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_if_empty_t, T& x, Ts&&... xs) {
if (! x.empty()) {
sep();
consume(x);
}
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
void traverse(meta::omittable_t, T&, Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
}
template <class... Ts>
void traverse(meta::type_name_t x, Ts&&... xs) {
sep();
result_ += x.value;
result_ += '(';
traverse(std::forward<Ts>(xs)...);
result_ += ')';
}
template <class... Ts>
void traverse(const meta::annotation&, Ts&&... xs) {
traverse(std::forward<Ts>(xs)...);
}
template <class T, class... Ts>
enable_if_t<! meta::is_annotation<T>::value> traverse(T&& x, Ts&&... xs) {
sep();
consume(deconst(x));
traverse(std::forward<Ts>(xs)...);
}
template <class T>
T& deconst(const T& x) {
return const_cast<T&>(x);
}
std::string& result_;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_STRINGIFICATION_INSPECTOR_HPP
...@@ -23,111 +23,60 @@ ...@@ -23,111 +23,60 @@
#include <tuple> #include <tuple>
#include <stdexcept> #include <stdexcept>
#include "caf/type_nr.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/make_type_erased_value.hpp"
#include "caf/type_nr.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/message_data.hpp" #include "caf/detail/message_data.hpp"
#include "caf/detail/try_serialize.hpp" #include "caf/detail/try_serialize.hpp"
#include "caf/make_type_erased_value.hpp" #include "caf/detail/stringification_inspector.hpp"
#define CAF_TUPLE_VALS_DISPATCH(x) \
case x: \
return tuple_inspect_delegate<x, sizeof...(Ts)-1>(data_, f)
namespace caf { namespace caf {
namespace detail { namespace detail {
template <size_t Pos, size_t Max, bool InRange = (Pos < Max)> // avoids triggering static asserts when using CAF_TUPLE_VALS_DISPATCH
struct tup_ptr_access { template <size_t X, size_t Max, class T, class F>
template <class T> auto tuple_inspect_delegate(T& data, F& f) -> decltype(f(std::get<Max>(data))) {
static typename std::conditional< return f(std::get<(X < Max ? X : Max)>(data));
std::is_const<T>::value, }
const void*,
void*
>::type
get(size_t pos, T& tup) {
if (pos == Pos) return &std::get<Pos>(tup);
return tup_ptr_access<Pos + 1, Max>::get(pos, tup);
}
template <class T> template <size_t X, size_t N>
static bool cmp(size_t pos, T& tup, const void* x) { struct tup_ptr_access_pos {
using type = typename std::tuple_element<Pos, T>::type; constexpr tup_ptr_access_pos() {
if (pos == Pos) // nop
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 Pos, size_t Max> template <size_t X, size_t N>
struct tup_ptr_access<Pos, Max, false> { constexpr tup_ptr_access_pos<X + 1, N> next(tup_ptr_access_pos<X, N>) {
template <class T> return {};
static typename std::conditional< }
std::is_const<T>::value,
const void*,
void*
>::type
get(size_t, T&) {
// end of recursion
return nullptr;
}
struct void_ptr_access {
template <class T> template <class T>
static bool cmp(size_t, T&, const void*) { void* operator()(T& x) const noexcept {
return false; return &x;
}
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;
} }
}; };
template <class T, uint16_t N = type_nr<T>::value> template <class T, uint16_t N = type_nr<T>::value>
struct tuple_vals_type_helper { struct tuple_vals_type_helper {
static typename message_data::rtti_pair get() { static typename message_data::rtti_pair get() noexcept {
return {N, nullptr}; return {N, nullptr};
} }
}; };
template <class T> template <class T>
struct tuple_vals_type_helper<T, 0> { struct tuple_vals_type_helper<T, 0> {
static typename message_data::rtti_pair get() { static typename message_data::rtti_pair get() noexcept {
return {0, &typeid(T)}; return {0, &typeid(T)};
} }
}; };
...@@ -135,14 +84,25 @@ struct tuple_vals_type_helper<T, 0> { ...@@ -135,14 +84,25 @@ struct tuple_vals_type_helper<T, 0> {
template <class Base, class... Ts> template <class Base, class... Ts>
class tuple_vals_impl : public Base { class tuple_vals_impl : public Base {
public: public:
// -- static invariants ------------------------------------------------------
static_assert(sizeof...(Ts) > 0, "tuple_vals is not allowed to be empty"); static_assert(sizeof...(Ts) > 0, "tuple_vals is not allowed to be empty");
// -- member types -----------------------------------------------------------
using super = message_data; using super = message_data;
using rtti_pair = typename message_data::rtti_pair; using rtti_pair = typename message_data::rtti_pair;
using data_type = std::tuple<Ts...>; using data_type = std::tuple<Ts...>;
// -- friend functions -------------------------------------------------------
template <class Inspector>
friend error inspect(Inspector& f, tuple_vals_impl& x) {
return apply_args(f, get_indices(x.data_), x.data_);
}
tuple_vals_impl(const tuple_vals_impl&) = default; tuple_vals_impl(const tuple_vals_impl&) = default;
template <class... Us> template <class... Us>
...@@ -166,26 +126,32 @@ public: ...@@ -166,26 +126,32 @@ public:
const void* get(size_t pos) const noexcept override { const void* get(size_t pos) const noexcept override {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
return tup_ptr_access<0, sizeof...(Ts)>::get(pos, data_); void_ptr_access f;
return mptr()->dispatch(pos, f);
} }
void* get_mutable(size_t pos) override { void* get_mutable(size_t pos) override {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
return const_cast<void*>(get(pos)); void_ptr_access f;
return this->dispatch(pos, f);
} }
std::string stringify(size_t pos) const override { std::string stringify(size_t pos) const override {
return tup_ptr_access<0, sizeof...(Ts)>::stringify(pos, data_); std::string result;
stringification_inspector f{result};
mptr()->dispatch(pos, f);
return result;
} }
using Base::copy; using Base::copy;
type_erased_value_ptr copy(size_t pos) const override { type_erased_value_ptr copy(size_t pos) const override {
return tup_ptr_access<0, sizeof...(Ts)>::copy(pos, data_); type_erased_value_factory f;
return mptr()->dispatch(pos, f);
} }
error load(size_t pos, deserializer& source) override { error load(size_t pos, deserializer& source) override {
return tup_ptr_access<0, sizeof...(Ts)>::serialize(pos, data_, source); return dispatch(pos, source);
} }
uint32_t type_token() const noexcept override { uint32_t type_token() const noexcept override {
...@@ -197,13 +163,49 @@ public: ...@@ -197,13 +163,49 @@ public:
} }
error save(size_t pos, serializer& sink) const override { error save(size_t pos, serializer& sink) const override {
// the serialization framework uses non-const arguments for deserialization, return mptr()->dispatch(pos, sink);
// but this cast is safe since the values are not actually changed
auto& nc_data = const_cast<data_type&>(data_);
return tup_ptr_access<0, sizeof...(Ts)>::serialize(pos, nc_data, sink);
} }
private: private:
template <class F>
auto dispatch(size_t pos, F& f) -> decltype(f(std::declval<int&>())) {
CAF_ASSERT(pos < sizeof...(Ts));
switch (pos) {
CAF_TUPLE_VALS_DISPATCH(0);
CAF_TUPLE_VALS_DISPATCH(1);
CAF_TUPLE_VALS_DISPATCH(2);
CAF_TUPLE_VALS_DISPATCH(3);
CAF_TUPLE_VALS_DISPATCH(4);
CAF_TUPLE_VALS_DISPATCH(5);
CAF_TUPLE_VALS_DISPATCH(6);
CAF_TUPLE_VALS_DISPATCH(7);
CAF_TUPLE_VALS_DISPATCH(8);
CAF_TUPLE_VALS_DISPATCH(9);
default:
// fall back to recursive dispatch function
static constexpr size_t max_pos = sizeof...(Ts) - 1;
tup_ptr_access_pos<(10 < max_pos ? 10 : max_pos), max_pos> first;
return rec_dispatch(pos, f, first);
}
}
template <class F, size_t N>
auto rec_dispatch(size_t, F& f, tup_ptr_access_pos<N, N>)
-> decltype(f(std::declval<int&>())) {
return tuple_inspect_delegate<N, N>(data_, f);
}
template <class F, size_t X, size_t N>
auto rec_dispatch(size_t pos, F& f, tup_ptr_access_pos<X, N> token)
-> decltype(f(std::declval<int&>())) {
return pos == X ? tuple_inspect_delegate<X, N>(data_, f)
: rec_dispatch(pos, f, next(token));
}
tuple_vals_impl* mptr() const {
return const_cast<tuple_vals_impl*>(this);
}
data_type data_; data_type data_;
std::array<rtti_pair, sizeof...(Ts)> types_; std::array<rtti_pair, sizeof...(Ts)> types_;
}; };
......
...@@ -68,9 +68,7 @@ public: ...@@ -68,9 +68,7 @@ public:
} }
error load(size_t pos, deserializer& source) override { error load(size_t pos, deserializer& source) override {
ptrs_[pos]->load(source); return ptrs_[pos]->load(source);
// TODO: refactor after visit API is in place (#470)
return {};
} }
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
......
...@@ -78,9 +78,7 @@ public: ...@@ -78,9 +78,7 @@ public:
} }
error load(deserializer& source) override { error load(deserializer& source) override {
detail::try_serialize(source, addr_of(x_)); return source(*addr_of(x_));
// TODO: refactor after visit API is in place (#470)
return {};
} }
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
...@@ -105,9 +103,7 @@ public: ...@@ -105,9 +103,7 @@ public:
} }
error save(serializer& sink) const override { error save(serializer& sink) const override {
detail::try_serialize(sink, addr_of(x_)); return sink(*addr_of(const_cast<T&>(x_)));
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::string stringify() const override { std::string stringify() const override {
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
#include <tuple> #include <tuple>
#include <string> #include <string>
#include <vector>
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <vector>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -34,6 +34,48 @@ ...@@ -34,6 +34,48 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class T>
using decay_t = typename std::decay<T>::type;
template <bool V, class T = void>
using enable_if_t = typename std::enable_if<V, T>::type;
template <class Trait, class T = void>
using enable_if_tt = typename std::enable_if<Trait::value, T>::type;
template <class Inspector, class T>
class is_inspectable {
private:
template <class U>
static auto sfinae(Inspector& x, U& y) -> decltype(inspect(x, y));
static std::false_type sfinae(Inspector&, ...);
using result_type = decltype(sfinae(std::declval<Inspector&>(),
std::declval<T&>()));
public:
static constexpr bool value = ! std::is_same<result_type, std::false_type>::value;
};
template <class T>
class has_to_string {
private:
template <class U>
static auto sfinae(const U& x) -> decltype(to_string(x));
static void sfinae(...);
using result = decltype(sfinae(std::declval<const T&>()));
public:
static constexpr bool value = std::is_convertible<result, std::string>::value;
};
// pointers are never inspectable
template <class Inspector, class T>
class is_inspectable<Inspector, T*> : std::false_type {};
template <bool X> template <bool X>
using bool_token = std::integral_constant<bool, X>; using bool_token = std::integral_constant<bool, X>;
...@@ -80,17 +122,17 @@ struct is_array_of { ...@@ -80,17 +122,17 @@ struct is_array_of {
/// Deduces the reference type of T0 and applies it to T1. /// Deduces the reference type of T0 and applies it to T1.
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type { struct deduce_ref_type {
using type = typename std::decay<T1>::type; using type = decay_t<T1>;
}; };
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type<T0&, T1> { struct deduce_ref_type<T0&, T1> {
using type = typename std::decay<T1>::type&; using type = decay_t<T1>&;
}; };
template <class T0, typename T1> template <class T0, typename T1>
struct deduce_ref_type<const T0&, T1> { struct deduce_ref_type<const T0&, T1> {
using type = const typename std::decay<T1>::type&; using type = const decay_t<T1>&;
}; };
/// Checks wheter `X` is in the template parameter pack Ts. /// Checks wheter `X` is in the template parameter pack Ts.
...@@ -143,11 +185,10 @@ class is_comparable { ...@@ -143,11 +185,10 @@ class is_comparable {
// candidate and thus decltype(cmp_help_fun(...)) is void. // candidate and thus decltype(cmp_help_fun(...)) is void.
template <class A, typename B> template <class A, typename B>
static bool cmp_help_fun(const A* arg0, const B* arg1, static bool cmp_help_fun(const A* arg0, const B* arg1,
decltype(*arg0 == *arg1)* = nullptr) { decltype(*arg0 == *arg1)* = nullptr);
return true;
}
template <class A, typename B> template <class A, typename B>
static void cmp_help_fun(const A*, const B*, void* = nullptr) { } static void cmp_help_fun(const A*, const B*, void* = nullptr);
using result_type = decltype(cmp_help_fun(static_cast<T1*>(nullptr), using result_type = decltype(cmp_help_fun(static_cast<T1*>(nullptr),
static_cast<T2*>(nullptr), static_cast<T2*>(nullptr),
...@@ -160,25 +201,19 @@ public: ...@@ -160,25 +201,19 @@ public:
template <class T> template <class T>
class is_forward_iterator { class is_forward_iterator {
template <class C> template <class C>
static bool sfinae_fun( static bool sfinae(C& x, C& y,
C* iter, // check for operator*
// check for 'C::value_type C::operator*()' returning a non-void type decay_t<decltype(*x)>* = 0,
typename std::decay<decltype(*(*iter))>::type* = 0, // check for operator++ returning an iterator
// check for 'C& C::operator++()' decay_t<decltype(x = ++y)>* = 0,
typename std::enable_if< // check for operator==
std::is_same<C&, decltype(++(*iter))>::value>::type* = 0, decay_t<decltype(x == y)>* = 0,
// check for 'bool C::operator==()' // check for operator!=
typename std::enable_if< decay_t<decltype(x != y)>* = 0);
std::is_same<bool, decltype(*iter == *iter)>::value>::type* = 0,
// check for 'bool C::operator!=()'
typename std::enable_if<
std::is_same<bool, decltype(*iter != *iter)>::value>::type* = 0) {
return true;
}
static void sfinae_fun(void*) {} static void sfinae(...);
using result_type = decltype(sfinae_fun(static_cast<T*>(nullptr))); using result_type = decltype(sfinae(std::declval<T&>(), std::declval<T&>()));
public: public:
static constexpr bool value = std::is_same<bool, result_type>::value; static constexpr bool value = std::is_same<bool, result_type>::value;
...@@ -189,21 +224,15 @@ public: ...@@ -189,21 +224,15 @@ public:
template <class T> template <class T>
class has_char_insert { class has_char_insert {
template <class C> template <class C>
static bool sfinae_fun(C* cc, static bool sfinae(C* cc,
const char* first = nullptr, const char* first = nullptr,
const char* last = nullptr, const char* last = nullptr,
decltype(cc->insert(cc->end(), first, last))* = 0) { decltype(cc->insert(cc->end(), first, last))* = 0);
return true;
}
// SFNINAE default // SFNINAE default
static void sfinae_fun(const void*) { static void sfinae(const void*);
// nop
}
using decayed = typename std::decay<T>::type;
using result_type = decltype(sfinae_fun(static_cast<decayed*>(nullptr))); using result_type = decltype(sfinae(static_cast<decay_t<T>>(nullptr)));
public: public:
static constexpr bool value = is_primitive<T>::value == false && static constexpr bool value = is_primitive<T>::value == false &&
...@@ -216,30 +245,20 @@ template <class T> ...@@ -216,30 +245,20 @@ template <class T>
class is_iterable { class is_iterable {
// this horrible code would just disappear if we had concepts // this horrible code would just disappear if we had concepts
template <class C> template <class C>
static bool sfinae_fun( static bool sfinae(C* cc,
const C* cc, // check if 'C::begin()' returns a forward iterator
// check for 'C::begin()' returning a forward iterator enable_if_tt<is_forward_iterator<decltype(cc->begin())>>* = 0,
typename std::enable_if<is_forward_iterator<decltype(cc->begin())>::value>:: // check if begin() and end() both exist and are comparable
type* = 0, decltype(cc->begin() != cc->end())* = 0);
// check for 'C::end()' returning the same kind of forward iterator
typename std::enable_if<std::is_same<decltype(cc->begin()),
decltype(cc->end())>::value>::type
* = 0) {
return true;
}
// SFNINAE default // SFNINAE default
static void sfinae_fun(const void*) { static void sfinae(void*);
// nop
}
using decayed = typename std::decay<T>::type; using result_type = decltype(sfinae(static_cast<decay_t<T>*>(nullptr)));
using result_type = decltype(sfinae_fun(static_cast<const decayed*>(nullptr)));
public: public:
static constexpr bool value = is_primitive<T>::value == false && static constexpr bool value = is_primitive<T>::value == false
std::is_same<bool, result_type>::value; && std::is_same<bool, result_type>::value;
}; };
template <class T> template <class T>
...@@ -320,6 +339,7 @@ struct has_serialize<T, true> { ...@@ -320,6 +339,7 @@ struct has_serialize<T, true> {
static constexpr bool value = false; static constexpr bool value = false;
}; };
/// Any inspectable type is considered to be serializable.
template <class T> template <class T>
struct is_serializable; struct is_serializable;
...@@ -329,11 +349,13 @@ template <class T, ...@@ -329,11 +349,13 @@ template <class T,
|| std::is_function<T>::value> || std::is_function<T>::value>
struct is_serializable_impl; struct is_serializable_impl;
/// Checks whether `T` is builtin or provides a `serialize` /// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function. /// (free or member) function.
template <class T> template <class T>
struct is_serializable_impl<T, false, false> { struct is_serializable_impl<T, false, false> {
static constexpr bool value = has_serialize<T>::value static constexpr bool value = has_serialize<T>::value
|| is_inspectable<serializer, T>::value
|| is_builtin<T>::value; || is_builtin<T>::value;
}; };
...@@ -371,6 +393,7 @@ struct is_serializable_impl<T, IsIterable, true> { ...@@ -371,6 +393,7 @@ struct is_serializable_impl<T, IsIterable, true> {
template <class T> template <class T>
struct is_serializable { struct is_serializable {
static constexpr bool value = is_serializable_impl<T>::value static constexpr bool value = is_serializable_impl<T>::value
|| is_inspectable<serializer, T>::value
|| std::is_empty<T>::value || std::is_empty<T>::value
|| std::is_enum<T>::value; || std::is_enum<T>::value;
}; };
...@@ -413,11 +436,13 @@ private: ...@@ -413,11 +436,13 @@ private:
class = typename std::enable_if< class = typename std::enable_if<
! std::is_member_pointer<decltype(&U::static_type_name)>::value ! std::is_member_pointer<decltype(&U::static_type_name)>::value
>::type> >::type>
static std::true_type sfinae_fun(int); static std::true_type sfinae(int);
template <class> template <class>
static std::false_type sfinae_fun(...); static std::false_type sfinae(...);
public: public:
static constexpr bool value = decltype(sfinae_fun<T>(0))::value; static constexpr bool value = decltype(sfinae<T>(0))::value;
}; };
/// Checks whether `T::memory_cache_flag` exists. /// Checks whether `T::memory_cache_flag` exists.
...@@ -494,7 +519,7 @@ struct get_callable_trait_helper<false, false, C> { ...@@ -494,7 +519,7 @@ struct get_callable_trait_helper<false, false, C> {
template <class T> template <class T>
struct get_callable_trait { struct get_callable_trait {
// type without cv qualifiers // type without cv qualifiers
using bare_type = typename std::decay<T>::type; using bare_type = decay_t<T>;
// if T is a function pointer, this type identifies the function // if T is a function pointer, this type identifies the function
using signature_type = typename std::remove_pointer<bare_type>::type; using signature_type = typename std::remove_pointer<bare_type>::type;
using type = using type =
...@@ -527,9 +552,7 @@ struct is_callable { ...@@ -527,9 +552,7 @@ struct is_callable {
static void _fun(void*) { } static void _fun(void*) { }
using pointer = typename std::decay<T>::type*; using result_type = decltype(_fun(static_cast<decay_t<T>*>(nullptr)));
using result_type = decltype(_fun(static_cast<pointer>(nullptr)));
public: public:
static constexpr bool value = std::is_same<bool, result_type>::value; static constexpr bool value = std::is_same<bool, result_type>::value;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <cstdint> #include <cstdint>
#include <stdexcept> #include <stdexcept>
#include "caf/error.hpp"
namespace caf { namespace caf {
/// SI time units to specify timeouts. /// SI time units to specify timeouts.
...@@ -138,15 +140,15 @@ private: ...@@ -138,15 +140,15 @@ private:
} }
}; };
std::string to_string(const duration& x);
/// @relates duration /// @relates duration
template <class Processor> template <class Inspector>
void serialize(Processor& proc, duration& x, const unsigned int) { error inspect(Inspector& f, duration& x) {
proc & x.unit; return f(x.unit, x.count);
proc & x.count;
} }
std::string to_string(const duration& x);
/// @relates duration /// @relates duration
bool operator==(const duration& lhs, const duration& rhs); bool operator==(const duration& lhs, const duration& rhs);
......
...@@ -21,10 +21,16 @@ ...@@ -21,10 +21,16 @@
#define CAF_ERROR_HPP #define CAF_ERROR_HPP
#include <cstdint> #include <cstdint>
#include <utility>
#include <functional>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/message.hpp" #include "caf/none.hpp"
#include "caf/atom.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
...@@ -87,70 +93,132 @@ using enable_if_has_make_error_t ...@@ -87,70 +93,132 @@ using enable_if_has_make_error_t
/// rendering error messages via `actor_system::render(const error&)`. /// rendering error messages via `actor_system::render(const error&)`.
class error : detail::comparable<error> { class error : detail::comparable<error> {
public: public:
// -- member types -----------------------------------------------------------
using inspect_fun = std::function<error (meta::type_name_t,
uint8_t&, atom_value&,
meta::omittable_if_empty_t,
message&)>;
// -- constructors, destructors, and assignment operators --------------------
error() noexcept; error() noexcept;
error(error&&) noexcept = default; error(none_t) noexcept;
error(const error&) noexcept = default;
error& operator=(error&&) noexcept = default; error(error&&) noexcept;
error& operator=(const error&) noexcept = default; error& operator=(error&&) noexcept;
error(const error&);
error& operator=(const error&);
error(uint8_t code, atom_value category) noexcept; error(uint8_t code, atom_value category);
error(uint8_t code, atom_value category, message msg) noexcept; error(uint8_t code, atom_value category, message msg);
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
error(E error_value) : error(make_error(error_value)) { error(E error_value) : error(make_error(error_value)) {
// nop // nop
} }
/// Returns the category-specific error code, whereas `0` means "no error". template <class E, class = enable_if_has_make_error_t<E>>
inline uint8_t code() const noexcept { error& operator=(E error_value) {
return code_; auto tmp = make_error(error_value);
std::swap(data_, tmp.data_);
return *this;
} }
/// Returns the category of this error. ~error();
inline atom_value category() const noexcept {
return category_;
}
/// Returns optional context information to this error. // -- observers --------------------------------------------------------------
inline message& context() noexcept {
return context_;
}
/// Returns optional context information to this error. /// Returns the category-specific error code, whereas `0` means "no error".
inline const message& context() const noexcept { /// @pre `*this != none`
return context_; uint8_t code() const noexcept;
}
/// Returns `code() != 0`. /// Returns the category of this error.
/// @pre `*this != none`
atom_value category() const noexcept;
/// Returns context information to this error.
/// @pre `*this != none`
const message& context() const noexcept;
/// Returns `*this != none`.
inline explicit operator bool() const noexcept { inline explicit operator bool() const noexcept {
return code_ != 0; return data_ != nullptr;
} }
/// Returns `code() == 0`. /// Returns `*this == none`.
inline bool operator!() const noexcept { inline bool operator!() const noexcept {
return code_ == 0; return data_ == nullptr;
} }
int compare(const error&) const noexcept;
int compare(uint8_t code, atom_value category) const noexcept;
// -- modifiers --------------------------------------------------------------
/// Returns context information to this error.
/// @pre `*this != none`
message& context() noexcept;
/// Sets the error code to 0. /// Sets the error code to 0.
inline void clear() noexcept { void clear() noexcept;
code_ = 0;
context_.reset(); // -- static convenience functions -------------------------------------------
/// @cond PRIVATE
static inline error eval() {
return none;
} }
friend void serialize(serializer& sink, error& x, const unsigned int); template <class F, class... Fs>
static error eval(F&& f, Fs&&... fs) {
auto x = f();
return x ? x : eval(std::forward<Fs>(fs)...);
}
friend void serialize(deserializer& source, error& x, const unsigned int); /// @endcond
int compare(const error&) const noexcept; // -- friend functions -------------------------------------------------------
int compare(uint8_t code, atom_value category) const noexcept; template <class Inspector>
friend error inspect(Inspector& f, error& x) {
auto fun = [&](meta::type_name_t x0, uint8_t& x1, atom_value& x2,
meta::omittable_if_empty_t x3, message& x4) -> error{
return f(x0, x1, x2, x3, x4);
};
return x.apply(fun);
}
private: private:
uint8_t code_; // -- inspection support -----------------------------------------------------
atom_value category_;
message context_; error apply(inspect_fun f);
// -- nested classes ---------------------------------------------------------
struct data;
// -- member variables -------------------------------------------------------
data* data_;
}; };
/// @relates error
std::string to_string(const error& x);
/// @relates error
inline bool operator==(const error& x, none_t) {
return ! x;
}
/// @relates error
inline bool operator==(none_t, const error& x) {
return ! x;
}
/// @relates error /// @relates error
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
bool operator==(const error& x, E y) { bool operator==(const error& x, E y) {
...@@ -163,6 +231,16 @@ bool operator==(E x, const error& y) { ...@@ -163,6 +231,16 @@ bool operator==(E x, const error& y) {
return make_error(x) == y; return make_error(x) == y;
} }
/// @relates error
inline bool operator!=(const error& x, none_t) {
return static_cast<bool>(x);
}
/// @relates error
inline bool operator!=(none_t, const error& x) {
return static_cast<bool>(x);
}
/// @relates error /// @relates error
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
bool operator!=(const error& x, E y) { bool operator!=(const error& x, E y) {
...@@ -175,9 +253,6 @@ bool operator!=(E x, const error& y) { ...@@ -175,9 +253,6 @@ bool operator!=(E x, const error& y) {
return ! (x == y); return ! (x == y);
} }
/// @relates error
std::string to_string(const error& x);
} // namespace caf } // namespace caf
#endif // CAF_ERROR_HPP #endif // CAF_ERROR_HPP
...@@ -81,7 +81,6 @@ class continue_helper; ...@@ -81,7 +81,6 @@ class continue_helper;
class mailbox_element; class mailbox_element;
class message_handler; class message_handler;
class scheduled_actor; class scheduled_actor;
class sync_timeout_msg;
class response_promise; class response_promise;
class event_based_actor; class event_based_actor;
class type_erased_tuple; class type_erased_tuple;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/group_module.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/abstract_group.hpp" #include "caf/abstract_group.hpp"
...@@ -84,9 +85,23 @@ public: ...@@ -84,9 +85,23 @@ public:
return ptr_ ? 1 : 0; return ptr_ ? 1 : 0;
} }
friend void serialize(serializer& sink, const group& x, const unsigned int); template <class Inspector>
friend error inspect(Inspector& f, group& x) {
std::string x_id;
std::string x_mod;
auto ptr = x.get();
if (ptr) {
x_id = ptr->identifier();
x_mod = ptr->module().name();
}
return f(meta::type_name("group"),
meta::omittable_if_empty(), x_id,
meta::omittable_if_empty(), x_mod);
}
friend error inspect(serializer&, group&);
friend void serialize(deserializer& sink, group& x, const unsigned int); friend error inspect(deserializer&, group&);
inline abstract_group* get() const noexcept { inline abstract_group* get() const noexcept {
return ptr_.get(); return ptr_.get();
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <string> #include <string>
#include <functional> #include <functional>
#include "caf/deep_to_string.hpp" #include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
...@@ -49,13 +49,10 @@ inline bool operator==(const index_mapping& x, const index_mapping& y) { ...@@ -49,13 +49,10 @@ inline bool operator==(const index_mapping& x, const index_mapping& y) {
return x.value == y.value; return x.value == y.value;
} }
template <class Processor> template <class Inspector>
void serialize(Processor& proc, index_mapping& x, const unsigned int) { auto inspect(Inspector& f, index_mapping& x)
proc & x.value; -> decltype(f(meta::type_name("index_mapping"), x.value)) {
} return f(meta::type_name("index_mapping"), x.value);
inline std::string to_string(const index_mapping& x) {
return "idx" + deep_to_string_as_tuple(x.value);
} }
} // namespace caf } // namespace caf
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#include "caf/type_erased_tuple.hpp" #include "caf/type_erased_tuple.hpp"
#include "caf/actor_control_block.hpp" #include "caf/actor_control_block.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/disposer.hpp" #include "caf/detail/disposer.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_erased_tuple_view.hpp" #include "caf/detail/type_erased_tuple_view.hpp"
...@@ -87,6 +90,13 @@ protected: ...@@ -87,6 +90,13 @@ protected:
empty_type_erased_tuple dummy_; empty_type_erased_tuple dummy_;
}; };
/// @relates mailbox_element
template <class Inspector>
void inspect(Inspector& f, mailbox_element& x) {
return f(meta::type_name("mailbox_element"), x.sender, x.mid,
meta::omittable_if_empty(), x.stages, x.content());
}
/// Encapsulates arbitrary data in a message element. /// Encapsulates arbitrary data in a message element.
template <class... Ts> template <class... Ts>
class mailbox_element_vals class mailbox_element_vals
...@@ -171,8 +181,6 @@ make_mailbox_element(strong_actor_ptr sender, message_id id, ...@@ -171,8 +181,6 @@ make_mailbox_element(strong_actor_ptr sender, message_id id,
return mailbox_element_ptr{ptr}; return mailbox_element_ptr{ptr};
} }
std::string to_string(const mailbox_element&);
} // namespace caf } // namespace caf
#endif // CAF_MAILBOX_ELEMENT_HPP #endif // CAF_MAILBOX_ELEMENT_HPP
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
...@@ -75,10 +76,11 @@ make_message(T&& x, Ts&&... xs) { ...@@ -75,10 +76,11 @@ make_message(T&& x, Ts&&... xs) {
>::type... >::type...
>; >;
static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value, static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value,
"at least one type is not serializable via free " "at least one type is neither inspectable via "
"inspect(Inspector&, T&) nor serializable via "
"'serialize(Processor&, T&, const unsigned int)' or " "'serialize(Processor&, T&, const unsigned int)' or "
"`T::serialize(Processor&, const unsigned int)` " "`T::serialize(Processor&, const unsigned int)`; "
"member function; you can whitelist individual types by " "you can whitelist individual types by "
"specializing `caf::allowed_unsafe_message_type<T>` " "specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE"); "or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
using storage = typename tl_apply<stored_types, tuple_vals>::type; using storage = typename tl_apply<stored_types, tuple_vals>::type;
......
...@@ -39,6 +39,15 @@ type_erased_value_ptr make_type_erased_value(Ts&&... xs) { ...@@ -39,6 +39,15 @@ type_erased_value_ptr make_type_erased_value(Ts&&... xs) {
return result; return result;
} }
/// @relates type_erased_value
/// Converts values to type-erased values.
struct type_erased_value_factory {
template <class T>
type_erased_value_ptr operator()(T&& x) const {
return make_type_erased_value<typename std::decay<T>::type>(std::forward<T>(x));
}
};
} // namespace caf } // namespace caf
#endif // CAF_MAKE_TYPE_ERASED_VALUE_HPP #endif // CAF_MAKE_TYPE_ERASED_VALUE_HPP
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/skip.hpp" #include "caf/skip.hpp"
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/make_counted.hpp" #include "caf/make_counted.hpp"
...@@ -67,6 +68,7 @@ public: ...@@ -67,6 +68,7 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
message() noexcept = default; message() noexcept = default;
message(none_t) noexcept;
message(const message&) noexcept = default; message(const message&) noexcept = default;
message& operator=(const message&) noexcept = default; message& operator=(const message&) noexcept = default;
...@@ -322,10 +324,10 @@ private: ...@@ -322,10 +324,10 @@ private:
}; };
/// @relates message /// @relates message
void serialize(serializer& sink, const message& msg, const unsigned int); error inspect(serializer& sink, message& msg);
/// @relates message /// @relates message
void serialize(deserializer& sink, message& msg, const unsigned int); error inspect(deserializer& sink, message& msg);
/// @relates message /// @relates message
std::string to_string(const message& msg); std::string to_string(const message& msg);
......
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include "caf/config.hpp" #include "caf/error.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
namespace caf { namespace caf {
...@@ -132,9 +135,9 @@ public: ...@@ -132,9 +135,9 @@ public:
: (value_ < other.value_ ? -1 : 1); : (value_ < other.value_ ? -1 : 1);
} }
template <class T> template <class Inspector>
friend void serialize(T& in_or_out, message_id& mid, const unsigned int) { friend error inspect(Inspector& f, message_id& x) {
in_or_out & mid.value_; return f(meta::type_name("message_id"), x.value_);
} }
private: private:
...@@ -145,11 +148,6 @@ private: ...@@ -145,11 +148,6 @@ private:
uint64_t value_; uint64_t value_;
}; };
/// @relates message_id
inline std::string to_string(const message_id& x) {
return std::to_string(x.integer_value());
}
} // namespace caf } // namespace caf
namespace std { namespace std {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_ANNOTATION_HPP
#define CAF_META_ANNOTATION_HPP
#include <type_traits>
namespace caf {
namespace meta {
/// Type tag for all meta annotations in CAF.
struct annotation {
constexpr annotation() {
// nop
}
};
template <class T>
struct is_annotation {
static constexpr bool value = std::is_base_of<annotation, T>::value;
};
} // namespace meta
} // namespace caf
#endif // CAF_META_ANNOTATION_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_HEX_FORMATTED_HPP
#define CAF_META_HEX_FORMATTED_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct hex_formatted_t : annotation {
constexpr hex_formatted_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr hex_formatted_t hex_formatted() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_HEX_FORMATTED_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_LOAD_CALLBACK_HPP
#define CAF_META_LOAD_CALLBACK_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
template <class F>
struct load_callback_t : annotation {
load_callback_t(F&& f) : fun(f) {
// nop
}
load_callback_t(load_callback_t&&) = default;
F fun;
};
/// Returns an annotation that allows inspectors to call
/// user-defined code after performing load operations.
template <class F>
load_callback_t<F> load_callback(F fun) {
return {std::move(fun)};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_LOAD_CALLBACK_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_HPP
#define CAF_META_OMITTABLE_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_t : annotation {
constexpr omittable_t() {
// nop
}
};
/// Allows an inspector to omit the following data field
/// unconditionally when producing human-friendly output.
constexpr omittable_t omittable() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_IF_EMPTY_HPP
#define CAF_META_OMITTABLE_IF_EMPTY_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_if_empty_t : annotation {
constexpr omittable_if_empty_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr omittable_if_empty_t omittable_if_empty() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_IF_EMPTY_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_OMITTABLE_IF_NONE_HPP
#define CAF_META_OMITTABLE_IF_NONE_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct omittable_if_none_t : annotation {
constexpr omittable_if_none_t() {
// nop
}
};
/// Allows an inspector to omit the following data field if it is empty.
constexpr omittable_if_none_t omittable_if_none() {
return {};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_OMITTABLE_IF_NONE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_SAVE_CALLBACK_HPP
#define CAF_META_SAVE_CALLBACK_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
template <class F>
struct save_callback_t : annotation {
save_callback_t(F&& f) : fun(f) {
// nop
}
save_callback_t(save_callback_t&&) = default;
F fun;
};
/// Returns an annotation that allows inspectors to call
/// user-defined code after performing save operations.
template <class F>
save_callback_t<F> save_callback(F fun) {
return {std::move(fun)};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_SAVE_CALLBACK_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_META_TYPE_NAME_HPP
#define CAF_META_TYPE_NAME_HPP
#include "caf/meta/annotation.hpp"
namespace caf {
namespace meta {
struct type_name_t : annotation {
constexpr type_name_t(const char* cstr) : value(cstr) {
// nop
}
const char* value;
};
/// Returns a type name annotation.
type_name_t constexpr type_name(const char* cstr) {
return {cstr};
}
} // namespace meta
} // namespace caf
#endif // CAF_META_TYPE_NAME_HPP
...@@ -26,26 +26,21 @@ ...@@ -26,26 +26,21 @@
#include <functional> #include <functional>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
#include "caf/make_counted.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
/// Objects of this type identify an invalid `node_id`.
/// @relates node_id
struct invalid_node_id_t {
constexpr invalid_node_id_t() {
// nop
}
};
/// Identifies an invalid `node_id`.
/// @relates node_id
constexpr invalid_node_id_t invalid_node_id = invalid_node_id_t{};
/// A node ID consists of a host ID and process ID. The host ID identifies /// A node ID consists of a host ID and process ID. The host ID identifies
/// the physical machine in the network, whereas the process ID identifies /// the physical machine in the network, whereas the process ID identifies
/// the running system-level process on that machine. /// the running system-level process on that machine.
...@@ -57,11 +52,11 @@ public: ...@@ -57,11 +52,11 @@ public:
node_id(const node_id&) = default; node_id(const node_id&) = default;
node_id(const invalid_node_id_t&); node_id(const none_t&);
node_id& operator=(const node_id&) = default; node_id& operator=(const node_id&) = default;
node_id& operator=(const invalid_node_id_t&); node_id& operator=(const none_t&);
/// A 160 bit hash (20 bytes). /// A 160 bit hash (20 bytes).
static constexpr size_t host_id_size = 20; static constexpr size_t host_id_size = 20;
...@@ -101,19 +96,6 @@ public: ...@@ -101,19 +96,6 @@ public:
// A reference counted container for host ID and process ID. // A reference counted container for host ID and process ID.
class data : public ref_counted { class data : public ref_counted {
public: public:
// for singleton API
void stop();
// for singleton API
inline void dispose() {
deref();
}
// for singleton API
inline void initialize() {
// nop
}
static intrusive_ptr<data> create_singleton(); static intrusive_ptr<data> create_singleton();
int compare(const node_id& other) const; int compare(const node_id& other) const;
...@@ -126,6 +108,10 @@ public: ...@@ -126,6 +108,10 @@ public:
data(uint32_t procid, const std::string& hash); data(uint32_t procid, const std::string& hash);
data& operator=(const data&) = default;
bool valid() const;
uint32_t pid_; uint32_t pid_;
host_id_type host_; host_id_type host_;
...@@ -135,15 +121,35 @@ public: ...@@ -135,15 +121,35 @@ public:
int compare(const node_id& other) const; int compare(const node_id& other) const;
// "inherited" from comparable<node_id, invalid_node_id_t> // "inherited" from comparable<node_id, invalid_node_id_t>
int compare(const invalid_node_id_t&) const; int compare(const none_t&) const;
explicit node_id(intrusive_ptr<data> dataptr); explicit node_id(intrusive_ptr<data> dataptr);
friend void serialize(serializer& sink, node_id& x, const unsigned int); template <class Inspector>
friend detail::enable_if_t<Inspector::is_saving::value, error>
friend void serialize(deserializer& source, node_id& x, const unsigned int); inspect(Inspector& f, node_id& x) {
data tmp;
data* ptr = x ? x.data_.get() : &tmp;
return f(meta::type_name("node_id"), ptr->pid_,
meta::hex_formatted(), ptr->host_);
}
void from_string(const std::string& str); template <class Inspector>
friend detail::enable_if_t<Inspector::is_loading::value, error>
inspect(Inspector& f, node_id& x) {
data tmp;
auto err = f(meta::type_name("node_id"), tmp.pid_,
meta::hex_formatted(), tmp.host_);
if (! err) {
if (! tmp.valid())
x.data_.reset();
else if (! x || ! x.data_->unique())
x.data_.reset(new data(tmp));
else
*x.data_ = tmp;
}
return err;
}
/// @endcond /// @endcond
...@@ -151,27 +157,38 @@ private: ...@@ -151,27 +157,38 @@ private:
intrusive_ptr<data> data_; intrusive_ptr<data> data_;
}; };
inline bool operator==(const node_id& lhs, const node_id& rhs) { /// @relates node_id
return lhs.compare(rhs) == 0; inline bool operator==(const node_id& x, none_t) {
return ! x;
} }
inline bool operator==(const node_id& lhs, invalid_node_id_t) { /// @relates node_id
return ! static_cast<bool>(lhs); inline bool operator==(none_t, const node_id& x) {
return ! x;
} }
inline bool operator!=(const node_id& lhs, const node_id& rhs) { /// @relates node_id
return ! (lhs == rhs); inline bool operator!=(const node_id& x, none_t) {
return static_cast<bool>(x);
} }
inline bool operator!=(const node_id& lhs, invalid_node_id_t) { /// @relates node_id
return static_cast<bool>(lhs); inline bool operator!=(none_t, const node_id& x) {
return static_cast<bool>(x);
}
inline bool operator==(const node_id& lhs, const node_id& rhs) {
return lhs.compare(rhs) == 0;
}
inline bool operator!=(const node_id& lhs, const node_id& rhs) {
return ! (lhs == rhs);
} }
inline bool operator<(const node_id& lhs, const node_id& rhs) { inline bool operator<(const node_id& lhs, const node_id& rhs) {
return lhs.compare(rhs) < 0; return lhs.compare(rhs) < 0;
} }
/// @relates node_id
std::string to_string(const node_id& x); std::string to_string(const node_id& x);
} // namespace caf } // namespace caf
...@@ -181,7 +198,7 @@ namespace std{ ...@@ -181,7 +198,7 @@ namespace std{
template<> template<>
struct hash<caf::node_id> { struct hash<caf::node_id> {
size_t operator()(const caf::node_id& nid) const { size_t operator()(const caf::node_id& nid) const {
if (nid == caf::invalid_node_id) if (nid == caf::none)
return 0; return 0;
// xor the first few bytes from the node ID and the process ID // xor the first few bytes from the node ID and the process ID
auto x = static_cast<size_t>(nid.process_id()); auto x = static_cast<size_t>(nid.process_id());
......
...@@ -42,12 +42,6 @@ struct none_t : detail::comparable<none_t> { ...@@ -42,12 +42,6 @@ struct none_t : detail::comparable<none_t> {
static constexpr none_t none = none_t{}; static constexpr none_t none = none_t{};
/// @relates none_t
template <class Processor>
void serialize(Processor&, const none_t&, const unsigned int) {
// nop
}
/// @relates none_t /// @relates none_t
template <class T> template <class T>
std::string to_string(const none_t&) { std::string to_string(const none_t&) {
......
...@@ -26,13 +26,12 @@ ...@@ -26,13 +26,12 @@
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/unit.hpp" #include "caf/unit.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
namespace caf { namespace caf {
/// A since C++17 compatible `optional` implementation. /// A C++17 compatible `optional` implementation.
template <class T> template <class T>
class optional { class optional {
public: public:
...@@ -255,35 +254,10 @@ class optional<void> { ...@@ -255,35 +254,10 @@ class optional<void> {
bool m_value; bool m_value;
}; };
/// @relates optional /// @relates optional
template <class T> template <class T>
std::string to_string(const optional<T>& x) { auto to_string(const optional<T>& x) -> decltype(to_string(*x)) {
return x ? "!" + deep_to_string(*x) : "<none>"; return x ? "*" + to_string(*x) : "<null>";
}
/// @relates optional
template <class Processor, class T>
typename std::enable_if<Processor::is_saving::value>::type
serialize(Processor& sink, optional<T>& x, const unsigned int) {
uint8_t flag = x ? 1 : 0;
sink & flag;
if (flag)
sink & *x;
}
/// @relates optional
template <class Processor, class T>
typename std::enable_if<Processor::is_loading::value>::type
serialize(Processor& source, optional<T>& x, const unsigned int) {
uint8_t flag;
source & flag;
if (flag) {
T value;
source & value;
x = std::move(value);
}
x = none;
} }
// -- [X.Y.8] comparison with optional ---------------------------------------- // -- [X.Y.8] comparison with optional ----------------------------------------
......
...@@ -79,6 +79,14 @@ enum class sec : uint8_t { ...@@ -79,6 +79,14 @@ enum class sec : uint8_t {
cannot_publish_invalid_actor, cannot_publish_invalid_actor,
/// A remote spawn failed because the provided types did not match. /// A remote spawn failed because the provided types did not match.
cannot_spawn_actor_from_arguments, cannot_spawn_actor_from_arguments,
/// Serialization failed because there was not enough data to read.
end_of_stream,
/// Serialization failed because no CAF context is available.
no_context,
/// Serialization failed because CAF misses run-time type information.
unknown_type,
/// Serialization of actors failed because no proxy registry is available.
no_proxy_registry,
/// A function view was called without assigning an actor first. /// A function view was called without assigning an actor first.
bad_function_call bad_function_call
}; };
......
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
#include <cstddef> // size_t #include <cstddef> // size_t
#include <type_traits> #include <type_traits>
#include "caf/config.hpp"
#ifndef CAF_NO_EXCEPTIONS
#include <exception>
#endif // CAF_NO_EXCEPTIONS
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/data_processor.hpp" #include "caf/data_processor.hpp"
...@@ -46,37 +52,36 @@ public: ...@@ -46,37 +52,36 @@ public:
virtual ~serializer(); virtual ~serializer();
}; };
#ifndef CAF_NO_EXCEPTIONS
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<serializer&>().apply(std::declval<T&>())) decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value, >::value
serializer&
>::type >::type
operator<<(serializer& sink, const T& x) { operator&(serializer& sink, const T& x) {
// implementations are required to not change an object while serializing // implementations are required to never modify `x` while saving
sink.apply(const_cast<T&>(x)); auto e = sink.apply(const_cast<T&>(x));
return sink; if (e)
throw std::runtime_error(to_string(e));
} }
template <class T> template <class T>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
void, error,
decltype(std::declval<serializer&>().apply(std::declval<T&>())) decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value, >::value,
serializer& serializer&
>::type >::type
operator<<(serializer& sink, T& x) { operator<<(serializer& sink, const T& x) {
sink.apply(x); sink & x;
return sink; return sink;
} }
template <class T> #endif // CAF_NO_EXCEPTIONS
auto operator&(serializer& sink, T& x) -> decltype(sink.apply(x)) {
sink.apply(x);
}
} // namespace caf } // namespace caf
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <type_traits> #include <type_traits>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
...@@ -63,15 +64,11 @@ public: ...@@ -63,15 +64,11 @@ public:
} }
error save_state(serializer& sink, const unsigned int version) override { error save_state(serializer& sink, const unsigned int version) override {
serialize_state(sink, state, version); return serialize_state(&sink, state, version);
// TODO: refactor after visit API is in place (#470)
return {};
} }
error load_state(deserializer& source, const unsigned int version) override { error load_state(deserializer& source, const unsigned int version) override {
serialize_state(source, state, version); return serialize_state(&source, state, version);
// TODO: refactor after visit API is in place (#470)
return {};
} }
/// A reference to the actor's state. /// A reference to the actor's state.
...@@ -87,16 +84,15 @@ public: ...@@ -87,16 +84,15 @@ public:
/// @endcond /// @endcond
private: private:
template <class Archive, class U> template <class Inspector, class T>
typename std::enable_if<detail::is_serializable<U>::value>::type auto serialize_state(Inspector* f, T& x, const unsigned int)
serialize_state(Archive& ar, U& st, const unsigned int) { -> decltype(inspect(*f, x)) {
ar & st; return inspect(*f, x);
} }
template <class Archive, class U> template <class T>
typename std::enable_if<! detail::is_serializable<U>::value>::type error serialize_state(void*, T&, const unsigned int) {
serialize_state(Archive&, U&, const unsigned int) { return sec::invalid_argument;
CAF_RAISE_ERROR("serialize_state with unserializable type called");
} }
template <class T> template <class T>
......
...@@ -20,19 +20,19 @@ ...@@ -20,19 +20,19 @@
#ifndef CAF_STREAM_DESERIALIZER_HPP #ifndef CAF_STREAM_DESERIALIZER_HPP
#define CAF_STREAM_DESERIALIZER_HPP #define CAF_STREAM_DESERIALIZER_HPP
#include <limits>
#include <string>
#include <sstream>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <exception>
#include <iomanip> #include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <type_traits> #include <type_traits>
#include "caf/deserializer.hpp" #include "caf/sec.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/ieee_754.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp" #include "caf/detail/network_order.hpp"
...@@ -45,6 +45,8 @@ class stream_deserializer : public deserializer { ...@@ -45,6 +45,8 @@ class stream_deserializer : public deserializer {
using streambuf_type = typename std::remove_reference<Streambuf>::type; using streambuf_type = typename std::remove_reference<Streambuf>::type;
using char_type = typename streambuf_type::char_type; using char_type = typename streambuf_type::char_type;
using streambuf_base = std::basic_streambuf<char_type>; using streambuf_base = std::basic_streambuf<char_type>;
using streamsize = std::streamsize;
static_assert(std::is_base_of<streambuf_base, streambuf_type>::value, static_assert(std::is_base_of<streambuf_base, streambuf_type>::value,
"Streambuf must inherit from std::streambuf"); "Streambuf must inherit from std::streambuf");
...@@ -75,152 +77,136 @@ public: ...@@ -75,152 +77,136 @@ public:
streambuf_(std::forward<S>(sb)) { streambuf_(std::forward<S>(sb)) {
} }
void begin_object(uint16_t& typenr, std::string& name) override { error begin_object(uint16_t& typenr, std::string& name) override {
apply_int(typenr); return error::eval([&] { return apply_int(typenr); },
if (typenr == 0) [&] { return typenr == 0 ? apply(name) : error{}; });
apply(name);
} }
void end_object() override { error end_object() override {
// nop return none;
} }
void begin_sequence(size_t& num_elements) override { error begin_sequence(size_t& num_elements) override {
varbyte_decode(num_elements); return varbyte_decode(num_elements);
} }
void end_sequence() override { error end_sequence() override {
// nop return none;
} }
void apply_raw(size_t num_bytes, void* data) override { error apply_raw(size_t num_bytes, void* data) override {
auto n = streambuf_.sgetn(reinterpret_cast<char_type*>(data), return range_check(streambuf_.sgetn(reinterpret_cast<char_type*>(data),
static_cast<std::streamsize>(num_bytes)); static_cast<streamsize>(num_bytes)),
CAF_ASSERT(n >= 0); num_bytes);
range_check(static_cast<size_t>(n), num_bytes);
} }
protected: protected:
// Decode an unsigned integral type as variable-byte-encoded byte sequence. // Decode an unsigned integral type as variable-byte-encoded byte sequence.
template <class T> template <class T>
size_t varbyte_decode(T& x) { error varbyte_decode(T& x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type"); static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
auto n = T{0}; T n = 0;
x = 0; x = 0;
uint8_t low7; uint8_t low7;
do { do {
auto c = streambuf_.sbumpc(); auto c = streambuf_.sbumpc();
using traits = typename streambuf_type::traits_type; using traits = typename streambuf_type::traits_type;
if (traits::eq_int_type(c, traits::eof())) if (traits::eq_int_type(c, traits::eof()))
CAF_RAISE_ERROR("stream_deserializer<T>::begin_sequence"); return sec::end_of_stream;
low7 = static_cast<uint8_t>(traits::to_char_type(c)); low7 = static_cast<uint8_t>(traits::to_char_type(c));
x |= static_cast<T>((low7 & 0x7F) << (7 * n)); x |= static_cast<T>((low7 & 0x7F) << (7 * n));
++n; ++n;
} while (low7 & 0x80); } while (low7 & 0x80);
return n; return none;
} }
void apply_builtin(builtin type, void* val) override { error apply_builtin(builtin type, void* val) override {
CAF_ASSERT(val != nullptr); CAF_ASSERT(val != nullptr);
switch (type) { switch (type) {
case i8_v: case i8_v:
case u8_v: case u8_v:
apply_raw(sizeof(uint8_t), val); return apply_raw(sizeof(uint8_t), val);
break;
case i16_v: case i16_v:
case u16_v: case u16_v:
apply_int(*reinterpret_cast<uint16_t*>(val)); return apply_int(*reinterpret_cast<uint16_t*>(val));
break;
case i32_v: case i32_v:
case u32_v: case u32_v:
apply_int(*reinterpret_cast<uint32_t*>(val)); return apply_int(*reinterpret_cast<uint32_t*>(val));
break;
case i64_v: case i64_v:
case u64_v: case u64_v:
apply_int(*reinterpret_cast<uint64_t*>(val)); return apply_int(*reinterpret_cast<uint64_t*>(val));
break;
case float_v: case float_v:
apply_float(*reinterpret_cast<float*>(val)); return apply_float(*reinterpret_cast<float*>(val));
break;
case double_v: case double_v:
apply_float(*reinterpret_cast<double*>(val)); return apply_float(*reinterpret_cast<double*>(val));
break;
case ldouble_v: { case ldouble_v: {
// the IEEE-754 conversion does not work for long double // the IEEE-754 conversion does not work for long double
// => fall back to string serialization (even though it sucks) // => fall back to string serialization (even though it sucks)
std::string tmp; std::string tmp;
apply(tmp); auto e = apply(tmp);
if (e)
return e;
std::istringstream iss{std::move(tmp)}; std::istringstream iss{std::move(tmp)};
iss >> *reinterpret_cast<long double*>(val); iss >> *reinterpret_cast<long double*>(val);
break; return none;
} }
case string8_v: { case string8_v: {
auto& str = *reinterpret_cast<std::string*>(val); auto& str = *reinterpret_cast<std::string*>(val);
size_t str_size; size_t str_size;
begin_sequence(str_size); return error::eval([&] { return begin_sequence(str_size); },
str.resize(str_size); [&] { str.resize(str_size);
// TODO: When using C++14, switch to str.data(), which then has a auto p = &str[0];
// non-const overload. auto data = reinterpret_cast<char_type*>(p);
auto data = reinterpret_cast<char_type*>(&str[0]); auto s = static_cast<streamsize>(str_size);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(str_size)); return range_check(streambuf_.sgetn(data, s),
CAF_ASSERT(n >= 0); str_size); },
range_check(static_cast<size_t>(n), str_size); [&] { return end_sequence(); });
end_sequence();
break;
} }
case string16_v: { case string16_v: {
auto& str = *reinterpret_cast<std::u16string*>(val); auto& str = *reinterpret_cast<std::u16string*>(val);
size_t str_size; str.clear();
begin_sequence(str_size); size_t ns;
auto bytes = str_size * sizeof(std::u16string::value_type); return error::eval([&] { return begin_sequence(ns); },
str.resize(str_size); [&] { return fill_range_c<uint16_t>(str, ns); },
// TODO: When using C++14, switch to str.data(), which then has a [&] { return end_sequence(); });
// non-const overload.
auto data = reinterpret_cast<char_type*>(&str[0]);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(bytes));
CAF_ASSERT(n >= 0);
range_check(static_cast<size_t>(n), str_size);
end_sequence();
break;
} }
case string32_v: { case string32_v: {
auto& str = *reinterpret_cast<std::u32string*>(val); auto& str = *reinterpret_cast<std::u32string*>(val);
size_t str_size; str.clear();
begin_sequence(str_size); size_t ns;
auto bytes = str_size * sizeof(std::u32string::value_type); return error::eval([&] { return begin_sequence(ns); },
str.resize(str_size); [&] { return fill_range_c<uint32_t>(str, ns); },
// TODO: When using C++14, switch to str.data(), which then has a [&] { return end_sequence(); });
// non-const overload.
auto data = reinterpret_cast<char_type*>(&str[0]);
auto n = streambuf_.sgetn(data, static_cast<std::streamsize>(bytes));
CAF_ASSERT(n >= 0);
range_check(static_cast<size_t>(n), str_size);
end_sequence();
break;
} }
} }
} }
private: private:
void range_check(size_t got, size_t need) { error range_check(size_t got, size_t need) {
if (got != need) { if (got == need)
CAF_LOG_ERROR("range_check failed"); return none;
CAF_RAISE_ERROR("stream_deserializer<T>::range_check()"); CAF_LOG_ERROR("range_check failed");
} return sec::end_of_stream;
} }
template <class T> template <class T>
void apply_int(T& x) { error apply_int(T& x) {
T tmp; T tmp;
apply_raw(sizeof(T), &tmp); auto e = apply_raw(sizeof(T), &tmp);
if (e)
return e;
x = detail::from_network_order(tmp); x = detail::from_network_order(tmp);
return none;
} }
template <class T> template <class T>
void apply_float(T& x) { error apply_float(T& x) {
typename detail::ieee_754_trait<T>::packed_type tmp; typename detail::ieee_754_trait<T>::packed_type tmp;
apply_int(tmp); auto e = apply_int(tmp);
if (e)
return e;
x = detail::unpack754(tmp); x = detail::unpack754(tmp);
return none;
} }
Streambuf streambuf_; Streambuf streambuf_;
......
...@@ -20,17 +20,18 @@ ...@@ -20,17 +20,18 @@
#ifndef CAF_STREAM_SERIALIZER_HPP #ifndef CAF_STREAM_SERIALIZER_HPP
#define CAF_STREAM_SERIALIZER_HPP #define CAF_STREAM_SERIALIZER_HPP
#include <string>
#include <limits>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <iomanip> #include <iomanip>
#include <limits>
#include <streambuf>
#include <sstream> #include <sstream>
#include <string> #include <streambuf>
#include <type_traits> #include <type_traits>
#include "caf/serializer.hpp" #include "caf/sec.hpp"
#include "caf/streambuf.hpp" #include "caf/streambuf.hpp"
#include "caf/serializer.hpp"
#include "caf/detail/ieee_754.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp" #include "caf/detail/network_order.hpp"
...@@ -73,33 +74,36 @@ public: ...@@ -73,33 +74,36 @@ public:
streambuf_(std::forward<S>(sb)) { streambuf_(std::forward<S>(sb)) {
} }
void begin_object(uint16_t& typenr, std::string& name) override { error begin_object(uint16_t& typenr, std::string& name) override {
apply(typenr); return error::eval([&] { return apply(typenr); },
if (typenr == 0) [&] { return typenr == 0 ? apply(name) : error{}; });
apply(name);
} }
void end_object() override { error end_object() override {
// nop return none;
} }
void begin_sequence(size_t& list_size) override { error begin_sequence(size_t& list_size) override {
varbyte_encode(list_size); return varbyte_encode(list_size);
} }
void end_sequence() override { error end_sequence() override {
// nop return none;
} }
void apply_raw(size_t num_bytes, void* data) override { error apply_raw(size_t num_bytes, void* data) override {
streambuf_.sputn(reinterpret_cast<char_type*>(data), auto ssize = static_cast<std::streamsize>(num_bytes);
static_cast<std::streamsize>(num_bytes)); auto n = streambuf_.sputn(reinterpret_cast<char_type*>(data), ssize);
if (n != ssize)
return sec::end_of_stream;
return none;
} }
protected: protected:
// Encode an unsigned integral type as variable-byte sequence. // Encode an unsigned integral type as variable-byte sequence.
template <class T> template <class T>
size_t varbyte_encode(T x) { error varbyte_encode(T x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type"); static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
// For 64-bit values, the encoded representation cannot get larger than 10 // For 64-bit values, the encoded representation cannot get larger than 10
// bytes. A scratch space of 16 bytes suffices as upper bound. // bytes. A scratch space of 16 bytes suffices as upper bound.
...@@ -112,35 +116,30 @@ protected: ...@@ -112,35 +116,30 @@ protected:
*i++ = static_cast<uint8_t>(x) & 0x7f; *i++ = static_cast<uint8_t>(x) & 0x7f;
auto res = streambuf_.sputn(reinterpret_cast<char_type*>(buf), auto res = streambuf_.sputn(reinterpret_cast<char_type*>(buf),
static_cast<std::streamsize>(i - buf)); static_cast<std::streamsize>(i - buf));
CAF_ASSERT(res >= 0); if (res != (i - buf))
return static_cast<size_t>(res); return sec::end_of_stream;
return none;
} }
void apply_builtin(builtin type, void* val) override { error apply_builtin(builtin type, void* val) override {
CAF_ASSERT(val != nullptr); CAF_ASSERT(val != nullptr);
switch (type) { switch (type) {
case i8_v: case i8_v:
case u8_v: case u8_v:
apply_raw(sizeof(uint8_t), val); return apply_raw(sizeof(uint8_t), val);
break;
case i16_v: case i16_v:
case u16_v: case u16_v:
apply_int(*reinterpret_cast<uint16_t*>(val)); return apply_int(*reinterpret_cast<uint16_t*>(val));
break;
case i32_v: case i32_v:
case u32_v: case u32_v:
apply_int(*reinterpret_cast<uint32_t*>(val)); return apply_int(*reinterpret_cast<uint32_t*>(val));
break;
case i64_v: case i64_v:
case u64_v: case u64_v:
apply_int(*reinterpret_cast<uint64_t*>(val)); return apply_int(*reinterpret_cast<uint64_t*>(val));
break;
case float_v: case float_v:
apply_int(detail::pack754(*reinterpret_cast<float*>(val))); return apply_int(detail::pack754(*reinterpret_cast<float*>(val)));
break;
case double_v: case double_v:
apply_int(detail::pack754(*reinterpret_cast<double*>(val))); return apply_int(detail::pack754(*reinterpret_cast<double*>(val)));
break;
case ldouble_v: { case ldouble_v: {
// the IEEE-754 conversion does not work for long double // the IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks) // => fall back to string serialization (event though it sucks)
...@@ -148,50 +147,41 @@ protected: ...@@ -148,50 +147,41 @@ protected:
oss << std::setprecision(std::numeric_limits<long double>::digits) oss << std::setprecision(std::numeric_limits<long double>::digits)
<< *reinterpret_cast<long double*>(val); << *reinterpret_cast<long double*>(val);
auto tmp = oss.str(); auto tmp = oss.str();
apply(tmp); return apply(tmp);
break;
} }
case string8_v: { case string8_v: {
auto str = reinterpret_cast<std::string*>(val); auto str = reinterpret_cast<std::string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s); auto data = reinterpret_cast<char_type*>(
auto data = const_cast<std::string::value_type*>(str->data()); const_cast<std::string::value_type*>(str->data()));
apply_raw(str->size(), reinterpret_cast<char_type*>(data)); return error::eval([&] { return begin_sequence(s); },
end_sequence(); [&] { return apply_raw(str->size(), data); },
break; [&] { return end_sequence(); });
} }
case string16_v: { case string16_v: {
auto str = reinterpret_cast<std::u16string*>(val); auto str = reinterpret_cast<std::u16string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s); // the standard does not guarantee that char16_t is exactly 16 bits...
for (auto c : *str) { return error::eval([&] { return begin_sequence(s); },
// the standard does not guarantee that char16_t is exactly 16 bits... [&] { return consume_range_c<uint16_t>(*str); },
auto tmp = static_cast<uint16_t>(c); [&] { return end_sequence(); });
apply_raw(sizeof(tmp), &tmp);
}
end_sequence();
break;
} }
case string32_v: { case string32_v: {
auto str = reinterpret_cast<std::u32string*>(val); auto str = reinterpret_cast<std::u32string*>(val);
auto s = str->size(); auto s = str->size();
begin_sequence(s); // the standard does not guarantee that char32_t is exactly 32 bits...
for (auto c : *str) { return error::eval([&] { return begin_sequence(s); },
// the standard does not guarantee that char32_t is exactly 32 bits... [&] { return consume_range_c<uint32_t>(*str); },
auto tmp = static_cast<uint32_t>(c); [&] { return end_sequence(); });
apply_raw(sizeof(tmp), &tmp);
}
end_sequence();
break;
} }
} }
} }
private: private:
template <class T> template <class T>
void apply_int(T x) { error apply_int(T x) {
auto y = detail::to_network_order(x); auto y = detail::to_network_order(x);
apply_raw(sizeof(T), &y); return apply_raw(sizeof(T), &y);
} }
Streambuf streambuf_; Streambuf streambuf_;
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/detail/tbind.hpp" #include "caf/detail/tbind.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
...@@ -67,14 +69,10 @@ private: ...@@ -67,14 +69,10 @@ private:
exit_msg() = default; exit_msg() = default;
}; };
inline std::string to_string(const exit_msg& x) { /// @relates exit_msg
return "exit_msg" + deep_to_string(std::tie(x.source, x.reason)); template <class Inspector>
} error inspect(Inspector& f, exit_msg& x) {
return f(meta::type_name("exit_msg"), x.source, x.reason);
template <class Processor>
void serialize(Processor& proc, exit_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
} }
/// Sent to all actors monitoring an actor when it is terminated. /// Sent to all actors monitoring an actor when it is terminated.
...@@ -108,14 +106,10 @@ private: ...@@ -108,14 +106,10 @@ private:
down_msg() = default; down_msg() = default;
}; };
inline std::string to_string(const down_msg& x) { /// @relates down_msg
return "down_msg" + deep_to_string(std::tie(x.source, x.reason)); template <class Inspector>
} error inspect(Inspector& f, down_msg& x) {
return f(meta::type_name("down_msg"), x.source, x.reason);
template <class Processor>
void serialize(Processor& proc, down_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
} }
/// Sent to all members of a group when it goes offline. /// Sent to all members of a group when it goes offline.
...@@ -124,29 +118,10 @@ struct group_down_msg { ...@@ -124,29 +118,10 @@ struct group_down_msg {
group source; group source;
}; };
inline std::string to_string(const group_down_msg& x) {
return "group_down" + deep_to_string(std::tie(x.source));
}
/// @relates group_down_msg /// @relates group_down_msg
template <class Processor> template <class Inspector>
void serialize(Processor& proc, group_down_msg& x, const unsigned int) { error inspect(Inspector& f, group_down_msg& x) {
proc & x.source; return f(meta::type_name("group_down_msg"), 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
} }
/// Signalizes a timeout event. /// Signalizes a timeout event.
...@@ -156,14 +131,10 @@ struct timeout_msg { ...@@ -156,14 +131,10 @@ struct timeout_msg {
uint32_t timeout_id; uint32_t timeout_id;
}; };
inline std::string to_string(const timeout_msg& x) {
return "timeout" + deep_to_string(std::tie(x.timeout_id));
}
/// @relates timeout_msg /// @relates timeout_msg
template <class Processor> template <class Inspector>
void serialize(Processor& proc, timeout_msg& x, const unsigned int) { error inspect(Inspector& f, timeout_msg& x) {
proc & x.timeout_id; return f(meta::type_name("timeout_msg"), x.timeout_id);
} }
} // namespace caf } // namespace caf
......
...@@ -17,52 +17,33 @@ ...@@ -17,52 +17,33 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/deep_to_string.hpp" #ifndef CAF_TO_STRING_HPP
#define CAF_TO_STRING_HPP
namespace caf {
namespace { #include <string>
bool is_escaped(const char* cstr) { #include "caf/error.hpp"
if (*cstr != '"') #include "caf/deep_to_string.hpp"
return false;
char last_char = *cstr++;
for (auto c = *cstr; c != '\0'; ++cstr)
if (c == '"' && last_char != '\\')
return false;
return last_char == '"';
}
} // namespace <anonymous> #include "caf/detail/type_traits.hpp"
#include "caf/detail/stringification_inspector.hpp"
std::string deep_to_string_t::operator()(const char* cstr) const { namespace caf {
if (! cstr || *cstr == '\0')
return "\"\"";
if (is_escaped(cstr))
return cstr;
std::string result = "\"";
char c;
while ((c = *cstr++) != '\0') {
switch (c) {
case '\\':
result += "\\\\";
break;
case '"':
result += "\\\"";
break;
default:
result += c;
}
}
result += "\"";
return result;
}
std::string deep_to_string_t::operator()(const atom_value& x) const { template <class T,
std::string result = "'"; class E = typename std::enable_if<
result += to_string(x); detail::is_inspectable<
result += "'"; detail::stringification_inspector,
return result; T
>::value
>::type>
std::string to_string(const T& x) {
std::string res;
detail::stringification_inspector f{res};
inspect(f, const_cast<T&>(x));
return res;
} }
} // namespace caf } // namespace caf
#endif // CAF_TO_STRING_HPP
...@@ -130,6 +130,15 @@ public: ...@@ -130,6 +130,15 @@ public:
return *reinterpret_cast<T*>(get_mutable(pos)); return *reinterpret_cast<T*>(get_mutable(pos));
} }
/// Convenience function for moving a value out of the tuple if it is
/// unshared. Returns a copy otherwise.
template <class T>
T move_if_unshared(size_t pos) {
if (shared())
return get_as<T>(pos);
return std::move(get_mutable_as<T>(pos));
}
/// Returns `true` if the element at `pos` matches `T`. /// Returns `true` if the element at `pos` matches `T`.
template <class T> template <class T>
bool match_element(size_t pos) const noexcept { bool match_element(size_t pos) const noexcept {
......
...@@ -97,17 +97,17 @@ public: ...@@ -97,17 +97,17 @@ public:
}; };
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
template <class Processor> template <class Inspector>
typename std::enable_if<Processor::is_saving::value>::type typename std::enable_if<Inspector::is_saving::value, error>::type
serialize(Processor& proc, type_erased_value& x) { inspect(Inspector& f, type_erased_value& x) {
x.save(proc); return x.save(f);
} }
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
template <class Processor> template <class Inspector>
typename std::enable_if<Processor::is_loading::value>::type typename std::enable_if<Inspector::is_loading::value, error>::type
serialize(Processor& proc, type_erased_value& x) { inspect(Inspector& f, type_erased_value& x) {
x.load(proc); return x.load(f);
} }
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
......
...@@ -62,7 +62,6 @@ using sorted_builtin_types = ...@@ -62,7 +62,6 @@ using sorted_builtin_types =
strong_actor_ptr, // @strong_actor_ptr strong_actor_ptr, // @strong_actor_ptr
std::set<std::string>, // @strset std::set<std::string>, // @strset
std::vector<std::string>, // @strvec std::vector<std::string>, // @strvec
sync_timeout_msg, // @sync_timeout_msg
timeout_msg, // @timeout timeout_msg, // @timeout
uint16_t, // @u16 uint16_t, // @u16
std::u16string, // @u16_str std::u16string, // @u16_str
......
...@@ -241,13 +241,9 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>, ...@@ -241,13 +241,9 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
// nop // nop
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, typed_actor& x, const unsigned int v) { friend error inspect(Inspector& f, typed_actor& x) {
serialize(proc, x.ptr_, v); return f(x.ptr_);
}
friend inline std::string to_string(const typed_actor& x) {
return to_string(x.ptr_);
} }
/// Releases the reference held by handle `x`. Using the /// Releases the reference held by handle `x`. Using the
......
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include "caf/actor_control_block.hpp" #include "caf/actor_control_block.hpp"
#include "caf/to_string.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/proxy_registry.hpp" #include "caf/proxy_registry.hpp"
...@@ -76,86 +81,34 @@ bool operator==(const abstract_actor* x, const strong_actor_ptr& y) { ...@@ -76,86 +81,34 @@ bool operator==(const abstract_actor* x, const strong_actor_ptr& y) {
return actor_control_block::from(x) == y.get(); return actor_control_block::from(x) == y.get();
} }
template <class T> error load_actor(strong_actor_ptr& storage, execution_unit* ctx,
void safe_actor(serializer& sink, T& storage) { actor_id aid, const node_id& nid) {
CAF_LOG_TRACE(CAF_ARG(storage)); if (! ctx)
if (! sink.context()) { return sec::no_context;
CAF_LOG_ERROR("Cannot serialize actors without context."); auto& sys = ctx->system();
CAF_RAISE_ERROR("Cannot serialize actors without context.");
}
auto& sys = sink.context()->system();
auto ptr = storage.get();
actor_id aid = invalid_actor_id;
node_id nid = invalid_node_id;
if (ptr) {
aid = ptr->aid;
nid = ptr->nid;
// register locally running actors to be able to deserialize them later
if (nid == sys.node())
sys.registry().put(aid, actor_cast<strong_actor_ptr>(storage));
}
sink << aid << nid;
}
template <class T>
void load_actor(deserializer& source, T& storage) {
CAF_LOG_TRACE("");
storage.reset();
if (! source.context())
CAF_RAISE_ERROR("Cannot deserialize actor_addr without context.");
auto& sys = source.context()->system();
actor_id aid;
node_id nid;
source >> aid >> nid;
CAF_LOG_DEBUG(CAF_ARG(aid) << CAF_ARG(nid));
// deal with local actors
if (sys.node() == nid) { if (sys.node() == nid) {
storage = actor_cast<T>(sys.registry().get(aid)); storage = sys.registry().get(aid);
CAF_LOG_DEBUG("fetch actor handle from local actor registry: " CAF_LOG_DEBUG("fetch actor handle from local actor registry: "
<< (storage ? "found" : "not found")); << (storage ? "found" : "not found"));
return; return none;
} }
auto prp = source.context()->proxy_registry_ptr(); auto prp = ctx->proxy_registry_ptr();
if (! prp) if (! prp)
CAF_RAISE_ERROR("Cannot deserialize remote actors without proxy registry."); return sec::no_proxy_registry;
// deal with (proxies for) remote actors // deal with (proxies for) remote actors
storage = actor_cast<T>(prp->get_or_put(nid, aid)); storage = prp->get_or_put(nid, aid);
} return none;
}
void serialize(serializer& sink, strong_actor_ptr& x, const unsigned int) {
CAF_LOG_TRACE(""); error save_actor(strong_actor_ptr& storage, execution_unit* ctx,
safe_actor(sink, x); actor_id aid, const node_id& nid) {
} if (! ctx)
return sec::no_context;
void serialize(deserializer& source, strong_actor_ptr& x, const unsigned int) { auto& sys = ctx->system();
CAF_LOG_TRACE(""); // register locally running actors to be able to deserialize them later
load_actor(source, x); if (nid == sys.node())
} sys.registry().put(aid, storage);
return none;
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());
} }
} // namespace caf } // namespace caf
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <unordered_set> #include <unordered_set>
#include "caf/send.hpp" #include "caf/send.hpp"
#include "caf/to_string.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
......
...@@ -318,14 +318,16 @@ actor_system_config& actor_system_config::set(const char* cn, config_value cv) { ...@@ -318,14 +318,16 @@ actor_system_config& actor_system_config::set(const char* cn, config_value cv) {
std::string actor_system_config::render_sec(uint8_t x, atom_value, std::string actor_system_config::render_sec(uint8_t x, atom_value,
const message& xs) { const message& xs) {
return "system_error" auto tmp = static_cast<sec>(x);
+ (xs.empty() ? deep_to_string_as_tuple(static_cast<sec>(x)) return deep_to_string(meta::type_name("system_error"), tmp,
: deep_to_string_as_tuple(static_cast<sec>(x), xs)); meta::omittable_if_empty(), xs);
} }
std::string actor_system_config::render_exit_reason(uint8_t x, atom_value, std::string actor_system_config::render_exit_reason(uint8_t x, atom_value,
const message&) { const message& xs) {
return "exit_reason" + deep_to_string_as_tuple(static_cast<exit_reason>(x)); auto tmp = static_cast<sec>(x);
return deep_to_string(meta::type_name("exit_reason"), tmp,
meta::omittable_if_empty(), xs);
} }
} // namespace caf } // namespace caf
...@@ -66,9 +66,7 @@ void* concatenated_tuple::get_mutable(size_t pos) { ...@@ -66,9 +66,7 @@ void* concatenated_tuple::get_mutable(size_t pos) {
error concatenated_tuple::load(size_t pos, deserializer& source) { error concatenated_tuple::load(size_t pos, deserializer& source) {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
auto selected = select(pos); auto selected = select(pos);
selected.first->load(selected.second, source); return selected.first->load(selected.second, source);
// TODO: refactor after visit API is in place (#470)
return {};
} }
size_t concatenated_tuple::size() const noexcept { size_t concatenated_tuple::size() const noexcept {
...@@ -106,9 +104,7 @@ type_erased_value_ptr concatenated_tuple::copy(size_t pos) const { ...@@ -106,9 +104,7 @@ type_erased_value_ptr concatenated_tuple::copy(size_t pos) const {
error concatenated_tuple::save(size_t pos, serializer& sink) const { error concatenated_tuple::save(size_t pos, serializer& sink) const {
CAF_ASSERT(pos < size()); CAF_ASSERT(pos < size());
auto selected = select(pos); auto selected = select(pos);
selected.first->save(selected.second, sink); return selected.first->save(selected.second, sink);
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) const { std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) const {
......
...@@ -20,62 +20,154 @@ ...@@ -20,62 +20,154 @@
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/deep_to_string.hpp"
namespace caf { namespace caf {
error::error() noexcept : code_(0), category_(atom("")) { // -- nested classes -----------------------------------------------------------
struct error::data {
uint8_t code;
atom_value category;
message context;
};
// -- constructors, destructors, and assignment operators ----------------------
error::error() noexcept : data_(nullptr) {
// nop
}
error::error(none_t) noexcept : data_(nullptr) {
// nop
}
error::error(error&& x) noexcept : data_(x.data_) {
if (data_)
x.data_ = nullptr;
}
error& error::operator=(error&& x) noexcept {
std::swap(data_, x.data_);
return *this;
}
error::error(const error& x) : data_(x ? new data(*x.data_) : nullptr) {
// nop // nop
} }
error::error(uint8_t x, atom_value y) noexcept : code_(x), category_(y) { error& error::operator=(const error& x) {
if (x) {
if (! data_)
data_ = new data(*x.data_);
else
*data_ = *x.data_;
} else {
clear();
}
return *this;
}
error::error(uint8_t x, atom_value y)
: data_(x != 0 ? new data{x, y, none} : nullptr) {
// nop // nop
} }
error::error(uint8_t x, atom_value y, message z) noexcept error::error(uint8_t x, atom_value y, message z)
: code_(x), : data_(x != 0 ? new data{x, y, std::move(z)} : nullptr) {
category_(y),
context_(std::move(z)) {
// nop // nop
} }
void serialize(serializer& sink, error& x, const unsigned int) { error::~error() {
sink << x.code_ << x.category_ << x.context_; delete data_;
} }
void serialize(deserializer& source, error& x, const unsigned int) { // -- observers ----------------------------------------------------------------
source >> x.code_ >> x.category_ >> x.context_;
uint8_t error::code() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->code;
}
atom_value error::category() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->category;
}
const message& error::context() const noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->context;
} }
int error::compare(const error& x) const noexcept { int error::compare(const error& x) const noexcept {
return compare(x.code(), x.category()); uint8_t x_code;
atom_value x_category;
if (x) {
x_code = x.data_->code;
x_category = x.data_->category;
} else {
x_code = 0;
x_category = atom("");
}
return compare(x_code, x_category);
} }
int error::compare(uint8_t x, atom_value y) const noexcept { int error::compare(uint8_t x, atom_value y) const noexcept {
// exception: all errors with default value are considered no error -> equal uint8_t mx;
if (code_ == 0 && x == 0) atom_value my;
if (data_) {
mx = data_->code;
my = data_->category;
} else {
mx = 0;
my = atom("");
}
// all errors with default value are considered no error -> equal
if (mx == x && x == 0)
return 0; return 0;
if (category_ < y) if (my < y)
return -1; return -1;
if (category_ > y) if (my > y)
return 1; return 1;
return static_cast<int>(code_) - x; return static_cast<int>(mx) - x;
} }
std::string to_string(const error& x) { // -- modifiers --------------------------------------------------------------
if (! x)
return "no-error"; message& error::context() noexcept {
std::string result = "error("; CAF_ASSERT(data_ != nullptr);
result += to_string(x.category()); return data_->context;
result += ", "; }
result += std::to_string(static_cast<int>(x.code()));
if (! x.context().empty()) { void error::clear() noexcept {
result += ", "; if (data_) {
result += to_string(x.context()); delete data_;
data_ = nullptr;
} }
result += ")"; }
// -- inspection support -----------------------------------------------------
error error::apply(inspect_fun f) {
data tmp{0, atom(""), message{}};
data& ref = data_ ? *data_ : tmp;
auto result = f(meta::type_name("error"), ref.code, ref.category,
meta::omittable_if_empty(), ref.context);
if (ref.code == 0)
clear();
else if (&tmp == &ref)
data_ = new data(std::move(tmp));
return result; return result;
} }
std::string to_string(const error& x) {
if (! x)
return "none";
return deep_to_string(meta::type_name("error"), x.code(), x.category(),
meta::omittable_if_empty(), x.context());
}
} // namespace caf } // namespace caf
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "caf/exit_reason.hpp" #include "caf/exit_reason.hpp"
#include "caf/message.hpp"
namespace caf { namespace caf {
namespace { namespace {
......
...@@ -55,36 +55,34 @@ intptr_t group::compare(const group& other) const noexcept { ...@@ -55,36 +55,34 @@ intptr_t group::compare(const group& other) const noexcept {
return compare(ptr_.get(), other.ptr_.get()); return compare(ptr_.get(), other.ptr_.get());
} }
void serialize(serializer& sink, const group& x, const unsigned int) { error inspect(serializer& f, group& x) {
std::string mod_name;
auto ptr = x.get(); auto ptr = x.get();
if (! ptr) { if (! ptr)
std::string dummy; return f(mod_name);
sink << dummy; mod_name = ptr->module().name();
} else { auto e = f(mod_name);
sink << ptr->module().name(); return e ? e : ptr->save(f);
ptr->save(sink);
}
} }
void serialize(deserializer& source, group& x, const unsigned int) { error inspect(deserializer& f, group& x) {
std::string module_name; std::string module_name;
source >> module_name; f(module_name);
if (module_name.empty()) { if (module_name.empty()) {
x = invalid_group; x = invalid_group;
return; return none;
} }
if (! source.context()) if (! f.context())
CAF_RAISE_ERROR("Cannot serialize group without context."); return sec::no_context;
auto& sys = source.context()->system(); auto& sys = f.context()->system();
auto mod = sys.groups().get_module(module_name); auto mod = sys.groups().get_module(module_name);
if (! mod) if (! mod)
CAF_RAISE_ERROR("Cannot deserialize a group for unknown module: " return sec::no_such_group_module;
+ module_name); return mod->load(f, x);
mod->load(source, x);
} }
std::string to_string(const group& x) { std::string to_string(const group& x) {
if (! x) if (x == invalid_group)
return "<invalid-group>"; return "<invalid-group>";
std::string result = x.get()->module().name(); std::string result = x.get()->module().name();
result += "/"; result += "/";
......
...@@ -350,22 +350,24 @@ public: ...@@ -350,22 +350,24 @@ public:
// deserialize identifier and broker // deserialize identifier and broker
std::string identifier; std::string identifier;
strong_actor_ptr broker_ptr; strong_actor_ptr broker_ptr;
source >> identifier >> broker_ptr; auto e = source(identifier, broker_ptr);
if (e)
return e;
CAF_LOG_DEBUG(CAF_ARG(identifier) << CAF_ARG(broker_ptr)); CAF_LOG_DEBUG(CAF_ARG(identifier) << CAF_ARG(broker_ptr));
if (! broker_ptr) { if (! broker_ptr) {
storage = invalid_group; storage = invalid_group;
return {}; return none;
} }
auto broker = actor_cast<actor>(broker_ptr); auto broker = actor_cast<actor>(broker_ptr);
if (broker->node() == system().node()) { if (broker->node() == system().node()) {
storage = *this->get(identifier); storage = *this->get(identifier);
return {}; return none;
} }
upgrade_guard guard(proxies_mtx_); upgrade_guard guard(proxies_mtx_);
auto i = proxies_.find(broker); auto i = proxies_.find(broker);
if (i != proxies_.end()) { if (i != proxies_.end()) {
storage = group{i->second}; storage = group{i->second};
return {}; return none;
} }
local_group_ptr tmp = make_counted<local_group_proxy>(system(), broker, local_group_ptr tmp = make_counted<local_group_proxy>(system(), broker,
*this, identifier, *this, identifier,
...@@ -374,15 +376,15 @@ public: ...@@ -374,15 +376,15 @@ public:
auto p = proxies_.emplace(broker, tmp); auto p = proxies_.emplace(broker, tmp);
// someone might preempt us // someone might preempt us
storage = group{p.first->second}; storage = group{p.first->second};
return {}; return none;
} }
error save(const local_group* ptr, serializer& sink) const { error save(const local_group* ptr, serializer& sink) const {
CAF_ASSERT(ptr != nullptr); CAF_ASSERT(ptr != nullptr);
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
sink << ptr->identifier() << actor_cast<strong_actor_ptr>(ptr->broker()); auto bro = actor_cast<strong_actor_ptr>(ptr->broker());
// TODO: refactor after visit API is in place (#470) auto& id = const_cast<std::string&>(ptr->identifier());
return {}; return sink(id, bro);
} }
void stop() override { void stop() override {
...@@ -423,9 +425,7 @@ error local_group::save(serializer& sink) const { ...@@ -423,9 +425,7 @@ error local_group::save(serializer& sink) const {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
// this cast is safe, because the only available constructor accepts // this cast is safe, because the only available constructor accepts
// local_group_module* as module pointer // local_group_module* as module pointer
static_cast<local_group_module&>(parent_).save(this, sink); return static_cast<local_group_module&>(parent_).save(this, sink);
// TODO: refactor after visit API is in place (#470)
return {};
} }
std::atomic<size_t> s_ad_hoc_id; std::atomic<size_t> s_ad_hoc_id;
......
...@@ -93,17 +93,4 @@ mailbox_element_ptr make_mailbox_element(strong_actor_ptr sender, message_id id, ...@@ -93,17 +93,4 @@ mailbox_element_ptr make_mailbox_element(strong_actor_ptr sender, message_id id,
return mailbox_element_ptr{ptr}; return mailbox_element_ptr{ptr};
} }
std::string to_string(const mailbox_element& x) {
std::string result = "mailbox_element(";
result += to_string(x.sender);
result += ", ";
result += to_string(x.mid);
result += ", ";
result += deep_to_string(x.stages);
result += ", ";
result += to_string(x.content());
result += ")";
return result;
}
} // namespace caf } // namespace caf
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
namespace caf { namespace caf {
message::message(none_t) noexcept {
// nop
}
message::message(message&& other) noexcept : vals_(std::move(other.vals_)) { message::message(message&& other) noexcept : vals_(std::move(other.vals_)) {
// nop // nop
} }
...@@ -378,17 +382,15 @@ message message::concat_impl(std::initializer_list<data_ptr> xs) { ...@@ -378,17 +382,15 @@ message message::concat_impl(std::initializer_list<data_ptr> xs) {
} }
} }
void serialize(serializer& sink, const message& msg, const unsigned int) { error inspect(serializer& sink, message& msg) {
if (! sink.context()) if (! sink.context())
CAF_RAISE_ERROR("Cannot serialize message without context."); return sec::no_context;
// build type name // build type name
uint16_t zero = 0; uint16_t zero = 0;
std::string tname = "@<>"; std::string tname = "@<>";
if (msg.empty()) { if (msg.empty())
sink.begin_object(zero, tname); return error::eval([&] { return sink.begin_object(zero, tname); },
sink.end_object(); [&] { return sink.end_object(); });
return;
}
auto& types = sink.context()->system().types(); auto& types = sink.context()->system().types();
auto n = msg.size(); auto n = msg.size();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
...@@ -399,31 +401,42 @@ void serialize(serializer& sink, const message& msg, const unsigned int) { ...@@ -399,31 +401,42 @@ void serialize(serializer& sink, const message& msg, const unsigned int) {
"not added to the types list, typeid name: " "not added to the types list, typeid name: "
<< (rtti.second ? rtti.second->name() : "-not-available-") << (rtti.second ? rtti.second->name() : "-not-available-")
<< std::endl; << std::endl;
CAF_RAISE_ERROR("unknown type while serializing"); return make_error(sec::unknown_type,
rtti.second ? rtti.second->name() : "-not-available-");
} }
tname += '+'; tname += '+';
tname += *ptr; tname += *ptr;
} }
sink.begin_object(zero, tname); auto save_loop = [&]() -> error {
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i) {
msg.cvals()->save(i, sink); auto e = msg.cvals()->save(i, sink);
sink.end_object(); if (e)
return e;
}
return none;
};
return error::eval([&] { return sink.begin_object(zero, tname); },
[&] { return save_loop(); },
[&] { return sink.end_object(); });
} }
void serialize(deserializer& source, message& msg, const unsigned int) { error inspect(deserializer& source, message& msg) {
if (! source.context()) if (! source.context())
CAF_RAISE_ERROR("Cannot deserialize message without context."); return sec::no_context;
uint16_t zero; uint16_t zero;
std::string tname; std::string tname;
source.begin_object(zero, tname); error err;
err = source.begin_object(zero, tname);
if (err)
return err;
if (zero != 0) if (zero != 0)
CAF_RAISE_ERROR("unexpected builtin type found in message"); return sec::unknown_type;
if (tname == "@<>") { if (tname == "@<>") {
msg = message{}; msg = message{};
return; return none;
} }
if (tname.compare(0, 4, "@<>+") != 0) if (tname.compare(0, 4, "@<>+") != 0)
CAF_RAISE_ERROR("type name does not start with @<>+: " + tname); return sec::unknown_type;
// iterate over concatenated type names // iterate over concatenated type names
auto eos = tname.end(); auto eos = tname.end();
auto next = [&](std::string::iterator iter) { auto next = [&](std::string::iterator iter) {
...@@ -439,17 +452,22 @@ void serialize(deserializer& source, message& msg, const unsigned int) { ...@@ -439,17 +452,22 @@ void serialize(deserializer& source, message& msg, const unsigned int) {
tmp.assign(i, n); tmp.assign(i, n);
auto ptr = types.make_value(tmp); auto ptr = types.make_value(tmp);
if (! ptr) if (! ptr)
CAF_RAISE_ERROR("cannot serialize a value of type " + tmp); return make_error(sec::unknown_type, tmp);
ptr->load(source); err = ptr->load(source);
if (err)
return err;
dmd->append(std::move(ptr)); dmd->append(std::move(ptr));
if (n != eos) if (n != eos)
i = n + 1; i = n + 1;
else else
i = eos; i = eos;
} while (i != eos); } while (i != eos);
source.end_object(); err = source.end_object();
if (err)
return err;
message result{std::move(dmd)}; message result{std::move(dmd)};
msg.swap(result); msg.swap(result);
return none;
} }
std::string to_string(const message& msg) { std::string to_string(const message& msg) {
......
...@@ -50,11 +50,11 @@ node_id::~node_id() { ...@@ -50,11 +50,11 @@ node_id::~node_id() {
// nop // nop
} }
node_id::node_id(const invalid_node_id_t&) { node_id::node_id(const none_t&) {
// nop // nop
} }
node_id& node_id::operator=(const invalid_node_id_t&) { node_id& node_id::operator=(const none_t&) {
data_.reset(); data_.reset();
return *this; return *this;
} }
...@@ -73,7 +73,7 @@ node_id::node_id(uint32_t procid, const host_id_type& hid) ...@@ -73,7 +73,7 @@ node_id::node_id(uint32_t procid, const host_id_type& hid)
// nop // nop
} }
int node_id::compare(const invalid_node_id_t&) const { int node_id::compare(const none_t&) const {
return data_ ? 1 : 0; // invalid instances are always smaller return data_ ? 1 : 0; // invalid instances are always smaller
} }
...@@ -95,7 +95,7 @@ int node_id::compare(const node_id& other) const { ...@@ -95,7 +95,7 @@ int node_id::compare(const node_id& other) const {
} }
node_id::data::data() : pid_(0) { node_id::data::data() : pid_(0) {
// nop memset(host_.data(), 0, host_.size());
} }
node_id::data::data(uint32_t procid, host_id_type hid) node_id::data::data(uint32_t procid, host_id_type hid)
...@@ -130,8 +130,9 @@ node_id::data::~data() { ...@@ -130,8 +130,9 @@ node_id::data::~data() {
// nop // nop
} }
void node_id::data::stop() { bool node_id::data::valid() const {
// nop auto is_zero = [](uint8_t x) { return x == 0; };
return pid_ != 0 && ! std::all_of(host_.begin(), host_.end(), is_zero);
} }
namespace { namespace {
...@@ -178,104 +179,13 @@ void node_id::swap(node_id& x) { ...@@ -178,104 +179,13 @@ void node_id::swap(node_id& x) {
data_.swap(x.data_); data_.swap(x.data_);
} }
void serialize(serializer& sink, node_id& x, const unsigned int) {
if (! x.data_) {
node_id::host_id_type zero_id;
std::fill(zero_id.begin(), zero_id.end(), 0);
uint32_t zero_pid = 0;
sink.apply_raw(zero_id.size(), zero_id.data());
sink << zero_pid;
} else {
sink.apply_raw(x.data_->host_.size(), x.data_->host_.data());
sink.apply(x.data_->pid_);
}
}
void serialize(deserializer& source, node_id& x, const unsigned int) {
node_id::host_id_type tmp_hid;
uint32_t tmp_pid;
source.apply_raw(tmp_hid.size(), tmp_hid.data());
source >> tmp_pid;
auto is_zero = [](uint8_t value) { return value == 0; };
// no need to keep the data if it is invalid
if (tmp_pid == 0 && std::all_of(tmp_hid.begin(), tmp_hid.end(), is_zero)) {
x = invalid_node_id;
return;
}
if (! x.data_ || ! x.data_->unique()) {
x.data_ = make_counted<node_id::data>(tmp_pid, tmp_hid);
} else {
x.data_->pid_ = tmp_pid;
x.data_->host_ = tmp_hid;
}
}
namespace {
inline uint8_t hex_nibble(char c) {
return static_cast<uint8_t>(c >= '0' && c <= '9'
? c - '0'
: (c >= 'a' && c <= 'f' ? (c - 'a') + 10
: (c - 'A') + 10));
}
} // namespace <anonymous>
void node_id::from_string(const std::string& str) {
data_.reset();
if (str == "<invalid-node>")
return;
static constexpr size_t hexstr_size = host_id_size * 2;
// node id format is: "[0-9a-zA-Z]{40}:[0-9]+"
if (str.size() < hexstr_size + 2)
return;
auto beg = str.begin();
auto sep = beg + hexstr_size; // separator ':' / end of hex-string
auto eos = str.end(); // end-of-string
if (*sep != ':')
return;
if (! std::all_of(beg, sep, ::isxdigit))
return;
if (! std::all_of(sep + 1, eos, ::isdigit))
return;
// iterate two digits in the input string as one byte in hex format
struct hex_byte_iter : std::iterator<std::input_iterator_tag, uint8_t> {
using const_iterator = std::string::const_iterator;
const_iterator i;
hex_byte_iter(const_iterator x) : i(x) {
// nop
}
uint8_t operator*() const {
return static_cast<uint8_t>(hex_nibble(*i) << 4) | hex_nibble(*(i + 1));
}
hex_byte_iter& operator++() {
i += 2;
return *this;
}
bool operator!=(const hex_byte_iter& x) const {
return i != x.i;
}
};
hex_byte_iter first{beg};
hex_byte_iter last{sep};
data_.reset(new data);
std::copy(first, last, data_->host_.begin());
data_->pid_ = static_cast<uint32_t>(atoll(&*(sep + 1)));
}
std::string to_string(const node_id& x) { std::string to_string(const node_id& x) {
if (! x) if (! x)
return "<invalid-node>"; return "none";
std::ostringstream oss; return deep_to_string(meta::type_name("node_id"),
oss << std::hex; x.process_id(),
oss.fill('0'); meta::hex_formatted(),
auto& hid = x.host_id(); x.host_id());
for (auto val : hid) {
oss.width(2);
oss << static_cast<uint32_t>(val);
}
oss << ":" << std::dec << x.process_id();
return oss.str();
} }
} // namespace caf } // namespace caf
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "caf/scheduled_actor.hpp" #include "caf/scheduled_actor.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/to_string.hpp"
#include "caf/actor_ostream.hpp" #include "caf/actor_ostream.hpp"
#include "caf/detail/private_thread.hpp" #include "caf/detail/private_thread.hpp"
...@@ -320,7 +321,7 @@ scheduled_actor::categorize(mailbox_element& x) { ...@@ -320,7 +321,7 @@ scheduled_actor::categorize(mailbox_element& x) {
: message_category::expired_timeout; : message_category::expired_timeout;
} }
case make_type_token<exit_msg>(): { case make_type_token<exit_msg>(): {
auto& em = content.get_mutable_as<exit_msg>(0); auto em = content.move_if_unshared<exit_msg>(0);
// make sure to get rid of attachables if they're no longer needed // make sure to get rid of attachables if they're no longer needed
unlink_from(em.source); unlink_from(em.source);
// exit_reason::kill is always fatal // exit_reason::kill is always fatal
...@@ -333,12 +334,12 @@ scheduled_actor::categorize(mailbox_element& x) { ...@@ -333,12 +334,12 @@ scheduled_actor::categorize(mailbox_element& x) {
return message_category::internal; return message_category::internal;
} }
case make_type_token<down_msg>(): { case make_type_token<down_msg>(): {
auto& dm = content.get_mutable_as<down_msg>(0); auto dm = content.move_if_unshared<down_msg>(0);
down_handler_(this, dm); down_handler_(this, dm);
return message_category::internal; return message_category::internal;
} }
case make_type_token<error>(): { case make_type_token<error>(): {
auto& err = content.get_mutable_as<error>(0); auto err = content.move_if_unshared<error>(0);
error_handler_(this, err); error_handler_(this, err);
return message_category::internal; return message_category::internal;
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "caf/scoped_actor.hpp" #include "caf/scoped_actor.hpp"
#include "caf/to_string.hpp"
#include "caf/spawn_options.hpp" #include "caf/spawn_options.hpp"
#include "caf/actor_registry.hpp" #include "caf/actor_registry.hpp"
#include "caf/scoped_execution_unit.hpp" #include "caf/scoped_execution_unit.hpp"
......
...@@ -24,7 +24,7 @@ namespace caf { ...@@ -24,7 +24,7 @@ namespace caf {
namespace { namespace {
const char* sec_strings[] = { const char* sec_strings[] = {
"<no-error>", "none",
"unexpected_message", "unexpected_message",
"unexpected_response", "unexpected_response",
"request_receiver_down", "request_receiver_down",
...@@ -48,6 +48,10 @@ const char* sec_strings[] = { ...@@ -48,6 +48,10 @@ const char* sec_strings[] = {
"invalid_protocol_family", "invalid_protocol_family",
"cannot_publish_invalid_actor", "cannot_publish_invalid_actor",
"cannot_spawn_actor_from_arguments", "cannot_spawn_actor_from_arguments",
"end_of_stream",
"no_context",
"unknown_type",
"no_proxy_registry",
"bad_function_call" "bad_function_call"
}; };
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/stringification_inspector.hpp"
namespace caf {
namespace detail {
void stringification_inspector::sep() {
if (! result_.empty())
switch (result_.back()) {
case '(':
case '[':
case ' ': // only at back if we've printed ", " before
break;
default:
result_ += ", ";
}
}
void stringification_inspector::consume(atom_value& x) {
result_ += '\'';
result_ += to_string(x);
result_ += '\'';
}
void stringification_inspector::consume(const char* cstr) {
if (! cstr || *cstr == '\0') {
result_ += "\"\"";
return;
}
if (*cstr == '"') {
// assume an already escaped string
result_ += cstr;
return;
}
result_ += '"';
char c;
for(;;) {
switch (c = *cstr++) {
default:
result_ += c;
break;
case '\\':
result_ += "\\\\";
break;
case '"':
result_ += "\\\"";
break;
case '\0':
goto end_of_string;
}
}
end_of_string:
result_ += '"';
}
void stringification_inspector::consume_hex(const uint8_t* xs, size_t n) {
if (n == 0) {
result_ += "00";
return;
}
auto tbl = "0123456789ABCDEF";
char buf[3] = {0, 0, 0};
for (size_t i = 0; i < n; ++i) {
auto c = xs[i];
buf[0] = tbl[c >> 4];
buf[1] = tbl[c & 0x0F];
result_ += buf;
}
}
} // namespace detail
} // namespace caf
...@@ -29,10 +29,12 @@ type_erased_tuple::~type_erased_tuple() { ...@@ -29,10 +29,12 @@ type_erased_tuple::~type_erased_tuple() {
} }
error type_erased_tuple::load(deserializer& source) { error type_erased_tuple::load(deserializer& source) {
for (size_t i = 0; i < size(); ++i) for (size_t i = 0; i < size(); ++i) {
load(i, source); auto e = load(i, source);
// TODO: refactor after visit API is in place (#470) if (e)
return {}; return e;
}
return none;
} }
bool type_erased_tuple::shared() const noexcept { bool type_erased_tuple::shared() const noexcept {
...@@ -57,10 +59,12 @@ std::string type_erased_tuple::stringify() const { ...@@ -57,10 +59,12 @@ std::string type_erased_tuple::stringify() const {
} }
error type_erased_tuple::save(serializer& sink) const { error type_erased_tuple::save(serializer& sink) const {
for (size_t i = 0; i < size(); ++i) for (size_t i = 0; i < size(); ++i) {
save(i, sink); auto e = save(i, sink);
// TODO: refactor after visit API is in place (#470) if (e)
return {}; return e;
}
return none;
} }
bool type_erased_tuple::matches(size_t pos, uint16_t nr, bool type_erased_tuple::matches(size_t pos, uint16_t nr,
......
...@@ -77,7 +77,6 @@ const char* numbered_type_names[] = { ...@@ -77,7 +77,6 @@ const char* numbered_type_names[] = {
"@strong_actor_ptr", "@strong_actor_ptr",
"@strset", "@strset",
"@strvec", "@strvec",
"@sync_timeout_msg",
"@timeout", "@timeout",
"@u16", "@u16",
"@u16str", "@u16str",
......
...@@ -42,6 +42,10 @@ public: ...@@ -42,6 +42,10 @@ public:
} }
behavior make_behavior() override { behavior make_behavior() override {
auto nested = exit_handler_;
set_exit_handler([=](scheduled_actor* self, exit_msg& em) {
nested(self, em);
});
return { return {
[](int x, int y) { [](int x, int y) {
return x + y; return x + y;
......
...@@ -120,9 +120,9 @@ public: ...@@ -120,9 +120,9 @@ public:
return str_; return str_;
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, counting_string& x) { friend error inspect(Inspector& f, counting_string& x) {
proc & x.str_; return f(x.str_);
} }
private: private:
......
...@@ -39,9 +39,12 @@ namespace { ...@@ -39,9 +39,12 @@ namespace {
using rtti_pair = std::pair<uint16_t, const std::type_info*>; using rtti_pair = std::pair<uint16_t, const std::type_info*>;
std::string to_string(const rtti_pair& x) { std::string to_string(const rtti_pair& x) {
if (x.second) std::string result = "(";
return deep_to_string_as_tuple(x.first, x.second->name()); result += std::to_string(x.first);
return deep_to_string_as_tuple(x.first, "<null>"); result += ", ";
result += x.second ? x.second->name() : "<null>";
result += ")";
return result;
} }
struct fixture { struct fixture {
......
...@@ -170,26 +170,18 @@ struct s1 { ...@@ -170,26 +170,18 @@ struct s1 {
int value[3] = {10, 20, 30}; int value[3] = {10, 20, 30};
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s1& x, const unsigned int) { error inspect(Inspector& f, s1& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s1& x) {
return deep_to_string(x.value);
} }
struct s2 { struct s2 {
int value[4][2] = {{1, 10}, {2, 20}, {3, 30}, {4, 40}}; int value[4][2] = {{1, 10}, {2, 20}, {3, 30}, {4, 40}};
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s2& x, const unsigned int) { error inspect(Inspector& f, s2& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s2& x) {
return deep_to_string(x.value);
} }
struct s3 { struct s3 {
...@@ -199,13 +191,9 @@ struct s3 { ...@@ -199,13 +191,9 @@ struct s3 {
} }
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, s3& x, const unsigned int) { error inspect(Inspector& f, s3& x) {
proc & x.value; return f(x.value);
}
std::string to_string(const s3& x) {
return deep_to_string(x.value);
} }
template <class... Ts> template <class... Ts>
...@@ -218,7 +206,8 @@ std::string msg_as_string(Ts&&... xs) { ...@@ -218,7 +206,8 @@ std::string msg_as_string(Ts&&... xs) {
CAF_TEST(compare_custom_types) { CAF_TEST(compare_custom_types) {
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
CAF_CHECK(to_string(make_message(s2{})) != to_string(make_message(tmp))); CAF_CHECK_NOT_EQUAL(to_string(make_message(s2{})),
to_string(make_message(tmp)));
} }
CAF_TEST(empty_to_string) { CAF_TEST(empty_to_string) {
......
...@@ -79,9 +79,9 @@ struct raw_struct { ...@@ -79,9 +79,9 @@ struct raw_struct {
string str; string str;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, raw_struct& x) { error inspect(Inspector& f, raw_struct& x) {
proc & x.str; return f(x.str);
} }
bool operator==(const raw_struct& lhs, const raw_struct& rhs) { bool operator==(const raw_struct& lhs, const raw_struct& rhs) {
...@@ -108,10 +108,9 @@ struct test_array { ...@@ -108,10 +108,9 @@ struct test_array {
int value2[2][4]; int value2[2][4];
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, test_array& x, const unsigned int) { error inspect(Inspector& f, test_array& x) {
proc & x.value; return f(x.value, x.value2);
proc & x.value2;
} }
struct test_empty_non_pod { struct test_empty_non_pod {
...@@ -125,9 +124,9 @@ struct test_empty_non_pod { ...@@ -125,9 +124,9 @@ struct test_empty_non_pod {
} }
}; };
template <class Processor> template <class Inspector>
void serialize(Processor&, test_empty_non_pod&, const unsigned int) { error inspect(Inspector&, test_empty_non_pod&) {
// nop return none;
} }
class config : public actor_system_config { class config : public actor_system_config {
...@@ -161,29 +160,18 @@ struct fixture { ...@@ -161,29 +160,18 @@ struct fixture {
scoped_execution_unit context; scoped_execution_unit context;
message msg; message msg;
template <class Processor>
void apply(Processor&) {
// end of recursion
}
template <class Processor, class T, class... Ts>
void apply(Processor& proc, T& x, Ts&... xs) {
proc & x;
apply(proc, xs...);
}
template <class T, class... Ts> template <class T, class... Ts>
vector<char> serialize(T& x, Ts&... xs) { vector<char> serialize(T& x, Ts&... xs) {
vector<char> buf; vector<char> buf;
binary_serializer bs{&context, buf}; binary_serializer bs{&context, buf};
apply(bs, x, xs...); bs(x, xs...);
return buf; return buf;
} }
template <class T, class... Ts> template <class T, class... Ts>
void deserialize(const vector<char>& buf, T& x, Ts&... xs) { void deserialize(const vector<char>& buf, T& x, Ts&... xs) {
binary_deserializer bd{&context, buf}; binary_deserializer bd{&context, buf};
apply(bd, x, xs...); bd(x, xs...);
} }
// serializes `x` and then deserializes and returns the serialized value // serializes `x` and then deserializes and returns the serialized value
...@@ -429,21 +417,25 @@ CAF_TEST(streambuf_serialization) { ...@@ -429,21 +417,25 @@ CAF_TEST(streambuf_serialization) {
// First, we check the standard use case in CAF where stream serializers own // First, we check the standard use case in CAF where stream serializers own
// their stream buffers. // their stream buffers.
stream_serializer<vectorbuf> bs{vectorbuf{buf}}; stream_serializer<vectorbuf> bs{vectorbuf{buf}};
bs << data; auto e = bs(data);
CAF_REQUIRE_EQUAL(e, none);
stream_deserializer<charbuf> bd{charbuf{buf}}; stream_deserializer<charbuf> bd{charbuf{buf}};
std::string target; std::string target;
bd >> target; e = bd(target);
CAF_CHECK(data == target); CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK_EQUAL(data, target);
// Second, we test another use case where the serializers only keep // Second, we test another use case where the serializers only keep
// references of the stream buffers. // references of the stream buffers.
buf.clear(); buf.clear();
target.clear(); target.clear();
vectorbuf vb{buf}; vectorbuf vb{buf};
stream_serializer<vectorbuf&> vs{vb}; stream_serializer<vectorbuf&> vs{vb};
vs << data; e = vs(data);
CAF_REQUIRE_EQUAL(e, none);
charbuf cb{buf}; charbuf cb{buf};
stream_deserializer<charbuf&> vd{cb}; stream_deserializer<charbuf&> vd{cb};
vd >> target; e = vd(target);
CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK(data == target); CAF_CHECK(data == target);
} }
...@@ -454,11 +446,13 @@ CAF_TEST(byte_sequence_optimization) { ...@@ -454,11 +446,13 @@ CAF_TEST(byte_sequence_optimization) {
using streambuf_type = containerbuf<std::vector<uint8_t>>; using streambuf_type = containerbuf<std::vector<uint8_t>>;
streambuf_type cb{buf}; streambuf_type cb{buf};
stream_serializer<streambuf_type&> bs{cb}; stream_serializer<streambuf_type&> bs{cb};
bs << data; auto e = bs(data);
CAF_REQUIRE(! e);
data.clear(); data.clear();
streambuf_type cb2{buf}; streambuf_type cb2{buf};
stream_deserializer<streambuf_type&> bd{cb2}; stream_deserializer<streambuf_type&> bd{cb2};
bd >> data; e = bd(data);
CAF_REQUIRE(! e);
CAF_CHECK_EQUAL(data.size(), 42u); CAF_CHECK_EQUAL(data.size(), 42u);
CAF_CHECK(std::all_of(data.begin(), data.end(), CAF_CHECK(std::all_of(data.begin(), data.end(),
[](uint8_t c) { return c == 0x2a; })); [](uint8_t c) { return c == 0x2a; }));
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE to_string
#include "caf/test/unit_test.hpp"
#include "caf/to_string.hpp"
using namespace std;
using namespace caf;
CAF_TEST(buffer) {
std::vector<char> buf;
CAF_CHECK_EQUAL(deep_to_string(buf), "[]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "00");
buf.push_back(-1);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF");
buf.push_back(0);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF00");
buf.push_back(127);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F");
buf.push_back(10);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127, 10]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F0A");
buf.push_back(16);
CAF_CHECK_EQUAL(deep_to_string(buf), "[-1, 0, 127, 10, 16]");
CAF_CHECK_EQUAL(deep_to_string(meta::hex_formatted(), buf), "FF007F0A10");
}
...@@ -84,10 +84,9 @@ struct my_request { ...@@ -84,10 +84,9 @@ struct my_request {
int b; int b;
}; };
template <class Processor> template <class Inspector>
void serialize(Processor& proc, my_request& x, const unsigned int) { error inspect(Inspector& f, my_request& x) {
proc & x.a; return f(x.a, x.b);
proc & x.b;
} }
using server_type = typed_actor<replies_to<my_request>::with<bool>>; using server_type = typed_actor<replies_to<my_request>::with<bool>>;
......
...@@ -182,8 +182,12 @@ public: ...@@ -182,8 +182,12 @@ public:
/// Closes the connection or acceptor identified by `handle`. /// Closes the connection or acceptor identified by `handle`.
/// Unwritten data will still be send. /// Unwritten data will still be send.
template <class Handle> template <class Handle>
void close(Handle hdl) { bool close(Handle hdl) {
by_id(hdl).stop_reading(); auto x = by_id(hdl);
if (! x)
return false;
x->stop_reading();
return true;
} }
/// Checks whether `hdl` is assigned to broker. /// Checks whether `hdl` is assigned to broker.
...@@ -255,11 +259,11 @@ protected: ...@@ -255,11 +259,11 @@ protected:
/// Returns a `scribe` or `doorman` identified by `hdl`. /// Returns a `scribe` or `doorman` identified by `hdl`.
template <class Handle> template <class Handle>
auto by_id(Handle hdl) -> decltype(*ptr_of(hdl)) { auto by_id(Handle hdl) -> optional<decltype(*ptr_of(hdl))> {
auto& elements = get_map(hdl); auto& elements = get_map(hdl);
auto i = elements.find(hdl); auto i = elements.find(hdl);
if (i == elements.end()) if (i == elements.end())
CAF_RAISE_ERROR("invalid handle"); return none;
return *(i->second); return *(i->second);
} }
...@@ -272,7 +276,7 @@ protected: ...@@ -272,7 +276,7 @@ protected:
decltype(ptr_of(hdl)) result; decltype(ptr_of(hdl)) result;
auto i = elements.find(hdl); auto i = elements.find(hdl);
if (i == elements.end()) if (i == elements.end())
CAF_RAISE_ERROR("invalid handle"); return nullptr;
swap(result, i->second); swap(result, i->second);
elements.erase(i); elements.erase(i);
return result; return result;
...@@ -282,6 +286,7 @@ private: ...@@ -282,6 +286,7 @@ private:
scribe_map scribes_; scribe_map scribes_;
doorman_map doormen_; doorman_map doormen_;
detail::intrusive_partitioned_list<mailbox_element, detail::disposer> cache_; detail::intrusive_partitioned_list<mailbox_element, detail::disposer> cache_;
std::vector<char> dummy_wr_buf_;
}; };
} // namespace io } // namespace io
......
...@@ -20,8 +20,14 @@ ...@@ -20,8 +20,14 @@
#ifndef CAF_IO_ACCEPT_HANDLE_HPP #ifndef CAF_IO_ACCEPT_HANDLE_HPP
#define CAF_IO_ACCEPT_HANDLE_HPP #define CAF_IO_ACCEPT_HANDLE_HPP
#include <functional>
#include "caf/error.hpp"
#include "caf/io/handle.hpp" #include "caf/io/handle.hpp"
#include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
namespace io { namespace io {
...@@ -49,9 +55,9 @@ public: ...@@ -49,9 +55,9 @@ public:
// nop // nop
} }
template <class Procesor> template <class Inspector>
friend void serialize(Procesor& proc, accept_handle& x, const unsigned int) { friend error inspect(Inspector& f, accept_handle& x) {
proc & x.id_; return f(meta::type_name("accept_handle"), x.id_);
} }
private: private:
......
...@@ -23,8 +23,12 @@ ...@@ -23,8 +23,12 @@
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#include "caf/error.hpp"
#include "caf/node_id.hpp" #include "caf/node_id.hpp"
#include "caf/meta/omittable.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/io/basp/message_type.hpp" #include "caf/io/basp/message_type.hpp"
namespace caf { namespace caf {
...@@ -76,24 +80,17 @@ struct header { ...@@ -76,24 +80,17 @@ struct header {
}; };
/// @relates header /// @relates header
template <class Processor> template <class Inspector>
void serialize(Processor& proc, header& hdr, const unsigned int) { error inspect(Inspector& f, header& hdr) {
uint8_t pad = 0; uint8_t pad = 0;
proc & hdr.operation; return f(meta::type_name("header"),
proc & pad; hdr.operation,
proc & pad; meta::omittable(), pad,
proc & hdr.flags; meta::omittable(), pad,
proc & hdr.payload_len; hdr.flags, hdr.payload_len, hdr.operation_data, hdr.source_node,
proc & hdr.operation_data; hdr.dest_node, hdr.source_actor, hdr.dest_actor);
proc & hdr.source_node;
proc & hdr.dest_node;
proc & hdr.source_actor;
proc & hdr.dest_actor;
} }
/// @relates header
std::string to_string(const header& hdr);
/// @relates header /// @relates header
bool operator==(const header& lhs, const header& rhs); bool operator==(const header& lhs, const header& rhs);
......
...@@ -59,7 +59,7 @@ public: ...@@ -59,7 +59,7 @@ public:
optional<route> lookup(const node_id& target); optional<route> lookup(const node_id& target);
/// Returns the ID of the peer connected via `hdl` or /// Returns the ID of the peer connected via `hdl` or
/// `invalid_node_id` if `hdl` is unknown. /// `none` if `hdl` is unknown.
node_id lookup_direct(const connection_handle& hdl) const; node_id lookup_direct(const connection_handle& hdl) const;
/// Returns the handle offering a direct connection to `nid` or /// Returns the handle offering a direct connection to `nid` or
...@@ -67,14 +67,14 @@ public: ...@@ -67,14 +67,14 @@ public:
connection_handle lookup_direct(const node_id& nid) const; connection_handle lookup_direct(const node_id& nid) const;
/// Returns the next hop that would be chosen for `nid` /// Returns the next hop that would be chosen for `nid`
/// or `invalid_node_id` if there's no indirect route to `nid`. /// or `none` if there's no indirect route to `nid`.
node_id lookup_indirect(const node_id& nid) const; node_id lookup_indirect(const node_id& nid) const;
/// Flush output buffer for `r`. /// Flush output buffer for `r`.
void flush(const route& r); void flush(const route& r);
/// Adds a new direct route to the table. /// Adds a new direct route to the table.
/// @pre `hdl != invalid_connection_handle && nid != invalid_node_id` /// @pre `hdl != invalid_connection_handle && nid != none`
void add_direct(const connection_handle& hdl, const node_id& dest); void add_direct(const connection_handle& hdl, const node_id& dest);
/// Adds a new indirect route to the table. /// Adds a new indirect route to the table.
......
...@@ -22,8 +22,12 @@ ...@@ -22,8 +22,12 @@
#include <functional> #include <functional>
#include "caf/error.hpp"
#include "caf/io/handle.hpp" #include "caf/io/handle.hpp"
#include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
namespace io { namespace io {
...@@ -52,10 +56,9 @@ public: ...@@ -52,10 +56,9 @@ public:
// nop // nop
} }
template <class Processor> template <class Inspector>
friend void serialize(Processor& proc, connection_handle& x, friend error inspect(Inspector& f, connection_handle& x) {
const unsigned int) { return f(meta::type_name("connection_handle"), x.id_);
proc & x.id_;
} }
private: private:
......
...@@ -76,8 +76,10 @@ error ip_bind(asio_tcp_socket_acceptor& fd, uint16_t port, ...@@ -76,8 +76,10 @@ error ip_bind(asio_tcp_socket_acceptor& fd, uint16_t port,
fd.bind(ep, ec); fd.bind(ep, ec);
if (ec) if (ec)
return sec::cannot_open_port; return sec::cannot_open_port;
fd.listen(); fd.listen(ec);
return {}; if (ec)
return sec::cannot_open_port;
return none;
}; };
if (addr) { if (addr) {
CAF_LOG_DEBUG(CAF_ARG(addr)); CAF_LOG_DEBUG(CAF_ARG(addr));
......
This diff is collapsed.
...@@ -75,16 +75,25 @@ abstract_broker::~abstract_broker() { ...@@ -75,16 +75,25 @@ abstract_broker::~abstract_broker() {
void abstract_broker::configure_read(connection_handle hdl, void abstract_broker::configure_read(connection_handle hdl,
receive_policy::config cfg) { receive_policy::config cfg) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(cfg)); CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(cfg));
by_id(hdl).configure_read(cfg); auto x = by_id(hdl);
if (x)
x->configure_read(cfg);
} }
void abstract_broker::ack_writes(connection_handle hdl, bool enable) { void abstract_broker::ack_writes(connection_handle hdl, bool enable) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(enable)); CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(enable));
by_id(hdl).ack_writes(enable); auto x = by_id(hdl);
if (x)
x->ack_writes(enable);
} }
std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) { std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) {
return by_id(hdl).wr_buf(); auto x = by_id(hdl);
if (! x) {
CAF_LOG_ERROR("tried to access wr_buf() of an unknown connection_handle");
return dummy_wr_buf_;
}
return x->wr_buf();
} }
void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) { void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) {
...@@ -95,7 +104,9 @@ void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) { ...@@ -95,7 +104,9 @@ void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) {
} }
void abstract_broker::flush(connection_handle hdl) { void abstract_broker::flush(connection_handle hdl) {
by_id(hdl).flush(); auto x = by_id(hdl);
if (x)
x->flush();
} }
std::vector<connection_handle> abstract_broker::connections() const { std::vector<connection_handle> abstract_broker::connections() const {
...@@ -175,7 +186,7 @@ accept_handle abstract_broker::hdl_by_port(uint16_t port) { ...@@ -175,7 +186,7 @@ accept_handle abstract_broker::hdl_by_port(uint16_t port) {
for (auto& kvp : doormen_) for (auto& kvp : doormen_)
if (kvp.second->port() == port) if (kvp.second->port() == port)
return kvp.first; return kvp.first;
CAF_RAISE_ERROR("no such port"); return invalid_accept_handle;
} }
void abstract_broker::close_all() { void abstract_broker::close_all() {
......
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