Commit ba74bb57 authored by Dominik Charousset's avatar Dominik Charousset

Fix serialization of chrono types

parent 98c970ea
......@@ -53,6 +53,17 @@ public:
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 -------------------------------------------------------------
/// Returns how many bytes are still available to read.
......@@ -123,10 +134,6 @@ public:
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(std::string&);
......
......@@ -41,6 +41,10 @@ public:
using result_type = error_code<sec>;
using container_type = byte_buffer;
using value_type = byte;
// -- constructors, destructors, and assignment operators --------------------
binary_serializer(actor_system& sys, byte_buffer& buf) noexcept;
......@@ -111,10 +115,6 @@ public:
void apply(long double x);
void apply(timespan x);
void apply(timestamp x);
void apply(string_view 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 @@
#include "caf/detail/append_hex.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/annotation.hpp"
......
......@@ -86,22 +86,6 @@ using enable_if_tt = typename std::enable_if<Trait::value, T>::type;
template <class T>
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`.
template <class T>
class has_to_string {
......@@ -117,10 +101,6 @@ public:
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>
using bool_token = std::integral_constant<bool, X>;
......@@ -274,137 +254,6 @@ public:
template <class T>
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.
template <class T>
struct is_mutable_ref : std::false_type { };
......
......@@ -18,15 +18,20 @@
#pragma once
#include <string>
#include <tuple>
#include <sstream>
#include <type_traits>
#include "caf/message.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/type_traits.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp"
namespace caf {
......@@ -55,10 +60,32 @@ struct unbox_message_element<actor_control_block*, 0> {
///
template <class T>
struct is_serializable_or_whitelisted {
static constexpr bool value = detail::is_serializable<T>::value
|| allowed_unsafe_message_type<T>::value;
static constexpr bool 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...)`.
/// @relates message
template <class T, class... Ts>
......@@ -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<Ts>::type>::type...>;
static_assert(tl_forall<stored_types,
is_serializable_or_whitelisted>::value,
"at least one type is neither inspectable via "
"inspect(Inspector&, T&) nor serializable via "
"'serialize(Processor&, T&, const unsigned int)' or "
"`T::serialize(Processor&, const unsigned int)`; "
"you can whitelist individual types by "
"specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
static_assert(
tl_forall<stored_types, is_serializable_or_whitelisted>::value,
"at least one type is not inspectable via inspect(Inspector&, T&). If "
"you are not sending this type over the network, you can whitelist "
"individual types by specializing caf::allowed_unsafe_message_type<T> "
"or by using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
using storage = typename tl_apply<stored_types, tuple_vals>::type;
auto ptr = make_counted<storage>(std::forward<T>(x),
std::forward<Ts>(xs)...);
......
......@@ -21,6 +21,7 @@
#include <type_traits>
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp"
......@@ -98,6 +99,7 @@ public:
CAF_READ_INSPECTOR_TRY(dref.end_sequence())
} else {
static_assert(detail::is_inspectable<Subtype, type>::value);
using caf::detail::inspect;
// We require that the implementation for `inspect` does not modify its
// arguments when passing a reading inspector.
CAF_READ_INSPECTOR_TRY(inspect(dref, const_cast<type&>(x)));
......
......@@ -42,8 +42,6 @@ public:
explicit stateful_actor(actor_config& cfg, Ts&&... xs)
: Base(cfg, std::forward<Ts>(xs)...),
state(state_) {
if (detail::is_serializable<State>::value)
this->setf(Base::is_serializable_flag);
cr_state(this);
}
......@@ -61,14 +59,6 @@ public:
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.
State& state;
......@@ -81,17 +71,6 @@ public:
/// @endcond
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>
typename std::enable_if<std::is_constructible<State, T>::value>::type
cr_state(T arg) {
......
......@@ -24,6 +24,7 @@
#include <utility>
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp"
......@@ -45,7 +46,6 @@ namespace caf {
/// - all fixed-size integer types from `<cstdint>`
/// - floating point numbers
/// - enum types
/// - `timespan`, `timestamp`, and `atom_value`
/// - `std::string`, `std::u16string`, and `std::u32string`
template <class Subtype>
class write_inspector {
......@@ -102,6 +102,7 @@ public:
static_assert(std::is_lvalue_reference_v<decltype(x)> //
&& !std::is_const_v<decltype(x)>);
static_assert(detail::is_inspectable<Subtype, type>::value);
using caf::detail::inspect;
CAF_WRITE_INSPECTOR_TRY(inspect(dref, x));
}
return true;
......
......@@ -189,22 +189,6 @@ result_type binary_deserializer::apply(long double& x) {
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 {
if (!range_check(x.size()))
return sec::end_of_stream;
......
......@@ -143,14 +143,6 @@ void binary_serializer::apply(long double x) {
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) {
begin_sequence(x.size());
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