Commit 0d97fcde authored by Dominik Charousset's avatar Dominik Charousset

Restore deep_to_string output for several cases

parent 34237d75
......@@ -20,19 +20,13 @@
#include <cstddef>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/save_inspector.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
namespace caf {
......@@ -135,6 +129,8 @@ public:
bool value(byte x);
bool value(bool x);
bool value(int8_t x);
bool value(uint8_t x);
......
......@@ -30,12 +30,20 @@ namespace caf {
/// `to_string` for user-defined types via argument-dependent
/// loopkup (ADL). Any user-defined type that does not
/// provide a `to_string` is mapped to `<unprintable>`.
template <class... Ts>
std::string deep_to_string(const Ts&... xs) {
template <class T>
std::string deep_to_string(const T& x) {
using inspector_type = detail::stringification_inspector;
auto access_token = guess_inspector_access_type<inspector_type, T>();
using access_type = decltype(access_token);
std::string result;
detail::stringification_inspector f{result};
auto inspect_result = (inspect_object(f, xs) && ...);
if constexpr (std::is_same<access_type,
inspector_access_type::inspect>::value) {
auto inspect_result = inspect(f, detail::as_mutable_ref(x));
static_cast<void>(inspect_result); // Always true.
} else {
detail::save_value(f, detail::as_mutable_ref(x), access_token);
}
return result;
}
......
......@@ -71,7 +71,8 @@ bool load(deserializer& source, void* ptr) {
template <class T>
void stringify(std::string& buf, const void* ptr) {
stringification_inspector f{buf};
auto unused = inspect_object(f, *static_cast<const T*>(ptr));
auto unused
= inspect_object(f, detail::as_mutable_ref(*static_cast<const T*>(ptr)));
static_cast<void>(unused);
}
......
......@@ -30,9 +30,7 @@ public:
using super::super;
size_t result() const noexcept {
return result_;
}
size_t result = 0;
bool begin_object(string_view) override;
......@@ -91,25 +89,22 @@ public:
bool value(span<const byte> x) override;
bool value(const std::vector<bool>& xs) override;
private:
size_t result_ = 0;
};
template <class T>
size_t serialized_size(const T& x) {
serialized_size_inspector f;
auto inspection_res = inspect_object(f, x);
auto inspection_res = inspect_object(f, detail::as_mutable_ref(x));
static_cast<void>(inspection_res); // Always true.
return f.result();
return f.result;
}
template <class T>
size_t serialized_size(actor_system& sys, const T& x) {
serialized_size_inspector f{sys};
auto inspection_res = inspect_object(f, x);
auto inspection_res = inspect_object(f, detail::as_mutable_ref(x));
static_cast<void>(inspection_res); // Always true.
return f.result();
return f.result;
}
} // namespace caf::detail
......@@ -21,6 +21,7 @@
#include <chrono>
#include <string>
#include <type_traits>
#include <vector>
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
......@@ -90,12 +91,18 @@ public:
bool value(long double x);
bool value(timespan x);
bool value(timestamp x);
bool value(string_view x);
bool value(const std::u16string& x);
bool value(const std::u32string& x);
bool value(const std::vector<bool>& xs);
template <class T>
std::enable_if_t<has_to_string<T>::value, bool> value(const T& x) {
auto str = to_string(x);
......@@ -103,6 +110,18 @@ public:
return true;
}
template <class T>
bool value(const T* x) {
sep();
if (!x) {
result_ += "null";
} else {
result_ += '*';
inspect_value(*this, detail::as_mutable_ref(*x));
}
return true;
}
template <class T>
void append(T&& str) {
sep();
......@@ -110,12 +129,19 @@ public:
}
template <class T>
bool opaque_value(const T&) {
bool opaque_value(const T& val) {
sep();
if constexpr (detail::is_iterable<T>::value) {
result_ += '[';
for (auto&& x : val)
inspect_value(*this, detail::as_mutable_ref(x));
result_ += ']';
} else {
result_ += "<unprintable>";
}
return true;
}
// -- DSL entry point --------------------------------------------------------
template <class Object>
......
......@@ -225,7 +225,7 @@ bool save_value(Inspector& f, T (&xs)[N], inspector_access_type::array) {
if (!f.begin_tuple(N))
return false;
for (size_t index = 0; index < N; ++index)
if (!inspect_value(f, xs[index]))
if (!inspect_value(f, as_mutable_ref(xs[index])))
return false;
return f.end_tuple();
}
......@@ -233,7 +233,7 @@ bool save_value(Inspector& f, T (&xs)[N], inspector_access_type::array) {
template <class Inspector, class T, size_t... Ns>
bool save_tuple(Inspector& f, T& xs, std::index_sequence<Ns...>) {
return f.begin_tuple(sizeof...(Ns)) //
&& (inspect_value(f, get<Ns>(xs)) && ...) //
&& (inspect_value(f, detail::as_mutable_ref(get<Ns>(xs))) && ...) //
&& f.end_tuple();
}
......@@ -248,9 +248,9 @@ bool save_value(Inspector& f, T& x, inspector_access_type::map) {
auto size = x.size();
if (!f.begin_sequence(size))
return false;
for (auto& kvp : x) {
for (auto&& kvp : x) {
if (!(f.begin_tuple(2) && inspect_value(f, as_mutable_ref(kvp.first))
&& inspect_value(f, kvp.second) && f.end_tuple()))
&& inspect_value(f, as_mutable_ref(kvp.second)) && f.end_tuple()))
return false;
}
return f.end_sequence();
......@@ -262,7 +262,7 @@ bool save_value(Inspector& f, T& x, inspector_access_type::list) {
if (!f.begin_sequence(size))
return false;
for (auto&& val : x)
if (!inspect_value(f, val))
if (!inspect_value(f, detail::as_mutable_ref(val)))
return false;
return f.end_sequence();
}
......@@ -295,15 +295,10 @@ constexpr auto inspector_access_tag_v
/// `inspector_access<T>::apply_value(f, x)` otherwise.
template <class Inspector, class T>
bool inspect_value(Inspector& f, T& x) {
static_assert(!std::is_const<T>::value);
return inspector_access<T>::apply_value(f, x);
}
/// @copydoc inspect_value
template <class Inspector, class T>
bool inspect_value(Inspector& f, const T& x) {
return inspector_access<T>::apply_value(f, const_cast<T&>(x));
}
template <class Inspector, class Get, class Set>
bool inspect_value(Inspector& f, Get&& get, Set&& set) {
using value_type = std::decay_t<decltype(get())>;
......@@ -335,7 +330,7 @@ struct inspector_access_base {
if (is_present()) {
auto&& x = get();
return f.begin_field(field_name, true) //
&& inspect_value(f, x) //
&& inspect_value(f, detail::as_mutable_ref(x)) //
&& f.end_field();
}
return f.begin_field(field_name, false) && f.end_field();
......@@ -450,6 +445,13 @@ template <class Inspector, class T>
return inspector_access<T>::apply_object(f, x);
}
/// Inspects `x` using the inspector `f`.
template <class Inspector, class T>
[[nodiscard]] bool inspect_object(Inspector& f, const T& x) {
static_assert(!Inspector::is_loading);
return inspect_object(f, detail::as_mutable_ref(x));
}
/// Inspects all `xs` using the inspector `f`.
template <class Inspector, class... Ts>
[[nodiscard]] bool inspect_objects(Inspector& f, Ts&... xs) {
......@@ -580,6 +582,10 @@ struct inspector_access<optional<T>> : optional_inspector_access<optional<T>> {
template <class... Ts>
struct inspector_access<variant<Ts...>> {
static_assert(
(has_type_id<Ts> && ...),
"inspectors requires that each type in a variant has a type_id");
using value_type = variant<Ts...>;
static constexpr type_id_t allowed_types_arr[] = {type_id_v<Ts>...};
......@@ -609,7 +615,9 @@ struct inspector_access<variant<Ts...>> {
auto allowed_types = make_span(allowed_types_arr);
if (is_present()) {
auto&& x = get();
auto g = [&f](auto& y) { return inspect_value(f, y); };
auto g = [&f](auto& y) {
return inspect_value(f, detail::as_mutable_ref(y));
};
return f.begin_field(field_name, true, allowed_types, x.index()) //
&& visit(g, x) //
&& f.end_field();
......@@ -677,18 +685,24 @@ struct inspector_access<variant<Ts...>> {
if (!f.begin_field(field_name, is_present, allowed_types, type_index))
return false;
if (is_present) {
if (type_index >= allowed_types.size())
return f.load_field_failed(field_name, sec::invalid_field_type);
if (type_index >= allowed_types.size()) {
f.emplace_error(sec::invalid_field_type, to_string(field_name));
return false;
}
auto runtime_type = allowed_types[type_index];
detail::type_list<Ts...> types;
if (!load_variant_value(f, field_name, x, runtime_type, types))
return false;
if (!is_valid(x))
return f.load_field_failed(field_name,
sec::field_invariant_check_failed);
if (!sync_value())
return f.load_field_failed(field_name,
sec::field_value_synchronization_failed);
if (!is_valid(x)) {
f.emplace_error(sec::field_invariant_check_failed,
to_string(field_name));
return false;
}
if (!sync_value()) {
f.emplace_error(sec::field_value_synchronization_failed,
to_string(field_name));
return false;
}
} else {
set_fallback();
}
......
......@@ -71,8 +71,11 @@ constexpr auto guess_inspector_access_type() {
using namespace detail;
if constexpr (is_inspectable<Inspector, T>::value) {
return inspector_access_type::inspect{};
} else if constexpr (std::is_integral<T>::value) {
} else if constexpr (std::is_integral<T>::value
&& !std::is_same<T, bool>::value) {
return inspector_access_type::integral{};
} else if constexpr (std::is_array<T>::value) {
return inspector_access_type::array{};
} else if constexpr (is_trivially_inspectable<Inspector, T>::value) {
return inspector_access_type::builtin{};
} else if constexpr (std::is_enum<T>::value) {
......@@ -81,8 +84,6 @@ constexpr auto guess_inspector_access_type() {
return inspector_access_type::empty{};
} else if constexpr (is_allowed_unsafe_message_type_v<T>) {
return inspector_access_type::unsafe{};
} else if constexpr (std::is_array<T>::value) {
return inspector_access_type::array{};
} else if constexpr (is_stl_tuple_type_v<T>) {
return inspector_access_type::tuple{};
} else if constexpr (is_map_like_v<T>) {
......
......@@ -271,6 +271,23 @@ public:
}
};
template <class T, class Reset, class Set>
struct optional_virt_field_t {
string_view field_name;
Reset reset;
Set set;
template <class Inspector>
bool operator()(Inspector& f) {
auto tmp = T{};
auto sync = [this, &tmp] { return set(std::move(tmp)); };
return inspector_access<T>::load_field(f, field_name, tmp,
detail::always_true, sync, reset);
}
};
// -- DSL type for the object ------------------------------------------------
template <class Inspector, class LoadCallback>
struct object_with_load_callback_t {
string_view object_name;
......@@ -339,6 +356,7 @@ public:
template <class T>
static auto field(string_view name, T& x) {
static_assert(!std::is_const<T>::value);
return field_t<T>{name, &x};
}
......@@ -348,6 +366,13 @@ public:
return virt_field_t<field_type, Set>{name, set};
}
template <class IsPresent, class Get, class Reset, class Set>
static auto
field(string_view name, IsPresent&&, Get&& get, Reset reset, Set set) {
using field_type = std::decay_t<decltype(get())>;
return optional_virt_field_t<field_type, Reset, Set>{name, reset, set};
}
protected:
error err_;
};
......
......@@ -85,8 +85,16 @@ public:
class CAF_CORE_EXPORT node_id_data : public ref_counted {
public:
// -- member types -----------------------------------------------------------
using variant_type = variant<uri, hashed_node_id>;
// -- constructors, destructors, and assignment operators --------------------
explicit node_id_data(variant_type value) : content(std::move(value)) {
// nop
}
explicit node_id_data(uri value) : content(std::move(value)) {
// nop
}
......@@ -110,15 +118,7 @@ public:
// -- member variables -------------------------------------------------------
variant<uri, hashed_node_id> content;
// -- friend functions -------------------------------------------------------
template <class Inspector>
friend bool inspect(Inspector& f, node_id_data& x) {
return f.object(x).fields(f.field("content", x.content));
}
variant_type content;
};
/// A node ID is an opaque value for representing CAF instances in the network.
......@@ -167,7 +167,17 @@ public:
template <class Inspector>
friend bool inspect(Inspector& f, node_id& x) {
return f.object(x).fields(f.field("data", x.data_));
auto is_present = [&x] { return x.data_ != nullptr; };
auto get = [&] { return x.data_->content; };
auto reset = [&] { x.data_.reset(); };
auto set = [&](node_id_data::variant_type&& val) {
if (!x.data_ || !x.data_->unique())
x.data_.emplace(std::move(val));
else
x.data_->content = std::move(val);
return true;
};
return f.object(x).fields(f.field("data", is_present, get, reset, set));
}
// -- private API ------------------------------------------------------------
......@@ -196,12 +206,6 @@ private:
intrusive_ptr<node_id_data> data_;
};
template <>
struct inspector_access<intrusive_ptr<node_id_data>>
: optional_inspector_access<intrusive_ptr<node_id_data>> {
// nop
};
/// Returns whether `x` contains an URI.
/// @relates node_id
inline bool wraps_uri(const node_id& x) noexcept {
......
......@@ -166,6 +166,20 @@ public:
}
};
template <class T, class IsPresent, class Get>
struct optional_virt_field_t {
string_view field_name;
IsPresent is_present;
Get get;
template <class Inspector>
bool operator()(Inspector& f) {
return inspector_access<T>::save_field(f, field_name, is_present, get);
}
};
// -- DSL type for the object ------------------------------------------------
template <class Inspector, class SaveCallback>
struct object_with_save_callback_t {
string_view object_name;
......@@ -234,7 +248,8 @@ public:
template <class T>
static auto field(string_view name, T& x) {
return field_t<T>{name, &x};
static_assert(!std::is_const<T>::value);
return field_t<T>{name, std::addressof(x)};
}
template <class Get, class Set>
......@@ -243,6 +258,16 @@ public:
return virt_field_t<field_type, Get>{name, get};
}
template <class IsPresent, class Get, class... Ts>
static auto field(string_view name, IsPresent is_present, Get get, Ts&&...) {
using field_type = std::decay_t<decltype(get())>;
return optional_virt_field_t<field_type, IsPresent, Get>{
name,
is_present,
get,
};
}
protected:
error err_;
};
......
......@@ -145,6 +145,10 @@ bool binary_serializer::value(byte x) {
return ok;
}
bool binary_serializer::value(bool x) {
return value(static_cast<uint8_t>(x));
}
bool binary_serializer::value(int8_t x) {
return value(static_cast<byte>(x));
}
......
......@@ -35,7 +35,7 @@ bool serialized_size_inspector::end_object() {
}
bool serialized_size_inspector::begin_field(string_view, bool) {
result_ += 1;
result += 1;
return true;
}
......@@ -47,20 +47,20 @@ constexpr size_t max_value = static_cast<size_t>(std::numeric_limits<T>::max());
} // namespace
bool serialized_size_inspector::begin_field(string_view) {
return false;
return true;
}
bool serialized_size_inspector::begin_field(string_view,
span<const type_id_t> types,
size_t) {
if (types.size() < max_value<int8_t>) {
result_ += sizeof(int8_t);
result += sizeof(int8_t);
} else if (types.size() < max_value<int16_t>) {
result_ += sizeof(int16_t);
result += sizeof(int16_t);
} else if (types.size() < max_value<int32_t>) {
result_ += sizeof(int32_t);
result += sizeof(int32_t);
} else {
result_ += sizeof(int64_t);
result += sizeof(int64_t);
}
return true;
}
......@@ -95,7 +95,7 @@ bool serialized_size_inspector::begin_sequence(size_t list_size) {
x >>= 7;
}
*i++ = static_cast<uint8_t>(x) & 0x7f;
result_ += static_cast<size_t>(i - buf);
result += static_cast<size_t>(i - buf);
return true;
}
......@@ -104,57 +104,57 @@ bool serialized_size_inspector::end_sequence() {
}
bool serialized_size_inspector::value(bool) {
result_ += sizeof(uint8_t);
result += sizeof(uint8_t);
return true;
}
bool serialized_size_inspector::value(int8_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(uint8_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(int16_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(uint16_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(int32_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(uint32_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(int64_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(uint64_t x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(float x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(double x) {
result_ += sizeof(x);
result += sizeof(x);
return true;
}
......@@ -169,30 +169,30 @@ bool serialized_size_inspector::value(long double x) {
bool serialized_size_inspector::value(string_view x) {
CAF_IGNORE_UNUSED(begin_sequence(x.size()));
result_ += x.size();
result += x.size();
return end_sequence();
}
bool serialized_size_inspector::value(const std::u16string& x) {
CAF_IGNORE_UNUSED(begin_sequence(x.size()));
result_ += x.size() * sizeof(uint16_t);
result += x.size() * sizeof(uint16_t);
return end_sequence();
}
bool serialized_size_inspector::value(const std::u32string& x) {
CAF_IGNORE_UNUSED(begin_sequence(x.size()));
result_ += x.size() * sizeof(uint32_t);
result += x.size() * sizeof(uint32_t);
return end_sequence();
}
bool serialized_size_inspector::value(span<const byte> x) {
result_ += x.size();
result += x.size();
return true;
}
bool serialized_size_inspector::value(const std::vector<bool>& xs) {
CAF_IGNORE_UNUSED(begin_sequence(xs.size()));
result_ += (xs.size() + static_cast<size_t>(xs.size() % 8 != 0)) / 8;
result += (xs.size() + static_cast<size_t>(xs.size() % 8 != 0)) / 8;
return end_sequence();
}
......
......@@ -50,7 +50,7 @@ namespace caf::detail {
bool stringification_inspector::begin_object(string_view name) {
sep();
result_.insert(result_.end(), name.begin(), name.end());
result_ += ')';
result_ += '(';
return ok;
}
......@@ -68,6 +68,7 @@ bool stringification_inspector::begin_field(string_view, bool is_present) {
sep();
result_ += "null";
}
result_ += '*';
return ok;
}
......@@ -82,6 +83,7 @@ bool stringification_inspector::begin_field(string_view, bool is_present,
sep();
result_ += "null";
}
result_ += '*';
return ok;
}
......@@ -115,24 +117,64 @@ bool stringification_inspector::value(bool x) {
}
bool stringification_inspector::value(float x) {
sep();
auto str = std::to_string(x);
result_ += str;
return true;
}
bool stringification_inspector::value(double x) {
sep();
auto str = std::to_string(x);
result_ += str;
return true;
}
bool stringification_inspector::value(long double x) {
sep();
auto str = std::to_string(x);
result_ += str;
return true;
}
namespace {
template <class Duration>
auto dcast(timespan x) {
return std::chrono::duration_cast<Duration>(x);
}
} // namespace
bool stringification_inspector::value(timespan x) {
namespace sc = std::chrono;
sep();
auto try_print = [this](auto converted, const char* suffix) {
if (converted.count() < 1)
return false;
value(converted.count());
result_ += suffix;
return true;
};
if (try_print(dcast<sc::hours>(x), "h")
|| try_print(dcast<sc::minutes>(x), "min")
|| try_print(dcast<sc::seconds>(x), "s")
|| try_print(dcast<sc::milliseconds>(x), "ms")
|| try_print(dcast<sc::microseconds>(x), "us"))
return true;
value(x.count());
result_ += "ns";
return true;
}
bool stringification_inspector::value(timestamp x) {
sep();
append_timestamp_to_string(result_, x);
return true;
}
bool stringification_inspector::value(string_view str) {
sep();
if (str.empty()) {
result_ += R"("")";
return true;
......@@ -151,18 +193,21 @@ bool stringification_inspector::value(string_view str) {
}
bool stringification_inspector::value(const std::u16string&) {
sep();
// Convert to UTF-8 and print?
result_ += "<unprintable>";
return true;
}
bool stringification_inspector::value(const std::u32string&) {
sep();
// Convert to UTF-8 and print?
result_ += "<unprintable>";
return true;
}
bool stringification_inspector::int_value(int64_t x) {
sep();
if (x >= 0)
return int_value(static_cast<uint64_t>(x));
result_ += '-';
......@@ -178,6 +223,7 @@ bool stringification_inspector::int_value(int64_t x) {
}
bool stringification_inspector::int_value(uint64_t x) {
sep();
auto begin = result_.size();
result_ += (x % 10) + '0';
x /= 10;
......@@ -189,12 +235,20 @@ bool stringification_inspector::int_value(uint64_t x) {
return true;
}
bool stringification_inspector::value(const std::vector<bool>& xs) {
begin_sequence(xs.size());
for (bool x : xs)
value(x);
return end_sequence();
}
void stringification_inspector::sep() {
if (!result_.empty())
switch (result_.back()) {
case '(':
case '[':
case '{':
case '*':
case ' ': // only at back if we've printed ", " before
break;
default:
......
......@@ -135,14 +135,16 @@ save_data(Serializer& sink, const message::data_ptr& data) {
&& sink.end_field() //
&& sink.end_object();
}
// Write type information.
GUARDED(sink.begin_object("message"));
auto type_ids = data->types();
GUARDED(sink.begin_object("message") //
&& sink.begin_field("types") //
// Write type information.
if (!sink.has_human_readable_format()) {
GUARDED(sink.begin_field("types") //
&& sink.begin_sequence(type_ids.size()));
for (auto id : type_ids)
GUARDED(sink.value(id));
GUARDED(sink.end_sequence() && sink.end_field());
}
// Write elements.
auto gmos = detail::global_meta_objects();
auto storage = data->storage();
......
......@@ -51,14 +51,16 @@ CAF_TEST(erase) {
CAF_TEST(serialization roundtrips go through the registry) {
auto hdl = sys.spawn(dummy);
CAF_MESSAGE("hdl.id: " << hdl->id());
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!inspect_object(sink,hdl))
if (!inspect_object(sink, hdl))
CAF_FAIL("serialization failed: " << sink.get_error());
CAF_MESSAGE("buf: " << buf);
actor hdl2;
binary_deserializer source{sys, buf};
if (!inspect_object(source, hdl2))
CAF_FAIL("serialization failed: " << source.get_error());
CAF_FAIL("deserialization failed: " << source.get_error());
CAF_CHECK_EQUAL(hdl, hdl2);
anon_send_exit(hdl, exit_reason::user_shutdown);
}
......
......@@ -43,6 +43,18 @@ byte operator"" _b(char x) {
return static_cast<byte>(x);
}
struct arr {
int8_t xs[3];
int8_t operator[](size_t index) const noexcept {
return xs[index];
}
};
template <class Inspector>
bool inspect(Inspector& f, arr& x) {
return f.object(x).fields(f.field("xs", x.xs));
}
struct fixture {
template <class... Ts>
void load(const std::vector<byte>& buf, Ts&... xs) {
......@@ -135,7 +147,7 @@ CAF_TEST(concatenation) {
0x80_b, 0x55_b, 7_b);
}
SUBTEST("arrays behave like tuples") {
int8_t xs[] = {0, 0, 0};
arr xs{{0, 0, 0}};
load(byte_buffer({1_b, 2_b, 3_b}), xs);
CAF_CHECK_EQUAL(xs[0], 1);
CAF_CHECK_EQUAL(xs[1], 2);
......@@ -158,7 +170,7 @@ CAF_TEST(binary serializer picks up inspect functions) {
SUBTEST("node ID") {
auto nid = make_node_id(123, "000102030405060708090A0B0C0D0E0F10111213");
CHECK_LOAD(node_id, unbox(nid),
// Implementation ID: node_id::default_data::class_id (1)
// content index for hashed_node_id (1)
1_b,
// Process ID.
0_b, 0_b, 0_b, 123_b,
......
......@@ -43,12 +43,21 @@ byte operator"" _b(char x) {
return static_cast<byte>(x);
}
struct arr {
int8_t xs[3];
};
template <class Inspector>
bool inspect(Inspector& f, arr& x) {
return f.object(x).fields(f.field("xs", x.xs));
}
struct fixture {
template <class... Ts>
auto save(const Ts&... xs) {
byte_buffer result;
binary_serializer sink{nullptr, result};
if (!inspect_objects(sink, xs...))
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
return result;
}
......@@ -56,7 +65,7 @@ struct fixture {
template <class... Ts>
void save_to_buf(byte_buffer& data, const Ts&... xs) {
binary_serializer sink{nullptr, data};
if (!inspect_objects(sink, xs...))
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
}
};
......@@ -139,7 +148,7 @@ CAF_TEST(concatenation) {
0x80_b, 0x55_b, 7_b);
}
SUBTEST("arrays behave like tuples") {
int8_t xs[] = {1, 2, 3};
arr xs{{1, 2, 3}};
CAF_CHECK_EQUAL(save(xs), byte_buffer({1_b, 2_b, 3_b}));
}
}
......@@ -159,7 +168,7 @@ CAF_TEST(binary serializer picks up inspect functions) {
SUBTEST("node ID") {
auto nid = make_node_id(123, "000102030405060708090A0B0C0D0E0F10111213");
CHECK_SAVE(node_id, unbox(nid),
// Implementation ID: node_id::default_data::class_id (1)
// content index for hashed_node_id (1)
1_b,
// Process ID.
0_b, 0_b, 0_b, 123_b,
......
......@@ -24,14 +24,6 @@
using namespace caf;
namespace {
void foobar() {
// nop
}
} // namespace
#define CHECK_DEEP_TO_STRING(val, str) CAF_CHECK_EQUAL(deep_to_string(val), str)
CAF_TEST(timespans) {
......@@ -65,7 +57,6 @@ CAF_TEST(boolean lists) {
CAF_TEST(pointers) {
auto i = 42;
CHECK_DEEP_TO_STRING(&i, "*42");
CHECK_DEEP_TO_STRING(foobar, "<fun>");
}
CAF_TEST(buffers) {
......
......@@ -39,7 +39,7 @@ struct fixture : test_coordinator_fixture<> {
size_t actual_size(const Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (inspect_objects(sink, xs...))
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
CAF_FAIL("failed to serialize data: " << sink.get_error());
return buf.size();
}
......
......@@ -205,21 +205,10 @@ struct binary_serialization_policy {
}
template <class T>
detail::enable_if_t<is_integral_or_enum<T>::value, bool> operator()(T& x) {
bool operator()(T& x) {
auto buf = to_buf(x);
binary_deserializer source{&context, buf};
auto y = static_cast<T>(0);
if (!inspect_objects(source, y))
CAF_FAIL("failed to deserialize from buffer: " << source.get_error());
CAF_CHECK_EQUAL(x, y);
return detail::safe_equal(x, y);
}
template <class T>
detail::enable_if_t<!is_integral_or_enum<T>::value, bool> operator()(T& x) {
auto buf = to_buf(x);
binary_deserializer source{&context, buf};
T y;
auto y = T{};
if (!inspect_objects(source, y))
CAF_FAIL("failed to deserialize from buffer: " << source.get_error());
CAF_CHECK_EQUAL(x, y);
......
......@@ -197,6 +197,11 @@ struct testee : load_inspector {
return object_t<testee>{T::tname, this};
}
template <class T>
auto object(optional<T>&) {
return object_t<testee>{"optional", this};
}
bool begin_object(string_view object_name) {
new_line();
indent += 2;
......@@ -373,6 +378,16 @@ begin object line
end object)_");
}
CAF_TEST(load inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(inspect_object(f, x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object optional
begin optional field value
end field
end object)_");
}
CAF_TEST(load inspectors support fields with fallbacks and invariants) {
duration d{"minutes", 42};
CAF_CHECK_EQUAL(inspect(f, d), true);
......
......@@ -294,6 +294,17 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(load_inspector_tests, fixture)
CAF_TEST(save inspectors can visit C arrays) {
int32_t xs[] = {1, 2, 3};
CAF_CHECK_EQUAL(inspect_value(f, xs), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin tuple of size 3
int32_t value
int32_t value
int32_t value
end tuple)_");
}
CAF_TEST(save inspectors can visit simple POD types) {
point_3d p{1, 1, 1};
CAF_CHECK_EQUAL(inspect(f, p), true);
......@@ -389,6 +400,16 @@ end object)_");
}
}
CAF_TEST(save inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(inspect_object(f, x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object optional
begin optional field value
end field
end object)_");
}
CAF_TEST(save inspectors support fields with optional values) {
person p1{"Eduard Example", none};
CAF_CHECK_EQUAL(inspect(f, p1), true);
......@@ -435,6 +456,7 @@ end object)_");
CAF_TEST(save inspectors support nasty data structures) {
nasty x;
CAF_CHECK(inspect(f, x));
CAF_CHECK_EQUAL(f.get_error(), error{});
CAF_CHECK_EQUAL(f.log, R"_(
begin object nasty
begin field field_01
......
......@@ -139,7 +139,10 @@ struct fixture : test_coordinator_fixture<> {
T msg_roundtrip(const T& x) {
message result;
auto tmp = make_message(x);
deserialize(serialize(tmp), result);
auto buf = serialize(tmp);
CAF_MESSAGE("serialized " << to_string(msg) << " into " << buf.size()
<< " bytes");
deserialize(buf, result);
if (!result.match_elements<T>())
CAF_FAIL("expected: " << x << ", got: " << result);
return result.get_as<T>(0);
......
......@@ -26,8 +26,6 @@
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/none.hpp"
......@@ -79,31 +77,22 @@ macro_repeat20(i_n)
using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
i11, i12, i13, i14, i15, i16, i17, i18, i19, i20>;
#define VARIANT_EQ(x, y) \
do { \
using type = std::decay_t<decltype(y)>; \
CAF_CHECK(holds_alternative<type>(x) && get<type>(x) == y); \
} while (false)
#define v20_test(n) \
x3 = i##n{0x##n}; \
CAF_CHECK_EQUAL(deep_to_string(x3), \
CAF_STR(i##n) + "("s + std::to_string(0x##n) + ")"); \
CAF_CHECK_EQUAL(v20{x3}, i##n{0x##n}); \
VARIANT_EQ(v20{x3}, i##n{0x##n}); \
x4 = x3; \
CAF_CHECK_EQUAL(x4, i##n{0x##n}); \
CAF_CHECK_EQUAL(v20{std::move(x3)}, i##n{0x##n}); \
CAF_CHECK_EQUAL(x3, i##n{0}); \
VARIANT_EQ(x4, i##n{0x##n}); \
VARIANT_EQ(v20{std::move(x3)}, i##n{0x##n}); \
VARIANT_EQ(x3, i##n{0}); \
x3 = std::move(x4); \
CAF_CHECK_EQUAL(x4, i##n{0}); \
CAF_CHECK_EQUAL(x3, i##n{0x##n});
// { \
// byte_buffer buf; \
// binary_serializer sink{sys.dummy_execution_unit(), buf}; \
// if (!inspect_object(sink, x3)) \
// CAF_FAIL("failed to serialize data: " << sink.get_error()); \
// CAF_CHECK_EQUAL(x3, i##n{0x##n}); \
// v20 tmp; \
// binary_deserializer source{sys.dummy_execution_unit(), buf}; \
// if (!inspect_object(source, tmp)) \
// CAF_FAIL("failed to deserialize data: " << source.get_error()); \
// CAF_CHECK_EQUAL(tmp, i##n{0x##n}); \
// CAF_CHECK_EQUAL(tmp, x3); \
// }
VARIANT_EQ(x4, i##n{0}); \
VARIANT_EQ(x3, i##n{0x##n});
// copy construction, copy assign, move construction, move assign
// and finally serialization round-trip
......@@ -114,9 +103,9 @@ CAF_TEST(copying_moving_roundtrips) {
variant<none_t> x1;
CAF_CHECK_EQUAL(x1, none);
variant<int, none_t> x2;
CAF_CHECK_EQUAL(x2, 0);
VARIANT_EQ(x2, 0);
v20 x3;
CAF_CHECK_EQUAL(x3, i01{0});
VARIANT_EQ(x3, i01{0});
v20 x4;
macro_repeat20(v20_test);
}
......
......@@ -81,7 +81,8 @@ constexpr uint64_t spawn_serv_id = basp::header::spawn_server_id;
constexpr uint64_t config_serv_id = basp::header::config_server_id;
std::string hexstr(const byte_buffer& buf) {
return deep_to_string(meta::hex_formatted(), buf);
// TODO: re-implement hex-formatted option
return deep_to_string(buf);
}
struct node {
......
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