Commit 6df8d506 authored by Dominik Charousset's avatar Dominik Charousset

Implement new settings reader

parent 083126c8
......@@ -158,6 +158,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/sec_strings.cpp
src/serializer.cpp
src/settings.cpp
src/settings_reader.cpp
src/settings_writer.cpp
src/size_based_credit_controller.cpp
src/skip.cpp
......@@ -312,6 +313,7 @@ caf_add_test_suites(caf-core-test
serial_reply
serialization
settings
settings_reader
settings_writer
simple_timeout
span
......
......@@ -97,6 +97,8 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, float& x);
CAF_CORE_EXPORT void parse(string_parser_state& ps, double& x);
CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x);
// -- CAF types ----------------------------------------------------------------
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_address& x);
......
......@@ -145,19 +145,21 @@ enum class sec : uint8_t {
no_tracing_context,
/// No request produced a valid result.
all_requests_failed,
/// Deserialization failed, because an invariant got violated after reading
/// Deserialization failed because an invariant got violated after reading
/// the content of a field.
field_invariant_check_failed = 55,
/// Deserialization failed, because a setter rejected the input.
/// Deserialization failed because a setter rejected the input.
field_value_synchronization_failed,
/// Deserialization failed, because the source announced an invalid type.
/// Deserialization failed because the source announced an invalid type.
invalid_field_type,
/// Serialization failed because a type was flagged as unsafe message type.
unsafe_type,
/// Serialization failed, because a save callback returned `false`.
/// Serialization failed because a save callback returned `false`.
save_callback_failed,
/// Deserialization failed, because a load callback returned `false`.
load_callback_failed,
/// Deserialization failed because a load callback returned `false`.
load_callback_failed = 60,
/// Converting between two types failed.
conversion_failed,
};
/// @relates sec
......
......@@ -25,6 +25,7 @@
#include "caf/optional.hpp"
#include "caf/raise_error.hpp"
#include "caf/string_view.hpp"
#include "caf/sum_type.hpp"
namespace caf {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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 "caf/deserializer.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include <stack>
#include <vector>
namespace caf {
/// Extracts objects from @ref settings.
class settings_reader : public deserializer {
public:
// -- member types------------------------------------------------------------
using super = deserializer;
using key_ptr = const std::string*;
struct absent_field {};
struct sequence {
using list_pointer = const std::vector<config_value>*;
size_t index;
list_pointer ls;
explicit sequence(list_pointer ls) : index(0), ls(ls) {
// nop
}
bool at_end() const noexcept;
const config_value& current();
void advance() {
++index;
}
};
struct associative_array {
settings::const_iterator pos;
settings::const_iterator end;
bool at_end() const noexcept;
const std::pair<const std::string, config_value>& current();
};
using value_type = variant<const settings*, const config_value*, key_ptr,
absent_field, sequence, associative_array>;
using stack_type = std::stack<value_type, std::vector<value_type>>;
// -- constructors, destructors, and assignment operators --------------------
settings_reader(const settings* input, actor_system& sys)
: super(sys), root_(input) {
st_.push(input);
has_human_readable_format_ = true;
}
settings_reader(const settings* input, execution_unit* ctx)
: super(ctx), root_(input) {
has_human_readable_format_ = true;
}
explicit settings_reader(const settings* input)
: settings_reader(input, nullptr) {
// nop
}
~settings_reader() override;
// -- stack access -----------------------------------------------------------
value_type& top() {
return st_.top();
}
void pop() {
return st_.pop();
}
// -- interface functions ----------------------------------------------------
bool fetch_next_object_type(type_id_t& type) override;
bool begin_object(string_view name) override;
bool end_object() override;
bool begin_field(string_view) override;
bool begin_field(string_view name, bool& is_present) override;
bool begin_field(string_view name, span<const type_id_t> types,
size_t& index) override;
bool begin_field(string_view name, bool& is_present,
span<const type_id_t> types, size_t& index) override;
bool end_field() override;
bool begin_tuple(size_t size) override;
bool end_tuple() override;
bool begin_key_value_pair() override;
bool end_key_value_pair() override;
bool begin_sequence(size_t& size) override;
bool end_sequence() override;
bool begin_associative_array(size_t& size) override;
bool end_associative_array() override;
bool value(bool& x) override;
bool value(int8_t& x) override;
bool value(uint8_t& x) override;
bool value(int16_t& x) override;
bool value(uint16_t& x) override;
bool value(int32_t& x) override;
bool value(uint32_t& x) override;
bool value(int64_t& x) override;
bool value(uint64_t& x) override;
bool value(float& x) override;
bool value(double& x) override;
bool value(long double& x) override;
bool value(std::string& x) override;
bool value(std::u16string& x) override;
bool value(std::u32string& x) override;
bool value(span<byte> x) override;
private:
bool fetch_object_type(const settings* obj, type_id_t& type);
stack_type st_;
const settings* root_;
};
} // namespace caf
......@@ -142,6 +142,8 @@ PARSE_IMPL(float, floating_point)
PARSE_IMPL(double, floating_point)
PARSE_IMPL(long double, floating_point)
void parse(string_parser_state& ps, uri& x) {
uri_builder builder;
if (ps.consume('<')) {
......
......@@ -137,6 +137,8 @@ std::string to_string(sec x) {
return "save_callback_failed";
case sec::load_callback_failed:
return "load_callback_failed";
case sec::conversion_failed:
return "conversion_failed";
};
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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. *
******************************************************************************/
#include "caf/settings_reader.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/print.hpp"
#include "caf/settings.hpp"
namespace {
template <class T>
struct pretty_name;
#define PRETTY_NAME(type, pretty_str) \
template <> \
struct pretty_name<type> { \
[[maybe_unused]] static constexpr const char* value = pretty_str; \
}
PRETTY_NAME(const caf::settings*, "dictionary");
PRETTY_NAME(const caf::config_value*, "config_value");
PRETTY_NAME(const std::string*, "key");
PRETTY_NAME(caf::settings_reader::absent_field, "absent field");
PRETTY_NAME(caf::settings_reader::sequence, "sequence");
PRETTY_NAME(caf::settings_reader::associative_array, "associative array");
template <class T>
constexpr auto pretty_name_v = pretty_name<T>::value;
auto get_pretty_name(const caf::settings_reader::value_type& x) {
const char* pretty_names[] = {
"dictionary", "config_value", "key",
"absent field", "sequence", "associative array",
};
return pretty_names[x.index()];
}
} // namespace
#define CHECK_NOT_EMPTY() \
do { \
if (st_.empty()) { \
emplace_error(sec::runtime_error, "mismatching calls to begin/end"); \
return false; \
} \
} while (false)
#define SCOPE(top_type) \
CHECK_NOT_EMPTY(); \
if (!holds_alternative<top_type>(st_.top())) { \
std::string msg; \
msg += "type clash in function "; \
msg += __func__; \
msg += ": expected "; \
msg += pretty_name_v<top_type>; \
msg += " got "; \
msg += get_pretty_name(st_.top()); \
emplace_error(sec::runtime_error, std::move(msg)); \
return false; \
} \
[[maybe_unused]] auto& top = get<top_type>(st_.top());
namespace caf {
// -- member types--------------------------------------------------------------
bool settings_reader::sequence::at_end() const noexcept {
return index >= ls->size();
}
const config_value& settings_reader::sequence::current() {
return (*ls)[index];
}
bool settings_reader::associative_array::at_end() const noexcept {
return pos == end;
}
const std::pair<const std::string, config_value>&
settings_reader::associative_array::current() {
return *pos;
}
// -- constructors, destructors, and assignment operators ----------------------
settings_reader::~settings_reader() {
// nop
}
// -- interface functions ------------------------------------------------------
bool settings_reader::fetch_next_object_type(type_id_t& type) {
if (st_.empty()) {
if (root_ == nullptr) {
emplace_error(sec::runtime_error,
"tried to read multiple objects from the root object");
return false;
}
return fetch_object_type(root_, type);
} else {
auto f = detail::make_overload(
[this](const settings*) {
emplace_error(sec::runtime_error,
"fetch_next_object_type called inside an object");
return false;
},
[this, &type](const config_value* val) {
if (auto obj = get_if<settings>(val); obj == nullptr) {
emplace_error(sec::conversion_failed, "cannot read input as object");
return false;
} else {
return fetch_object_type(obj, type);
}
},
[this](key_ptr) {
emplace_error(
sec::runtime_error,
"reading an object from a dictionary key not implemented yet");
return false;
},
[this](absent_field) {
emplace_error(
sec::runtime_error,
"fetch_next_object_type called inside non-existent optional field");
return false;
},
[this, &type](sequence& seq) {
if (seq.at_end()) {
emplace_error(sec::runtime_error, "list index out of bounds");
return false;
}
if (auto obj = get_if<settings>(std::addressof(seq.current())); !obj) {
emplace_error(sec::conversion_failed, "cannot read input as object");
return false;
} else {
return fetch_object_type(obj, type);
}
},
[this](associative_array&) {
emplace_error(sec::runtime_error,
"fetch_next_object_type called inside associative array");
return false;
});
return visit(f, st_.top());
}
}
bool settings_reader::begin_object(string_view) {
if (st_.empty()) {
if (root_ == nullptr) {
emplace_error(sec::runtime_error,
"tried to read multiple objects from the root object");
return false;
}
st_.push(root_);
root_ = nullptr;
return true;
}
auto f = detail::make_overload(
[this](const settings*) {
emplace_error(sec::runtime_error,
"begin_object called inside another object");
return false;
},
[this](const config_value* val) {
if (auto obj = get_if<settings>(val)) {
// Morph into an object. This value gets "consumed" by
// begin_object/end_object.
st_.top() = obj;
return true;
} else {
emplace_error(sec::conversion_failed, "cannot read input as object");
return false;
}
},
[this](key_ptr) {
emplace_error(
sec::runtime_error,
"reading an object from a dictionary key not implemented yet");
return false;
},
[this](absent_field) {
emplace_error(sec::runtime_error,
"begin_object called inside non-existent optional field");
return false;
},
[this](sequence& seq) {
if (seq.at_end()) {
emplace_error(sec::runtime_error,
"begin_object: sequence out of bounds");
return false;
}
if (auto obj = get_if<settings>(std::addressof(seq.current()))) {
seq.advance();
st_.push(obj);
return true;
} else {
emplace_error(sec::conversion_failed, "cannot read input as object");
return false;
}
},
[this](associative_array&) {
emplace_error(sec::runtime_error,
"fetch_next_object_type called inside associative array");
return false;
});
return visit(f, st_.top());
}
bool settings_reader::end_object() {
SCOPE(const settings*);
st_.pop();
return true;
}
bool settings_reader::begin_field(string_view name) {
SCOPE(const settings*);
if (auto i = top->find(name); i != top->end()) {
st_.push(std::addressof(i->second));
return true;
} else {
emplace_error(sec::runtime_error, "no such field: " + to_string(name));
return false;
}
}
bool settings_reader::begin_field(string_view name, bool& is_present) {
SCOPE(const settings*);
if (auto i = top->find(name); i != top->end()) {
is_present = true;
st_.push(std::addressof(i->second));
} else {
is_present = false;
}
return true;
}
bool settings_reader::begin_field(string_view name, span<const type_id_t> types,
size_t& index) {
SCOPE(const settings*);
std::string key;
key += '@';
key.insert(key.end(), name.begin(), name.end());
key += "-type";
type_id_t id = 0;
if (auto str = get_if<std::string>(top, key); !str) {
emplace_error(sec::runtime_error, "could not find type annotation: " + key);
return false;
} else if (id = query_type_id(*str); id == invalid_type_id) {
emplace_error(sec::runtime_error, "no such type: " + *str);
return false;
} else if (auto i = std::find(types.begin(), types.end(), id);
i == types.end()) {
emplace_error(sec::conversion_failed,
"instrid type for variant field: " + *str);
return false;
} else {
index = static_cast<size_t>(std::distance(types.begin(), i));
}
return begin_field(name);
}
bool settings_reader::begin_field(string_view name, bool& is_present,
span<const type_id_t> types, size_t& index) {
SCOPE(const settings*);
if (top->contains(name)) {
is_present = true;
return begin_field(name, types, index);
} else {
is_present = false;
return true;
}
}
bool settings_reader::end_field() {
CHECK_NOT_EMPTY();
// Note: no pop() here, because the value(s) were already consumed.
return true;
}
bool settings_reader::begin_tuple(size_t size) {
size_t list_size = 0;
if (begin_sequence(list_size)) {
if (list_size == size)
return true;
std::string msg;
msg += "expected tuple of size ";
detail::print(msg, size);
msg += ", got tuple of size ";
detail::print(msg, list_size);
emplace_error(sec::conversion_failed, std::move(msg));
return false;
}
return false;
}
bool settings_reader::end_tuple() {
return end_sequence();
}
bool settings_reader::begin_key_value_pair() {
SCOPE(associative_array);
if (top.at_end()) {
emplace_error(sec::runtime_error,
"tried to read associate array past its end");
return false;
}
auto& kvp = top.current();
st_.push(std::addressof(kvp.second));
st_.push(std::addressof(kvp.first));
return true;
}
bool settings_reader::end_key_value_pair() {
SCOPE(associative_array);
++top.pos;
return true;
}
bool settings_reader::begin_sequence(size_t& size) {
SCOPE(const config_value*);
if (auto ls = get_if<config_value::list>(top)) {
size = ls->size();
// "Transform" the top element to a list. Otherwise, we would need some
// extra logic only to clean up the object.
st_.top() = sequence{ls};
return true;
}
std::string msg = "expected a list, got a ";
msg += top->type_name();
emplace_error(sec::conversion_failed, std::move(msg));
return false;
}
bool settings_reader::end_sequence() {
SCOPE(sequence);
if (!top.at_end()) {
emplace_error(sec::runtime_error,
"failed to consume all elements in a sequence");
return false;
}
st_.pop();
return true;
}
bool settings_reader::begin_associative_array(size_t& size) {
SCOPE(const config_value*);
if (auto dict = get_if<settings>(top)) {
size = dict->size();
// Morph top object, it's being "consumed" by begin_.../end_....
st_.top() = associative_array{dict->begin(), dict->end()};
return true;
}
std::string msg = "expected a dictionary, got a ";
msg += top->type_name();
emplace_error(sec::conversion_failed, std::move(msg));
return false;
}
bool settings_reader::end_associative_array() {
SCOPE(associative_array);
if (!top.at_end()) {
emplace_error(sec::runtime_error,
"failed to consume all elements in an associative array");
return false;
}
st_.pop();
return true;
}
namespace {
template <class T>
bool pull(settings_reader& reader, T& x) {
using internal_type = std::conditional_t<std::is_floating_point<T>::value,
config_value::real, T>;
auto assign = [&x](auto& result) {
if constexpr (std::is_floating_point<T>::value) {
x = static_cast<T>(result);
} else {
x = result;
}
};
auto& top = reader.top();
if (holds_alternative<const config_value*>(top)) {
auto ptr = get<const config_value*>(top);
if (auto val = get_if<internal_type>(ptr)) {
assign(*val);
reader.pop();
return true;
} else {
std::string msg = "expected a dictionary, got a ";
msg += to_string(type_name_v<T>);
reader.emplace_error(sec::conversion_failed, std::move(msg));
return false;
}
}
if (holds_alternative<settings_reader::sequence>(top)) {
auto& seq = get<settings_reader::sequence>(top);
if (seq.at_end()) {
reader.emplace_error(sec::runtime_error, "value: sequence out of bounds");
return false;
}
auto ptr = std::addressof(seq.current());
if (auto val = get_if<internal_type>(ptr)) {
assign(*val);
seq.advance();
return true;
} else {
std::string msg = "expected a dictionary, got a ";
msg += to_string(type_name_v<T>);
reader.emplace_error(sec::conversion_failed, std::move(msg));
return false;
}
}
if (holds_alternative<settings_reader::key_ptr>(top)) {
auto ptr = get<settings_reader::key_ptr>(top);
if constexpr (std::is_same<std::string, T>::value) {
x = *ptr;
reader.pop();
return true;
} else {
if (auto err = detail::parse(*ptr, x)) {
reader.set_error(std::move(err));
return false;
}
return true;
}
}
reader.emplace_error(sec::conversion_failed,
"expected a value, sequence, or key");
return false;
}
} // namespace
bool settings_reader::value(bool& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(int8_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(uint8_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(int16_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(uint16_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(int32_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(uint32_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(int64_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(uint64_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(float& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(double& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(long double& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(std::string& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool settings_reader::value(std::u16string&) {
emplace_error(sec::runtime_error, "u16string support not implemented yet");
return false;
}
bool settings_reader::value(std::u32string&) {
emplace_error(sec::runtime_error, "u32string support not implemented yet");
return false;
}
bool settings_reader::value(span<byte> bytes) {
CHECK_NOT_EMPTY();
std::string x;
if (!pull(*this, x))
return false;
if (x.size() != bytes.size() * 2) {
emplace_error(sec::runtime_error,
"hex-formatted string does not match expected size");
return false;
}
for (size_t index = 0; index < x.size(); index += 2) {
uint8_t value = 0;
for (size_t i = 0; i < 2; ++i) {
auto c = x[index + i];
if (!isxdigit(c)) {
emplace_error(sec::runtime_error,
"invalid character in hex-formatted string");
return false;
}
detail::parser::add_ascii<16>(value, c);
}
bytes[index / 2] = static_cast<byte>(value);
}
return true;
}
bool settings_reader::fetch_object_type(const settings* obj, type_id_t& type) {
if (auto str = get_if<std::string>(obj, "@type"); str == nullptr) {
emplace_error(sec::runtime_error,
"cannot fetch object type: no '@type' entry found");
return false;
} else if (auto id = query_type_id(*str); id == invalid_type_id) {
emplace_error(sec::runtime_error, "no such type: " + *str);
return false;
} else {
type = id;
return true;
}
}
} // namespace caf
......@@ -47,6 +47,10 @@ struct point_3d {
int32_t z;
};
[[maybe_unused]] bool operator==(const point_3d& x, const point_3d& y) {
return std::tie(x.x, x.y, x.z) == std::tie(y.x, y.y, y.z);
}
template <class Inspector>
bool inspect(Inspector& f, point_3d& x) {
return f.object(x).fields(f.field("x", x.x), f.field("y", x.y),
......@@ -58,6 +62,10 @@ struct line {
point_3d p2;
};
[[maybe_unused]] bool operator==(const line& x, const line& y) {
return std::tie(x.p1, x.p2) == std::tie(y.p1, y.p2);
}
template <class Inspector>
bool inspect(Inspector& f, line& x) {
return f.object(x).fields(f.field("p1", x.p1), f.field("p2", x.p2));
......@@ -141,6 +149,11 @@ struct dummy_message {
caf::variant<std::string, double> content;
};
[[maybe_unused]] bool operator==(const dummy_message& x,
const dummy_message& y) {
return x.content == y.content;
}
template <class Inspector>
bool inspect(Inspector& f, dummy_message& x) {
return f.object(x).fields(f.field("content", x.content));
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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. *
******************************************************************************/
#define CAF_SUITE settings_reader
#include "caf/settings_reader.hpp"
#include "caf/test/dsl.hpp"
#include "inspector-tests.hpp"
#include "caf/settings_writer.hpp"
using namespace caf;
using namespace std::literals::string_literals;
namespace {
using i64 = int64_t;
constexpr i64 operator""_i64(unsigned long long int x) {
return static_cast<int64_t>(x);
}
using i64_list = std::vector<i64>;
struct fixture {
settings xs;
template <class T>
void deserialize(const settings& src, T& value) {
settings_reader reader{&src};
if (!inspect_object(reader, value))
CAF_FAIL("failed to deserialize from settings: " << reader.get_error());
}
template <class T>
void deserialize(T& value) {
return deserialize(xs, value);
}
template <class T>
optional<T> get(const settings& cfg, string_view key) {
if (auto ptr = get_if<T>(&cfg, key))
return *ptr;
return none;
}
template <class T>
optional<T> get(string_view key) {
return get<T>(xs, key);
}
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(settings_reader_tests, fixture)
CAF_TEST(readers deserialize simple objects from configs) {
put(xs, "foo", "hello");
put(xs, "bar", "world");
foobar fb;
deserialize(fb);
CAF_CHECK_EQUAL(fb.foo(), "hello"s);
CAF_CHECK_EQUAL(fb.bar(), "world"s);
}
CAF_TEST(readers deserialize complex objects from configs) {
CAF_MESSAGE("fill a dictionary with data for a 'basics' object");
put(xs, "v1", settings{});
put(xs, "v2", 42_i64);
put(xs, "v3", i64_list({1, 2, 3, 4}));
settings msg1;
put(msg1, "content", 2.0);
put(msg1, "@content-type", "double");
settings msg2;
put(msg2, "content", "foobar"s);
put(msg2, "@content-type", "std::string");
put(xs, "v4", make_config_value_list(msg1, msg2));
put(xs, "v5", i64_list({10, 20}));
config_value::list v6;
v6.emplace_back(i64{123});
v6.emplace_back(msg1);
put(xs, "v6", v6);
put(xs, "v7.one", i64{1});
put(xs, "v7.two", i64{2});
put(xs, "v7.three", i64{3});
put(xs, "v8", i64_list());
CAF_MESSAGE("deserialize and verify the 'basics' object");
basics obj;
deserialize(obj);
CAF_CHECK_EQUAL(obj.v2, 42);
CAF_CHECK_EQUAL(obj.v3[0], 1);
CAF_CHECK_EQUAL(obj.v3[1], 2);
CAF_CHECK_EQUAL(obj.v3[2], 3);
CAF_CHECK_EQUAL(obj.v3[3], 4);
CAF_CHECK_EQUAL(obj.v4[0], dummy_message{{2.0}});
CAF_CHECK_EQUAL(obj.v4[1], dummy_message{{"foobar"s}});
CAF_CHECK_EQUAL(obj.v5[0], i64{10});
CAF_CHECK_EQUAL(obj.v5[1], i64{20});
CAF_CHECK_EQUAL(obj.v6, std::make_tuple(int32_t{123}, dummy_message{{2.0}}));
CAF_CHECK_EQUAL(obj.v7["one"], 1);
CAF_CHECK_EQUAL(obj.v7["two"], 2);
CAF_CHECK_EQUAL(obj.v7["three"], 3);
}
CAF_TEST(readers deserialize objects from the output of writers) {
CAF_MESSAGE("serialize the 'line' object");
{
line l{{10, 20, 30}, {70, 60, 50}};
settings_writer writer{&xs};
if (!inspect_object(writer, l))
CAF_FAIL("failed two write to settings: " << writer.get_error());
}
CAF_MESSAGE("serialize and verify the 'line' object");
{
line l{{0, 0, 0}, {0, 0, 0}};
deserialize(l);
CAF_CHECK_EQUAL(l.p1.x, 10);
CAF_CHECK_EQUAL(l.p1.y, 20);
CAF_CHECK_EQUAL(l.p1.z, 30);
CAF_CHECK_EQUAL(l.p2.x, 70);
CAF_CHECK_EQUAL(l.p2.y, 60);
CAF_CHECK_EQUAL(l.p2.z, 50);
}
}
CAF_TEST_FIXTURE_SCOPE_END()
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