Commit 59ed7e08 authored by Dominik Charousset's avatar Dominik Charousset

Implement new settings writer

parent 2d771bf0
......@@ -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_writer.cpp
src/size_based_credit_controller.cpp
src/skip.cpp
src/stream_aborter.cpp
......@@ -177,6 +178,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/timestamp.cpp
src/tracing_data.cpp
src/tracing_data_factory.cpp
src/type_id.cpp
src/type_id_list.cpp
src/uri.cpp
src/uri_builder.cpp
......@@ -310,6 +312,7 @@ caf_add_test_suites(caf-core-test
serial_reply
serialization
settings
settings_writer
simple_timeout
span
stateful_actor
......
......@@ -138,12 +138,28 @@ public:
return ok;
}
constexpr bool begin_key_value_pair() noexcept {
return ok;
}
constexpr bool end_key_value_pair() noexcept {
return ok;
}
bool begin_sequence(size_t& list_size) noexcept;
constexpr bool end_sequence() noexcept {
return ok;
}
bool begin_associative_array(size_t& size) noexcept {
return begin_sequence(size);
}
bool end_associative_array() noexcept {
return end_sequence();
}
bool value(bool& x) noexcept;
bool value(byte& x) noexcept;
......
......@@ -124,12 +124,28 @@ public:
return ok;
}
constexpr bool begin_key_value_pair() {
return ok;
}
constexpr bool end_key_value_pair() {
return ok;
}
bool begin_sequence(size_t list_size);
constexpr bool end_sequence() {
return ok;
}
bool begin_associative_array(size_t size) {
return begin_sequence(size);
}
bool end_associative_array() {
return end_sequence();
}
bool value(byte x);
bool value(bool x);
......
......@@ -88,12 +88,29 @@ public:
/// Ends processing of a sequence.
virtual bool end_tuple() = 0;
/// Begins processing of a tuple with two elements, whereas the first element
/// represents the key in an associative array.
/// @note the default implementation calls `begin_tuple(2)`.
virtual bool begin_key_value_pair();
/// Ends processing of a key-value pair after both values were written.
/// @note the default implementation calls `end_tuple()`.
virtual bool end_key_value_pair();
/// Begins processing of a sequence.
virtual bool begin_sequence(size_t& size) = 0;
/// Ends processing of a sequence.
virtual bool end_sequence() = 0;
/// Begins processing of an associative array (map).
/// @note the default implementation calls `begin_sequence(size)`.
virtual bool begin_associative_array(size_t& size);
/// Ends processing of an associative array (map).
/// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array();
/// Reads primitive value from the input.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
......
......@@ -71,13 +71,33 @@ public:
bool end_field();
bool begin_tuple(size_t size) {
return begin_sequence(size);
}
bool end_tuple() {
return end_sequence();
}
bool begin_key_value_pair() {
return begin_tuple(2);
}
bool end_key_value_pair() {
return end_tuple();
}
bool begin_sequence(size_t size);
bool end_sequence();
bool begin_tuple(size_t size);
bool begin_associative_array(size_t size) {
return begin_sequence(size);
}
bool end_tuple();
bool end_associative_array() {
return end_sequence();
}
bool value(bool x);
......
......@@ -93,6 +93,14 @@ public:
return true;
}
constexpr bool begin_key_value_pair() {
return true;
}
constexpr bool end_key_value_pair() {
return true;
}
constexpr bool begin_sequence(size_t) {
return true;
}
......@@ -101,6 +109,14 @@ public:
return true;
}
constexpr bool begin_associative_array(size_t) {
return true;
}
constexpr bool end_associative_array() {
return true;
}
template <class Integral>
std::enable_if_t<std::is_integral<Integral>::value, bool>
value(Integral x) noexcept {
......
......@@ -142,22 +142,22 @@ template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::map) {
x.clear();
size_t size = 0;
if (!f.begin_sequence(size))
if (!f.begin_associative_array(size))
return false;
for (size_t i = 0; i < size; ++i) {
auto key = typename T::key_type{};
auto val = typename T::mapped_type{};
if (!(f.begin_tuple(2) //
&& load_value(f, key) //
&& load_value(f, val) //
&& f.end_tuple()))
if (!(f.begin_key_value_pair() //
&& load_value(f, key) //
&& load_value(f, val) //
&& f.end_key_value_pair()))
return false;
if (!x.emplace(std::move(key), std::move(val)).second) {
f.emplace_error(sec::runtime_error, "multiple key definitions");
return false;
}
}
return f.end_sequence();
return f.end_associative_array();
}
template <class Inspector, class T>
......@@ -310,17 +310,16 @@ bool save_value(Inspector& f, T& x, inspector_access_type::tuple) {
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::map) {
auto size = x.size();
if (!f.begin_sequence(size))
if (!f.begin_associative_array(x.size()))
return false;
for (auto&& kvp : x) {
if (!(f.begin_tuple(2) //
if (!(f.begin_key_value_pair() //
&& save_value(f, kvp.first) //
&& save_value(f, kvp.second) //
&& f.end_tuple()))
&& f.end_key_value_pair()))
return false;
}
return f.end_sequence();
return f.end_associative_array();
}
template <class Inspector, class T>
......
......@@ -93,12 +93,29 @@ public:
/// Ends processing of a tuple.
virtual bool end_tuple() = 0;
/// Begins processing of a tuple with two elements, whereas the first element
/// represents the key in an associative array.
/// @note the default implementation calls `begin_tuple(2)`.
virtual bool begin_key_value_pair();
/// Ends processing of a key-value pair after both values were written.
/// @note the default implementation calls `end_tuple()`.
virtual bool end_key_value_pair();
/// Begins processing of a sequence. Saves the size to the underlying storage.
virtual bool begin_sequence(size_t size) = 0;
/// Ends processing of a sequence.
virtual bool end_sequence() = 0;
/// Begins processing of an associative array (map).
/// @note the default implementation calls `begin_sequence(size)`.
virtual bool begin_associative_array(size_t size);
/// Ends processing of an associative array (map).
/// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array();
/// Adds the primitive type `x` to the output.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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/config_value.hpp"
#include "caf/serializer.hpp"
#include "caf/settings.hpp"
#include <stack>
#include <vector>
namespace caf {
/// Writes objects into @ref settings.
class settings_writer final : public serializer {
public:
// -- member types------------------------------------------------------------
using super = serializer;
struct present_field {
settings* parent;
string_view name;
string_view type;
};
struct absent_field {};
using value_type
= variant<settings*, absent_field, present_field, config_value::list*>;
using stack_type = std::stack<value_type, std::vector<value_type>>;
// -- constructors, destructors, and assignment operators --------------------
settings_writer(settings* destination, actor_system& sys)
: super(sys), root_(destination) {
st_.push(destination);
has_human_readable_format_ = true;
}
settings_writer(settings* destination, execution_unit* ctx)
: super(ctx), root_(destination) {
has_human_readable_format_ = true;
}
explicit settings_writer(settings* destination)
: settings_writer(destination, nullptr) {
// nop
}
~settings_writer() override;
// -- interface functions ----------------------------------------------------
bool inject_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(string_view x) override;
bool value(const std::u16string& x) override;
bool value(const std::u32string& x) override;
bool value(span<const byte> x) override;
private:
bool push(config_value&& x);
stack_type st_;
string_view type_hint_;
settings* root_;
};
} // namespace caf
......@@ -35,6 +35,22 @@ deserializer::~deserializer() {
// nop
}
bool deserializer::begin_key_value_pair() {
return begin_tuple(2);
}
bool deserializer::end_key_value_pair() {
return end_tuple();
}
bool deserializer::begin_associative_array(size_t& size) {
return begin_sequence(size);
}
bool deserializer::end_associative_array() {
return end_sequence();
}
bool deserializer::value(std::vector<bool>& x) {
x.clear();
size_t size = 0;
......
......@@ -104,14 +104,6 @@ bool stringification_inspector::end_sequence() {
return ok;
}
bool stringification_inspector::begin_tuple(size_t size) {
return begin_sequence(size);
}
bool stringification_inspector::end_tuple() {
return end_sequence();
}
bool stringification_inspector::value(bool x) {
sep();
result_ += x ? "true" : "false";
......
......@@ -35,6 +35,22 @@ serializer::~serializer() {
// nop
}
bool serializer::begin_key_value_pair() {
return begin_tuple(2);
}
bool serializer::end_key_value_pair() {
return end_tuple();
}
bool serializer::begin_associative_array(size_t size) {
return begin_sequence(size);
}
bool serializer::end_associative_array() {
return end_sequence();
}
bool serializer::value(const std::vector<bool>& xs) {
if (!begin_sequence(xs.size()))
return false;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_writer.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/overload.hpp"
#define CHECK_NOT_EMPTY() \
do { \
if (st_.empty()) { \
emplace_error(sec::runtime_error, "mismatching calls to begin/end"); \
return false; \
} \
} while (false)
#define CHECK_VALID() \
do { \
CHECK_NOT_EMPTY(); \
if (holds_alternative<none_t>(st_.top())) { \
emplace_error(sec::runtime_error, \
"attempted to write to a non-existent optional field"); \
return false; \
} \
} while (false)
#define SCOPE(top_type) \
CHECK_VALID(); \
if (!holds_alternative<top_type>(st_.top())) { \
if constexpr (std::is_same<top_type, settings>::value) { \
emplace_error(sec::runtime_error, \
"attempted to add list items before calling " \
"begin_sequence or begin_tuple"); \
} else { \
emplace_error(sec::runtime_error, \
"attempted to add fields to a list item"); \
} \
return false; \
} \
[[maybe_unused]] auto& top = get<top_type>(st_.top());
namespace caf {
// -- constructors, destructors, and assignment operators ----------------------
settings_writer::~settings_writer() {
// nop
}
// -- interface functions ------------------------------------------------------
bool settings_writer::inject_next_object_type(type_id_t type) {
CHECK_NOT_EMPTY();
type_hint_ = query_type_name(type);
if (type_hint_.empty()) {
emplace_error(sec::runtime_error,
"query_type_name returned an empty string for type ID");
return false;
}
return true;
}
bool settings_writer::begin_object(string_view) {
if (st_.empty()) {
if (root_ == nullptr) {
emplace_error(sec::runtime_error,
"tried to serialize multiple objects into the root object");
return false;
}
st_.push(root_);
root_ = nullptr;
} else {
auto f = detail::make_overload(
[this](settings*) {
emplace_error(sec::runtime_error,
"begin_object called inside another object");
return false;
},
[this](absent_field) {
emplace_error(sec::runtime_error,
"begin_object called inside non-existent optional field");
return false;
},
[this](present_field fld) {
auto [iter, added] = fld.parent->emplace(fld.name, settings{});
if (!added) {
emplace_error(sec::runtime_error,
"field already defined: " + to_string(fld.name));
return false;
}
auto obj = std::addressof(get<settings>(iter->second));
if (!fld.type.empty())
put(*obj, "@type", fld.type);
st_.push(obj);
return true;
},
[this](config_value::list* ls) {
ls->emplace_back(settings{});
st_.push(std::addressof(get<settings>(ls->back())));
return true;
});
if (!visit(f, st_.top()))
return false;
}
if (!type_hint_.empty()) {
put(*get<settings*>(st_.top()), "@type", type_hint_);
type_hint_ = string_view{};
}
return true;
}
bool settings_writer::end_object() {
SCOPE(settings*);
st_.pop();
return true;
}
bool settings_writer::begin_field(string_view name) {
SCOPE(settings*);
st_.push(present_field{top, name, string_view{}});
return true;
}
bool settings_writer::begin_field(string_view name, bool is_present) {
SCOPE(settings*);
if (is_present)
st_.push(present_field{top, name, string_view{}});
else
st_.push(absent_field{});
return true;
}
bool settings_writer::begin_field(string_view name, span<const type_id_t> types,
size_t index) {
SCOPE(settings*);
if (index >= types.size()) {
emplace_error(sec::invalid_argument,
"index out of range in optional variant field "
+ to_string(name));
return false;
}
auto tn = query_type_name(types[index]);
if (tn.empty()) {
emplace_error(sec::runtime_error,
"query_type_name returned an empty string for type ID");
return false;
}
st_.push(present_field{top, name, tn});
return true;
}
bool settings_writer::begin_field(string_view name, bool is_present,
span<const type_id_t> types, size_t index) {
if (is_present)
return begin_field(name, types, index);
else
return begin_field(name, false);
}
bool settings_writer::end_field() {
CHECK_NOT_EMPTY();
if (!holds_alternative<present_field>(st_.top())
&& !holds_alternative<absent_field>(st_.top())) {
emplace_error(sec::runtime_error, "end_field called outside of a field");
return false;
}
st_.pop();
return true;
}
bool settings_writer::begin_tuple(size_t size) {
return begin_sequence(size);
}
bool settings_writer::end_tuple() {
return end_sequence();
}
bool settings_writer::begin_key_value_pair() {
SCOPE(settings*);
auto [iter, added] = top->emplace("@tmp", config_value::list{});
if (!added) {
emplace_error(sec::runtime_error, "temporary entry @tmp already exists");
return false;
}
st_.push(std::addressof(get<config_value::list>(iter->second)));
return true;
}
bool settings_writer::end_key_value_pair() {
config_value::list tmp;
/* lifetime scope of the list */ {
SCOPE(config_value::list*);
if (top->size() != 2) {
emplace_error(sec::runtime_error,
"a key-value pair must have exactly two elements");
return false;
}
tmp = std::move(*top);
st_.pop();
}
SCOPE(settings*);
// Get key and value from the temporary list.
top->container().erase("@tmp");
std::string key;
if (auto str = get_if<std::string>(std::addressof(tmp[0])))
key = std::move(*str);
else
key = to_string(tmp[0]);
if (!top->emplace(std::move(key), std::move(tmp[1])).second) {
emplace_error(sec::runtime_error, "multiple definitions for key");
return false;
}
return true;
}
bool settings_writer::begin_sequence(size_t) {
CHECK_NOT_EMPTY();
auto f = detail::make_overload(
[this](settings*) {
emplace_error(sec::runtime_error,
"cannot start sequence/tuple inside an object");
return false;
},
[this](absent_field) {
emplace_error(
sec::runtime_error,
"cannot start sequence/tuple inside non-existent optional field");
return false;
},
[this](present_field fld) {
auto [iter, added] = fld.parent->emplace(fld.name, config_value::list{});
if (!added) {
emplace_error(sec::runtime_error,
"field already defined: " + to_string(fld.name));
return false;
}
st_.push(std::addressof(get<config_value::list>(iter->second)));
return true;
},
[this](config_value::list* ls) {
ls->emplace_back(config_value::list{});
st_.push(std::addressof(get<config_value::list>(ls->back())));
return true;
});
return visit(f, st_.top());
}
bool settings_writer::end_sequence() {
SCOPE(config_value::list*);
st_.pop();
return true;
}
bool settings_writer::begin_associative_array(size_t) {
CHECK_NOT_EMPTY();
settings* inner = nullptr;
auto f = detail::make_overload(
[this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields");
return false;
},
[this](absent_field) {
emplace_error(sec::runtime_error,
"cannot add values to non-existent optional field");
return false;
},
[this, &inner](present_field fld) {
auto [iter, added] = fld.parent->emplace(fld.name,
config_value{settings{}});
if (!added) {
emplace_error(sec::runtime_error,
"field already defined: " + to_string(fld.name));
return false;
}
if (!fld.type.empty()) {
std::string key;
key += '@';
key.insert(key.end(), fld.name.begin(), fld.name.end());
key += "-type";
if (fld.parent->contains(key)) {
emplace_error(sec::runtime_error,
"type of variant field already defined.");
return false;
}
put(*fld.parent, key, fld.type);
}
inner = std::addressof(get<settings>(iter->second));
return true;
},
[&inner](config_value::list* ls) {
ls->emplace_back(config_value{settings{}});
inner = std::addressof(get<settings>(ls->back()));
return true;
});
if (visit(f, st_.top())) {
CAF_ASSERT(inner != nullptr);
st_.push(inner);
return true;
}
return false;
}
bool settings_writer::end_associative_array() {
SCOPE(settings*);
st_.pop();
return true;
}
bool settings_writer::value(bool x) {
return push(config_value{x});
}
bool settings_writer::value(int8_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(uint8_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(int16_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(uint16_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(int32_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(uint32_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(int64_t x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(uint64_t x) {
auto max_val = std::numeric_limits<config_value::integer>::max();
if (x > static_cast<uint64_t>(max_val)) {
emplace_error(sec::runtime_error, "integer overflow");
return false;
}
return push(config_value{static_cast<config_value::integer>(x)});
}
bool settings_writer::value(float x) {
return push(config_value{double{x}});
}
bool settings_writer::value(double x) {
return push(config_value{x});
}
bool settings_writer::value(long double x) {
return push(config_value{std::to_string(x)});
}
bool settings_writer::value(string_view x) {
return push(config_value{to_string(x)});
}
bool settings_writer::value(const std::u16string&) {
emplace_error(sec::runtime_error, "u16string support not implemented yet");
return false;
}
bool settings_writer::value(const std::u32string&) {
emplace_error(sec::runtime_error, "u32string support not implemented yet");
return false;
}
bool settings_writer::value(span<const byte> x) {
std::string str;
detail::append_hex(str, x.data(), x.size());
return push(config_value{std::move(str)});
}
bool settings_writer::push(config_value&& x) {
CHECK_NOT_EMPTY();
auto f = detail::make_overload(
[this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields");
return false;
},
[this](absent_field) {
emplace_error(sec::runtime_error,
"cannot add values to non-existent optional field");
return false;
},
[this, &x](present_field fld) {
auto [iter, added] = fld.parent->emplace(fld.name, std::move(x));
if (!added) {
emplace_error(sec::runtime_error,
"field already defined: " + to_string(fld.name));
return false;
}
if (!fld.type.empty()) {
std::string key;
key += '@';
key.insert(key.end(), fld.name.begin(), fld.name.end());
key += "-type";
if (fld.parent->contains(key)) {
emplace_error(sec::runtime_error,
"type of variant field already defined.");
return false;
}
put(*fld.parent, key, fld.type);
}
return true;
},
[&x](config_value::list* ls) {
ls->emplace_back(std::move(x));
return true;
});
return visit(f, st_.top());
}
} // 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. *
******************************************************************************/
#include "caf/type_id.hpp"
#include "caf/detail/meta_object.hpp"
namespace caf {
string_view query_type_name(type_id_t type) {
if (auto ptr = detail::global_meta_object(type))
return ptr->type_name;
return {};
}
type_id_t query_type_id(string_view name) {
auto objects = detail::global_meta_objects();
for (size_t index = 0; index < objects.size(); ++index)
if (objects[index].type_name == name)
return static_cast<type_id_t>(index);
return invalid_type_id;
}
} // namespace caf
// This header includes various types for testing save and load inspectors.
#pragma once
#include "caf/optional.hpp"
#include "caf/type_id.hpp"
#include "caf/variant.hpp"
#include "nasty.hpp"
namespace {
struct point_3d;
struct line;
struct duration;
struct person;
class foobar;
struct dummy_message;
struct fallback_dummy_message;
struct basics;
} // namespace
#define CAF_TEST_SET_TYPE_NAME(type) \
namespace caf { \
template <> \
struct type_name<type> { \
static constexpr string_view value = #type; \
}; \
}
CAF_TEST_SET_TYPE_NAME(point_3d)
CAF_TEST_SET_TYPE_NAME(line)
CAF_TEST_SET_TYPE_NAME(duration)
CAF_TEST_SET_TYPE_NAME(person)
CAF_TEST_SET_TYPE_NAME(foobar)
CAF_TEST_SET_TYPE_NAME(dummy_message)
CAF_TEST_SET_TYPE_NAME(fallback_dummy_message)
CAF_TEST_SET_TYPE_NAME(basics)
CAF_TEST_SET_TYPE_NAME(nasty)
namespace {
struct point_3d {
int32_t x;
int32_t y;
int32_t 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),
f.field("z", x.z));
}
struct line {
point_3d p1;
point_3d 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));
}
struct duration {
std::string unit;
double count;
};
[[maybe_unused]] bool valid_time_unit(const std::string& unit) {
return unit == "seconds" || unit == "minutes";
}
template <class Inspector>
bool inspect(Inspector& f, duration& x) {
return f.object(x).fields(
f.field("unit", x.unit).fallback("seconds").invariant(valid_time_unit),
f.field("count", x.count));
}
struct person {
std::string name;
caf::optional<std::string> phone;
};
template <class Inspector>
bool inspect(Inspector& f, person& x) {
return f.object(x).fields(f.field("name", x.name), f.field("phone", x.phone));
}
class foobar {
public:
foobar() = default;
foobar(const foobar&) = default;
foobar& operator=(const foobar&) = default;
foobar(std::string foo, std::string bar)
: foo_(std::move(foo)), bar_(std::move(bar)) {
// nop
}
const std::string& foo() {
return foo_;
}
void foo(std::string value) {
foo_ = std::move(value);
}
const std::string& bar() {
return bar_;
}
void bar(std::string value) {
bar_ = std::move(value);
}
private:
std::string foo_;
std::string bar_;
};
template <class Inspector>
bool inspect(Inspector& f, foobar& x) {
auto get_foo = [&x]() -> decltype(auto) { return x.foo(); };
auto set_foo = [&x](std::string value) {
x.foo(std::move(value));
return true;
};
auto get_bar = [&x]() -> decltype(auto) { return x.bar(); };
auto set_bar = [&x](std::string value) {
x.bar(std::move(value));
return true;
};
return f.object(x).fields(f.field("foo", get_foo, set_foo),
f.field("bar", get_bar, set_bar));
}
struct dummy_message {
caf::variant<std::string, double> content;
};
template <class Inspector>
bool inspect(Inspector& f, dummy_message& x) {
return f.object(x).fields(f.field("content", x.content));
}
struct fallback_dummy_message {
caf::variant<std::string, double> content;
};
template <class Inspector>
bool inspect(Inspector& f, fallback_dummy_message& x) {
return f.object(x).fields(f.field("content", x.content).fallback(42.0));
}
struct basics {
struct tag {};
tag v1;
int32_t v2;
int32_t v3[4];
dummy_message v4[2];
std::array<int32_t, 2> v5;
std::tuple<int32_t, dummy_message> v6;
std::map<std::string, int32_t> v7;
std::vector<std::list<std::pair<std::string, std::array<int32_t, 3>>>> v8;
};
template <class Inspector>
bool inspect(Inspector& f, basics& x) {
return f.object(x).fields(f.field("v1", x.v1), f.field("v2", x.v2),
f.field("v3", x.v3), f.field("v4", x.v4),
f.field("v5", x.v5), f.field("v6", x.v6),
f.field("v7", x.v7), f.field("v8", x.v8));
}
} // namespace
......@@ -33,153 +33,13 @@
#include "caf/type_id.hpp"
#include "caf/variant.hpp"
#include "nasty.hpp"
#include "inspector-tests.hpp"
using namespace caf;
namespace {
using string_list = std::vector<std::string>;
struct point_3d {
static inline string_view tname = "point_3d";
int32_t x;
int32_t y;
int32_t 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),
f.field("z", x.z));
}
struct line {
static inline string_view tname = "line";
point_3d p1;
point_3d 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));
}
struct duration {
static inline string_view tname = "duration";
std::string unit;
double count;
};
bool valid_time_unit(const std::string& unit) {
return unit == "seconds" || unit == "minutes";
}
template <class Inspector>
bool inspect(Inspector& f, duration& x) {
return f.object(x).fields(
f.field("unit", x.unit).fallback("seconds").invariant(valid_time_unit),
f.field("count", x.count));
}
struct person {
static inline string_view tname = "person";
std::string name;
optional<std::string> phone;
};
template <class Inspector>
bool inspect(Inspector& f, person& x) {
return f.object(x).fields(f.field("name", x.name), f.field("phone", x.phone));
}
class foobar {
public:
static inline string_view tname = "foobar";
const std::string& foo() {
return foo_;
}
void foo(std::string value) {
foo_ = std::move(value);
}
const std::string& bar() {
return bar_;
}
void bar(std::string value) {
bar_ = std::move(value);
}
private:
std::string foo_;
std::string bar_;
};
template <class Inspector>
bool inspect(Inspector& f, foobar& x) {
auto get_foo = [&x]() -> decltype(auto) { return x.foo(); };
auto set_foo = [&x](std::string value) {
x.foo(std::move(value));
return true;
};
auto get_bar = [&x]() -> decltype(auto) { return x.bar(); };
auto set_bar = [&x](std::string value) {
x.bar(std::move(value));
return true;
};
return f.object(x).fields(f.field("foo", get_foo, set_foo),
f.field("bar", get_bar, set_bar));
}
struct dummy_message {
static inline string_view tname = "dummy_message";
variant<std::string, double> content;
};
template <class Inspector>
bool inspect(Inspector& f, dummy_message& x) {
return f.object(x).fields(f.field("content", x.content));
}
struct fallback_dummy_message {
static inline string_view tname = "fallback_dummy_message";
variant<std::string, double> content;
};
template <class Inspector>
bool inspect(Inspector& f, fallback_dummy_message& x) {
return f.object(x).fields(f.field("content", x.content).fallback(42.0));
}
struct basics {
static inline string_view tname = "basics";
struct tag {
static inline string_view tname = "tag";
};
tag v1;
int32_t v2;
int32_t v3[4];
dummy_message v4[2];
std::array<int32_t, 2> v5;
std::tuple<int32_t, dummy_message> v6;
std::map<std::string, int32_t> v7;
std::vector<std::list<std::pair<std::string, std::array<int32_t, 3>>>> v8;
};
template <class Inspector>
bool inspect(Inspector& f, basics& x) {
return f.object(x).fields(f.field("v1", x.v1), f.field("v2", x.v2),
f.field("v3", x.v3), f.field("v4", x.v4),
f.field("v5", x.v5), f.field("v6", x.v6),
f.field("v7", x.v7), f.field("v8", x.v8));
}
struct testee : load_inspector_base<testee> {
struct testee : deserializer {
std::string log;
bool load_field_failed(string_view, sec code) {
......@@ -194,9 +54,10 @@ struct testee : load_inspector_base<testee> {
log.insert(log.end(), indent, ' ');
}
template <class T>
auto object(T&) {
return object_t<testee>{T::tname, this};
using deserializer::object;
bool fetch_next_object_type(type_id_t&) override {
return false;
}
template <class T>
......@@ -204,7 +65,12 @@ struct testee : load_inspector_base<testee> {
return object_t<testee>{"optional", this};
}
bool begin_object(string_view object_name) {
template <class... Ts>
auto object(variant<Ts...>&) {
return object_t<testee>{"variant", this};
}
bool begin_object(string_view object_name) override {
new_line();
indent += 2;
log += "begin object ";
......@@ -212,14 +78,14 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool end_object() {
bool end_object() override {
indent -= 2;
new_line();
log += "end object";
return ok;
}
bool begin_field(string_view name) {
bool begin_field(string_view name) override {
new_line();
indent += 2;
log += "begin field ";
......@@ -227,7 +93,7 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool begin_field(string_view name, bool& is_present) {
bool begin_field(string_view name, bool& is_present) override {
new_line();
indent += 2;
log += "begin optional field ";
......@@ -237,7 +103,7 @@ struct testee : load_inspector_base<testee> {
}
bool begin_field(string_view name, span<const type_id_t>,
size_t& type_index) {
size_t& type_index) override {
new_line();
indent += 2;
log += "begin variant field ";
......@@ -247,7 +113,7 @@ struct testee : load_inspector_base<testee> {
}
bool begin_field(string_view name, bool& is_present, span<const type_id_t>,
size_t&) {
size_t&) override {
new_line();
indent += 2;
log += "begin optional variant field ";
......@@ -256,14 +122,14 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool end_field() {
bool end_field() override {
indent -= 2;
new_line();
log += "end field";
return ok;
}
bool begin_tuple(size_t size) {
bool begin_tuple(size_t size) override {
new_line();
indent += 2;
log += "begin tuple of size ";
......@@ -271,14 +137,28 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool end_tuple() {
bool end_tuple() override {
indent -= 2;
new_line();
log += "end tuple";
return ok;
}
bool begin_sequence(size_t& size) {
bool begin_key_value_pair() override {
new_line();
indent += 2;
log += "begin key-value pair";
return ok;
}
bool end_key_value_pair() override {
indent -= 2;
new_line();
log += "end key-value pair";
return ok;
}
bool begin_sequence(size_t& size) override {
size = 0;
new_line();
indent += 2;
......@@ -287,15 +167,38 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool end_sequence() {
bool end_sequence() override {
indent -= 2;
new_line();
log += "end sequence";
return ok;
}
bool begin_associative_array(size_t& size) override {
size = 0;
new_line();
indent += 2;
log += "begin associative array of size ";
log += std::to_string(size);
return ok;
}
bool end_associative_array() override {
indent -= 2;
new_line();
log += "end associative array";
return ok;
}
bool value(bool& x) override {
new_line();
log += "bool value";
x = false;
return ok;
}
template <class T>
std::enable_if_t<std::is_arithmetic<T>::value, bool> value(T& x) {
bool primitive_value(T& x) {
new_line();
auto tn = type_name_v<T>;
log.insert(log.end(), tn.begin(), tn.end());
......@@ -304,10 +207,67 @@ struct testee : load_inspector_base<testee> {
return ok;
}
bool value(std::string& x) {
bool value(int8_t& x) override {
return primitive_value(x);
}
bool value(uint8_t& x) override {
return primitive_value(x);
}
bool value(int16_t& x) override {
return primitive_value(x);
}
bool value(uint16_t& x) override {
return primitive_value(x);
}
bool value(int32_t& x) override {
return primitive_value(x);
}
bool value(uint32_t& x) override {
return primitive_value(x);
}
bool value(int64_t& x) override {
return primitive_value(x);
}
bool value(uint64_t& x) override {
return primitive_value(x);
}
bool value(float& x) override {
return primitive_value(x);
}
bool value(double& x) override {
return primitive_value(x);
}
bool value(long double& x) override {
return primitive_value(x);
}
bool value(std::string& x) override {
return primitive_value(x);
}
bool value(std::u16string& x) override {
return primitive_value(x);
}
bool value(std::u32string& x) override {
return primitive_value(x);
}
bool value(span<byte> xs) override {
new_line();
log += "std::string value";
x.clear();
log += "caf::span<caf::byte> value";
for (auto& x : xs)
x = byte{0};
return ok;
}
};
......@@ -566,7 +526,7 @@ CAF_TEST(load inspectors support all basic STL types) {
CAF_CHECK_EQUAL(f.log, R"_(
begin object basics
begin field v1
begin object tag
begin object anonymous
end object
end field
begin field v2
......@@ -611,8 +571,8 @@ begin object basics
end tuple
end field
begin field v7
begin sequence of size 0
end sequence
begin associative array of size 0
end associative array
end field
begin field v8
begin sequence of size 0
......
......@@ -29,159 +29,12 @@
#include "caf/message.hpp"
#include "caf/serializer.hpp"
#include "nasty.hpp"
namespace {
struct point_3d;
struct line;
struct duration;
struct person;
class foobar;
struct dummy_message;
struct basics;
} // namespace
#define CAF_TYPE_NAME(type) \
namespace caf { \
template <> \
struct type_name<type> { \
static constexpr string_view value = #type; \
}; \
}
CAF_TYPE_NAME(point_3d)
CAF_TYPE_NAME(line)
CAF_TYPE_NAME(duration)
CAF_TYPE_NAME(person)
CAF_TYPE_NAME(foobar)
CAF_TYPE_NAME(dummy_message)
CAF_TYPE_NAME(basics)
CAF_TYPE_NAME(nasty)
#include "inspector-tests.hpp"
using namespace caf;
namespace {
using string_list = std::vector<std::string>;
struct point_3d {
int32_t x;
int32_t y;
int32_t 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),
f.field("z", x.z));
}
struct line {
point_3d p1;
point_3d 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));
}
struct duration {
std::string unit;
double count;
};
bool valid_time_unit(const std::string& unit) {
return unit == "seconds" || unit == "minutes";
}
template <class Inspector>
bool inspect(Inspector& f, duration& x) {
return f.object(x).fields(
f.field("unit", x.unit).fallback("seconds").invariant(valid_time_unit),
f.field("count", x.count));
}
struct person {
std::string name;
optional<std::string> phone;
};
template <class Inspector>
bool inspect(Inspector& f, person& x) {
return f.object(x).fields(f.field("name", x.name), f.field("phone", x.phone));
}
class foobar {
public:
const std::string& foo() {
return foo_;
}
void foo(std::string value) {
foo_ = std::move(value);
}
const std::string& bar() {
return bar_;
}
void bar(std::string value) {
bar_ = std::move(value);
}
private:
std::string foo_;
std::string bar_;
};
template <class Inspector>
bool inspect(Inspector& f, foobar& x) {
auto get_foo = [&x]() -> decltype(auto) { return x.foo(); };
auto set_foo = [&x](std::string value) {
x.foo(std::move(value));
return true;
};
auto get_bar = [&x]() -> decltype(auto) { return x.bar(); };
auto set_bar = [&x](std::string value) {
x.bar(std::move(value));
return true;
};
return f.object(x).fields(f.field("foo", get_foo, set_foo),
f.field("bar", get_bar, set_bar));
}
struct dummy_message {
variant<std::string, double> content;
};
template <class Inspector>
bool inspect(Inspector& f, dummy_message& x) {
return f.object(x).fields(f.field("content", x.content));
}
struct basics {
struct tag {};
tag v1;
int32_t v2;
int32_t v3[4];
dummy_message v4[2];
std::array<int32_t, 2> v5;
std::tuple<int32_t, dummy_message> v6;
std::map<std::string, int32_t> v7;
std::vector<std::list<std::pair<std::string, std::array<int32_t, 3>>>> v8;
};
template <class Inspector>
bool inspect(Inspector& f, basics& x) {
return f.object(x).fields(f.field("v1", x.v1), f.field("v2", x.v2),
f.field("v3", x.v3), f.field("v4", x.v4),
f.field("v5", x.v5), f.field("v6", x.v6),
f.field("v7", x.v7), f.field("v8", x.v8));
}
struct testee : serializer {
std::string log;
......@@ -292,6 +145,22 @@ struct testee : serializer {
return ok;
}
bool begin_key_value_pair() override {
new_line();
indent += 2;
log += "begin key-value pair";
return ok;
}
bool end_key_value_pair() override {
if (indent < 2)
CAF_FAIL("begin/end mismatch");
indent -= 2;
new_line();
log += "end key-value pair";
return ok;
}
bool begin_sequence(size_t size) override {
new_line();
indent += 2;
......@@ -309,6 +178,23 @@ struct testee : serializer {
return ok;
}
bool begin_associative_array(size_t size) override {
new_line();
indent += 2;
log += "begin associative array of size ";
log += std::to_string(size);
return ok;
}
bool end_associative_array() override {
if (indent < 2)
CAF_FAIL("begin/end mismatch");
indent -= 2;
new_line();
log += "end associative array";
return ok;
}
bool value(bool) override {
new_line();
log += "bool value";
......@@ -778,20 +664,20 @@ begin object basics
end tuple
end field
begin field v7
begin sequence of size 3
begin tuple of size 2
begin associative array of size 3
begin key-value pair
std::string value
int32_t value
end tuple
begin tuple of size 2
end key-value pair
begin key-value pair
std::string value
int32_t value
end tuple
begin tuple of size 2
end key-value pair
begin key-value pair
std::string value
int32_t value
end tuple
end sequence
end key-value pair
end associative array
end field
begin field v8
begin sequence of size 2
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_writer
#include "caf/settings_writer.hpp"
#include "caf/test/dsl.hpp"
#include "inspector-tests.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 set(const T& value) {
settings_writer writer{&xs};
if (!inspect_object(writer, value))
CAF_FAIL("failed two write to settings: " << writer.get_error());
}
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_writer_tests, fixture)
CAF_TEST(structs become dictionaries) {
set(foobar{"hello", "world"});
CAF_CHECK_EQUAL(get<std::string>("foo"), "hello"s);
CAF_CHECK_EQUAL(get<std::string>("bar"), "world"s);
}
CAF_TEST(nested structs become nested dictionaries) {
set(line{{10, 20, 30}, {70, 60, 50}});
CAF_CHECK_EQUAL(get<i64>("p1.x"), 10_i64);
CAF_CHECK_EQUAL(get<i64>("p1.y"), 20_i64);
CAF_CHECK_EQUAL(get<i64>("p1.z"), 30_i64);
CAF_CHECK_EQUAL(get<i64>("p2.x"), 70_i64);
CAF_CHECK_EQUAL(get<i64>("p2.y"), 60_i64);
CAF_CHECK_EQUAL(get<i64>("p2.z"), 50_i64);
}
CAF_TEST(empty types and maps become dictionaries) {
basics tst;
tst.v2 = 42;
for (size_t index = 0; index < 4; ++index)
tst.v3[index] = -static_cast<int32_t>(index + 1);
for (size_t index = 0; index < 2; ++index)
tst.v4[index] = dummy_message{{static_cast<double>(index)}};
for (size_t index = 0; index < 2; ++index)
tst.v5[index] = static_cast<int32_t>(index + 1) * 10;
tst.v6 = std::make_tuple(42, dummy_message{{"foobar"s}});
tst.v7["one"] = 1;
tst.v7["two"] = 2;
tst.v7["three"] = 3;
set(tst);
CAF_MESSAGE(xs);
CAF_CHECK_EQUAL(get<settings>("v1"), settings{});
CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64);
CAF_CHECK_EQUAL(get<i64_list>("v3"), i64_list({-1, -2, -3, -4}));
if (auto v4 = get<config_value::list>("v4");
CAF_CHECK_EQUAL(v4->size(), 2u)) {
if (auto v1 = v4->front(); CAF_CHECK(holds_alternative<settings>(v1))) {
auto& v1_xs = caf::get<settings>(v1);
CAF_CHECK_EQUAL(get<double>(v1_xs, "content"), 0.0);
CAF_CHECK_EQUAL(get<std::string>(v1_xs, "@content-type"),
to_string(type_name_v<double>));
}
if (auto v2 = v4->back(); CAF_CHECK(holds_alternative<settings>(v2))) {
auto& v2_xs = caf::get<settings>(v2);
CAF_CHECK_EQUAL(get<double>(v2_xs, "content"), 1.0);
CAF_CHECK_EQUAL(get<std::string>(v2_xs, "@content-type"),
to_string(type_name_v<double>));
}
}
CAF_CHECK_EQUAL(get<i64_list>("v5"), i64_list({10, 20}));
// TODO: check v6
CAF_CHECK_EQUAL(get<i64>("v7.one"), 1_i64);
CAF_CHECK_EQUAL(get<i64>("v7.two"), 2_i64);
CAF_CHECK_EQUAL(get<i64>("v7.three"), 3_i64);
CAF_CHECK_EQUAL(get<config_value::list>("v8"), config_value::list());
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -546,26 +546,29 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
} while (false)
#define CAF_CHECK(...) \
do { \
static_cast<void>(::caf::test::detail::check( \
::caf::test::engine::current_test(), __FILE__, __LINE__, #__VA_ARGS__, \
false, static_cast<bool>(__VA_ARGS__))); \
([&] { \
auto caf_check_res \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \
__FILE__, __LINE__, #__VA_ARGS__, false, \
static_cast<bool>(__VA_ARGS__)); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
return caf_check_res; \
})()
#define CAF_CHECK_FUNC(func, x_expr, y_expr) \
do { \
([&] { \
func comparator; \
auto&& x_val___ = x_expr; \
auto&& y_val___ = y_expr; \
static_cast<void>(::caf::test::detail::check( \
auto caf_check_res = ::caf::test::detail::check( \
::caf::test::engine::current_test(), __FILE__, __LINE__, \
CAF_FUNC_EXPR(func, x_expr, y_expr), false, \
comparator(x_val___, y_val___), x_val___, y_val___)); \
comparator(x_val___, y_val___), x_val___, y_val___); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
return caf_check_res; \
})()
#define CAF_CHECK_FAIL(...) \
do { \
......
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