Commit ba74bb57 authored by Dominik Charousset's avatar Dominik Charousset

Fix serialization of chrono types

parent 98c970ea
...@@ -53,6 +53,17 @@ public: ...@@ -53,6 +53,17 @@ public:
reset(as_bytes(make_span(input))); reset(as_bytes(make_span(input)));
} }
binary_deserializer(execution_unit* ctx, const void* buf,
size_t size) noexcept
: binary_deserializer(ctx, span{reinterpret_cast<const byte*>(buf), size}) {
// nop
}
binary_deserializer(actor_system& sys, const void* buf, size_t size) noexcept
: binary_deserializer(sys, span{reinterpret_cast<const byte*>(buf), size}) {
// nop
}
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
/// Returns how many bytes are still available to read. /// Returns how many bytes are still available to read.
...@@ -123,10 +134,6 @@ public: ...@@ -123,10 +134,6 @@ public:
result_type apply(long double&); result_type apply(long double&);
result_type apply(timespan& x) noexcept;
result_type apply(timestamp& x) noexcept;
result_type apply(span<byte>) noexcept; result_type apply(span<byte>) noexcept;
result_type apply(std::string&); result_type apply(std::string&);
......
...@@ -41,6 +41,10 @@ public: ...@@ -41,6 +41,10 @@ public:
using result_type = error_code<sec>; using result_type = error_code<sec>;
using container_type = byte_buffer;
using value_type = byte;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
binary_serializer(actor_system& sys, byte_buffer& buf) noexcept; binary_serializer(actor_system& sys, byte_buffer& buf) noexcept;
...@@ -111,10 +115,6 @@ public: ...@@ -111,10 +115,6 @@ public:
void apply(long double x); void apply(long double x);
void apply(timespan x);
void apply(timestamp x);
void apply(string_view x); void apply(string_view x);
void apply(const std::u16string& x); void apply(const std::u16string& x);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* 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. *
******************************************************************************/
#pragma once
#include <chrono>
#include "caf/meta/load_callback.hpp"
namespace caf {
namespace detail {
// -- inject `inspect` overloads for some STL types ----------------------------
template <class Inspector, class Rep, class Period>
auto inspect(Inspector& f, std::chrono::duration<Rep, Period>& x) {
if constexpr (Inspector::reads_state) {
return f(x.count());
} else {
auto tmp = Rep{};
auto cb = [&] { x = std::chrono::duration<Rep, Period>{tmp}; };
return f(tmp, meta::load_callback(cb));
}
}
template <class Inspector, class Clock, class Duration>
auto inspect(Inspector& f, std::chrono::time_point<Clock, Duration>& x) {
if constexpr (Inspector::reads_state) {
return f(x.time_since_epoch());
} else {
auto tmp = Duration{};
auto cb = [&] { x = std::chrono::time_point<Clock, Duration>{tmp}; };
return f(tmp, meta::load_callback(cb));
}
}
// -- provide is_inspectable trait for metaprogramming -------------------------
/// Checks whether `T` is inspectable by `Inspector`.
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;
};
// pointers are never inspectable
template <class Inspector, class T>
struct is_inspectable<Inspector, T*> : std::false_type {};
} // namespace detail
} // namespace caf
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/detail/append_hex.hpp" #include "caf/detail/append_hex.hpp"
#include "caf/detail/apply_args.hpp" #include "caf/detail/apply_args.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/meta/annotation.hpp" #include "caf/meta/annotation.hpp"
......
...@@ -86,22 +86,6 @@ using enable_if_tt = typename std::enable_if<Trait::value, T>::type; ...@@ -86,22 +86,6 @@ using enable_if_tt = typename std::enable_if<Trait::value, T>::type;
template <class T> template <class T>
using remove_reference_t = typename std::remove_reference<T>::type; using remove_reference_t = typename std::remove_reference<T>::type;
/// Checks whether `T` is inspectable by `Inspector`.
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;
};
/// Checks whether `T` defines a free function `to_string`. /// Checks whether `T` defines a free function `to_string`.
template <class T> template <class T>
class has_to_string { class has_to_string {
...@@ -117,10 +101,6 @@ public: ...@@ -117,10 +101,6 @@ public:
static constexpr bool value = std::is_convertible<result, std::string>::value; static constexpr bool value = std::is_convertible<result, std::string>::value;
}; };
// pointers are never inspectable
template <class Inspector, class T>
struct 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>;
...@@ -274,137 +254,6 @@ public: ...@@ -274,137 +254,6 @@ public:
template <class T> template <class T>
constexpr bool is_iterable<T>::value; constexpr bool is_iterable<T>::value;
/// Checks whether `T` provides either a free function or a member function for
/// serialization. The checks test whether both serialization and
/// deserialization can succeed. The meta function tests the following
/// functions with `Processor` being both `serializer` and `deserializer` and
/// returns an integral constant if and only if the test succeeds for both.
///
/// - `serialize(Processor&, T&, const unsigned int)`
/// - `serialize(Processor&, T&)`
/// - `T::serialize(Processor&, const unsigned int)`.
/// - `T::serialize(Processor&)`.
template <class T,
bool Ignore = std::is_pointer<T>::value
|| std::is_function<T>::value>
struct has_serialize {
template <class U>
static auto test_serialize(caf::serializer* sink, U* x, unsigned int y = 0)
-> decltype(serialize(*sink, *x, y));
template <class U>
static auto test_serialize(caf::serializer* sink, U* x)
-> decltype(serialize(*sink, *x));
template <class>
static auto test_serialize(...) -> std::false_type;
template <class U>
static auto test_deserialize(caf::deserializer* source, U* x, unsigned int y = 0)
-> decltype(serialize(*source, *x, y));
template <class U>
static auto test_deserialize(caf::deserializer* source, U* x)
-> decltype(serialize(*source, *x));
template <class>
static auto test_deserialize(...) -> std::false_type;
using serialize_type = decltype(test_serialize<T>(nullptr, nullptr));
using deserialize_type = decltype(test_deserialize<T>(nullptr, nullptr));
using type = std::integral_constant<
bool,
std::is_same<serialize_type, void>::value
&& std::is_same<deserialize_type, void>::value
>;
static constexpr bool value = type::value;
};
template <class T>
struct has_serialize<T, true> {
static constexpr bool value = false;
};
/// Any inspectable type is considered to be serializable.
template <class T>
struct is_serializable;
template <class T,
bool IsIterable = is_iterable<T>::value,
bool Ignore = std::is_pointer<T>::value
|| std::is_function<T>::value>
struct is_serializable_impl;
/// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function.
template <class T>
struct is_serializable_impl<T, false, false> {
static constexpr bool value = has_serialize<T>::value
|| is_inspectable<serializer, T>::value
|| is_builtin<T>::value;
};
template <class F, class S>
struct is_serializable_impl<std::pair<F, S>, false, false> {
static constexpr bool value = is_serializable<F>::value
&& is_serializable<S>::value;
};
template <class... Ts>
struct is_serializable_impl<std::tuple<Ts...>, false, false> {
static constexpr bool value = conjunction<
is_serializable<Ts>::value...
>::value;
};
template <class T>
struct is_serializable_impl<T, true, false> {
using value_type = typename T::value_type;
static constexpr bool value = is_serializable<value_type>::value;
};
template <class T, size_t S>
struct is_serializable_impl<T[S], false, false> {
static constexpr bool value = is_serializable<T>::value;
};
template <class T, bool IsIterable>
struct is_serializable_impl<T, IsIterable, true> {
static constexpr bool value = false;
};
/// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function.
template <class T>
struct is_serializable {
static constexpr bool value = is_serializable_impl<T>::value
|| is_inspectable<serializer, T>::value
|| std::is_empty<T>::value
|| std::is_enum<T>::value;
};
template <>
struct is_serializable<bool> : std::true_type {
// nop
};
template <class T>
struct is_serializable<T&> : is_serializable<T> {
// nop
};
template <class T>
struct is_serializable<const T> : is_serializable<T> {
// nop
};
template <class T>
struct is_serializable<const T&> : is_serializable<T> {
// nop
};
/// Checks wheter `T` is a non-const reference. /// Checks wheter `T` is a non-const reference.
template <class T> template <class T>
struct is_mutable_ref : std::false_type { }; struct is_mutable_ref : std::false_type { };
......
...@@ -18,15 +18,20 @@ ...@@ -18,15 +18,20 @@
#pragma once #pragma once
#include <string>
#include <tuple> #include <tuple>
#include <sstream>
#include <type_traits> #include <type_traits>
#include "caf/message.hpp"
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/tuple_vals.hpp" #include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp"
namespace caf { namespace caf {
...@@ -55,10 +60,32 @@ struct unbox_message_element<actor_control_block*, 0> { ...@@ -55,10 +60,32 @@ struct unbox_message_element<actor_control_block*, 0> {
/// ///
template <class T> template <class T>
struct is_serializable_or_whitelisted { struct is_serializable_or_whitelisted {
static constexpr bool value = detail::is_serializable<T>::value static constexpr bool value
|| allowed_unsafe_message_type<T>::value; = std::is_arithmetic<T>::value //
|| std::is_empty<T>::value //
|| std::is_enum<T>::value //
|| detail::is_stl_tuple_type<T>::value //
|| detail::is_map_like<T>::value //
|| detail::is_list_like<T>::value
|| (detail::is_inspectable<binary_serializer, T>::value
&& detail::is_inspectable<binary_deserializer, T>::value
&& detail::is_inspectable<serializer, T>::value
&& detail::is_inspectable<deserializer, T>::value)
|| allowed_unsafe_message_type<T>::value;
}; };
template <>
struct is_serializable_or_whitelisted<byte> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::string> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::u16string> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::u32string> : std::true_type {};
/// Returns a new `message` containing the values `(x, xs...)`. /// Returns a new `message` containing the values `(x, xs...)`.
/// @relates message /// @relates message
template <class T, class... Ts> template <class T, class... Ts>
...@@ -72,15 +99,12 @@ message make_message(T&& x, Ts&&... xs) { ...@@ -72,15 +99,12 @@ message make_message(T&& x, Ts&&... xs) {
typename unbox_message_element<typename strip_and_convert<T>::type>::type, typename unbox_message_element<typename strip_and_convert<T>::type>::type,
typename unbox_message_element< typename unbox_message_element<
typename strip_and_convert<Ts>::type>::type...>; typename strip_and_convert<Ts>::type>::type...>;
static_assert(tl_forall<stored_types, static_assert(
is_serializable_or_whitelisted>::value, tl_forall<stored_types, is_serializable_or_whitelisted>::value,
"at least one type is neither inspectable via " "at least one type is not inspectable via inspect(Inspector&, T&). If "
"inspect(Inspector&, T&) nor serializable via " "you are not sending this type over the network, you can whitelist "
"'serialize(Processor&, T&, const unsigned int)' or " "individual types by specializing caf::allowed_unsafe_message_type<T> "
"`T::serialize(Processor&, const unsigned int)`; " "or by using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
"you can whitelist individual types by "
"specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
using storage = typename tl_apply<stored_types, tuple_vals>::type; using storage = typename tl_apply<stored_types, tuple_vals>::type;
auto ptr = make_counted<storage>(std::forward<T>(x), auto ptr = make_counted<storage>(std::forward<T>(x),
std::forward<Ts>(xs)...); std::forward<Ts>(xs)...);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <type_traits> #include <type_traits>
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/squashed_int.hpp" #include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp" #include "caf/meta/annotation.hpp"
...@@ -98,6 +99,7 @@ public: ...@@ -98,6 +99,7 @@ public:
CAF_READ_INSPECTOR_TRY(dref.end_sequence()) CAF_READ_INSPECTOR_TRY(dref.end_sequence())
} else { } else {
static_assert(detail::is_inspectable<Subtype, type>::value); static_assert(detail::is_inspectable<Subtype, type>::value);
using caf::detail::inspect;
// We require that the implementation for `inspect` does not modify its // We require that the implementation for `inspect` does not modify its
// arguments when passing a reading inspector. // arguments when passing a reading inspector.
CAF_READ_INSPECTOR_TRY(inspect(dref, const_cast<type&>(x))); CAF_READ_INSPECTOR_TRY(inspect(dref, const_cast<type&>(x)));
......
...@@ -42,8 +42,6 @@ public: ...@@ -42,8 +42,6 @@ public:
explicit stateful_actor(actor_config& cfg, Ts&&... xs) explicit stateful_actor(actor_config& cfg, Ts&&... xs)
: Base(cfg, std::forward<Ts>(xs)...), : Base(cfg, std::forward<Ts>(xs)...),
state(state_) { state(state_) {
if (detail::is_serializable<State>::value)
this->setf(Base::is_serializable_flag);
cr_state(this); cr_state(this);
} }
...@@ -61,14 +59,6 @@ public: ...@@ -61,14 +59,6 @@ public:
return get_name(state_); return get_name(state_);
} }
error save_state(serializer& sink, unsigned int version) override {
return serialize_state(&sink, state, version);
}
error load_state(deserializer& source, unsigned int version) override {
return serialize_state(&source, state, version);
}
/// A reference to the actor's state. /// A reference to the actor's state.
State& state; State& state;
...@@ -81,17 +71,6 @@ public: ...@@ -81,17 +71,6 @@ public:
/// @endcond /// @endcond
private: private:
template <class Inspector, class T>
auto serialize_state(Inspector* f, T& x, unsigned int)
-> decltype(inspect(*f, x)) {
return inspect(*f, x);
}
template <class T>
error serialize_state(void*, T&, unsigned int) {
return sec::invalid_argument;
}
template <class T> template <class T>
typename std::enable_if<std::is_constructible<State, T>::value>::type typename std::enable_if<std::is_constructible<State, T>::value>::type
cr_state(T arg) { cr_state(T arg) {
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <utility> #include <utility>
#include "caf/allowed_unsafe_message_type.hpp" #include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/squashed_int.hpp" #include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp" #include "caf/meta/annotation.hpp"
...@@ -45,7 +46,6 @@ namespace caf { ...@@ -45,7 +46,6 @@ namespace caf {
/// - all fixed-size integer types from `<cstdint>` /// - all fixed-size integer types from `<cstdint>`
/// - floating point numbers /// - floating point numbers
/// - enum types /// - enum types
/// - `timespan`, `timestamp`, and `atom_value`
/// - `std::string`, `std::u16string`, and `std::u32string` /// - `std::string`, `std::u16string`, and `std::u32string`
template <class Subtype> template <class Subtype>
class write_inspector { class write_inspector {
...@@ -102,6 +102,7 @@ public: ...@@ -102,6 +102,7 @@ public:
static_assert(std::is_lvalue_reference_v<decltype(x)> // static_assert(std::is_lvalue_reference_v<decltype(x)> //
&& !std::is_const_v<decltype(x)>); && !std::is_const_v<decltype(x)>);
static_assert(detail::is_inspectable<Subtype, type>::value); static_assert(detail::is_inspectable<Subtype, type>::value);
using caf::detail::inspect;
CAF_WRITE_INSPECTOR_TRY(inspect(dref, x)); CAF_WRITE_INSPECTOR_TRY(inspect(dref, x));
} }
return true; return true;
......
...@@ -189,22 +189,6 @@ result_type binary_deserializer::apply(long double& x) { ...@@ -189,22 +189,6 @@ result_type binary_deserializer::apply(long double& x) {
return sec::invalid_argument; return sec::invalid_argument;
} }
result_type binary_deserializer::apply(timespan& x) noexcept {
int64_t tmp = 0;
if (auto err = apply(tmp))
return err;
x = timespan{tmp};
return none;
}
result_type binary_deserializer::apply(timestamp& x) noexcept {
int64_t tmp = 0;
if (auto err = apply(tmp))
return err;
x = timestamp{timespan{tmp}};
return none;
}
result_type binary_deserializer::apply(span<byte> x) noexcept { result_type binary_deserializer::apply(span<byte> x) noexcept {
if (!range_check(x.size())) if (!range_check(x.size()))
return sec::end_of_stream; return sec::end_of_stream;
......
...@@ -143,14 +143,6 @@ void binary_serializer::apply(long double x) { ...@@ -143,14 +143,6 @@ void binary_serializer::apply(long double x) {
apply(tmp); apply(tmp);
} }
void binary_serializer::apply(timespan x) {
apply(x.count());
}
void binary_serializer::apply(timestamp x) {
apply(x.time_since_epoch().count());
}
void binary_serializer::apply(string_view x) { void binary_serializer::apply(string_view x) {
begin_sequence(x.size()); begin_sequence(x.size());
apply(as_bytes(make_span(x))); apply(as_bytes(make_span(x)));
......
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