Commit 15f149bd authored by Dominik Charousset's avatar Dominik Charousset

Add new object-based access API

The new utility class config_value_object_access makes it much simpler
for users to specialize config_value_access. Now, users only need to
implement a trait class for config_value_object_access to get all the
low-level sum type calls as well as the parsing code out of their way.

The center piece of the trait class is a getter for fields of the
user-defined type. This new fields-based API either works with pointers
to members or with paris of getter and setter functions. In both cases,
users can also add default values and validity predicates.
parent b2272073
......@@ -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"
......
......@@ -259,12 +259,28 @@ CAF_DEFAULT_CONFIG_VALUE_ACCESS(bool, "boolean");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(double, "real64");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(atom_value, "atom");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(timespan, "timespan");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(std::string, "string");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(config_value::list, "list");
CAF_DEFAULT_CONFIG_VALUE_ACCESS(config_value::dictionary, "dictionary");
#undef CAF_DEFAULT_CONFIG_VALUE_ACCESS
template <>
struct config_value_access<std::string>
: default_config_value_access<std::string> {
using super = default_config_value_access<std::string>;
static std::string type_name() {
return "string";
}
using super::parse_cli;
static void parse_cli(string_parser_state& ps, std::string& x,
const char* char_blacklist) {
detail::parse_element(ps, x, char_blacklist);
}
};
enum class select_config_value_hint {
is_integral,
is_map,
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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` .
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
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 <type_traits>
#include "caf/config_value.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
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)...);
}
};
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 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>
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
} // namespace caf
......@@ -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 {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
......@@ -67,6 +67,11 @@ enum class pec : uint8_t {
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.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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(not 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(not 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_MESSAGE("with foo member");
}
}
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