Unverified Commit 11c4eaca authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #929

Allow users to extend config_value API
parents 41df839f 25a601e1
......@@ -50,6 +50,8 @@
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/config_value_object_access.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/defaults.hpp"
#include "caf/deserializer.hpp"
......@@ -69,6 +71,7 @@
#include "caf/local_actor.hpp"
#include "caf/logger.hpp"
#include "caf/make_config_option.hpp"
#include "caf/make_config_value_field.hpp"
#include "caf/may_have_timeout.hpp"
#include "caf/memory_managed.hpp"
#include "caf/message.hpp"
......
This diff is collapsed.
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 --------------------------------------------------------------
/// Checks whether `x` matches the type of this field.
virtual bool type_check(const config_value& x) const noexcept = 0;
/// Returns whether this field in `object` contains valid data.
virtual bool valid(const Object& object) const noexcept = 0;
/// 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;
// -- modifiers --------------------------------------------------------------
/// Sets this field in `object` to `x`.
/// @pre `can_set(x)`
virtual void 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,
const char* char_blacklist = "") 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()) {
auto value = caf::get_if(dict, field->name());
if (!value) {
if (!field->has_default())
return false;
if (dst)
field->set_default(*dst);
} else if (field->type_check(*value)) {
if (dst)
field->set(*dst, *value);
} else {
return false;
}
}
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;
}
static void parse_cli(string_parser_state& ps, object_type& x) {
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, "=}");
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, ",}");
if (ps.code > pec::trailing_character)
return;
if (ps.at_end()) {
ps.code = pec::unexpected_eof;
return;
}
if (!fptr->valid(x)) {
ps.code = pec::illegal_argument;
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-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/dispatch_parse_cli.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
#include "caf/string_view.hpp"
namespace caf {
namespace 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<Object> {
public:
using member_pointer = Value Object::*;
using object_type = Object;
using value_type = Value;
using predicate_pointer = bool (*)(const value_type&);
constexpr config_value_field_impl(string_view name, member_pointer ptr,
optional<value_type> default_value = none,
predicate_pointer predicate = nullptr)
: name_(name),
ptr_(ptr),
default_value_(std::move(default_value)),
predicate_(predicate) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
bool type_check(const config_value& x) const noexcept override {
return holds_alternative<value_type>(x);
}
bool valid(const object_type& x) const noexcept override {
return predicate_ ? predicate_(x.*ptr_) : true;
}
void set(object_type& x, const config_value& y) const override {
x.*ptr_ = caf::get<value_type>(y);
}
config_value get(const object_type& x) const override {
using access = select_config_value_access_t<value_type>;
return config_value{access::convert(x.*ptr_)};
}
void parse_cli(string_parser_state& ps, object_type& x,
const char* char_blacklist) const override {
detail::dispatch_parse_cli(ps, x.*ptr_, char_blacklist);
}
string_view name() const noexcept override {
return name_;
}
bool has_default() const noexcept override {
return static_cast<bool>(default_value_);
}
void set_default(object_type& x) const override {
x.*ptr_ = *default_value_;
}
private:
string_view name_;
member_pointer ptr_;
optional<value_type> default_value_;
predicate_pointer predicate_;
};
template <class Get>
struct config_value_field_base {
using trait = get_callable_trait_t<Get>;
static_assert(trait::num_args == 1,
"Get must take exactly one argument (the object)");
using arg_type = tl_head_t<typename trait::arg_types>;
using value_type = decay_t<typename trait::result_type>;
using type = config_value_field<decay_t<arg_type>>;
};
template <class Get>
using config_value_field_base_t = typename config_value_field_base<Get>::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_t<Get> {
public:
using super = config_value_field_base_t<Get>;
using object_type = typename super::object_type;
using get_trait = get_callable_trait_t<Get>;
using value_type = decay_t<typename get_trait::result_type>;
using predicate_pointer = bool (*)(const value_type&);
constexpr config_value_field_impl(string_view name, Get getter, Set setter,
optional<value_type> default_value = none,
predicate_pointer predicate = nullptr)
: name_(name),
get_(std::move(getter)),
set_(std::move(setter)),
default_value_(std::move(default_value)),
predicate_(predicate) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
bool type_check(const config_value& x) const noexcept override {
return holds_alternative<value_type>(x);
}
void set(object_type& x, const config_value& y) const override {
set_(x, caf::get<value_type>(y));
}
config_value get(const object_type& x) const override {
using access = select_config_value_access_t<value_type>;
return config_value{access::convert(get_(x))};
}
bool valid(const object_type& x) const noexcept override {
return predicate_ ? predicate_(get_(x)) : true;
}
void parse_cli(string_parser_state& ps, object_type& x,
const char* char_blacklist) const override {
value_type tmp;
detail::dispatch_parse_cli(ps, tmp, char_blacklist);
if (ps.code <= pec::trailing_character)
set_(x, std::move(tmp));
}
string_view name() const noexcept override {
return name_;
}
bool has_default() const noexcept override {
return static_cast<bool>(default_value_);
}
void set_default(object_type& x) const override {
set_(x, *default_value_);
}
private:
string_view name_;
Get get_;
Set set_;
optional<value_type> default_value_;
predicate_pointer predicate_;
};
} // namespace detail
} // namespace caf
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* 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 *
......@@ -18,116 +18,44 @@
#pragma once
#include <string>
#include <type_traits>
#include <vector>
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include "caf/timestamp.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
template <size_t Bytes>
struct type_name_builder_int_size;
template <>
struct type_name_builder_int_size<1> {
void operator()(std::string& result) const {
result += "8";
}
};
template <>
struct type_name_builder_int_size<2> {
void operator()(std::string& result) const {
result += "16";
}
};
template <>
struct type_name_builder_int_size<4> {
void operator()(std::string& result) const {
result += "32";
}
};
template <>
struct type_name_builder_int_size<8> {
void operator()(std::string& result) const {
result += "64";
}
};
template <class T, bool IsInteger = std::is_integral<T>::value>
struct type_name_builder;
template <>
struct type_name_builder<bool, true> {
void operator()(std::string& result) const {
result += "boolean";
template <class Trait>
struct dispatch_parse_cli_helper {
template <class... Ts>
auto operator()(Ts&&... xs)
-> decltype(Trait::parse_cli(std::forward<Ts>(xs)...)) {
return Trait::parse_cli(std::forward<Ts>(xs)...);
}
};
#define CAF_TYPE_NAME_BUILDER_NOINT(class_name, pretty_name) \
template <> \
struct type_name_builder<class_name, false> { \
void operator()(std::string& result) const { \
result += pretty_name; \
} \
}
CAF_TYPE_NAME_BUILDER_NOINT(float, "32-bit real");
CAF_TYPE_NAME_BUILDER_NOINT(double, "64-bit real");
CAF_TYPE_NAME_BUILDER_NOINT(timespan, "timespan");
CAF_TYPE_NAME_BUILDER_NOINT(std::string, "string");
CAF_TYPE_NAME_BUILDER_NOINT(atom_value, "atom");
CAF_TYPE_NAME_BUILDER_NOINT(uri, "uri");
#undef CAF_TYPE_NAME_BUILDER
template <class T>
struct type_name_builder<T, true> {
void operator()(std::string& result) const {
// TODO: replace with if constexpr when switching to C++17
if (!std::is_signed<T>::value)
result += 'u';
result += "int";
type_name_builder_int_size<sizeof(T)> g;
g(result);
}
};
template <class T>
struct type_name_builder<std::vector<T>, false> {
void operator()(std::string& result) const {
result += "list of ";
type_name_builder<T> g;
g(result);
}
};
template <class Access, class T>
void dispatch_parse_cli(std::true_type, string_parser_state& ps, T& x,
const char* char_blacklist) {
Access::parse_cli(ps, x, char_blacklist);
}
template <class T>
struct type_name_builder<dictionary<T>, false> {
void operator()(std::string& result) const {
result += "dictionary of ";
type_name_builder<T> g;
g(result);
}
};
template <class Access, class T>
void dispatch_parse_cli(std::false_type, string_parser_state& ps, T& x,
const char*) {
Access::parse_cli(ps, x);
}
template <class T>
std::string type_name() {
std::string result;
type_name_builder<T> f;
f(result);
return result;
void dispatch_parse_cli(string_parser_state& ps, T& x,
const char* char_blacklist) {
using access = caf::select_config_value_access_t<T>;
using helper_fun = dispatch_parse_cli_helper<access>;
using token_type = bool_token<detail::is_callable_with<
helper_fun, string_parser_state&, T&, const char*>::value>;
token_type token;
dispatch_parse_cli<access>(token, ps, x, char_blacklist);
}
} // namespace detail
......
......@@ -25,94 +25,94 @@
#include <utility>
#include <vector>
#include "caf/detail/parser/state.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/error.hpp"
#include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/unit.hpp"
namespace caf {
namespace detail {
using parse_state = parser::state<string_view::iterator>;
// -- boolean type -------------------------------------------------------------
void parse(parse_state& ps, bool& x);
void parse(string_parser_state& ps, bool& x);
// -- signed integer types -----------------------------------------------------
void parse(parse_state& ps, int8_t& x);
void parse(string_parser_state& ps, int8_t& x);
void parse(parse_state& ps, int16_t& x);
void parse(string_parser_state& ps, int16_t& x);
void parse(parse_state& ps, int32_t& x);
void parse(string_parser_state& ps, int32_t& x);
void parse(parse_state& ps, int64_t& x);
void parse(string_parser_state& ps, int64_t& x);
// -- unsigned integer types ---------------------------------------------------
void parse(parse_state& ps, uint8_t& x);
void parse(string_parser_state& ps, uint8_t& x);
void parse(parse_state& ps, uint16_t& x);
void parse(string_parser_state& ps, uint16_t& x);
void parse(parse_state& ps, uint32_t& x);
void parse(string_parser_state& ps, uint32_t& x);
void parse(parse_state& ps, uint64_t& x);
void parse(string_parser_state& ps, uint64_t& x);
// -- non-fixed size integer types ---------------------------------------------
template <class T>
detail::enable_if_t<std::is_integral<T>::value> parse(parse_state& ps, T& x) {
detail::enable_if_t<std::is_integral<T>::value> parse(string_parser_state& ps,
T& x) {
using squashed_type = squashed_int_t<T>;
return parse(ps, reinterpret_cast<squashed_type&>(x));
}
// -- floating point types -----------------------------------------------------
void parse(parse_state& ps, float& x);
void parse(string_parser_state& ps, float& x);
void parse(parse_state& ps, double& x);
void parse(string_parser_state& ps, double& x);
// -- CAF types ----------------------------------------------------------------
void parse(parse_state& ps, timespan& x);
void parse(string_parser_state& ps, timespan& x);
void parse(parse_state& ps, atom_value& x);
void parse(string_parser_state& ps, atom_value& x);
void parse(parse_state& ps, ipv4_address& x);
void parse(string_parser_state& ps, ipv4_address& x);
void parse(parse_state& ps, ipv4_subnet& x);
void parse(string_parser_state& ps, ipv4_subnet& x);
void parse(parse_state& ps, ipv4_endpoint& x);
void parse(string_parser_state& ps, ipv4_endpoint& x);
void parse(parse_state& ps, ipv6_address& x);
void parse(string_parser_state& ps, ipv6_address& x);
void parse(parse_state& ps, ipv6_subnet& x);
void parse(string_parser_state& ps, ipv6_subnet& x);
void parse(parse_state& ps, ipv6_endpoint& x);
void parse(string_parser_state& ps, ipv6_endpoint& x);
void parse(parse_state& ps, uri& x);
void parse(string_parser_state& ps, uri& x);
// -- STL types ----------------------------------------------------------------
void parse(parse_state& ps, std::string& x);
void parse(string_parser_state& ps, std::string& x);
// -- container types ----------------------------------------------------------
void parse_element(parse_state& ps, std::string& x, const char* char_blacklist);
void parse_element(string_parser_state& ps, std::string& x,
const char* char_blacklist);
template <class T>
enable_if_t<!is_pair<T>::value> parse_element(parse_state& ps, T& x,
enable_if_t<!is_pair<T>::value> parse_element(string_parser_state& ps, T& x,
const char*) {
parse(ps, x);
}
template <class First, class Second, size_t N>
void parse_element(parse_state& ps, std::pair<First, Second>& kvp,
void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp,
const char (&char_blacklist)[N]) {
static_assert(N > 0, "empty array");
// TODO: consider to guard the blacklist computation with
......@@ -133,7 +133,7 @@ void parse_element(parse_state& ps, std::pair<First, Second>& kvp,
}
template <class T>
enable_if_tt<is_iterable<T>> parse(parse_state& ps, T& xs) {
enable_if_tt<is_iterable<T>> parse(string_parser_state& ps, T& xs) {
using value_type = deconst_kvp_t<typename T::value_type>;
static constexpr auto is_map_type = is_pair<value_type>::value;
static constexpr auto opening_char = is_map_type ? '{' : '[';
......@@ -181,7 +181,7 @@ enable_if_tt<is_iterable<T>> parse(parse_state& ps, T& xs) {
template <class T>
error parse(string_view str, T& x) {
parse_state ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
parse(ps, x);
if (ps.code == pec::success)
return none;
......
......@@ -26,7 +26,6 @@
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -40,9 +39,8 @@ namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_atom(state<Iterator, Sentinel>& ps, Consumer&& consumer,
bool accept_unquoted = false) {
template <class State, class Consumer>
void read_atom(State& ps, Consumer&& consumer, bool accept_unquoted = false) {
size_t pos = 0;
char buf[11];
memset(buf, 0, sizeof(buf));
......
......@@ -24,7 +24,6 @@
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -37,8 +36,8 @@ namespace detail {
namespace parser {
/// Reads a boolean.
template <class Iterator, class Sentinel, class Consumer>
void read_bool(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_bool(State& ps, Consumer&& consumer) {
bool res = false;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
......
......@@ -26,7 +26,6 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
......@@ -45,8 +44,8 @@ namespace parser {
/// @param consumer Sink for generated values.
/// @param start_value Allows another parser to pre-initialize this parser with
/// the pre-decimal value.
template <class Iterator, class Sentinel, class Consumer, class ValueType>
void read_floating_point(state<Iterator, Sentinel>& ps, Consumer&& consumer,
template <class State, class Consumer, class ValueType>
void read_floating_point(State& ps, Consumer&& consumer,
optional<ValueType> start_value,
bool negative = false) {
// Any exponent larger than 511 always overflows.
......@@ -185,8 +184,8 @@ void read_floating_point(state<Iterator, Sentinel>& ps, Consumer&& consumer,
// clang-format on
}
template <class Iterator, class Sentinel, class Consumer>
void read_floating_point(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_floating_point(State& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
return read_floating_point(ps, consumer, optional<value_type>{});
......
......@@ -58,8 +58,8 @@ namespace parser {
// }]
//
template <class Iterator, class Sentinel, class Consumer>
void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&&) {
template <class State, class Consumer>
void read_ini_comment(State& ps, Consumer&&) {
start();
term_state(init) {
transition(done, '\n')
......@@ -71,11 +71,11 @@ void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&&) {
fin();
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer);
template <class State, class Consumer>
void read_ini_value(State& ps, Consumer&& consumer);
template <class Iterator, class Sentinel, class Consumer>
void read_ini_list(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini_list(State& ps, Consumer&& consumer) {
start();
state(init) {
epsilon(before_value)
......@@ -98,8 +98,8 @@ void read_ini_list(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
fin();
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_map(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini_map(State& ps, Consumer&& consumer) {
std::string key;
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
......@@ -154,8 +154,8 @@ void read_ini_map(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
// clang-format on
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_uri(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini_uri(State& ps, Consumer&& consumer) {
uri_builder builder;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
......@@ -180,8 +180,8 @@ void read_ini_uri(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
fin();
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini_value(State& ps, Consumer&& consumer) {
start();
state(init) {
fsm_epsilon(read_string(ps, consumer), done, '"')
......@@ -200,8 +200,8 @@ void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
}
/// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer>
void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini_section(State& ps, Consumer&& consumer) {
using std::swap;
std::string tmp;
auto alnum = [](char x) { return isalnum(x) || x == '_'; };
......@@ -254,8 +254,8 @@ void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
/// Reads a nested group, e.g., "[foo.bar]" would consume "[foo.]" in read_ini
/// and then delegate to this function for parsing "bar]".
template <class Iterator, class Sentinel, class Consumer>
void read_nested_group(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_nested_group(State& ps, Consumer&& consumer) {
using std::swap;
std::string key;
auto alnum = [](char x) { return isalnum(x) || x == '_'; };
......@@ -290,8 +290,8 @@ void read_nested_group(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
}
/// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ini(State& ps, Consumer&& consumer) {
using std::swap;
std::string tmp{"global"};
auto alnum = [](char x) { return isalnum(x) || x == '_'; };
......
......@@ -25,7 +25,6 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/ipv4_address.hpp"
......@@ -48,8 +47,8 @@ struct read_ipv4_octet_consumer {
}
};
template <class Iterator, class Sentinel, class Consumer>
void read_ipv4_octet(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class State, class Consumer>
void read_ipv4_octet(State& ps, Consumer& consumer) {
uint8_t res = 0;
// Reads the a decimal place.
auto rd_decimal = [&](char c) {
......@@ -72,8 +71,8 @@ void read_ipv4_octet(state<Iterator, Sentinel>& ps, Consumer& consumer) {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ipv4_address(State& ps, Consumer&& consumer) {
read_ipv4_octet_consumer f;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
......
......@@ -27,7 +27,6 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/ipv4_address.hpp"
......@@ -57,8 +56,8 @@ namespace parser {
// h16 = 1*4HEXDIG
/// Reads 16 (hex) bits of an IPv6 address.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_h16(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class State, class Consumer>
void read_ipv6_h16(State& ps, Consumer& consumer) {
uint16_t res = 0;
size_t digits = 0;
// Reads the a hexadecimal place.
......@@ -83,8 +82,8 @@ void read_ipv6_h16(state<Iterator, Sentinel>& ps, Consumer& consumer) {
}
/// Reads 16 (hex) or 32 (IPv4 notation) bits of an IPv6 address.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_h16_or_l32(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class State, class Consumer>
void read_ipv6_h16_or_l32(State& ps, Consumer& consumer) {
enum mode_t { indeterminate, v6_bits, v4_octets };
mode_t mode = indeterminate;
uint16_t hex_res = 0;
......@@ -173,8 +172,8 @@ read_ipv6_address_piece_consumer<F> make_read_ipv6_address_piece_consumer(F f) {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_ipv6_address(State& ps, Consumer&& consumer) {
// IPv6 allows omitting blocks of zeros, splitting the string into a part
// before the zeros (prefix) and a part after the zeros (suffix). For example,
// ff::1 is 00FF0000000000000000000000000001
......
......@@ -26,7 +26,6 @@
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -41,8 +40,8 @@ namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
template <class State, class Consumer>
void read_number(State& ps, Consumer& consumer) {
// Our result when reading an integer number.
int64_t result = 0;
// Computes the result on success.
......
......@@ -27,7 +27,6 @@
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
......@@ -45,9 +44,8 @@ namespace parser {
/// Reads a number or a duration, i.e., on success produces an `int64_t`, a
/// `double`, or a `timespan`.
template <class Iterator, class Sentinel, class Consumer>
void read_number_or_timespan(state<Iterator, Sentinel>& ps,
Consumer& consumer) {
template <class State, class Consumer>
void read_number_or_timespan(State& ps, Consumer& consumer) {
using namespace std::chrono;
struct interim_consumer {
variant<none_t, int64_t, double> interim;
......
......@@ -26,7 +26,6 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -41,8 +40,8 @@ namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_signed_integer(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_signed_integer(State& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
static_assert(std::is_integral<value_type>::value
......
......@@ -23,7 +23,6 @@
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -37,8 +36,8 @@ namespace parser {
/// Reads a quoted or unquoted string. Quoted strings allow escaping, while
/// unquoted strings may only include alphanumeric characters.
template <class Iterator, class Sentinel, class Consumer>
void read_string(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_string(State& ps, Consumer&& consumer) {
std::string res;
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
......
......@@ -24,7 +24,6 @@
#include "caf/config.hpp"
#include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
......@@ -39,8 +38,8 @@ namespace detail {
namespace parser {
/// Reads a timespan.
template <class Iterator, class Sentinel, class Consumer>
void read_timespan(state<Iterator, Sentinel>& ps, Consumer&& consumer,
template <class State, class Consumer>
void read_timespan(State& ps, Consumer&& consumer,
optional<int64_t> num = none) {
using namespace std::chrono;
struct interim_consumer {
......
......@@ -26,7 +26,6 @@
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
......@@ -41,8 +40,8 @@ namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_unsigned_integer(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_unsigned_integer(State& ps, Consumer&& consumer) {
using consumer_type = typename std::decay<Consumer>::type;
using value_type = typename consumer_type::value_type;
static_assert(std::is_integral<value_type>::value
......
......@@ -22,7 +22,6 @@
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
#include "caf/uri.hpp"
......@@ -47,8 +46,8 @@ namespace parser {
// generate ranges for the subcomponents. URIs can't have linebreaks, so we can
// safely keep track of the position by looking at the column.
template <class Iterator, class Sentinel>
void read_uri_percent_encoded(state<Iterator, Sentinel>& ps, std::string& str) {
template <class State>
void read_uri_percent_encoded(State& ps, std::string& str) {
uint8_t char_code = 0;
auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
......@@ -75,8 +74,8 @@ inline bool uri_unprotected_char(char c) {
transition(next_state, uri_unprotected_char, dest += ch) \
fsm_transition(read_uri_percent_encoded(ps, dest), next_state, '%')
template <class Iterator, class Sentinel, class Consumer>
void read_uri_query(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_uri_query(State& ps, Consumer&& consumer) {
// Local variables.
uri::query_map result;
std::string key;
......@@ -113,8 +112,8 @@ void read_uri_query(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
fin();
}
template <class Iterator, class Sentinel, class Consumer>
void read_uri(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class State, class Consumer>
void read_uri(State& ps, Consumer&& consumer) {
// Local variables.
std::string str;
uint16_t port = 0;
......
......@@ -445,6 +445,7 @@ struct callable_trait<R (Ts...)> {
using arg_types = type_list<Ts...>;
using fun_sig = R (Ts...);
using fun_type = std::function<R (Ts...)>;
static constexpr size_t num_args = sizeof...(Ts);
};
// member const function pointer
......@@ -508,6 +509,9 @@ struct get_callable_trait_helper<T, false, false> {};
template <class T>
struct get_callable_trait : get_callable_trait_helper<decay_t<T>> {};
template <class T>
using get_callable_trait_t = typename get_callable_trait<T>::type;
/// Checks wheter `T` is a function or member function.
template <class T>
struct is_callable {
......
......@@ -56,6 +56,8 @@ template <class> struct timeout_definition;
template <class, class> class stream_stage;
template <class Iterator, class Sentinel = Iterator> struct parser_state;
// -- 3 param templates --------------------------------------------------------
template <class, class, int> class actor_cast_access;
......
......@@ -23,7 +23,7 @@
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_name.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/fwd.hpp"
......@@ -48,20 +48,22 @@ void store_impl(void* ptr, const config_value& x) {
template <class T>
config_value get_impl(const void* ptr) {
return config_value{*reinterpret_cast<const T*>(ptr)};
using trait = select_config_value_access_t<T>;
return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))};
}
template <class T>
expected<config_value> parse_impl(T* ptr, string_view str) {
if (ptr != nullptr) {
if (auto err = parse(str, *ptr))
return err;
return config_value{*ptr};
if (!ptr) {
T tmp;
return parse_impl(&tmp, str);
}
T tmp;
if (auto err = parse(str, tmp))
return err;
return config_value{std::move(tmp)};
using trait = select_config_value_access_t<T>;
string_parser_state ps{str.begin(), str.end()};
trait::parse_cli(ps, *ptr);
if (ps.code != pec::success)
return make_error(ps);
return config_value{trait::convert(*ptr)};
}
expected<config_value> parse_impl(std::string* ptr, string_view str);
......@@ -73,9 +75,10 @@ expected<config_value> parse_impl_delegate(void* ptr, string_view str) {
template <class T>
config_option::meta_state* option_meta_state_instance() {
using trait = select_config_value_access_t<T>;
static config_option::meta_state obj{check_impl<T>, store_impl<T>,
get_impl<T>, parse_impl_delegate<T>,
detail::type_name<T>()};
trait::type_name()};
return &obj;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* 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 *
......@@ -21,29 +21,39 @@
#include <cctype>
#include <cstdint>
#include "caf/fwd.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
namespace detail {
namespace parser {
template <class Iterator, class Sentinel = Iterator>
struct state {
/// Stores all informations necessary for implementing an FSM-based parser.
template <class Iterator, class Sentinel>
struct parser_state {
/// Current position of the parser.
Iterator i;
/// End-of-input marker.
Sentinel e;
/// Current state of the parser.
pec code;
/// Current line in the input.
int32_t line;
/// Position in the current line.
int32_t column;
state() noexcept : i(), e(), code(pec::success), line(1), column(1) {
parser_state() noexcept : i(), e(), code(pec::success), line(1), column(1) {
// nop
}
explicit state(Iterator first) noexcept : state() {
explicit parser_state(Iterator first) noexcept : parser_state() {
i = first;
}
state(Iterator first, Sentinel last) noexcept : state() {
parser_state(Iterator first, Sentinel last) noexcept : parser_state() {
i = first;
e = last;
}
......@@ -92,6 +102,15 @@ struct state {
}
};
} // namespace parser
} // namespace detail
/// Returns an error object from the current code in `ps` as well as its
/// current position.
template <class Iterator, class Sentinel>
auto make_error(const parser_state<Iterator, Sentinel>& ps)
-> decltype(make_error(ps.code, ps.line, ps.column)) {
return make_error(ps.code, ps.line, ps.column);
}
/// Specialization for parsers operating on string views.
using string_parser_state = parser_state<string_view::iterator>;
} // namespace caf
......@@ -65,6 +65,13 @@ enum class pec : uint8_t {
missing_argument,
/// Stopped because the key of a category was taken.
illegal_category,
/// Stopped at an unexpected field name while reading a user-defined type.
invalid_field_name,
/// Stopped at a repeated field name while reading a user-defined type.
repeated_field_name,
/// Stopped while reading a user-defined type with one or more missing
/// mandatory fields.
missing_field,
};
/// Returns an error object from given error code.
......@@ -72,7 +79,7 @@ error make_error(pec code);
/// Returns an error object from given error code with additional context
/// information for where the parser stopped in the input.
error make_error(pec code, size_t line, size_t column);
error make_error(pec code, int32_t line, int32_t column);
/// Returns an error object from given error code with additional context
/// information for where the parser stopped in the argument.
......
......@@ -221,6 +221,13 @@ auto make_span(T& xs) -> span<detail::remove_reference_t<decltype(xs[0])>> {
return {xs.data(), xs.size()};
}
/// Convenience function to make using `caf::span` more convenient without the
/// deduction guides.
template <class T, size_t N>
span<T> make_span(T (&xs)[N]) {
return {xs, N};
}
/// Convenience function to make using `caf::span` more convenient without the
/// deduction guides.
template <class T>
......
......@@ -22,7 +22,6 @@
#include <vector>
#include "caf/detail/comparable.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/unordered_flat_map.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive_ptr.hpp"
......
......@@ -494,8 +494,7 @@ error actor_system_config::parse_config(std::istream& source,
if (!source)
return make_error(sec::runtime_error, "source stream invalid");
detail::ini_consumer consumer{opts, result};
detail::parser::state<ini_iter, ini_sentinel> res;
res.i = ini_iter{&source};
parser_state<ini_iter, ini_sentinel> res{ini_iter{&source}};
detail::parser::read_ini(res, consumer);
if (res.i != res.e)
return make_error(res.code, res.line, res.column);
......
......@@ -26,7 +26,9 @@
#include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
......@@ -66,10 +68,8 @@ expected<config_value> config_value::parse(string_view::iterator first,
if (++i == last)
return make_error(pec::unexpected_eof);
// Dispatch to parser.
parser::state<string_view::iterator> res;
detail::ini_value_consumer f;
res.i = i;
res.e = last;
string_parser_state res{i, last};
parser::read_ini_value(res, f);
if (res.code == pec::success)
return std::move(f.result);
......
......@@ -39,7 +39,7 @@
#include "caf/uri_builder.hpp"
#define PARSE_IMPL(type, parser_name) \
void parse(parse_state& ps, type& x) { \
void parse(string_parser_state& ps, type& x) { \
parser::read_##parser_name(ps, make_consumer(x)); \
}
......@@ -55,7 +55,7 @@ struct literal {
}
};
void parse(parse_state& ps, literal& x) {
void parse(string_parser_state& ps, literal& x) {
CAF_ASSERT(x.str.size() > 0);
if (ps.current() != x.str[0]) {
ps.code = pec::unexpected_character;
......@@ -72,12 +72,12 @@ void parse(parse_state& ps, literal& x) {
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
void parse_sequence(parse_state&) {
void parse_sequence(string_parser_state&) {
// End of recursion.
}
template <class T, class... Ts>
void parse_sequence(parse_state& ps, T&& x, Ts&&... xs) {
void parse_sequence(string_parser_state& ps, T&& x, Ts&&... xs) {
parse(ps, x);
// TODO: use `if constexpr` when switching to C++17
if (sizeof...(Ts) > 0) {
......@@ -111,11 +111,11 @@ PARSE_IMPL(double, floating_point)
PARSE_IMPL(timespan, timespan)
void parse(parse_state& ps, atom_value& x) {
void parse(string_parser_state& ps, atom_value& x) {
parser::read_atom(ps, make_consumer(x), true);
}
void parse(parse_state& ps, uri& x) {
void parse(string_parser_state& ps, uri& x) {
uri_builder builder;
if (ps.consume('<')) {
parser::read_uri(ps, builder);
......@@ -126,7 +126,7 @@ void parse(parse_state& ps, uri& x) {
return;
}
} else {
read_uri(ps, builder);
parser::read_uri(ps, builder);
}
if (ps.code <= pec::trailing_character)
x = builder.make();
......@@ -134,7 +134,7 @@ void parse(parse_state& ps, uri& x) {
PARSE_IMPL(ipv4_address, ipv4_address)
void parse(parse_state& ps, ipv4_subnet& x) {
void parse(string_parser_state& ps, ipv4_subnet& x) {
ipv4_address addr;
uint8_t prefix_length;
parse_sequence(ps, addr, literal{"/"}, prefix_length);
......@@ -147,7 +147,7 @@ void parse(parse_state& ps, ipv4_subnet& x) {
}
}
void parse(parse_state& ps, ipv4_endpoint& x) {
void parse(string_parser_state& ps, ipv4_endpoint& x) {
ipv4_address addr;
uint16_t port;
parse_sequence(ps, addr, literal{":"}, port);
......@@ -157,7 +157,7 @@ void parse(parse_state& ps, ipv4_endpoint& x) {
PARSE_IMPL(ipv6_address, ipv6_address)
void parse(parse_state& ps, ipv6_subnet& x) {
void parse(string_parser_state& ps, ipv6_subnet& x) {
// TODO: this algorithm is currently not one-pass. The reason we need to
// check whether the input is a valid ipv4_subnet first is that "1.2.3.0" is
// a valid IPv6 address, but "1.2.3.0/16" results in the wrong subnet when
......@@ -185,7 +185,7 @@ void parse(parse_state& ps, ipv6_subnet& x) {
}
}
void parse(parse_state& ps, ipv6_endpoint& x) {
void parse(string_parser_state& ps, ipv6_endpoint& x) {
ipv6_address addr;
uint16_t port;
if (ps.consume('[')) {
......@@ -200,7 +200,7 @@ void parse(parse_state& ps, ipv6_endpoint& x) {
x = ipv6_endpoint{addr, port};
}
void parse(parse_state& ps, std::string& x) {
void parse(string_parser_state& ps, std::string& x) {
ps.skip_whitespaces();
if (ps.current() == '"') {
parser::read_string(ps, make_consumer(x));
......@@ -213,7 +213,7 @@ void parse(parse_state& ps, std::string& x) {
ps.code = pec::success;
}
void parse_element(parse_state& ps, std::string& x,
void parse_element(string_parser_state& ps, std::string& x,
const char* char_blacklist) {
ps.skip_whitespaces();
if (ps.current() == '"') {
......
......@@ -21,6 +21,7 @@
#include "caf/detail/network_order.hpp"
#include "caf/detail/parser/read_ipv4_address.hpp"
#include "caf/error.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
......@@ -89,8 +90,8 @@ std::string to_string(const ipv4_address& x) {
error parse(string_view str, ipv4_address& dest) {
using namespace detail;
parser::state<string_view::iterator> res{str.begin(), str.end()};
ipv4_address_consumer f{dest};
string_parser_state res{str.begin(), str.end()};
parser::read_ipv4_address(res, f);
if (res.code == pec::success)
return none;
......
......@@ -23,6 +23,7 @@
#include "caf/detail/parser/read_ipv6_address.hpp"
#include "caf/error.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
......@@ -212,8 +213,8 @@ std::string to_string(ipv6_address x) {
error parse(string_view str, ipv6_address& dest) {
using namespace detail;
parser::state<string_view::iterator> res{str.begin(), str.end()};
ipv6_address_consumer f{dest};
string_parser_state res{str.begin(), str.end()};
parser::read_ipv6_address(res, f);
if (res.code == pec::success)
return none;
......
......@@ -24,9 +24,11 @@
#include "caf/optional.hpp"
#define DEFAULT_META(type, parse_fun) \
config_option::meta_state type##_meta_state{ \
default_config_option_check<type>, default_config_option_store<type>, \
get_impl<type>, parse_fun, detail::type_name<type>()};
config_option::meta_state \
type##_meta_state{default_config_option_check<type>, \
default_config_option_store<type>, get_impl<type>, \
parse_fun, \
select_config_value_access_t<type>::type_name()};
using std::string;
......@@ -75,43 +77,34 @@ config_value bool_get_neg(const void* ptr) {
}
meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg,
nullptr, detail::type_name<bool>()};
meta_state us_res_meta{
[](const config_value& x) -> error {
if (holds_alternative<timespan>(x))
return none;
return make_error(pec::type_mismatch);
},
[](void* ptr, const config_value& x) {
*static_cast<size_t*>(ptr) = static_cast<size_t>(get<timespan>(x).count())
/ 1000;
},
[](const void* ptr) -> config_value {
auto ival = static_cast<int64_t>(*static_cast<const size_t*>(ptr));
timespan val{ival * 1000};
return config_value{val};
},
nullptr,
detail::type_name<timespan>()
};
meta_state ms_res_meta{[](const config_value& x) -> error {
if (holds_alternative<timespan>(x))
return none;
return make_error(pec::type_mismatch);
},
[](void* ptr, const config_value& x) {
*static_cast<size_t*>(ptr) = static_cast<size_t>(
get<timespan>(x).count() / 1000000);
},
[](const void* ptr) -> config_value {
auto ival = static_cast<int64_t>(
*static_cast<const size_t*>(ptr));
timespan val{ival * 1000000};
return config_value{val};
},
nullptr, detail::type_name<timespan>()};
nullptr,
select_config_value_access_t<bool>::type_name()};
error check_timespan(const config_value& x) {
if (holds_alternative<timespan>(x))
return none;
return make_error(pec::type_mismatch);
}
template <uint64_t Denominator>
void store_timespan(void* ptr, const config_value& x) {
*static_cast<size_t*>(ptr) = static_cast<size_t>(get<timespan>(x).count())
/ Denominator;
}
template <uint64_t Denominator>
config_value get_timespan(const void* ptr) {
auto ival = static_cast<int64_t>(*static_cast<const size_t*>(ptr));
timespan val{ival * Denominator};
return config_value{val};
}
meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>,
nullptr,
select_config_value_access_t<timespan>::type_name()};
meta_state ms_res_meta{check_timespan, store_timespan<1000000>,
get_timespan<1000000>, nullptr,
select_config_value_access_t<timespan>::type_name()};
} // namespace
......
......@@ -29,7 +29,7 @@ error make_error(pec code) {
return {static_cast<uint8_t>(code), atom("parser")};
}
error make_error(pec code, size_t line, size_t column) {
error make_error(pec code, int32_t line, int32_t column) {
config_value::dictionary context;
context["line"] = line;
context["column"] = column;
......
......@@ -146,12 +146,11 @@ std::string to_string(const uri::authority_type& x) {
}
error parse(string_view str, uri& dest) {
detail::parse_state ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
parse(ps, dest);
if (ps.code == pec::success)
return none;
return make_error(ps.code, static_cast<size_t>(ps.line),
static_cast<size_t>(ps.column));
return make_error(ps);
}
expected<uri> make_uri(string_view str) {
......
......@@ -185,7 +185,6 @@ CAF_TEST(type timespan) {
CAF_TEST(lists) {
using int_list = std::vector<int>;
CAF_CHECK_EQUAL(read<int_list>(""), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("[]"), int_list({}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
......
......@@ -33,7 +33,7 @@ using ls = std::vector<std::string>;
namespace {
const char test_ini[] = R"(
constexpr const string_view test_ini = R"(
is_server=true
port=4242
nodes=["sun", "venus", ]
......@@ -44,7 +44,7 @@ file-name = "foobar.ini" ; our file name
impl = 'foo';some atom
)";
const char test_ini2[] = R"(
constexpr const string_view test_ini2 = R"(
is_server = true
logger = {
file-name = "foobar.ini"
......@@ -58,7 +58,6 @@ nodes = ["sun", "venus"]
)";
struct fixture {
detail::parser::state<std::string::const_iterator> res;
config_option_set options;
settings config;
......@@ -78,20 +77,18 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(ini_consumer_tests, fixture)
CAF_TEST(ini_value_consumer) {
std::string str = R"("hello world")";
string_view str = R"("hello world")";
detail::ini_value_consumer consumer;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_ini_value(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<string>(consumer.result), "hello world");
}
CAF_TEST(ini_consumer) {
std::string str = test_ini;
string_view str = test_ini;
detail::ini_consumer consumer{options, config};
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<bool>(config, "is_server"), true);
......@@ -103,12 +100,11 @@ CAF_TEST(ini_consumer) {
}
CAF_TEST(simplified syntax) {
std::string str = test_ini;
string_view str = test_ini;
CAF_MESSAGE("read test_ini");
{
detail::ini_consumer consumer{options, config};
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
}
......@@ -117,8 +113,7 @@ CAF_TEST(simplified syntax) {
CAF_MESSAGE("read test_ini2");
{
detail::ini_consumer consumer{options, config2};
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
}
......
......@@ -61,11 +61,11 @@ timespan operator"" _h(unsigned long long x) {
template <class T>
expected<T> read(string_view str) {
T result;
detail::parse_state ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parse(ps, result);
if (ps.code == pec::success)
return result;
return make_error(ps.code, ps.line, ps.column);
return make_error(ps);
}
} // namespace
......
......@@ -24,6 +24,8 @@
#include <string>
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -40,11 +42,9 @@ struct atom_parser_consumer {
using res_t = variant<pec, atom_value>;
struct atom_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
res_t operator()(string_view str) {
atom_parser_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_atom(res, f);
if (res.code == pec::success)
return f.x;
......
......@@ -24,6 +24,8 @@
#include <string>
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -40,11 +42,9 @@ struct bool_parser_consumer {
using res_t = variant<pec, bool>;
struct bool_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
res_t operator()(string_view str) {
bool_parser_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_bool(res, f);
if (res.code == pec::success)
return f.x;
......
......@@ -24,8 +24,9 @@
#include <string>
#include "caf/detail/parser/state.hpp"
#include "caf/pec.hpp"
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -43,7 +44,7 @@ struct double_consumer {
optional<double> read(string_view str) {
double_consumer consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_floating_point(ps, consumer);
if (ps.code != pec::success)
return none;
......
......@@ -25,6 +25,11 @@
#include <string>
#include <vector>
#include "caf/config_value.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
using namespace caf;
namespace {
......@@ -85,11 +90,9 @@ struct ini_consumer {
};
struct fixture {
expected<log_type> parse(std::string str, bool expect_success = true) {
detail::parser::state<std::string::iterator> res;
expected<log_type> parse(string_view str, bool expect_success = true) {
test_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_ini(res, f);
if ((res.code == pec::success) != expect_success) {
CAF_MESSAGE("unexpected parser result state: " << res.code);
......@@ -105,7 +108,7 @@ log_type make_log(Ts&&... xs) {
}
// Tests basic functionality.
const auto ini0 = R"(
constexpr const string_view ini0 = R"(
[1group]
1value=321
[_foo]
......@@ -204,7 +207,7 @@ const auto ini0_log = make_log(
// clang-format on
// Tests nested parameters.
const auto ini1 = R"(
constexpr const string_view ini1 = R"(
foo {
bar = {
value1 = 1
......@@ -241,11 +244,11 @@ const auto ini1_log = make_log(
);
// clang-format on
const auto ini2 = "#";
constexpr const string_view ini2 = "#";
const auto ini2_log = make_log();
const auto ini3 = "; foobar\n!";
constexpr const string_view ini3 = "; foobar\n!";
const auto ini3_log = make_log();
......
......@@ -27,7 +27,9 @@
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/expected.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -67,11 +69,9 @@ bool operator==(const res_t& x, const res_t& y) {
}
struct numbers_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
res_t operator()(string_view str) {
numbers_parser_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_number(res, f);
if (res.code == pec::success)
return f.x;
......
......@@ -24,6 +24,8 @@
#include <string>
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -68,11 +70,9 @@ bool operator==(const res_t& x, const res_t& y) {
}
struct number_or_timespan_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
res_t operator()(string_view str) {
number_or_timespan_parser_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_number_or_timespan(res, f);
if (res.code == pec::success)
return f.x;
......
......@@ -22,8 +22,9 @@
#include "caf/test/dsl.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -43,7 +44,7 @@ struct signed_integer_consumer {
template <class T>
optional<T> read(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
if (ps.code != pec::success)
return none;
......@@ -53,7 +54,7 @@ optional<T> read(string_view str) {
template <class T>
bool underflow(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
return ps.code == pec::integer_underflow;
}
......@@ -61,7 +62,7 @@ bool underflow(string_view str) {
template <class T>
bool overflow(string_view str) {
signed_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_signed_integer(ps, consumer);
return ps.code == pec::integer_overflow;
}
......
......@@ -24,6 +24,8 @@
#include <string>
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -40,11 +42,9 @@ struct string_parser_consumer {
using res_t = variant<pec, std::string>;
struct string_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
res_t operator()(string_view str) {
string_parser_consumer f;
res.i = str.begin();
res.e = str.end();
string_parser_state res{str.begin(), str.end()};
detail::parser::read_string(res, f);
if (res.code == pec::success)
return f.x;
......
......@@ -24,6 +24,10 @@
#include <chrono>
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
namespace {
......@@ -62,7 +66,7 @@ struct timespan_consumer {
optional<timespan> read(string_view str) {
timespan_consumer consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_timespan(ps, consumer);
if (ps.code != pec::success)
return none;
......
......@@ -22,8 +22,9 @@
#include "caf/test/dsl.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -43,7 +44,7 @@ struct unsigned_integer_consumer {
template <class T>
optional<T> read(string_view str) {
unsigned_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_unsigned_integer(ps, consumer);
if (ps.code != pec::success)
return none;
......@@ -53,7 +54,7 @@ optional<T> read(string_view str) {
template <class T>
bool overflow(string_view str) {
unsigned_integer_consumer<T> consumer;
detail::parser::state<string_view::iterator> ps{str.begin(), str.end()};
string_parser_state ps{str.begin(), str.end()};
detail::parser::read_unsigned_integer(ps, consumer);
return ps.code == pec::integer_overflow;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 "caf/test/dsl.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(foo_field.valid(x));
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.type_check(config_value(1.)));
CAF_CHECK(foo_field.type_check(config_value(-1)));
foo_field.set(x, config_value(-1));
CAF_CHECK_EQUAL(foo_field.get(x), config_value(-1));
CAF_CHECK(!foo_field.valid(x));
string_view input = "123";
string_parser_state ps{input.begin(), input.end()};
foo_field.parse_cli(ps, x);
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(oject 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(oject 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!"}));
}
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