Commit c6e7fdb6 authored by Dominik Charousset's avatar Dominik Charousset

Merge 'topic/inspector-api'

parents 97fea3fd e26b322a
...@@ -68,6 +68,8 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -68,6 +68,8 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/config_option_adder.cpp src/config_option_adder.cpp
src/config_option_set.cpp src/config_option_set.cpp
src/config_value.cpp src/config_value.cpp
src/config_value_reader.cpp
src/config_value_writer.cpp
src/credit_controller.cpp src/credit_controller.cpp
src/decorator/sequencer.cpp src/decorator/sequencer.cpp
src/default_attachable.cpp src/default_attachable.cpp
...@@ -160,7 +162,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -160,7 +162,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/sec_strings.cpp src/sec_strings.cpp
src/serializer.cpp src/serializer.cpp
src/settings.cpp src/settings.cpp
src/settings_writer.cpp
src/skip.cpp src/skip.cpp
src/stream_aborter.cpp src/stream_aborter.cpp
src/stream_manager.cpp src/stream_manager.cpp
...@@ -232,7 +233,8 @@ caf_add_test_suites(caf-core-test ...@@ -232,7 +233,8 @@ caf_add_test_suites(caf-core-test
config_option config_option
config_option_set config_option_set
config_value config_value
config_value_adaptor config_value_reader
config_value_writer
const_typed_message_view const_typed_message_view
constructor_attach constructor_attach
continuous_streaming continuous_streaming
...@@ -288,7 +290,6 @@ caf_add_test_suites(caf-core-test ...@@ -288,7 +290,6 @@ caf_add_test_suites(caf-core-test
local_group local_group
logger logger
mailbox_element mailbox_element
make_config_value_field
message message
message_builder message_builder
message_id message_id
...@@ -312,7 +313,6 @@ caf_add_test_suites(caf-core-test ...@@ -312,7 +313,6 @@ caf_add_test_suites(caf-core-test
serial_reply serial_reply
serialization serialization
settings settings
settings_writer
simple_timeout simple_timeout
span span
stateful_actor stateful_actor
......
...@@ -48,16 +48,10 @@ ...@@ -48,16 +48,10 @@
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp" #include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/config_value_adaptor.hpp"
#include "caf/config_value_adaptor_access.hpp"
#include "caf/config_value_adaptor_field.hpp"
#include "caf/config_value_field.hpp"
#include "caf/config_value_object_access.hpp"
#include "caf/const_typed_message_view.hpp" #include "caf/const_typed_message_view.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/defaults.hpp" #include "caf/defaults.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/detail/config_value_adaptor_field_impl.hpp"
#include "caf/downstream_msg.hpp" #include "caf/downstream_msg.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
...@@ -74,7 +68,6 @@ ...@@ -74,7 +68,6 @@
#include "caf/local_actor.hpp" #include "caf/local_actor.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/make_config_option.hpp" #include "caf/make_config_option.hpp"
#include "caf/make_config_value_field.hpp"
#include "caf/may_have_timeout.hpp" #include "caf/may_have_timeout.hpp"
#include "caf/memory_managed.hpp" #include "caf/memory_managed.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include "caf/config_value_reader.hpp"
#include "caf/config_value_writer.hpp"
#include "caf/detail/bounds_checker.hpp" #include "caf/detail/bounds_checker.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/move_if_not_ptr.hpp" #include "caf/detail/move_if_not_ptr.hpp"
...@@ -257,11 +259,9 @@ struct default_config_value_access { ...@@ -257,11 +259,9 @@ struct default_config_value_access {
} }
}; };
struct config_value_access_unspecialized {};
/// @relates config_value /// @relates config_value
template <class T> template <class T>
struct config_value_access : config_value_access_unspecialized {}; struct config_value_access;
#define CAF_DEFAULT_CONFIG_VALUE_ACCESS(type, name) \ #define CAF_DEFAULT_CONFIG_VALUE_ACCESS(type, name) \
template <> \ template <> \
...@@ -279,6 +279,36 @@ CAF_DEFAULT_CONFIG_VALUE_ACCESS(uri, "uri"); ...@@ -279,6 +279,36 @@ CAF_DEFAULT_CONFIG_VALUE_ACCESS(uri, "uri");
#undef CAF_DEFAULT_CONFIG_VALUE_ACCESS #undef CAF_DEFAULT_CONFIG_VALUE_ACCESS
template <>
struct CAF_CORE_EXPORT config_value_access<float> {
static std::string type_name() {
return "real32";
}
static bool is(const config_value& x) {
return holds_alternative<double>(x.get_data());
}
static optional<float> get_if(const config_value* x) {
if (auto res = caf::get_if<double>(&(x->get_data())))
return static_cast<float>(*res);
return none;
}
static float get(const config_value& x) {
return static_cast<float>(caf::get<double>(x.get_data()));
}
static double convert(float x) {
return x;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, float& x, Nested) {
detail::parse(ps, x);
}
};
template <> template <>
struct CAF_CORE_EXPORT config_value_access<std::string> { struct CAF_CORE_EXPORT config_value_access<std::string> {
using super = default_config_value_access<std::string>; using super = default_config_value_access<std::string>;
...@@ -313,380 +343,317 @@ struct CAF_CORE_EXPORT config_value_access<std::string> { ...@@ -313,380 +343,317 @@ struct CAF_CORE_EXPORT config_value_access<std::string> {
} }
}; };
enum class select_config_value_hint { // -- implementation details for get/get_if/holds_alternative ------------------
is_integral,
is_map, namespace detail {
is_list,
is_custom, /// Wraps tag types for static dispatching.
is_missing, /// @relates config_value_access_type
struct config_value_access_type {
/// Flags types that provide a `config_value_access` specialization.
struct specialization {};
/// Flags builtin integral types.
struct integral {};
/// Flags types with `std::tuple`-like API.
struct tuple {};
/// Flags types with `std::map`-like API.
struct map {};
/// Flags types with `std::vector`-like API.
struct list {};
/// Flags types without default access that shall fall back to using the
/// inspection API.
struct inspect {};
}; };
/// @relates config_value_access_type
template <class T> template <class T>
constexpr select_config_value_hint select_config_value_oracle() { constexpr auto config_value_access_token() {
return std::is_integral<T>::value && !std::is_same<T, bool>::value if constexpr (detail::is_complete<config_value_access<T>>)
? select_config_value_hint::is_integral return config_value_access_type::specialization{};
: (detail::is_map_like<T>::value else if constexpr (std::is_integral<T>::value)
? select_config_value_hint::is_map return config_value_access_type::integral{};
: (detail::is_list_like<T>::value else if constexpr (detail::is_stl_tuple_type_v<T>)
? select_config_value_hint::is_list return config_value_access_type::tuple{};
: (!std::is_base_of<config_value_access_unspecialized, else if constexpr (detail::is_map_like<T>::value)
config_value_access<T>>::value return config_value_access_type::map{};
? select_config_value_hint::is_custom else if constexpr (detail::is_list_like<T>::value)
: select_config_value_hint::is_missing))); return config_value_access_type::list{};
else
return config_value_access_type::inspect{};
} }
/// Delegates to config_value_access for all specialized versions. template <class T>
template <class T, struct integral_config_value_access;
select_config_value_hint Hint = select_config_value_oracle<T>()>
struct select_config_value_access {
static_assert(Hint == select_config_value_hint::is_custom,
"no default or specialization for config_value_access found");
using type = config_value_access<T>;
};
template <class T> template <class T>
using select_config_value_access_t = struct tuple_config_value_access;
typename select_config_value_access<T>::type;
template <> template <class T>
struct sum_type_access<config_value> { struct map_config_value_access;
using types = typename config_value::types;
using type0 = typename detail::tl_head<types>::type; template <class T>
struct list_config_value_access;
static constexpr bool specialized = true; template <class T>
struct inspect_config_value_access;
template <class U, int Pos> template <class T, class Token>
static bool is(const config_value& x, sum_type_token<U, Pos> token) { struct config_value_access_oracle;
return x.get_data().is(pos(token));
}
template <class U> template <class T>
static bool is(const config_value& x, sum_type_token<U, -1>) { struct config_value_access_oracle<T, config_value_access_type::specialization> {
return select_config_value_access_t<U>::is(x); using type = config_value_access<T>;
} };
template <class U, int Pos> #define CAF_CONFIG_VALUE_ACCESS_ORACLE(access_type) \
static U& get(config_value& x, sum_type_token<U, Pos> token) { template <class T> \
return x.get_data().get(pos(token)); struct config_value_access_oracle<T, \
config_value_access_type::access_type> { \
using type = access_type##_config_value_access<T>; \
} }
template <class U> CAF_CONFIG_VALUE_ACCESS_ORACLE(integral);
static U get(config_value& x, sum_type_token<U, -1>) { CAF_CONFIG_VALUE_ACCESS_ORACLE(tuple);
return select_config_value_access_t<U>::get(x); CAF_CONFIG_VALUE_ACCESS_ORACLE(map);
} CAF_CONFIG_VALUE_ACCESS_ORACLE(list);
CAF_CONFIG_VALUE_ACCESS_ORACLE(inspect);
template <class U, int Pos> #undef CAF_CONFIG_VALUE_ACCESS_ORACLE
static const U& get(const config_value& x, sum_type_token<U, Pos> token) {
return x.get_data().get(pos(token));
}
template <class U> template <class T>
static U get(const config_value& x, sum_type_token<U, -1>) { using config_value_access_t = typename config_value_access_oracle<
return select_config_value_access_t<U>::get(x); T, decltype(config_value_access_token<T>())>::type;
}
template <class U, int Pos> template <class T>
static U* get_if(config_value* x, sum_type_token<U, Pos> token) { struct integral_config_value_access {
return is(*x, token) ? &get(*x, token) : nullptr; using integer_type = config_value::integer;
static std::string type_name() {
std::string result;
if (std::is_signed<T>::value)
result = "int";
else
result = "uint";
result += std::to_string(sizeof(T) * 8);
return result;
} }
template <class U> static bool is(const config_value& x) {
static optional<U> get_if(config_value* x, sum_type_token<U, -1>) { auto ptr = caf::get_if<integer_type>(x.get_data_ptr());
return select_config_value_access_t<U>::get_if(x); return ptr != nullptr && detail::bounds_checker<T>::check(*ptr);
} }
template <class U, int Pos> static optional<T> get_if(const config_value* x) {
static const U* get_if(const config_value* x, sum_type_token<U, Pos> token) { auto ptr = caf::get_if<integer_type>(x->get_data_ptr());
return is(*x, token) ? &get(*x, token) : nullptr; if (ptr != nullptr && detail::bounds_checker<T>::check(*ptr))
return static_cast<T>(*ptr);
return none;
} }
template <class U> static T get(const config_value& x) {
static optional<U> get_if(const config_value* x, sum_type_token<U, -1>) { auto res = get_if(&x);
return select_config_value_access_t<U>::get_if(x); CAF_ASSERT(res != none);
return *res;
} }
template <class Result, class Visitor, class... Ts> static T convert(T x) {
static Result apply(config_value& x, Visitor&& visitor, Ts&&... xs) { return x;
return x.get_data().template apply<Result>(std::forward<Visitor>(visitor),
std::forward<Ts>(xs)...);
} }
template <class Result, class Visitor, class... Ts> template <class Nested>
static Result apply(const config_value& x, Visitor&& visitor, Ts&&... xs) { static void parse_cli(string_parser_state& ps, T& x, Nested) {
return x.get_data().template apply<Result>(std::forward<Visitor>(visitor), detail::parse(ps, x);
std::forward<Ts>(xs)...);
} }
}; };
/// Catches all non-specialized integer types.
template <class T> template <class T>
struct select_config_value_access<T, select_config_value_hint::is_integral> { struct list_config_value_access {
struct type { using list_type = T;
using integer_type = config_value::integer;
static std::string type_name() {
std::string result;
if (std::is_signed<T>::value)
result = "int";
else
result = "uint";
result += std::to_string(sizeof(T) * 8);
return result;
}
static bool is(const config_value& x) { using value_type = typename list_type::value_type;
auto ptr = caf::get_if<integer_type>(x.get_data_ptr());
return ptr != nullptr && detail::bounds_checker<T>::check(*ptr);
}
static optional<T> get_if(const config_value* x) { using value_access = config_value_access_t<value_type>;
auto ptr = caf::get_if<integer_type>(x->get_data_ptr());
if (ptr != nullptr && detail::bounds_checker<T>::check(*ptr))
return static_cast<T>(*ptr);
return none;
}
static T get(const config_value& x) { static std::string type_name() {
auto res = get_if(&x); return "list of " + value_access::type_name();
CAF_ASSERT(res != none); }
return *res;
}
static T convert(T x) {
return x;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, T& x, Nested) {
detail::parse(ps, x);
}
};
};
/// Catches all non-specialized list types.
template <class T>
struct select_config_value_access<T, select_config_value_hint::is_list> {
struct type {
using list_type = T;
using value_type = typename list_type::value_type;
using value_trait = select_config_value_access_t<value_type>;
static std::string type_name() { static bool is(const config_value& x) {
return "list of " + value_trait::type_name(); auto lst = caf::get_if<config_value::list>(&(x.get_data()));
} return lst != nullptr
&& std::all_of(lst->begin(), lst->end(), [](const config_value& y) {
return caf::holds_alternative<value_type>(y);
});
return false;
}
static bool is(const config_value& x) { static optional<list_type> get_if(const config_value* x) {
auto lst = caf::get_if<config_value::list>(&x); list_type result;
return lst != nullptr auto out = std::inserter(result, result.end());
&& std::all_of(lst->begin(), lst->end(), auto extract = [&](const config_value& y) {
[](const config_value& y) { if (auto opt = caf::get_if<value_type>(&y)) {
return caf::holds_alternative<value_type>(y); *out++ = move_if_optional(opt);
}); return true;
}
return false; return false;
} };
auto lst = caf::get_if<config_value::list>(&(x->get_data()));
if (lst != nullptr && std::all_of(lst->begin(), lst->end(), extract))
return result;
return none;
}
static optional<list_type> get_if(const config_value* x) { static list_type get(const config_value& x) {
list_type result; auto result = get_if(&x);
auto out = std::inserter(result, result.end()); if (!result)
auto extract = [&](const config_value& y) { CAF_RAISE_ERROR("invalid type found");
if (auto opt = caf::get_if<value_type>(&y)) { return std::move(*result);
*out++ = move_if_optional(opt); }
return true;
}
return false;
};
auto lst = caf::get_if<config_value::list>(x);
if (lst != nullptr && std::all_of(lst->begin(), lst->end(), extract))
return result;
return none;
}
static list_type get(const config_value& x) { static config_value::list convert(const list_type& xs) {
auto result = get_if(&x); config_value::list result;
if (!result) for (const auto& x : xs)
CAF_RAISE_ERROR("invalid type found"); result.emplace_back(value_access::convert(x));
return std::move(*result); return result;
} }
static config_value::list convert(const list_type& xs) { static void parse_cli(string_parser_state& ps, T& xs,
config_value::list result; top_level_cli_parsing_t) {
for (const auto& x : xs) bool has_open_token;
result.emplace_back(value_trait::convert(x)); auto val_token = config_value_access_token<value_type>();
return result; if constexpr (std::is_same<decltype(val_token),
config_value_access_type::list>::value) {
// The outer square brackets are optional in nested lists. This means we
// need to check for "[[" at the beginning and otherwise we assume the
// leading '[' was omitted.
string_parser_state tmp{ps.i, ps.e};
has_open_token = tmp.consume('[') && tmp.consume('[');
if (has_open_token)
ps.consume('[');
} else {
has_open_token = ps.consume('[');
} }
do {
static void parse_cli(string_parser_state& ps, T& xs,
top_level_cli_parsing_t) {
bool has_open_token;
auto subtype = select_config_value_oracle<value_type>();
if (subtype == select_config_value_hint::is_list) {
// The outer square brackets are optional in nested lists. This means we
// need to check for "[[" at the beginning and otherwise we assume the
// leading '[' was omitted.
string_parser_state tmp{ps.i, ps.e};
has_open_token = tmp.consume('[') && tmp.consume('[');
if (has_open_token)
ps.consume('[');
} else {
has_open_token = ps.consume('[');
}
do {
ps.skip_whitespaces();
if (has_open_token) {
if (ps.consume(']')) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
return;
}
} else if (ps.at_end()) {
// Allow trailing commas and empty strings.
ps.code = pec::success;
return;
}
value_type tmp;
value_trait::parse_cli(ps, tmp, nested_cli_parsing);
if (ps.code > pec::trailing_character)
return;
xs.insert(xs.end(), std::move(tmp));
} while (ps.consume(','));
if (has_open_token && !ps.consume(']')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
ps.skip_whitespaces(); ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character; if (has_open_token) {
}
static void parse_cli(string_parser_state& ps, T& xs,
nested_cli_parsing_t) {
if (!ps.consume('[')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
do {
if (ps.consume(']')) { if (ps.consume(']')) {
ps.skip_whitespaces(); ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character; ps.code = ps.at_end() ? pec::success : pec::trailing_character;
return; return;
} }
value_type tmp; } else if (ps.at_end()) {
value_trait::parse_cli(ps, tmp, nested_cli_parsing); // Allow trailing commas and empty strings.
if (ps.code > pec::trailing_character) ps.code = pec::success;
return;
xs.insert(xs.end(), std::move(tmp));
} while (ps.consume(','));
if (!ps.consume(']')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return; return;
} }
ps.skip_whitespaces(); value_type tmp;
ps.code = ps.at_end() ? pec::success : pec::trailing_character; value_access::parse_cli(ps, tmp, nested_cli_parsing);
} if (ps.code > pec::trailing_character)
}; return;
}; xs.insert(xs.end(), std::move(tmp));
} while (ps.consume(','));
/// Catches all non-specialized map types. if (has_open_token && !ps.consume(']')) {
template <class T> ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
struct select_config_value_access<T, select_config_value_hint::is_map> { return;
struct type {
using map_type = T;
using mapped_type = typename map_type::mapped_type;
using mapped_trait = select_config_value_access_t<mapped_type>;
static std::string type_name() {
std::string result = "dictionary of ";
auto nested_name = mapped_trait::type_name();
result.insert(result.end(), nested_name.begin(), nested_name.end());
return result;
} }
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
static bool is(const config_value& x) { static void parse_cli(string_parser_state& ps, T& xs, nested_cli_parsing_t) {
using value_type = config_value::dictionary::value_type; if (!ps.consume('[')) {
auto is_mapped_type = [](const value_type& y) { ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return caf::holds_alternative<mapped_type>(y.second); return;
};
if (auto dict = caf::get_if<config_value::dictionary>(&x))
return std::all_of(dict->begin(), dict->end(), is_mapped_type);
return false;
} }
do {
static optional<map_type> get_if(const config_value* x) { if (ps.consume(']')) {
using value_type = config_value::dictionary::value_type; ps.skip_whitespaces();
map_type result; ps.code = ps.at_end() ? pec::success : pec::trailing_character;
auto extract = [&](const value_type& y) { return;
if (auto opt = caf::get_if<mapped_type>(&y.second)) { }
result.emplace(y.first, move_if_optional(opt)); value_type tmp;
return true; value_access::parse_cli(ps, tmp, nested_cli_parsing);
} if (ps.code > pec::trailing_character)
return false; return;
}; xs.insert(xs.end(), std::move(tmp));
if (auto dict = caf::get_if<config_value::dictionary>(x)) } while (ps.consume(','));
if (std::all_of(dict->begin(), dict->end(), extract)) if (!ps.consume(']')) {
return result; ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return none; return;
} }
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
};
static map_type get(const config_value& x) { template <class T>
auto result = get_if(&x); struct map_config_value_access {
if (!result) using map_type = T;
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
template <class Nested> using mapped_type = typename map_type::mapped_type;
static void parse_cli(string_parser_state& ps, map_type& xs, Nested) {
detail::parse(ps, xs);
}
static config_value::dictionary convert(const map_type& xs) { using mapped_access = config_value_access_t<mapped_type>;
config_value::dictionary result;
for (const auto& x : xs)
result.emplace(x.first, mapped_trait::convert(x.second));
return result;
}
};
};
template <>
struct config_value_access<float> {
static std::string type_name() { static std::string type_name() {
return "real32"; std::string result = "dictionary of ";
auto nested_name = mapped_access::type_name();
result.insert(result.end(), nested_name.begin(), nested_name.end());
return result;
} }
static bool is(const config_value& x) { static bool is(const config_value& x) {
return holds_alternative<double>(x.get_data()); using value_type = config_value::dictionary::value_type;
auto is_mapped_type = [](const value_type& y) {
return caf::holds_alternative<mapped_type>(y.second);
};
if (auto dict = caf::get_if<config_value::dictionary>(&(x.get_data())))
return std::all_of(dict->begin(), dict->end(), is_mapped_type);
return false;
} }
static optional<float> get_if(const config_value* x) { static optional<map_type> get_if(const config_value* x) {
if (auto res = caf::get_if<double>(&(x->get_data()))) using value_type = config_value::dictionary::value_type;
return static_cast<float>(*res); map_type result;
auto extract = [&](const value_type& y) {
if (auto opt = caf::get_if<mapped_type>(&y.second)) {
result.emplace(y.first, move_if_optional(opt));
return true;
}
return false;
};
if (auto dict = caf::get_if<config_value::dictionary>(&(x->get_data())))
if (std::all_of(dict->begin(), dict->end(), extract))
return result;
return none; return none;
} }
static float get(const config_value& x) { static map_type get(const config_value& x) {
return static_cast<float>(caf::get<double>(x.get_data())); auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
} }
static double convert(float x) { template <class Nested>
return x; static void parse_cli(string_parser_state& ps, map_type& xs, Nested) {
detail::parse(ps, xs);
} }
template <class Nested> static config_value::dictionary convert(const map_type& xs) {
static void parse_cli(string_parser_state& ps, float& x, Nested) { config_value::dictionary result;
detail::parse(ps, x); for (const auto& x : xs)
result.emplace(x.first, mapped_access::convert(x.second));
return result;
} }
}; };
/// Implements automagic unboxing of `std::tuple<Ts...>` from a heterogeneous
///`config_value::list`.
/// @relates config_value
template <class... Ts> template <class... Ts>
struct config_value_access<std::tuple<Ts...>> { struct tuple_config_value_access<std::tuple<Ts...>> {
using tuple_type = std::tuple<Ts...>; using tuple_type = std::tuple<Ts...>;
static std::string type_name() { static std::string type_name() {
...@@ -697,7 +664,7 @@ struct config_value_access<std::tuple<Ts...>> { ...@@ -697,7 +664,7 @@ struct config_value_access<std::tuple<Ts...>> {
} }
static bool is(const config_value& x) { static bool is(const config_value& x) {
if (auto lst = caf::get_if<config_value::list>(&x)) { if (auto lst = caf::get_if<config_value::list>(&(x.get_data()))) {
if (lst->size() != sizeof...(Ts)) if (lst->size() != sizeof...(Ts))
return false; return false;
return rec_is(*lst, detail::int_token<0>(), detail::type_list<Ts...>()); return rec_is(*lst, detail::int_token<0>(), detail::type_list<Ts...>());
...@@ -706,7 +673,7 @@ struct config_value_access<std::tuple<Ts...>> { ...@@ -706,7 +673,7 @@ struct config_value_access<std::tuple<Ts...>> {
} }
static optional<tuple_type> get_if(const config_value* x) { static optional<tuple_type> get_if(const config_value* x) {
if (auto lst = caf::get_if<config_value::list>(x)) { if (auto lst = caf::get_if<config_value::list>(&(x->get_data()))) {
if (lst->size() != sizeof...(Ts)) if (lst->size() != sizeof...(Ts))
return none; return none;
tuple_type result; tuple_type result;
...@@ -793,7 +760,7 @@ private: ...@@ -793,7 +760,7 @@ private:
template <int Pos, class U, class... Us> template <int Pos, class U, class... Us>
static void rec_convert(config_value::list& result, const tuple_type& xs, static void rec_convert(config_value::list& result, const tuple_type& xs,
detail::int_token<Pos>, detail::type_list<U, Us...>) { detail::int_token<Pos>, detail::type_list<U, Us...>) {
using trait = select_config_value_access_t<U>; using trait = config_value_access_t<U>;
result.emplace_back(trait::convert(std::get<Pos>(xs))); result.emplace_back(trait::convert(std::get<Pos>(xs)));
return rec_convert(result, xs, detail::int_token<Pos + 1>(), return rec_convert(result, xs, detail::int_token<Pos + 1>(),
detail::type_list<Us...>()); detail::type_list<Us...>());
...@@ -808,7 +775,7 @@ private: ...@@ -808,7 +775,7 @@ private:
template <int Pos, class U, class... Us> template <int Pos, class U, class... Us>
static void rec_parse(string_parser_state& ps, tuple_type& xs, static void rec_parse(string_parser_state& ps, tuple_type& xs,
detail::int_token<Pos>, detail::type_list<U, Us...>) { detail::int_token<Pos>, detail::type_list<U, Us...>) {
using trait = select_config_value_access_t<U>; using trait = config_value_access_t<U>;
trait::parse_cli(std::get<Pos>(xs), nested_cli_parsing); trait::parse_cli(std::get<Pos>(xs), nested_cli_parsing);
if (ps.code > pec::trailing_character) if (ps.code > pec::trailing_character)
return; return;
...@@ -818,7 +785,121 @@ private: ...@@ -818,7 +785,121 @@ private:
} }
}; };
// -- SumType-like access of dictionary values --------------------------------- template <class T>
struct inspect_config_value_access {
static std::string type_name() {
return to_string(type_name_or_anonymous<T>());
}
static optional<T> get_if(const config_value* x) {
config_value_reader reader{x};
auto tmp = T{};
if (detail::load_value(reader, tmp))
return optional<T>{std::move(tmp)};
return none;
}
static bool is(const config_value& x) {
return get_if(&x) != none;
}
static T get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
template <class Nested>
static void parse_cli(string_parser_state& ps, T&, Nested) {
// TODO: we could try to read a config_value here and then deserialize the
// value from that.
ps.code = pec::invalid_argument;
}
static config_value convert(const T& x) {
config_value result;
config_value_writer writer{&result};
if (!detail::save_value(writer, x))
CAF_RAISE_ERROR("unable to convert type to a config_value");
return result;
}
};
} // namespace detail
// -- SumType access of dictionary values --------------------------------------
template <>
struct sum_type_access<config_value> {
using types = typename config_value::types;
using type0 = typename detail::tl_head<types>::type;
static constexpr bool specialized = true;
template <class U, int Pos>
static bool is(const config_value& x, sum_type_token<U, Pos> token) {
return x.get_data().is(pos(token));
}
template <class U>
static bool is(const config_value& x, sum_type_token<U, -1>) {
return detail::config_value_access_t<U>::is(x);
}
template <class U, int Pos>
static U& get(config_value& x, sum_type_token<U, Pos> token) {
return x.get_data().get(pos(token));
}
template <class U>
static U get(config_value& x, sum_type_token<U, -1>) {
return detail::config_value_access_t<U>::get(x);
}
template <class U, int Pos>
static const U& get(const config_value& x, sum_type_token<U, Pos> token) {
return x.get_data().get(pos(token));
}
template <class U>
static U get(const config_value& x, sum_type_token<U, -1>) {
return detail::config_value_access_t<U>::get(x);
}
template <class U, int Pos>
static U* get_if(config_value* x, sum_type_token<U, Pos> token) {
return is(*x, token) ? &get(*x, token) : nullptr;
}
template <class U>
static optional<U> get_if(config_value* x, sum_type_token<U, -1>) {
return detail::config_value_access_t<U>::get_if(x);
}
template <class U, int Pos>
static const U* get_if(const config_value* x, sum_type_token<U, Pos> token) {
return is(*x, token) ? &get(*x, token) : nullptr;
}
template <class U>
static optional<U> get_if(const config_value* x, sum_type_token<U, -1>) {
return detail::config_value_access_t<U>::get_if(x);
}
template <class Result, class Visitor, class... Ts>
static Result apply(config_value& x, Visitor&& visitor, Ts&&... xs) {
return x.get_data().template apply<Result>(std::forward<Visitor>(visitor),
std::forward<Ts>(xs)...);
}
template <class Result, class Visitor, class... Ts>
static Result apply(const config_value& x, Visitor&& visitor, Ts&&... xs) {
return x.get_data().template apply<Result>(std::forward<Visitor>(visitor),
std::forward<Ts>(xs)...);
}
};
/// @relates config_value /// @relates config_value
CAF_CORE_EXPORT bool operator<(const config_value& x, const config_value& y); CAF_CORE_EXPORT bool operator<(const config_value& x, const config_value& y);
...@@ -855,15 +936,16 @@ template <> ...@@ -855,15 +936,16 @@ template <>
struct variant_inspector_traits<config_value> { struct variant_inspector_traits<config_value> {
using value_type = config_value; using value_type = config_value;
static constexpr type_id_t allowed_types[] static constexpr type_id_t allowed_types[] = {
= {type_id_v<config_value::integer>, type_id_v<config_value::integer>,
type_id_v<config_value::boolean>, type_id_v<config_value::boolean>,
type_id_v<config_value::real>, type_id_v<config_value::real>,
type_id_v<config_value::timespan>, type_id_v<config_value::timespan>,
type_id_v<uri>, type_id_v<uri>,
type_id_v<config_value::string>, type_id_v<config_value::string>,
type_id_v<config_value::list>, type_id_v<config_value::list>,
type_id_v<config_value::dictionary>}; type_id_v<config_value::dictionary>,
};
static auto type_index(const config_value& x) { static auto type_index(const config_value& x) {
return x.get_data().index(); return x.get_data().index();
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <tuple>
#include <type_traits>
#include "caf/config_value_adaptor_field.hpp"
#include "caf/detail/config_value_adaptor_field_impl.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Interfaces between a user-defined type and CAF config values by going
/// through intermediate values.
template <class... Ts>
class config_value_adaptor {
public:
using value_type = std::tuple<Ts...>;
using indices = typename detail::il_indices<value_type>::type;
using fields_tuple =
typename detail::select_adaptor_fields<value_type, indices>::type;
using array_type = std::array<config_value_field<value_type>*, sizeof...(Ts)>;
template <class U,
class = detail::enable_if_t<
!std::is_same<detail::decay_t<U>, config_value_adaptor>::value>,
class... Us>
config_value_adaptor(U&& x, Us&&... xs)
: fields_(
make_fields(indices{}, std::forward<U>(x), std::forward<Us>(xs)...)) {
init(indices{});
}
config_value_adaptor(config_value_adaptor&&) = default;
span<typename array_type::value_type> fields() {
return make_span(ptr_fields_);
}
private:
// TODO: This is a workaround for GCC <= 5 because initializing fields_
// directly from (xs...) fails. Remove when moving on to newer
// compilers.
template <long... Pos, class... Us>
fields_tuple make_fields(detail::int_list<Pos...>, Us&&... xs) {
return std::make_tuple(
detail::config_value_adaptor_field_impl<value_type, Pos>(
std::forward<Us>(xs))...);
}
template <long... Pos>
void init(detail::int_list<Pos...>) {
ptr_fields_ = array_type{{&std::get<Pos>(fields_)...}};
}
fields_tuple fields_;
array_type ptr_fields_;
};
template <class... Ts>
config_value_adaptor<typename Ts::value_type...>
make_config_value_adaptor(Ts... fields) {
return {std::move(fields)...};
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/config_value_object_access.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Enables user-defined types in config files and on the CLI by converting
/// them to and from tuples. Wraps a `config_value_object_access` in order to
/// allow CAF to interact with the underlying tuple.
///
/// ~~
/// struct trait {
/// using value_type = ...;
///
/// using tuple_type = ...;
///
/// static config_value_adaptor<...> adaptor_ref();
///
/// static span<config_value_field<object_type>*> fields();
///
/// static void convert(const value_type& src, tuple_type& dst);
///
/// static void convert(const tuple_type& src, value_type& dst);
/// };
/// ~~
template <class Trait>
struct config_value_adaptor_access {
struct object_trait {
using object_type = typename Trait::tuple_type;
static std::string type_name() {
return Trait::type_name();
}
static caf::span<config_value_field<object_type>*> fields() {
return Trait::adaptor_ref().fields();
}
};
using tuple_access = config_value_object_access<object_trait>;
using value_type = typename Trait::value_type;
using tuple_type = typename Trait::tuple_type;
static std::string type_name() {
return Trait::type_name();
}
static bool is(const config_value& x) {
return tuple_access::is(x);
}
static optional<value_type> get_if(const config_value* x) {
if (auto tmp = tuple_access::get_if(x)) {
value_type result;
convert(*tmp, result);
return result;
}
return none;
}
static value_type get(const config_value& x) {
auto tmp = tuple_access::get(x);
value_type result;
convert(tmp, result);
return result;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, value_type& x, Nested nested) {
tuple_type tmp;
tuple_access::parse_cli(ps, tmp, nested);
if (ps.code <= pec::trailing_character)
convert(tmp, x);
}
static void convert(const value_type& src, tuple_type& dst) {
Trait::convert(src, dst);
}
static void convert(const tuple_type& src, value_type& dst) {
Trait::convert(src, dst);
}
static config_value::dictionary convert(const value_type& x) {
tuple_type tmp;
convert(x, tmp);
return tuple_access::convert(tmp);
}
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/optional.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Describes a field of type T of an adaptor.
template <class T>
struct config_value_adaptor_field {
/// Type of the field.
using value_type = T;
/// Predicate function for verifying user input.
using predicate_function = bool (*)(const value_type&);
/// Name of the field in configuration files and on the CLI.
string_view name;
/// If set, makes the field optional in configuration files and on the CLI by
/// assigning the default whenever the user provides no value.
optional<value_type> default_value;
/// If set, makes the field only accept values that pass this predicate.
predicate_function predicate;
};
/// Convenience function for creating a `config_value_adaptor_field`.
/// @param name name of the field in configuration files and on the CLI.
/// @param default_value if set, provides a fallback value if the user does not
/// provide a value.
/// @param predicate if set, restricts what values the field accepts.
/// @returns a `config_value_adaptor_field` object, constructed from given
/// arguments.
/// @relates config_value_adaptor_field
template <class T>
config_value_adaptor_field<T>
make_config_value_adaptor_field(string_view name,
optional<T> default_value = none,
bool (*predicate)(const T&) = nullptr) {
return {name, std::move(default_value), predicate};
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/fwd.hpp"
#include "caf/parser_state.hpp"
namespace caf {
/// Describes a field of `Object`.
template <class Object>
class config_value_field {
public:
using object_type = Object;
virtual ~config_value_field() = default;
// -- observers --------------------------------------------------------------
/// Returns whether this field has a default value.
virtual bool has_default() const noexcept = 0;
/// Returns the name of this field.
virtual string_view name() const noexcept = 0;
/// Returns the value of this field in `object` as config value.
virtual config_value get(const Object& object) const = 0;
/// Returns whether calling `set` with `x` would succeed.
virtual bool valid_input(const config_value& x) const = 0;
// -- modifiers --------------------------------------------------------------
/// Tries to set this field in `object` to `x`.
/// @returns `true` on success, `false` otherwise.
virtual bool set(Object& object, const config_value& x) const = 0;
/// Restores the default value for this field in `object`.
/// @pre `has_default()`
virtual void set_default(Object& object) const = 0;
/// Parses the content for this field in `object` from `ps`.
virtual void parse_cli(string_parser_state& ps, Object& object,
bool is_nested) const = 0;
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
namespace caf {
/// Enables user-defined types in config files and on the CLI by converting
/// them to and from `config_value::dictionary`.
///
/// ~~
/// struct trait {
/// using object_type = ...;
///
/// static string_value type_name();
///
/// static span<config_value_field<object_type>*> fields();
/// };
/// ~~
template <class Trait>
struct config_value_object_access {
using object_type = typename Trait::object_type;
static std::string type_name() {
return Trait::type_name();
}
static bool extract(const config_value* src, object_type* dst) {
auto dict = caf::get_if<config_value::dictionary>(src);
if (!dict)
return false;
for (auto field : Trait::fields()) {
if (auto value = caf::get_if(dict, field->name())) {
if (dst) {
if (!field->set(*dst, *value))
return false;
} else {
if (!field->valid_input(*value))
return false;
}
} else {
if (!field->has_default())
return false;
if (dst)
field->set_default(*dst);
}
}
return true;
}
static bool is(const config_value& x) {
return extract(&x, nullptr);
}
static optional<object_type> get_if(const config_value* x) {
object_type result;
if (extract(x, &result))
return result;
return none;
}
static object_type get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("config_value does not contain requested object");
return std::move(*result);
}
static config_value::dictionary convert(const object_type& x) {
config_value::dictionary result;
for (auto field : Trait::fields())
result.emplace(field->name(), field->get(x));
return result;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, object_type& x, Nested) {
using field_type = config_value_field<object_type>;
std::vector<field_type*> parsed_fields;
auto got = [&](field_type* f) {
auto e = parsed_fields.end();
return std::find(parsed_fields.begin(), e, f) != e;
};
auto push = [&](field_type* f) {
if (got(f))
return false;
parsed_fields.emplace_back(f);
return true;
};
auto finalize = [&] {
for (auto field : Trait::fields()) {
if (!got(field)) {
if (field->has_default()) {
field->set_default(x);
} else {
ps.code = pec::missing_field;
return;
}
}
}
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
};
auto fs = Trait::fields();
if (!ps.consume('{')) {
ps.code = pec::unexpected_character;
return;
}
using string_access = select_config_value_access_t<std::string>;
config_value::dictionary result;
do {
if (ps.consume('}')) {
finalize();
return;
}
std::string field_name;
string_access::parse_cli(ps, field_name, nested_cli_parsing);
if (ps.code > pec::trailing_character)
return;
if (!ps.consume('=')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
auto predicate = [&](config_value_field<object_type>* x) {
return x->name() == field_name;
};
auto f = std::find_if(fs.begin(), fs.end(), predicate);
if (f == fs.end()) {
ps.code = pec::invalid_field_name;
return;
}
auto fptr = *f;
if (!push(fptr)) {
ps.code = pec::repeated_field_name;
return;
}
fptr->parse_cli(ps, x, true);
if (ps.code > pec::trailing_character)
return;
if (ps.at_end()) {
ps.code = pec::unexpected_eof;
return;
}
result[fptr->name()] = fptr->get(x);
} while (ps.consume(','));
if (!ps.consume('}')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
finalize();
}
};
} // 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 config_value_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 --------------------
config_value_reader(const config_value* input, actor_system& sys)
: super(sys) {
st_.push(input);
has_human_readable_format_ = true;
}
config_value_reader(const config_value* input, execution_unit* ctx)
: super(ctx) {
st_.push(input);
has_human_readable_format_ = true;
}
explicit config_value_reader(const config_value* input)
: config_value_reader(input, nullptr) {
// nop
}
~config_value_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_;
};
} // namespace caf
...@@ -18,17 +18,16 @@ ...@@ -18,17 +18,16 @@
#pragma once #pragma once
#include "caf/config_value.hpp" #include "caf/fwd.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/settings.hpp"
#include <stack> #include <stack>
#include <vector> #include <vector>
namespace caf { namespace caf {
/// Writes objects into @ref settings. /// Serializes an objects into a @ref config_value.
class settings_writer final : public serializer { class config_value_writer final : public serializer {
public: public:
// -- member types------------------------------------------------------------ // -- member types------------------------------------------------------------
...@@ -42,30 +41,29 @@ public: ...@@ -42,30 +41,29 @@ public:
struct absent_field {}; struct absent_field {};
using value_type using value_type = variant<config_value*, settings*, absent_field,
= variant<settings*, absent_field, present_field, config_value::list*>; present_field, std::vector<config_value>*>;
using stack_type = std::stack<value_type, std::vector<value_type>>; using stack_type = std::stack<value_type, std::vector<value_type>>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
settings_writer(settings* destination, actor_system& sys) config_value_writer(config_value* dst, actor_system& sys) : super(sys) {
: super(sys), root_(destination) { st_.push(dst);
st_.push(destination);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
settings_writer(settings* destination, execution_unit* ctx) config_value_writer(config_value* dst, execution_unit* ctx) : super(ctx) {
: super(ctx), root_(destination) { st_.push(dst);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
explicit settings_writer(settings* destination) explicit config_value_writer(config_value* destination)
: settings_writer(destination, nullptr) { : config_value_writer(destination, nullptr) {
// nop // nop
} }
~settings_writer() override; ~config_value_writer() override;
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
...@@ -140,7 +138,6 @@ private: ...@@ -140,7 +138,6 @@ private:
stack_type st_; stack_type st_;
string_view type_hint_; string_view type_hint_;
settings* root_;
}; };
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <tuple>
#include "caf/config_value.hpp"
#include "caf/config_value_adaptor_field.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/string_view.hpp"
namespace caf::detail {
template <class T, size_t Pos>
class config_value_adaptor_field_impl
: public config_value_field_base<T,
typename std::tuple_element<Pos, T>::type> {
public:
using object_type = T;
using value_type = typename std::tuple_element<Pos, T>::type;
using field_type = config_value_adaptor_field<value_type>;
using predicate_type = bool (*)(const value_type&);
using super = config_value_field_base<object_type, value_type>;
explicit config_value_adaptor_field_impl(field_type x)
: super(x.name, std::move(x.default_value), x.predicate) {
// nop
}
config_value_adaptor_field_impl(config_value_adaptor_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
return std::get<Pos>(x);
}
void set_value(object_type& x, value_type y) const override {
std::get<Pos>(x) = std::move(y);
}
};
template <class T, class Ps>
struct select_adaptor_fields;
template <class T, long... Pos>
struct select_adaptor_fields<T, detail::int_list<Pos...>> {
using type = std::tuple<config_value_adaptor_field_impl<T, Pos>...>;
};
} // namespace caf::detail
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/optional.hpp"
namespace caf::detail {
template <class Object, class Value>
class config_value_field_base : public config_value_field<Object> {
public:
using super = config_value_field<Object>;
using object_type = typename super::object_type;
using value_type = Value;
using predicate_type = bool (*)(const value_type&);
config_value_field_base(string_view name, optional<value_type> default_value,
predicate_type predicate)
: name_(name),
default_value_(std::move(default_value)),
predicate_(predicate) {
// nop
}
config_value_field_base(config_value_field_base&&) = default;
bool has_default() const noexcept override {
return static_cast<bool>(default_value_);
}
string_view name() const noexcept override {
return name_;
}
config_value get(const object_type& object) const override {
using access = caf::select_config_value_access_t<value_type>;
return config_value{access::convert(get_value(object))};
}
bool valid_input(const config_value& x) const override {
if (!predicate_)
return holds_alternative<value_type>(x);
if (auto value = get_if<value_type>(&x))
return predicate_(*value);
return false;
}
bool set(object_type& x, const config_value& y) const override {
if (auto value = get_if<value_type>(&y)) {
if (predicate_ && !predicate_(*value))
return false;
set_value(x, move_if_optional(value));
return true;
}
return false;
}
void set_default(object_type& x) const override {
set_value(x, *default_value_);
}
void parse_cli(string_parser_state& ps, object_type& x,
bool nested) const override {
using access = caf::select_config_value_access_t<value_type>;
value_type tmp;
if (nested)
access::parse_cli(ps, tmp, nested_cli_parsing);
else
access::parse_cli(ps, tmp, top_level_cli_parsing);
if (ps.code <= pec::trailing_character) {
if (predicate_ && !predicate_(tmp))
ps.code = pec::invalid_argument;
else
set_value(x, std::move(tmp));
}
}
virtual const value_type& get_value(const object_type& object) const = 0;
virtual void set_value(object_type& object, value_type value) const = 0;
protected:
string_view name_;
optional<value_type> default_value_;
predicate_type predicate_;
};
} // namespace caf::detail
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include <utility>
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
#include "caf/string_view.hpp"
namespace caf::detail {
template <class MemberObjectPointer>
class config_value_field_impl;
// A config value with direct access to a field via member object pointer.
template <class Value, class Object>
class config_value_field_impl<Value Object::*>
: public config_value_field_base<Object, Value> {
public:
using super = config_value_field_base<Object, Value>;
using member_pointer = Value Object::*;
using object_type = Object;
using value_type = Value;
using predicate_type = bool (*)(const value_type&);
constexpr config_value_field_impl(string_view name, member_pointer ptr,
optional<value_type> default_value = none,
predicate_type predicate = nullptr)
: super(name, std::move(default_value), predicate), ptr_(ptr) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
return x.*ptr_;
}
void set_value(object_type& x, value_type y) const override {
x.*ptr_ = std::move(y);
}
private:
member_pointer ptr_;
};
template <class Get>
struct config_value_field_trait {
using trait = get_callable_trait_t<Get>;
static_assert(trait::num_args == 1,
"Get must take exactly one argument (the object)");
using get_argument_type = tl_head_t<typename trait::arg_types>;
using object_type = decay_t<get_argument_type>;
using get_result_type = typename trait::result_type;
using value_type = decay_t<get_result_type>;
};
// A config value with access to a field via getter and setter.
template <class Get, class Set>
class config_value_field_impl<std::pair<Get, Set>>
: public config_value_field_base<
typename config_value_field_trait<Get>::object_type,
typename config_value_field_trait<Get>::value_type> {
public:
using trait = config_value_field_trait<Get>;
using object_type = typename trait::object_type;
using get_result_type = typename trait::get_result_type;
using value_type = typename trait::value_type;
using predicate_type = bool (*)(const value_type&);
using super = config_value_field_base<object_type, value_type>;
constexpr config_value_field_impl(string_view name, Get getter, Set setter,
optional<value_type> default_value = none,
predicate_type predicate = nullptr)
: super(name, std::move(default_value), predicate),
get_(std::move(getter)),
set_(std::move(setter)) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
bool_token<std::is_lvalue_reference<get_result_type>::value> token;
return get_value_impl(x, token);
}
void set_value(object_type& x, value_type y) const override {
set_(x, std::move(y));
}
private:
template <class O>
const value_type& get_value_impl(const O& x, std::true_type) const {
return get_(x);
}
template <class O>
const value_type& get_value_impl(const O& x, std::false_type) const {
dummy_ = get_(x);
return dummy_;
}
Get get_;
Set set_;
mutable value_type dummy_;
};
} // namespace caf::detail
...@@ -97,6 +97,8 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, float& x); ...@@ -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, double& x);
CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x);
// -- CAF types ---------------------------------------------------------------- // -- CAF types ----------------------------------------------------------------
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_address& x); CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_address& x);
......
...@@ -48,7 +48,7 @@ void store_impl(void* ptr, const config_value& x) { ...@@ -48,7 +48,7 @@ void store_impl(void* ptr, const config_value& x) {
template <class T> template <class T>
config_value get_impl(const void* ptr) { config_value get_impl(const void* ptr) {
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))}; return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))};
} }
...@@ -60,7 +60,7 @@ expected<config_value> parse_impl(T* ptr, string_view str) { ...@@ -60,7 +60,7 @@ expected<config_value> parse_impl(T* ptr, string_view str) {
} }
if constexpr (detail::has_clear_member<T>::value) if constexpr (detail::has_clear_member<T>::value)
ptr->clear(); ptr->clear();
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
string_parser_state ps{str.begin(), str.end()}; string_parser_state ps{str.begin(), str.end()};
trait::parse_cli(ps, *ptr, top_level_cli_parsing); trait::parse_cli(ps, *ptr, top_level_cli_parsing);
if (ps.code != pec::success) if (ps.code != pec::success)
...@@ -78,7 +78,7 @@ expected<config_value> parse_impl_delegate(void* ptr, string_view str) { ...@@ -78,7 +78,7 @@ expected<config_value> parse_impl_delegate(void* ptr, string_view str) {
template <class T> template <class T>
config_option::meta_state* option_meta_state_instance() { config_option::meta_state* option_meta_state_instance() {
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
static config_option::meta_state obj{check_impl<T>, store_impl<T>, static config_option::meta_state obj{check_impl<T>, store_impl<T>,
get_impl<T>, parse_impl_delegate<T>, get_impl<T>, parse_impl_delegate<T>,
trait::type_name()}; trait::type_name()};
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <tuple>
#include <type_traits>
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_impl.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
/// Creates a field with direct access to a member in `T` via member-to-object
/// pointer.
template <class T, class U, class... Args>
detail::config_value_field_impl<U T::*>
make_config_value_field(string_view name, U T::*ptr, Args&&... xs) {
return {name, ptr, std::forward<Args>(xs)...};
}
/// Creates a field with access to a member in `T` via `getter` and `setter`.
template <class Getter, class Setter,
class E = detail::enable_if_t<!std::is_member_pointer<Getter>::value>,
class... Args>
detail::config_value_field_impl<std::pair<Getter, Setter>>
make_config_value_field(string_view name, Getter getter, Setter setter,
Args&&... xs) {
return {name, std::move(getter), std::move(setter),
std::forward<Args>(xs)...};
}
template <class T, class... Ts>
class config_value_field_storage {
public:
using tuple_type = std::tuple<T, Ts...>;
using object_type = typename T::object_type;
using indices = typename detail::il_indices<tuple_type>::type;
using array_type = std::array<config_value_field<object_type>*,
sizeof...(Ts) + 1>;
template <class... Us>
config_value_field_storage(T x, Us&&... xs)
: fields_(std::move(x), std::forward<Us>(xs)...) {
init(detail::get_indices(fields_));
}
config_value_field_storage(config_value_field_storage&&) = default;
span<config_value_field<object_type>*> fields() {
return make_span(ptr_fields_);
}
private:
template <long... Pos>
void init(detail::int_list<Pos...>) {
ptr_fields_ = array_type{{&std::get<Pos>(fields_)...}};
}
std::tuple<T, Ts...> fields_;
array_type ptr_fields_;
};
template <class... Ts>
config_value_field_storage<Ts...>
make_config_value_field_storage(Ts... fields) {
return {std::move(fields)...};
}
} // namespace caf
...@@ -145,19 +145,21 @@ enum class sec : uint8_t { ...@@ -145,19 +145,21 @@ enum class sec : uint8_t {
no_tracing_context, no_tracing_context,
/// No request produced a valid result. /// No request produced a valid result.
all_requests_failed, 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. /// the content of a field.
field_invariant_check_failed = 55, 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, 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, invalid_field_type,
/// Serialization failed because a type was flagged as unsafe message type. /// Serialization failed because a type was flagged as unsafe message type.
unsafe_type, unsafe_type,
/// Serialization failed, because a save callback returned `false`. /// Serialization failed because a save callback returned `false`.
save_callback_failed, save_callback_failed,
/// Deserialization failed, because a load callback returned `false`. /// Deserialization failed because a load callback returned `false`.
load_callback_failed, load_callback_failed = 60,
/// Converting between two types failed.
conversion_failed,
}; };
/// @relates sec /// @relates sec
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/raise_error.hpp" #include "caf/raise_error.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
#include "caf/sum_type.hpp"
namespace caf { namespace caf {
...@@ -50,7 +51,7 @@ auto get_if(const settings* xs, string_view name) { ...@@ -50,7 +51,7 @@ auto get_if(const settings* xs, string_view name) {
/// @relates config_value /// @relates config_value
template <class T> template <class T>
bool holds_alternative(const settings& xs, string_view name) { bool holds_alternative(const settings& xs, string_view name) {
using access = select_config_value_access_t<T>; using access = detail::config_value_access_t<T>;
if (auto value = get_if(&xs, name)) if (auto value = get_if(&xs, name))
return access::is(*value); return access::is(*value);
return false; 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/config_value_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::config_value_reader::absent_field, "absent field");
PRETTY_NAME(caf::config_value_reader::sequence, "sequence");
PRETTY_NAME(caf::config_value_reader::associative_array, "associative array");
template <class T>
constexpr auto pretty_name_v = pretty_name<T>::value;
auto get_pretty_name(const caf::config_value_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 config_value_reader::sequence::at_end() const noexcept {
return index >= ls->size();
}
const config_value& config_value_reader::sequence::current() {
return (*ls)[index];
}
bool config_value_reader::associative_array::at_end() const noexcept {
return pos == end;
}
const std::pair<const std::string, config_value>&
config_value_reader::associative_array::current() {
return *pos;
}
// -- constructors, destructors, and assignment operators ----------------------
config_value_reader::~config_value_reader() {
// nop
}
// -- interface functions ------------------------------------------------------
bool config_value_reader::fetch_next_object_type(type_id_t& type) {
if (st_.empty()) {
emplace_error(sec::runtime_error,
"tried to read multiple objects from the root object");
return false;
} 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 config_value_reader::begin_object(string_view) {
if (st_.empty()) {
emplace_error(sec::runtime_error,
"tried to read multiple objects from the root object");
return false;
}
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 config_value_reader::end_object() {
SCOPE(const settings*);
st_.pop();
return true;
}
bool config_value_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 config_value_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 config_value_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 config_value_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 config_value_reader::end_field() {
CHECK_NOT_EMPTY();
// Note: no pop() here, because the value(s) were already consumed.
return true;
}
bool config_value_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 config_value_reader::end_tuple() {
return end_sequence();
}
bool config_value_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 config_value_reader::end_key_value_pair() {
SCOPE(associative_array);
++top.pos;
return true;
}
bool config_value_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 config_value_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 config_value_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 config_value_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(config_value_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<config_value_reader::sequence>(top)) {
auto& seq = get<config_value_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<config_value_reader::key_ptr>(top)) {
auto ptr = get<config_value_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 config_value_reader::value(bool& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(int8_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(uint8_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(int16_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(uint16_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(int32_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(uint32_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(int64_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(uint64_t& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(float& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(double& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(long double& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(std::string& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
}
bool config_value_reader::value(std::u16string&) {
emplace_error(sec::runtime_error, "u16string support not implemented yet");
return false;
}
bool config_value_reader::value(std::u32string&) {
emplace_error(sec::runtime_error, "u32string support not implemented yet");
return false;
}
bool config_value_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 config_value_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
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/settings_writer.hpp" #include "caf/config_value_writer.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/append_hex.hpp" #include "caf/detail/append_hex.hpp"
#include "caf/detail/overload.hpp" #include "caf/detail/overload.hpp"
#include "caf/settings.hpp"
#define CHECK_NOT_EMPTY() \ #define CHECK_NOT_EMPTY() \
do { \ do { \
...@@ -58,13 +60,13 @@ namespace caf { ...@@ -58,13 +60,13 @@ namespace caf {
// -- constructors, destructors, and assignment operators ---------------------- // -- constructors, destructors, and assignment operators ----------------------
settings_writer::~settings_writer() { config_value_writer::~config_value_writer() {
// nop // nop
} }
// -- interface functions ------------------------------------------------------ // -- interface functions ------------------------------------------------------
bool settings_writer::inject_next_object_type(type_id_t type) { bool config_value_writer::inject_next_object_type(type_id_t type) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
type_hint_ = query_type_name(type); type_hint_ = query_type_name(type);
if (type_hint_.empty()) { if (type_hint_.empty()) {
...@@ -75,48 +77,47 @@ bool settings_writer::inject_next_object_type(type_id_t type) { ...@@ -75,48 +77,47 @@ bool settings_writer::inject_next_object_type(type_id_t type) {
return true; return true;
} }
bool settings_writer::begin_object(string_view) { bool config_value_writer::begin_object(string_view) {
if (st_.empty()) { CHECK_NOT_EMPTY();
if (root_ == nullptr) { auto f = detail::make_overload(
[this](config_value* x) {
// Morph the root element into a dictionary.
auto& dict = x->as_dictionary();
dict.clear();
st_.top() = &dict;
return true;
},
[this](settings*) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"tried to serialize multiple objects into the root object"); "begin_object called inside another object");
return false; return false;
} },
st_.push(root_); [this](absent_field) {
root_ = nullptr; emplace_error(sec::runtime_error,
} else { "begin_object called inside non-existent optional field");
auto f = detail::make_overload( return false;
[this](settings*) { },
emplace_error(sec::runtime_error, [this](present_field fld) {
"begin_object called inside another object"); CAF_ASSERT(fld.parent != nullptr);
return false; auto [iter, added] = fld.parent->emplace(fld.name, settings{});
}, if (!added) {
[this](absent_field) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"begin_object called inside non-existent optional field"); "field already defined: " + to_string(fld.name));
return false; return false;
}, }
[this](present_field fld) { auto obj = std::addressof(get<settings>(iter->second));
auto [iter, added] = fld.parent->emplace(fld.name, settings{}); if (!fld.type.empty())
if (!added) { put(*obj, "@type", fld.type);
emplace_error(sec::runtime_error, st_.push(obj);
"field already defined: " + to_string(fld.name)); return true;
return false; },
} [this](config_value::list* ls) {
auto obj = std::addressof(get<settings>(iter->second)); ls->emplace_back(settings{});
if (!fld.type.empty()) st_.push(std::addressof(get<settings>(ls->back())));
put(*obj, "@type", fld.type); return true;
st_.push(obj); });
return true; if (!visit(f, st_.top()))
}, return false;
[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()) { if (!type_hint_.empty()) {
put(*get<settings*>(st_.top()), "@type", type_hint_); put(*get<settings*>(st_.top()), "@type", type_hint_);
type_hint_ = string_view{}; type_hint_ = string_view{};
...@@ -124,19 +125,19 @@ bool settings_writer::begin_object(string_view) { ...@@ -124,19 +125,19 @@ bool settings_writer::begin_object(string_view) {
return true; return true;
} }
bool settings_writer::end_object() { bool config_value_writer::end_object() {
SCOPE(settings*); SCOPE(settings*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::begin_field(string_view name) { bool config_value_writer::begin_field(string_view name) {
SCOPE(settings*); SCOPE(settings*);
st_.push(present_field{top, name, string_view{}}); st_.push(present_field{top, name, string_view{}});
return true; return true;
} }
bool settings_writer::begin_field(string_view name, bool is_present) { bool config_value_writer::begin_field(string_view name, bool is_present) {
SCOPE(settings*); SCOPE(settings*);
if (is_present) if (is_present)
st_.push(present_field{top, name, string_view{}}); st_.push(present_field{top, name, string_view{}});
...@@ -145,8 +146,9 @@ bool settings_writer::begin_field(string_view name, bool is_present) { ...@@ -145,8 +146,9 @@ bool settings_writer::begin_field(string_view name, bool is_present) {
return true; return true;
} }
bool settings_writer::begin_field(string_view name, span<const type_id_t> types, bool config_value_writer::begin_field(string_view name,
size_t index) { span<const type_id_t> types,
size_t index) {
SCOPE(settings*); SCOPE(settings*);
if (index >= types.size()) { if (index >= types.size()) {
emplace_error(sec::invalid_argument, emplace_error(sec::invalid_argument,
...@@ -164,15 +166,16 @@ bool settings_writer::begin_field(string_view name, span<const type_id_t> types, ...@@ -164,15 +166,16 @@ bool settings_writer::begin_field(string_view name, span<const type_id_t> types,
return true; return true;
} }
bool settings_writer::begin_field(string_view name, bool is_present, bool config_value_writer::begin_field(string_view name, bool is_present,
span<const type_id_t> types, size_t index) { span<const type_id_t> types,
size_t index) {
if (is_present) if (is_present)
return begin_field(name, types, index); return begin_field(name, types, index);
else else
return begin_field(name, false); return begin_field(name, false);
} }
bool settings_writer::end_field() { bool config_value_writer::end_field() {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
if (!holds_alternative<present_field>(st_.top()) if (!holds_alternative<present_field>(st_.top())
&& !holds_alternative<absent_field>(st_.top())) { && !holds_alternative<absent_field>(st_.top())) {
...@@ -183,15 +186,15 @@ bool settings_writer::end_field() { ...@@ -183,15 +186,15 @@ bool settings_writer::end_field() {
return true; return true;
} }
bool settings_writer::begin_tuple(size_t size) { bool config_value_writer::begin_tuple(size_t size) {
return begin_sequence(size); return begin_sequence(size);
} }
bool settings_writer::end_tuple() { bool config_value_writer::end_tuple() {
return end_sequence(); return end_sequence();
} }
bool settings_writer::begin_key_value_pair() { bool config_value_writer::begin_key_value_pair() {
SCOPE(settings*); SCOPE(settings*);
auto [iter, added] = top->emplace("@tmp", config_value::list{}); auto [iter, added] = top->emplace("@tmp", config_value::list{});
if (!added) { if (!added) {
...@@ -202,7 +205,7 @@ bool settings_writer::begin_key_value_pair() { ...@@ -202,7 +205,7 @@ bool settings_writer::begin_key_value_pair() {
return true; return true;
} }
bool settings_writer::end_key_value_pair() { bool config_value_writer::end_key_value_pair() {
config_value::list tmp; config_value::list tmp;
/* lifetime scope of the list */ { /* lifetime scope of the list */ {
SCOPE(config_value::list*); SCOPE(config_value::list*);
...@@ -229,9 +232,16 @@ bool settings_writer::end_key_value_pair() { ...@@ -229,9 +232,16 @@ bool settings_writer::end_key_value_pair() {
return true; return true;
} }
bool settings_writer::begin_sequence(size_t) { bool config_value_writer::begin_sequence(size_t) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* val) {
// Morph the value into a list.
auto& ls = val->as_list();
ls.clear();
st_.top() = &ls;
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"cannot start sequence/tuple inside an object"); "cannot start sequence/tuple inside an object");
...@@ -261,16 +271,23 @@ bool settings_writer::begin_sequence(size_t) { ...@@ -261,16 +271,23 @@ bool settings_writer::begin_sequence(size_t) {
return visit(f, st_.top()); return visit(f, st_.top());
} }
bool settings_writer::end_sequence() { bool config_value_writer::end_sequence() {
SCOPE(config_value::list*); SCOPE(config_value::list*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::begin_associative_array(size_t) { bool config_value_writer::begin_associative_array(size_t) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
settings* inner = nullptr; settings* inner = nullptr;
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* val) {
// Morph the top element into a dictionary.
auto& dict = val->as_dictionary();
dict.clear();
st_.top() = &dict;
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields"); emplace_error(sec::runtime_error, "cannot write values outside fields");
return false; return false;
...@@ -316,45 +333,45 @@ bool settings_writer::begin_associative_array(size_t) { ...@@ -316,45 +333,45 @@ bool settings_writer::begin_associative_array(size_t) {
return false; return false;
} }
bool settings_writer::end_associative_array() { bool config_value_writer::end_associative_array() {
SCOPE(settings*); SCOPE(settings*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::value(bool x) { bool config_value_writer::value(bool x) {
return push(config_value{x}); return push(config_value{x});
} }
bool settings_writer::value(int8_t x) { bool config_value_writer::value(int8_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint8_t x) { bool config_value_writer::value(uint8_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int16_t x) { bool config_value_writer::value(int16_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint16_t x) { bool config_value_writer::value(uint16_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int32_t x) { bool config_value_writer::value(int32_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint32_t x) { bool config_value_writer::value(uint32_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int64_t x) { bool config_value_writer::value(int64_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint64_t x) { bool config_value_writer::value(uint64_t x) {
auto max_val = std::numeric_limits<config_value::integer>::max(); auto max_val = std::numeric_limits<config_value::integer>::max();
if (x > static_cast<uint64_t>(max_val)) { if (x > static_cast<uint64_t>(max_val)) {
emplace_error(sec::runtime_error, "integer overflow"); emplace_error(sec::runtime_error, "integer overflow");
...@@ -363,41 +380,45 @@ bool settings_writer::value(uint64_t x) { ...@@ -363,41 +380,45 @@ bool settings_writer::value(uint64_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(float x) { bool config_value_writer::value(float x) {
return push(config_value{double{x}}); return push(config_value{double{x}});
} }
bool settings_writer::value(double x) { bool config_value_writer::value(double x) {
return push(config_value{x}); return push(config_value{x});
} }
bool settings_writer::value(long double x) { bool config_value_writer::value(long double x) {
return push(config_value{std::to_string(x)}); return push(config_value{std::to_string(x)});
} }
bool settings_writer::value(string_view x) { bool config_value_writer::value(string_view x) {
return push(config_value{to_string(x)}); return push(config_value{to_string(x)});
} }
bool settings_writer::value(const std::u16string&) { bool config_value_writer::value(const std::u16string&) {
emplace_error(sec::runtime_error, "u16string support not implemented yet"); emplace_error(sec::runtime_error, "u16string support not implemented yet");
return false; return false;
} }
bool settings_writer::value(const std::u32string&) { bool config_value_writer::value(const std::u32string&) {
emplace_error(sec::runtime_error, "u32string support not implemented yet"); emplace_error(sec::runtime_error, "u32string support not implemented yet");
return false; return false;
} }
bool settings_writer::value(span<const byte> x) { bool config_value_writer::value(span<const byte> x) {
std::string str; std::string str;
detail::append_hex(str, x.data(), x.size()); detail::append_hex(str, x.data(), x.size());
return push(config_value{std::move(str)}); return push(config_value{std::move(str)});
} }
bool settings_writer::push(config_value&& x) { bool config_value_writer::push(config_value&& x) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
auto f = detail::make_overload( auto f = detail::make_overload(
[&x](config_value* val) {
*val = std::move(x);
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields"); emplace_error(sec::runtime_error, "cannot write values outside fields");
return false; return false;
......
...@@ -142,6 +142,8 @@ PARSE_IMPL(float, floating_point) ...@@ -142,6 +142,8 @@ PARSE_IMPL(float, floating_point)
PARSE_IMPL(double, floating_point) PARSE_IMPL(double, floating_point)
PARSE_IMPL(long double, floating_point)
void parse(string_parser_state& ps, uri& x) { void parse(string_parser_state& ps, uri& x) {
uri_builder builder; uri_builder builder;
if (ps.consume('<')) { if (ps.consume('<')) {
......
...@@ -24,11 +24,10 @@ ...@@ -24,11 +24,10 @@
#include "caf/optional.hpp" #include "caf/optional.hpp"
#define DEFAULT_META(type, parse_fun) \ #define DEFAULT_META(type, parse_fun) \
config_option::meta_state \ config_option::meta_state type##_meta_state{ \
type##_meta_state{default_config_option_check<type>, \ default_config_option_check<type>, default_config_option_store<type>, \
default_config_option_store<type>, get_impl<type>, \ get_impl<type>, parse_fun, \
parse_fun, \ detail::config_value_access_t<type>::type_name()};
select_config_value_access_t<type>::type_name()};
using std::string; using std::string;
...@@ -78,7 +77,7 @@ config_value bool_get_neg(const void* ptr) { ...@@ -78,7 +77,7 @@ config_value bool_get_neg(const void* ptr) {
meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg, meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg,
nullptr, nullptr,
select_config_value_access_t<bool>::type_name()}; detail::config_value_access_t<bool>::type_name()};
error check_timespan(const config_value& x) { error check_timespan(const config_value& x) {
if (holds_alternative<timespan>(x)) if (holds_alternative<timespan>(x))
...@@ -100,11 +99,11 @@ config_value get_timespan(const void* ptr) { ...@@ -100,11 +99,11 @@ config_value get_timespan(const void* ptr) {
meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>, meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>,
nullptr, nullptr,
select_config_value_access_t<timespan>::type_name()}; detail::config_value_access_t<timespan>::type_name()};
meta_state ms_res_meta{check_timespan, store_timespan<1000000>, meta_state ms_res_meta{check_timespan, store_timespan<1000000>,
get_timespan<1000000>, nullptr, get_timespan<1000000>, nullptr,
select_config_value_access_t<timespan>::type_name()}; detail::config_value_access_t<timespan>::type_name()};
} // namespace } // namespace
...@@ -114,17 +113,15 @@ config_option make_negated_config_option(bool& storage, string_view category, ...@@ -114,17 +113,15 @@ config_option make_negated_config_option(bool& storage, string_view category,
return {category, name, description, &bool_neg_meta, &storage}; return {category, name, description, &bool_neg_meta, &storage};
} }
config_option make_us_resolution_config_option(size_t& storage, config_option
string_view category, make_us_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view name, string_view description) {
string_view description) {
return {category, name, description, &us_res_meta, &storage}; return {category, name, description, &us_res_meta, &storage};
} }
config_option make_ms_resolution_config_option(size_t& storage, config_option
string_view category, make_ms_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view name, string_view description) {
string_view description) {
return {category, name, description, &ms_res_meta, &storage}; return {category, name, description, &ms_res_meta, &storage};
} }
......
...@@ -137,6 +137,8 @@ std::string to_string(sec x) { ...@@ -137,6 +137,8 @@ std::string to_string(sec x) {
return "save_callback_failed"; return "save_callback_failed";
case sec::load_callback_failed: case sec::load_callback_failed:
return "load_callback_failed"; return "load_callback_failed";
case sec::conversion_failed:
return "conversion_failed";
}; };
} }
......
...@@ -439,3 +439,58 @@ CAF_TEST(conversion to std::unordered_multimap) { ...@@ -439,3 +439,58 @@ CAF_TEST(conversion to std::unordered_multimap) {
CAF_REQUIRE(ys); CAF_REQUIRE(ys);
CAF_CHECK_EQUAL(*ys, map_type({{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}})); CAF_CHECK_EQUAL(*ys, map_type({{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}}));
} }
namespace {
struct point_3d {
int32_t x;
int32_t y;
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),
f.field("z", x.z));
}
struct line {
point_3d p1;
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));
}
} // namespace
CAF_TEST(config values pick up user defined inspect overloads) {
config_value x;
CAF_MESSAGE("fill config value with the fields necessary for a 'line'");
{
auto& dict = x.as_dictionary();
put(dict, "p1.x", 1);
put(dict, "p1.y", 2);
put(dict, "p1.z", 3);
put(dict, "p2.x", 10);
put(dict, "p2.y", 20);
put(dict, "p2.z", 30);
}
CAF_MESSAGE("read 'line' via get_if and verify the object");
{
auto l = get_if<line>(&x);
CAF_CHECK_NOT_EQUAL(l, none);
if (l)
CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}}));
}
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE config_value_adaptor
#include "caf/config_value_adaptor.hpp"
#include "core-test.hpp"
#include "caf/config_value_adaptor_access.hpp"
using namespace caf;
namespace {
// We want to configure this type as follows:
// my-duration = {
// count = 1
// resolution = "s"
// }
struct my_duration {
public:
constexpr my_duration() noexcept : ns_(0) {
// nop
}
constexpr my_duration(const my_duration&) noexcept = default;
my_duration& operator=(const my_duration&) noexcept = default;
int64_t ns() const {
return ns_;
}
int64_t us() const {
return ns() / 1000;
}
int64_t ms() const {
return us() / 1000;
}
int64_t s() const {
return ms() / 1000;
}
static my_duration from_ns(int64_t count) {
my_duration result;
result.ns_ = count;
return result;
}
static my_duration from_us(int64_t count) {
return from_ns(count * 1000);
}
static my_duration from_ms(int64_t count) {
return from_us(count * 1000);
}
static my_duration from_s(int64_t count) {
return from_ms(count * 1000);
}
private:
int64_t ns_;
};
bool operator==(my_duration x, my_duration y) {
return x.ns() == y.ns();
}
std::string to_string(my_duration x) {
return std::to_string(x.ns()) + "ns";
}
struct my_duration_adaptor {
using value_type = my_duration;
using tuple_type = std::tuple<int64_t, std::string>;
static std::string type_name() noexcept {
return "my-duration";
}
static bool resolution_valid(const std::string& str) {
static constexpr string_view whitelist[] = {"s", "ms", "us", "ns"};
auto matches = [&](string_view x) { return str == x; };
return std::any_of(std::begin(whitelist), std::end(whitelist), matches);
}
static config_value_adaptor<int64_t, std::string>& adaptor_ref() {
static auto singleton = make_config_value_adaptor(
make_config_value_adaptor_field<int64_t>("count"),
make_config_value_adaptor_field<std::string>("resolution", none,
resolution_valid));
return singleton;
}
static void convert(const value_type& src, tuple_type& dst) {
int count = src.ns();
if (count / 1000 != 0) {
dst = std::tie(count, "ns");
return;
}
count /= 1000;
if (count / 1000 != 0) {
dst = std::tie(count, "us");
return;
}
count /= 1000;
if (count / 1000 != 0) {
dst = std::tie(count, "ms");
return;
}
count /= 1000;
dst = std::tie(count, "s");
}
static void convert(const tuple_type& src, value_type& dst) {
auto count = std::get<0>(src);
const auto& resolution = std::get<1>(src);
if (resolution == "ns")
dst = my_duration::from_ns(count);
else if (resolution == "us")
dst = my_duration::from_us(count);
else if (resolution == "ms")
dst = my_duration::from_ms(count);
else
dst = my_duration::from_s(count);
}
};
struct fixture {
config_option_set opts;
template <class T>
expected<T> read(std::vector<std::string> args) {
settings cfg;
auto res = opts.parse(cfg, args);
if (res.first != pec::success)
return make_error(res.first, *res.second);
auto x = get_if<T>(&cfg, "value");
if (x == none)
return sec::invalid_argument;
return *x;
}
};
} // namespace
namespace caf {
template <>
struct config_value_access<my_duration>
: config_value_adaptor_access<my_duration_adaptor> {};
} // namespace caf
CAF_TEST_FIXTURE_SCOPE(config_value_adaptor_tests, fixture)
CAF_TEST(holds_alternative) {
auto make_value = [](int64_t count, std::string resolution) {
settings x;
put(x, "count", count);
put(x, "resolution", std::move(resolution));
return config_value{std::move(x)};
};
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "s")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "ms")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "us")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "ns")));
CAF_CHECK(!holds_alternative<my_duration>(make_value(1, "foo")));
}
CAF_TEST(access from dictionary) {
settings x;
put(x, "value.count", 42);
put(x, "value.resolution", "s");
auto value = x["value"];
CAF_REQUIRE(holds_alternative<my_duration>(value));
CAF_CHECK_EQUAL(get_if<my_duration>(&value), my_duration::from_s(42));
CAF_CHECK_EQUAL(get<my_duration>(value), my_duration::from_s(42));
}
namespace {
constexpr const char* config_text = R"__(
max-delay = {
count = 123
resolution = "s"
}
)__";
struct test_config : actor_system_config {
test_config() {
opt_group{custom_options_, "global"}.add(max_delay, "max-delay,m",
"maximum delay");
}
my_duration max_delay;
};
} // namespace
CAF_TEST(adaptor access from actor system config - file input) {
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(0, nullptr, in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.max_delay, my_duration::from_s(123));
}
CAF_TEST(adaptor access from actor system config - file input and arguments) {
std::vector<std::string> args{
"--max-delay={count = 20, resolution = ms}",
};
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(std::move(args), in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.max_delay, my_duration::from_ms(20));
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 config_value_reader
#include "caf/config_value_reader.hpp"
#include "caf/test/dsl.hpp"
#include "inspector-tests.hpp"
#include "caf/config_value.hpp"
#include "caf/config_value_writer.hpp"
#include "caf/inspector_access.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 config_value& src, T& value) {
config_value_reader reader{&src};
if (!detail::load_value(reader, value))
CAF_FAIL("failed to deserialize from settings: " << reader.get_error());
}
template <class T>
void deserialize(const settings& src, T& value) {
deserialize(config_value{src}, value);
}
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(config_value_reader_tests, fixture)
CAF_TEST(readers deserialize builtin types from config values) {
std::string value;
put(xs, "foo", "bar");
deserialize(xs["foo"], value);
CAF_CHECK_EQUAL(value, "bar");
}
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}};
config_value tmp;
config_value_writer writer{&tmp};
if (!detail::save_value(writer, l))
CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(tmp))
CAF_FAIL("writer failed to produce a dictionary");
xs = std::move(caf::get<settings>(tmp));
}
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()
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#define CAF_SUITE settings_writer #define CAF_SUITE config_value_writer
#include "caf/settings_writer.hpp" #include "caf/config_value_writer.hpp"
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
#include "inspector-tests.hpp" #include "inspector-tests.hpp"
#include "caf/inspector_access.hpp"
using namespace caf; using namespace caf;
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
...@@ -43,9 +45,13 @@ struct fixture { ...@@ -43,9 +45,13 @@ struct fixture {
template <class T> template <class T>
void set(const T& value) { void set(const T& value) {
settings_writer writer{&xs}; config_value val;
if (!writer.apply_object(value)) config_value_writer writer{&val};
if (!detail::save_value(writer, value))
CAF_FAIL("failed two write to settings: " << writer.get_error()); CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(val))
CAF_FAIL("serializing T did not result in a dictionary");
xs = std::move(caf::get<settings>(val));
} }
template <class T> template <class T>
...@@ -63,7 +69,7 @@ struct fixture { ...@@ -63,7 +69,7 @@ struct fixture {
} // namespace } // namespace
CAF_TEST_FIXTURE_SCOPE(settings_writer_tests, fixture) CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture)
CAF_TEST(structs become dictionaries) { CAF_TEST(structs become dictionaries) {
set(foobar{"hello", "world"}); set(foobar{"hello", "world"});
......
...@@ -47,6 +47,10 @@ struct point_3d { ...@@ -47,6 +47,10 @@ struct point_3d {
int32_t z; 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> template <class Inspector>
bool inspect(Inspector& f, point_3d& x) { bool inspect(Inspector& f, point_3d& x) {
return f.object(x).fields(f.field("x", x.x), f.field("y", x.y), return f.object(x).fields(f.field("x", x.x), f.field("y", x.y),
...@@ -58,6 +62,10 @@ struct line { ...@@ -58,6 +62,10 @@ struct line {
point_3d p2; 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> template <class Inspector>
bool inspect(Inspector& f, line& x) { bool inspect(Inspector& f, line& x) {
return f.object(x).fields(f.field("p1", x.p1), f.field("p2", x.p2)); return f.object(x).fields(f.field("p1", x.p1), f.field("p2", x.p2));
...@@ -141,6 +149,11 @@ struct dummy_message { ...@@ -141,6 +149,11 @@ struct dummy_message {
caf::variant<std::string, double> content; 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> template <class Inspector>
bool inspect(Inspector& f, dummy_message& x) { bool inspect(Inspector& f, dummy_message& x) {
return f.object(x).fields(f.field("content", x.content)); return f.object(x).fields(f.field("content", x.content));
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE make_config_value_field
#include "caf/make_config_value_field.hpp"
#include "core-test.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/config_option_set.hpp"
#include "caf/config_value_object_access.hpp"
using namespace caf;
namespace {
struct foobar {
int foo = 0;
std::string bar;
foobar() = default;
foobar(int foo, std::string bar) : foo(foo), bar(std::move(bar)) {
// nop
}
};
std::string to_string(const foobar& x) {
return deep_to_string(std::forward_as_tuple(x.foo, x.bar));
}
bool operator==(const foobar& x, const foobar& y) {
return x.foo == y.foo && x.bar == y.bar;
}
bool foo_valid(const int& x) {
return x >= 0;
}
int get_foo_fun(foobar x) {
return x.foo;
}
void set_foo_fun(foobar& x, const int& value) {
x.foo = value;
}
struct get_foo_t {
int operator()(const foobar& x) const noexcept {
return x.foo;
}
};
struct set_foo_t {
int& operator()(foobar& x, int value) const noexcept {
x.foo = value;
return x.foo;
}
};
struct foobar_trait {
using object_type = foobar;
static std::string type_name() {
return "foobar";
}
static span<config_value_field<object_type>*> fields() {
static auto singleton = make_config_value_field_storage(
make_config_value_field("foo", &foobar::foo, 123),
make_config_value_field("bar", &foobar::bar));
return singleton.fields();
}
};
struct foobar_foobar {
foobar x;
foobar y;
foobar_foobar() = default;
foobar_foobar(foobar x, foobar y) : x(x), y(y) {
// nop
}
};
std::string to_string(const foobar_foobar& x) {
return deep_to_string(std::forward_as_tuple(x.x, x.y));
}
bool operator==(const foobar_foobar& x, const foobar_foobar& y) {
return x.x == y.x && x.y == y.y;
}
struct foobar_foobar_trait {
using object_type = foobar_foobar;
static std::string type_name() {
return "foobar-foobar";
}
static span<config_value_field<object_type>*> fields() {
static auto singleton = make_config_value_field_storage(
make_config_value_field("x", &foobar_foobar::x),
make_config_value_field("y", &foobar_foobar::y));
return singleton.fields();
}
};
struct fixture {
get_foo_t get_foo;
set_foo_t set_foo;
config_option_set opts;
void test_foo_field(config_value_field<foobar>& foo_field) {
foobar x;
CAF_CHECK_EQUAL(foo_field.name(), "foo");
CAF_REQUIRE(foo_field.has_default());
CAF_CHECK_EQUAL(foo_field.get(x), config_value(0));
foo_field.set_default(x);
CAF_CHECK_EQUAL(foo_field.get(x), config_value(42));
CAF_CHECK(!foo_field.valid_input(config_value(1.)));
CAF_CHECK(!foo_field.valid_input(config_value(-1)));
CAF_CHECK(!foo_field.set(x, config_value(-1)));
string_view input = "123";
string_parser_state ps{input.begin(), input.end()};
foo_field.parse_cli(ps, x, false);
CAF_CHECK_EQUAL(ps.code, pec::success);
CAF_CHECK_EQUAL(foo_field.get(x), config_value(123));
}
template <class T>
expected<T> read(std::vector<std::string> args) {
settings cfg;
auto res = opts.parse(cfg, args);
if (res.first != pec::success)
return make_error(res.first, *res.second);
auto x = get_if<T>(&cfg, "value");
if (x == none)
return sec::invalid_argument;
return *x;
}
};
} // namespace
namespace caf {
template <>
struct config_value_access<foobar> : config_value_object_access<foobar_trait> {
};
template <>
struct config_value_access<foobar_foobar>
: config_value_object_access<foobar_foobar_trait> {};
} // namespace caf
CAF_TEST_FIXTURE_SCOPE(make_config_value_field_tests, fixture)
CAF_TEST(construction from pointer to member) {
make_config_value_field("foo", &foobar::foo);
make_config_value_field("foo", &foobar::foo, none);
make_config_value_field("foo", &foobar::foo, none, nullptr);
make_config_value_field("foo", &foobar::foo, 42);
make_config_value_field("foo", &foobar::foo, 42, nullptr);
make_config_value_field("foo", &foobar::foo, 42, foo_valid);
make_config_value_field("foo", &foobar::foo, 42,
[](const int& x) { return x != 0; });
}
CAF_TEST(pointer to member access) {
auto foo_field = make_config_value_field("foo", &foobar::foo, 42, foo_valid);
test_foo_field(foo_field);
}
CAF_TEST(construction from getter and setter) {
auto get_foo_lambda = [](const foobar& x) { return x.foo; };
auto set_foo_lambda = [](foobar& x, int value) { x.foo = value; };
make_config_value_field("foo", get_foo, set_foo);
make_config_value_field("foo", get_foo_fun, set_foo);
make_config_value_field("foo", get_foo_fun, set_foo_fun);
make_config_value_field("foo", get_foo_lambda, set_foo_lambda);
}
CAF_TEST(getter and setter access) {
auto foo_field = make_config_value_field("foo", get_foo, set_foo, 42,
foo_valid);
test_foo_field(foo_field);
}
CAF_TEST(object access from dictionary - foobar) {
settings x;
put(x, "my-value.bar", "hello");
CAF_MESSAGE("without foo member");
{
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none);
auto fb = get<foobar>(x, "my-value");
CAF_CHECK_EQUAL(fb.foo, 123);
CAF_CHECK_EQUAL(fb.bar, "hello");
}
CAF_MESSAGE("with foo member");
put(x, "my-value.foo", 42);
{
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none);
auto fb = get<foobar>(x, "my-value");
CAF_CHECK_EQUAL(fb.foo, 42);
CAF_CHECK_EQUAL(fb.bar, "hello");
}
}
CAF_TEST(object access from dictionary - foobar_foobar) {
settings x;
put(x, "my-value.x.foo", 1);
put(x, "my-value.x.bar", "hello");
put(x, "my-value.y.bar", "world");
CAF_REQUIRE(holds_alternative<foobar_foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar_foobar>(&x, "my-value") != caf::none);
auto fbfb = get<foobar_foobar>(x, "my-value");
CAF_CHECK_EQUAL(fbfb.x.foo, 1);
CAF_CHECK_EQUAL(fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(fbfb.y.foo, 123);
CAF_CHECK_EQUAL(fbfb.y.bar, "world");
}
CAF_TEST(object access from CLI arguments - foobar) {
opts.add<foobar>("value,v", "some value");
CAF_CHECK_EQUAL(read<foobar>({"--value={foo = 1, bar = hello}"}),
foobar(1, "hello"));
CAF_CHECK_EQUAL(read<foobar>({"-v{bar = \"hello\"}"}), foobar(123, "hello"));
CAF_CHECK_EQUAL(read<foobar>({"-v", "{foo = 1, bar =hello ,}"}),
foobar(1, "hello"));
}
CAF_TEST(object access from CLI arguments - foobar_foobar) {
using fbfb = foobar_foobar;
opts.add<fbfb>("value,v", "some value");
CAF_CHECK_EQUAL(read<fbfb>({"-v{x={bar = hello},y={foo=1,bar=world!},}"}),
fbfb({123, "hello"}, {1, "world!"}));
}
namespace {
constexpr const char* config_text = R"__(
arg1 = {
foo = 42
bar = "Don't panic!"
}
arg2 = {
x = {
foo = 1
bar = "hello"
}
y = {
foo = 2
bar = "world"
}
}
)__";
struct test_config : actor_system_config {
test_config() {
opt_group{custom_options_, "global"}
.add(fb, "arg1,1", "some foobar")
.add(fbfb, "arg2,2", "somme foobar-foobar");
}
foobar fb;
foobar_foobar fbfb;
};
} // namespace
CAF_TEST(object access from actor system config - file input) {
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(0, nullptr, in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.fb.foo, 42);
CAF_CHECK_EQUAL(cfg.fb.bar, "Don't panic!");
CAF_CHECK_EQUAL(cfg.fbfb.x.foo, 1);
CAF_CHECK_EQUAL(cfg.fbfb.y.foo, 2);
CAF_CHECK_EQUAL(cfg.fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(cfg.fbfb.y.bar, "world");
}
CAF_TEST(object access from actor system config - file input and arguments) {
std::vector<std::string> args{
"-2",
"{y = {bar = CAF, foo = 20}, x = {foo = 10, bar = hello}}",
};
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(std::move(args), in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.fb.foo, 42);
CAF_CHECK_EQUAL(cfg.fb.bar, "Don't panic!");
CAF_CHECK_EQUAL(cfg.fbfb.x.foo, 10);
CAF_CHECK_EQUAL(cfg.fbfb.y.foo, 20);
CAF_CHECK_EQUAL(cfg.fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(cfg.fbfb.y.bar, "CAF");
}
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