Commit e26b322a authored by Dominik Charousset's avatar Dominik Charousset

Integrate new config valur readers / writers

parent 6df8d506
...@@ -69,6 +69,8 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -69,6 +69,8 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/config_option_adder.cpp src/config_option_adder.cpp
src/config_option_set.cpp src/config_option_set.cpp
src/config_value.cpp src/config_value.cpp
src/config_value_reader.cpp
src/config_value_writer.cpp
src/credit_controller.cpp src/credit_controller.cpp
src/decorator/sequencer.cpp src/decorator/sequencer.cpp
src/default_attachable.cpp src/default_attachable.cpp
...@@ -158,8 +160,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -158,8 +160,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/sec_strings.cpp src/sec_strings.cpp
src/serializer.cpp src/serializer.cpp
src/settings.cpp src/settings.cpp
src/settings_reader.cpp
src/settings_writer.cpp
src/size_based_credit_controller.cpp src/size_based_credit_controller.cpp
src/skip.cpp src/skip.cpp
src/stream_aborter.cpp src/stream_aborter.cpp
...@@ -233,7 +233,8 @@ caf_add_test_suites(caf-core-test ...@@ -233,7 +233,8 @@ caf_add_test_suites(caf-core-test
config_option config_option
config_option_set config_option_set
config_value config_value
config_value_adaptor config_value_reader
config_value_writer
const_typed_message_view const_typed_message_view
constructor_attach constructor_attach
continuous_streaming continuous_streaming
...@@ -289,7 +290,6 @@ caf_add_test_suites(caf-core-test ...@@ -289,7 +290,6 @@ caf_add_test_suites(caf-core-test
local_group local_group
logger logger
mailbox_element mailbox_element
make_config_value_field
message message
message_builder message_builder
message_id message_id
...@@ -313,8 +313,6 @@ caf_add_test_suites(caf-core-test ...@@ -313,8 +313,6 @@ caf_add_test_suites(caf-core-test
serial_reply serial_reply
serialization serialization
settings settings
settings_reader
settings_writer
simple_timeout simple_timeout
span span
stateful_actor stateful_actor
......
...@@ -48,16 +48,10 @@ ...@@ -48,16 +48,10 @@
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp" #include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/config_value_adaptor.hpp"
#include "caf/config_value_adaptor_access.hpp"
#include "caf/config_value_adaptor_field.hpp"
#include "caf/config_value_field.hpp"
#include "caf/config_value_object_access.hpp"
#include "caf/const_typed_message_view.hpp" #include "caf/const_typed_message_view.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/defaults.hpp" #include "caf/defaults.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/detail/config_value_adaptor_field_impl.hpp"
#include "caf/downstream_msg.hpp" #include "caf/downstream_msg.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
...@@ -74,7 +68,6 @@ ...@@ -74,7 +68,6 @@
#include "caf/local_actor.hpp" #include "caf/local_actor.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/make_config_option.hpp" #include "caf/make_config_option.hpp"
#include "caf/make_config_value_field.hpp"
#include "caf/may_have_timeout.hpp" #include "caf/may_have_timeout.hpp"
#include "caf/memory_managed.hpp" #include "caf/memory_managed.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
......
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 <array>
#include <tuple>
#include <type_traits>
#include "caf/config_value_adaptor_field.hpp"
#include "caf/detail/config_value_adaptor_field_impl.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Interfaces between a user-defined type and CAF config values by going
/// through intermediate values.
template <class... Ts>
class config_value_adaptor {
public:
using value_type = std::tuple<Ts...>;
using indices = typename detail::il_indices<value_type>::type;
using fields_tuple =
typename detail::select_adaptor_fields<value_type, indices>::type;
using array_type = std::array<config_value_field<value_type>*, sizeof...(Ts)>;
template <class U,
class = detail::enable_if_t<
!std::is_same<detail::decay_t<U>, config_value_adaptor>::value>,
class... Us>
config_value_adaptor(U&& x, Us&&... xs)
: fields_(
make_fields(indices{}, std::forward<U>(x), std::forward<Us>(xs)...)) {
init(indices{});
}
config_value_adaptor(config_value_adaptor&&) = default;
span<typename array_type::value_type> fields() {
return make_span(ptr_fields_);
}
private:
// TODO: This is a workaround for GCC <= 5 because initializing fields_
// directly from (xs...) fails. Remove when moving on to newer
// compilers.
template <long... Pos, class... Us>
fields_tuple make_fields(detail::int_list<Pos...>, Us&&... xs) {
return std::make_tuple(
detail::config_value_adaptor_field_impl<value_type, Pos>(
std::forward<Us>(xs))...);
}
template <long... Pos>
void init(detail::int_list<Pos...>) {
ptr_fields_ = array_type{{&std::get<Pos>(fields_)...}};
}
fields_tuple fields_;
array_type ptr_fields_;
};
template <class... Ts>
config_value_adaptor<typename Ts::value_type...>
make_config_value_adaptor(Ts... fields) {
return {std::move(fields)...};
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/config_value_object_access.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Enables user-defined types in config files and on the CLI by converting
/// them to and from tuples. Wraps a `config_value_object_access` in order to
/// allow CAF to interact with the underlying tuple.
///
/// ~~
/// struct trait {
/// using value_type = ...;
///
/// using tuple_type = ...;
///
/// static config_value_adaptor<...> adaptor_ref();
///
/// static span<config_value_field<object_type>*> fields();
///
/// static void convert(const value_type& src, tuple_type& dst);
///
/// static void convert(const tuple_type& src, value_type& dst);
/// };
/// ~~
template <class Trait>
struct config_value_adaptor_access {
struct object_trait {
using object_type = typename Trait::tuple_type;
static std::string type_name() {
return Trait::type_name();
}
static caf::span<config_value_field<object_type>*> fields() {
return Trait::adaptor_ref().fields();
}
};
using tuple_access = config_value_object_access<object_trait>;
using value_type = typename Trait::value_type;
using tuple_type = typename Trait::tuple_type;
static std::string type_name() {
return Trait::type_name();
}
static bool is(const config_value& x) {
return tuple_access::is(x);
}
static optional<value_type> get_if(const config_value* x) {
if (auto tmp = tuple_access::get_if(x)) {
value_type result;
convert(*tmp, result);
return result;
}
return none;
}
static value_type get(const config_value& x) {
auto tmp = tuple_access::get(x);
value_type result;
convert(tmp, result);
return result;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, value_type& x, Nested nested) {
tuple_type tmp;
tuple_access::parse_cli(ps, tmp, nested);
if (ps.code <= pec::trailing_character)
convert(tmp, x);
}
static void convert(const value_type& src, tuple_type& dst) {
Trait::convert(src, dst);
}
static void convert(const tuple_type& src, value_type& dst) {
Trait::convert(src, dst);
}
static config_value::dictionary convert(const value_type& x) {
tuple_type tmp;
convert(x, tmp);
return tuple_access::convert(tmp);
}
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/optional.hpp"
#include "caf/string_view.hpp"
namespace caf {
/// Describes a field of type T of an adaptor.
template <class T>
struct config_value_adaptor_field {
/// Type of the field.
using value_type = T;
/// Predicate function for verifying user input.
using predicate_function = bool (*)(const value_type&);
/// Name of the field in configuration files and on the CLI.
string_view name;
/// If set, makes the field optional in configuration files and on the CLI by
/// assigning the default whenever the user provides no value.
optional<value_type> default_value;
/// If set, makes the field only accept values that pass this predicate.
predicate_function predicate;
};
/// Convenience function for creating a `config_value_adaptor_field`.
/// @param name name of the field in configuration files and on the CLI.
/// @param default_value if set, provides a fallback value if the user does not
/// provide a value.
/// @param predicate if set, restricts what values the field accepts.
/// @returns a `config_value_adaptor_field` object, constructed from given
/// arguments.
/// @relates config_value_adaptor_field
template <class T>
config_value_adaptor_field<T>
make_config_value_adaptor_field(string_view name,
optional<T> default_value = none,
bool (*predicate)(const T&) = nullptr) {
return {name, std::move(default_value), predicate};
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/fwd.hpp"
#include "caf/parser_state.hpp"
namespace caf {
/// Describes a field of `Object`.
template <class Object>
class config_value_field {
public:
using object_type = Object;
virtual ~config_value_field() = default;
// -- observers --------------------------------------------------------------
/// Returns whether this field has a default value.
virtual bool has_default() const noexcept = 0;
/// Returns the name of this field.
virtual string_view name() const noexcept = 0;
/// Returns the value of this field in `object` as config value.
virtual config_value get(const Object& object) const = 0;
/// Returns whether calling `set` with `x` would succeed.
virtual bool valid_input(const config_value& x) const = 0;
// -- modifiers --------------------------------------------------------------
/// Tries to set this field in `object` to `x`.
/// @returns `true` on success, `false` otherwise.
virtual bool set(Object& object, const config_value& x) const = 0;
/// Restores the default value for this field in `object`.
/// @pre `has_default()`
virtual void set_default(Object& object) const = 0;
/// Parses the content for this field in `object` from `ps`.
virtual void parse_cli(string_parser_state& ps, Object& object,
bool is_nested) const = 0;
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
namespace caf {
/// Enables user-defined types in config files and on the CLI by converting
/// them to and from `config_value::dictionary`.
///
/// ~~
/// struct trait {
/// using object_type = ...;
///
/// static string_value type_name();
///
/// static span<config_value_field<object_type>*> fields();
/// };
/// ~~
template <class Trait>
struct config_value_object_access {
using object_type = typename Trait::object_type;
static std::string type_name() {
return Trait::type_name();
}
static bool extract(const config_value* src, object_type* dst) {
auto dict = caf::get_if<config_value::dictionary>(src);
if (!dict)
return false;
for (auto field : Trait::fields()) {
if (auto value = caf::get_if(dict, field->name())) {
if (dst) {
if (!field->set(*dst, *value))
return false;
} else {
if (!field->valid_input(*value))
return false;
}
} else {
if (!field->has_default())
return false;
if (dst)
field->set_default(*dst);
}
}
return true;
}
static bool is(const config_value& x) {
return extract(&x, nullptr);
}
static optional<object_type> get_if(const config_value* x) {
object_type result;
if (extract(x, &result))
return result;
return none;
}
static object_type get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("config_value does not contain requested object");
return std::move(*result);
}
static config_value::dictionary convert(const object_type& x) {
config_value::dictionary result;
for (auto field : Trait::fields())
result.emplace(field->name(), field->get(x));
return result;
}
template <class Nested>
static void parse_cli(string_parser_state& ps, object_type& x, Nested) {
using field_type = config_value_field<object_type>;
std::vector<field_type*> parsed_fields;
auto got = [&](field_type* f) {
auto e = parsed_fields.end();
return std::find(parsed_fields.begin(), e, f) != e;
};
auto push = [&](field_type* f) {
if (got(f))
return false;
parsed_fields.emplace_back(f);
return true;
};
auto finalize = [&] {
for (auto field : Trait::fields()) {
if (!got(field)) {
if (field->has_default()) {
field->set_default(x);
} else {
ps.code = pec::missing_field;
return;
}
}
}
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
};
auto fs = Trait::fields();
if (!ps.consume('{')) {
ps.code = pec::unexpected_character;
return;
}
using string_access = select_config_value_access_t<std::string>;
config_value::dictionary result;
do {
if (ps.consume('}')) {
finalize();
return;
}
std::string field_name;
string_access::parse_cli(ps, field_name, nested_cli_parsing);
if (ps.code > pec::trailing_character)
return;
if (!ps.consume('=')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
auto predicate = [&](config_value_field<object_type>* x) {
return x->name() == field_name;
};
auto f = std::find_if(fs.begin(), fs.end(), predicate);
if (f == fs.end()) {
ps.code = pec::invalid_field_name;
return;
}
auto fptr = *f;
if (!push(fptr)) {
ps.code = pec::repeated_field_name;
return;
}
fptr->parse_cli(ps, x, true);
if (ps.code > pec::trailing_character)
return;
if (ps.at_end()) {
ps.code = pec::unexpected_eof;
return;
}
result[fptr->name()] = fptr->get(x);
} while (ps.consume(','));
if (!ps.consume('}')) {
ps.code = ps.at_end() ? pec::unexpected_eof : pec::unexpected_character;
return;
}
finalize();
}
};
} // namespace caf
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
namespace caf { namespace caf {
/// Extracts objects from @ref settings. /// Extracts objects from @ref settings.
class settings_reader : public deserializer { class config_value_reader : public deserializer {
public: public:
// -- member types------------------------------------------------------------ // -- member types------------------------------------------------------------
...@@ -66,23 +66,23 @@ public: ...@@ -66,23 +66,23 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
settings_reader(const settings* input, actor_system& sys) config_value_reader(const config_value* input, actor_system& sys)
: super(sys), root_(input) { : super(sys) {
st_.push(input); st_.push(input);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
settings_reader(const settings* input, execution_unit* ctx) config_value_reader(const config_value* input, execution_unit* ctx)
: super(ctx), root_(input) { : super(ctx) {
st_.push(input);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
explicit config_value_reader(const config_value* input)
explicit settings_reader(const settings* input) : config_value_reader(input, nullptr) {
: settings_reader(input, nullptr) {
// nop // nop
} }
~settings_reader() override; ~config_value_reader() override;
// -- stack access ----------------------------------------------------------- // -- stack access -----------------------------------------------------------
...@@ -166,7 +166,6 @@ private: ...@@ -166,7 +166,6 @@ private:
bool fetch_object_type(const settings* obj, type_id_t& type); bool fetch_object_type(const settings* obj, type_id_t& type);
stack_type st_; stack_type st_;
const settings* root_;
}; };
} // namespace caf } // namespace caf
...@@ -18,17 +18,16 @@ ...@@ -18,17 +18,16 @@
#pragma once #pragma once
#include "caf/config_value.hpp" #include "caf/fwd.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/settings.hpp"
#include <stack> #include <stack>
#include <vector> #include <vector>
namespace caf { namespace caf {
/// Writes objects into @ref settings. /// Serializes an objects into a @ref config_value.
class settings_writer final : public serializer { class config_value_writer final : public serializer {
public: public:
// -- member types------------------------------------------------------------ // -- member types------------------------------------------------------------
...@@ -42,30 +41,29 @@ public: ...@@ -42,30 +41,29 @@ public:
struct absent_field {}; struct absent_field {};
using value_type using value_type = variant<config_value*, settings*, absent_field,
= variant<settings*, absent_field, present_field, config_value::list*>; present_field, std::vector<config_value>*>;
using stack_type = std::stack<value_type, std::vector<value_type>>; using stack_type = std::stack<value_type, std::vector<value_type>>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
settings_writer(settings* destination, actor_system& sys) config_value_writer(config_value* dst, actor_system& sys) : super(sys) {
: super(sys), root_(destination) { st_.push(dst);
st_.push(destination);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
settings_writer(settings* destination, execution_unit* ctx) config_value_writer(config_value* dst, execution_unit* ctx) : super(ctx) {
: super(ctx), root_(destination) { st_.push(dst);
has_human_readable_format_ = true; has_human_readable_format_ = true;
} }
explicit settings_writer(settings* destination) explicit config_value_writer(config_value* destination)
: settings_writer(destination, nullptr) { : config_value_writer(destination, nullptr) {
// nop // nop
} }
~settings_writer() override; ~config_value_writer() override;
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
...@@ -140,7 +138,6 @@ private: ...@@ -140,7 +138,6 @@ private:
stack_type st_; stack_type st_;
string_view type_hint_; string_view type_hint_;
settings* root_;
}; };
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <tuple>
#include "caf/config_value.hpp"
#include "caf/config_value_adaptor_field.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/string_view.hpp"
namespace caf::detail {
template <class T, size_t Pos>
class config_value_adaptor_field_impl
: public config_value_field_base<T,
typename std::tuple_element<Pos, T>::type> {
public:
using object_type = T;
using value_type = typename std::tuple_element<Pos, T>::type;
using field_type = config_value_adaptor_field<value_type>;
using predicate_type = bool (*)(const value_type&);
using super = config_value_field_base<object_type, value_type>;
explicit config_value_adaptor_field_impl(field_type x)
: super(x.name, std::move(x.default_value), x.predicate) {
// nop
}
config_value_adaptor_field_impl(config_value_adaptor_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
return std::get<Pos>(x);
}
void set_value(object_type& x, value_type y) const override {
std::get<Pos>(x) = std::move(y);
}
};
template <class T, class Ps>
struct select_adaptor_fields;
template <class T, long... Pos>
struct select_adaptor_fields<T, detail::int_list<Pos...>> {
using type = std::tuple<config_value_adaptor_field_impl<T, Pos>...>;
};
} // namespace caf::detail
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/optional.hpp"
namespace caf::detail {
template <class Object, class Value>
class config_value_field_base : public config_value_field<Object> {
public:
using super = config_value_field<Object>;
using object_type = typename super::object_type;
using value_type = Value;
using predicate_type = bool (*)(const value_type&);
config_value_field_base(string_view name, optional<value_type> default_value,
predicate_type predicate)
: name_(name),
default_value_(std::move(default_value)),
predicate_(predicate) {
// nop
}
config_value_field_base(config_value_field_base&&) = default;
bool has_default() const noexcept override {
return static_cast<bool>(default_value_);
}
string_view name() const noexcept override {
return name_;
}
config_value get(const object_type& object) const override {
using access = caf::select_config_value_access_t<value_type>;
return config_value{access::convert(get_value(object))};
}
bool valid_input(const config_value& x) const override {
if (!predicate_)
return holds_alternative<value_type>(x);
if (auto value = get_if<value_type>(&x))
return predicate_(*value);
return false;
}
bool set(object_type& x, const config_value& y) const override {
if (auto value = get_if<value_type>(&y)) {
if (predicate_ && !predicate_(*value))
return false;
set_value(x, move_if_optional(value));
return true;
}
return false;
}
void set_default(object_type& x) const override {
set_value(x, *default_value_);
}
void parse_cli(string_parser_state& ps, object_type& x,
bool nested) const override {
using access = caf::select_config_value_access_t<value_type>;
value_type tmp;
if (nested)
access::parse_cli(ps, tmp, nested_cli_parsing);
else
access::parse_cli(ps, tmp, top_level_cli_parsing);
if (ps.code <= pec::trailing_character) {
if (predicate_ && !predicate_(tmp))
ps.code = pec::invalid_argument;
else
set_value(x, std::move(tmp));
}
}
virtual const value_type& get_value(const object_type& object) const = 0;
virtual void set_value(object_type& object, value_type value) const = 0;
protected:
string_view name_;
optional<value_type> default_value_;
predicate_type predicate_;
};
} // namespace caf::detail
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include <utility>
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
#include "caf/string_view.hpp"
namespace caf::detail {
template <class MemberObjectPointer>
class config_value_field_impl;
// A config value with direct access to a field via member object pointer.
template <class Value, class Object>
class config_value_field_impl<Value Object::*>
: public config_value_field_base<Object, Value> {
public:
using super = config_value_field_base<Object, Value>;
using member_pointer = Value Object::*;
using object_type = Object;
using value_type = Value;
using predicate_type = bool (*)(const value_type&);
constexpr config_value_field_impl(string_view name, member_pointer ptr,
optional<value_type> default_value = none,
predicate_type predicate = nullptr)
: super(name, std::move(default_value), predicate), ptr_(ptr) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
return x.*ptr_;
}
void set_value(object_type& x, value_type y) const override {
x.*ptr_ = std::move(y);
}
private:
member_pointer ptr_;
};
template <class Get>
struct config_value_field_trait {
using trait = get_callable_trait_t<Get>;
static_assert(trait::num_args == 1,
"Get must take exactly one argument (the object)");
using get_argument_type = tl_head_t<typename trait::arg_types>;
using object_type = decay_t<get_argument_type>;
using get_result_type = typename trait::result_type;
using value_type = decay_t<get_result_type>;
};
// A config value with access to a field via getter and setter.
template <class Get, class Set>
class config_value_field_impl<std::pair<Get, Set>>
: public config_value_field_base<
typename config_value_field_trait<Get>::object_type,
typename config_value_field_trait<Get>::value_type> {
public:
using trait = config_value_field_trait<Get>;
using object_type = typename trait::object_type;
using get_result_type = typename trait::get_result_type;
using value_type = typename trait::value_type;
using predicate_type = bool (*)(const value_type&);
using super = config_value_field_base<object_type, value_type>;
constexpr config_value_field_impl(string_view name, Get getter, Set setter,
optional<value_type> default_value = none,
predicate_type predicate = nullptr)
: super(name, std::move(default_value), predicate),
get_(std::move(getter)),
set_(std::move(setter)) {
// nop
}
constexpr config_value_field_impl(config_value_field_impl&&) = default;
const value_type& get_value(const object_type& x) const override {
bool_token<std::is_lvalue_reference<get_result_type>::value> token;
return get_value_impl(x, token);
}
void set_value(object_type& x, value_type y) const override {
set_(x, std::move(y));
}
private:
template <class O>
const value_type& get_value_impl(const O& x, std::true_type) const {
return get_(x);
}
template <class O>
const value_type& get_value_impl(const O& x, std::false_type) const {
dummy_ = get_(x);
return dummy_;
}
Get get_;
Set set_;
mutable value_type dummy_;
};
} // namespace caf::detail
...@@ -48,7 +48,7 @@ void store_impl(void* ptr, const config_value& x) { ...@@ -48,7 +48,7 @@ void store_impl(void* ptr, const config_value& x) {
template <class T> template <class T>
config_value get_impl(const void* ptr) { config_value get_impl(const void* ptr) {
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))}; return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))};
} }
...@@ -60,7 +60,7 @@ expected<config_value> parse_impl(T* ptr, string_view str) { ...@@ -60,7 +60,7 @@ expected<config_value> parse_impl(T* ptr, string_view str) {
} }
if constexpr (detail::has_clear_member<T>::value) if constexpr (detail::has_clear_member<T>::value)
ptr->clear(); ptr->clear();
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
string_parser_state ps{str.begin(), str.end()}; string_parser_state ps{str.begin(), str.end()};
trait::parse_cli(ps, *ptr, top_level_cli_parsing); trait::parse_cli(ps, *ptr, top_level_cli_parsing);
if (ps.code != pec::success) if (ps.code != pec::success)
...@@ -78,7 +78,7 @@ expected<config_value> parse_impl_delegate(void* ptr, string_view str) { ...@@ -78,7 +78,7 @@ expected<config_value> parse_impl_delegate(void* ptr, string_view str) {
template <class T> template <class T>
config_option::meta_state* option_meta_state_instance() { config_option::meta_state* option_meta_state_instance() {
using trait = select_config_value_access_t<T>; using trait = detail::config_value_access_t<T>;
static config_option::meta_state obj{check_impl<T>, store_impl<T>, static config_option::meta_state obj{check_impl<T>, store_impl<T>,
get_impl<T>, parse_impl_delegate<T>, get_impl<T>, parse_impl_delegate<T>,
trait::type_name()}; trait::type_name()};
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <tuple>
#include <type_traits>
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_impl.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
/// Creates a field with direct access to a member in `T` via member-to-object
/// pointer.
template <class T, class U, class... Args>
detail::config_value_field_impl<U T::*>
make_config_value_field(string_view name, U T::*ptr, Args&&... xs) {
return {name, ptr, std::forward<Args>(xs)...};
}
/// Creates a field with access to a member in `T` via `getter` and `setter`.
template <class Getter, class Setter,
class E = detail::enable_if_t<!std::is_member_pointer<Getter>::value>,
class... Args>
detail::config_value_field_impl<std::pair<Getter, Setter>>
make_config_value_field(string_view name, Getter getter, Setter setter,
Args&&... xs) {
return {name, std::move(getter), std::move(setter),
std::forward<Args>(xs)...};
}
template <class T, class... Ts>
class config_value_field_storage {
public:
using tuple_type = std::tuple<T, Ts...>;
using object_type = typename T::object_type;
using indices = typename detail::il_indices<tuple_type>::type;
using array_type = std::array<config_value_field<object_type>*,
sizeof...(Ts) + 1>;
template <class... Us>
config_value_field_storage(T x, Us&&... xs)
: fields_(std::move(x), std::forward<Us>(xs)...) {
init(detail::get_indices(fields_));
}
config_value_field_storage(config_value_field_storage&&) = default;
span<config_value_field<object_type>*> fields() {
return make_span(ptr_fields_);
}
private:
template <long... Pos>
void init(detail::int_list<Pos...>) {
ptr_fields_ = array_type{{&std::get<Pos>(fields_)...}};
}
std::tuple<T, Ts...> fields_;
array_type ptr_fields_;
};
template <class... Ts>
config_value_field_storage<Ts...>
make_config_value_field_storage(Ts... fields) {
return {std::move(fields)...};
}
} // namespace caf
...@@ -51,7 +51,7 @@ auto get_if(const settings* xs, string_view name) { ...@@ -51,7 +51,7 @@ auto get_if(const settings* xs, string_view name) {
/// @relates config_value /// @relates config_value
template <class T> template <class T>
bool holds_alternative(const settings& xs, string_view name) { bool holds_alternative(const settings& xs, string_view name) {
using access = select_config_value_access_t<T>; using access = detail::config_value_access_t<T>;
if (auto value = get_if(&xs, name)) if (auto value = get_if(&xs, name))
return access::is(*value); return access::is(*value);
return false; return false;
......
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/settings_writer.hpp" #include "caf/config_value_writer.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/append_hex.hpp" #include "caf/detail/append_hex.hpp"
#include "caf/detail/overload.hpp" #include "caf/detail/overload.hpp"
#include "caf/settings.hpp"
#define CHECK_NOT_EMPTY() \ #define CHECK_NOT_EMPTY() \
do { \ do { \
...@@ -58,13 +60,13 @@ namespace caf { ...@@ -58,13 +60,13 @@ namespace caf {
// -- constructors, destructors, and assignment operators ---------------------- // -- constructors, destructors, and assignment operators ----------------------
settings_writer::~settings_writer() { config_value_writer::~config_value_writer() {
// nop // nop
} }
// -- interface functions ------------------------------------------------------ // -- interface functions ------------------------------------------------------
bool settings_writer::inject_next_object_type(type_id_t type) { bool config_value_writer::inject_next_object_type(type_id_t type) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
type_hint_ = query_type_name(type); type_hint_ = query_type_name(type);
if (type_hint_.empty()) { if (type_hint_.empty()) {
...@@ -75,17 +77,16 @@ bool settings_writer::inject_next_object_type(type_id_t type) { ...@@ -75,17 +77,16 @@ bool settings_writer::inject_next_object_type(type_id_t type) {
return true; return true;
} }
bool settings_writer::begin_object(string_view) { bool config_value_writer::begin_object(string_view) {
if (st_.empty()) { CHECK_NOT_EMPTY();
if (root_ == nullptr) {
emplace_error(sec::runtime_error,
"tried to serialize multiple objects into the root object");
return false;
}
st_.push(root_);
root_ = nullptr;
} else {
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* x) {
// Morph the root element into a dictionary.
auto& dict = x->as_dictionary();
dict.clear();
st_.top() = &dict;
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"begin_object called inside another object"); "begin_object called inside another object");
...@@ -97,6 +98,7 @@ bool settings_writer::begin_object(string_view) { ...@@ -97,6 +98,7 @@ bool settings_writer::begin_object(string_view) {
return false; return false;
}, },
[this](present_field fld) { [this](present_field fld) {
CAF_ASSERT(fld.parent != nullptr);
auto [iter, added] = fld.parent->emplace(fld.name, settings{}); auto [iter, added] = fld.parent->emplace(fld.name, settings{});
if (!added) { if (!added) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
...@@ -116,7 +118,6 @@ bool settings_writer::begin_object(string_view) { ...@@ -116,7 +118,6 @@ bool settings_writer::begin_object(string_view) {
}); });
if (!visit(f, st_.top())) if (!visit(f, st_.top()))
return false; return false;
}
if (!type_hint_.empty()) { if (!type_hint_.empty()) {
put(*get<settings*>(st_.top()), "@type", type_hint_); put(*get<settings*>(st_.top()), "@type", type_hint_);
type_hint_ = string_view{}; type_hint_ = string_view{};
...@@ -124,19 +125,19 @@ bool settings_writer::begin_object(string_view) { ...@@ -124,19 +125,19 @@ bool settings_writer::begin_object(string_view) {
return true; return true;
} }
bool settings_writer::end_object() { bool config_value_writer::end_object() {
SCOPE(settings*); SCOPE(settings*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::begin_field(string_view name) { bool config_value_writer::begin_field(string_view name) {
SCOPE(settings*); SCOPE(settings*);
st_.push(present_field{top, name, string_view{}}); st_.push(present_field{top, name, string_view{}});
return true; return true;
} }
bool settings_writer::begin_field(string_view name, bool is_present) { bool config_value_writer::begin_field(string_view name, bool is_present) {
SCOPE(settings*); SCOPE(settings*);
if (is_present) if (is_present)
st_.push(present_field{top, name, string_view{}}); st_.push(present_field{top, name, string_view{}});
...@@ -145,7 +146,8 @@ bool settings_writer::begin_field(string_view name, bool is_present) { ...@@ -145,7 +146,8 @@ bool settings_writer::begin_field(string_view name, bool is_present) {
return true; return true;
} }
bool settings_writer::begin_field(string_view name, span<const type_id_t> types, bool config_value_writer::begin_field(string_view name,
span<const type_id_t> types,
size_t index) { size_t index) {
SCOPE(settings*); SCOPE(settings*);
if (index >= types.size()) { if (index >= types.size()) {
...@@ -164,15 +166,16 @@ bool settings_writer::begin_field(string_view name, span<const type_id_t> types, ...@@ -164,15 +166,16 @@ bool settings_writer::begin_field(string_view name, span<const type_id_t> types,
return true; return true;
} }
bool settings_writer::begin_field(string_view name, bool is_present, bool config_value_writer::begin_field(string_view name, bool is_present,
span<const type_id_t> types, size_t index) { span<const type_id_t> types,
size_t index) {
if (is_present) if (is_present)
return begin_field(name, types, index); return begin_field(name, types, index);
else else
return begin_field(name, false); return begin_field(name, false);
} }
bool settings_writer::end_field() { bool config_value_writer::end_field() {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
if (!holds_alternative<present_field>(st_.top()) if (!holds_alternative<present_field>(st_.top())
&& !holds_alternative<absent_field>(st_.top())) { && !holds_alternative<absent_field>(st_.top())) {
...@@ -183,15 +186,15 @@ bool settings_writer::end_field() { ...@@ -183,15 +186,15 @@ bool settings_writer::end_field() {
return true; return true;
} }
bool settings_writer::begin_tuple(size_t size) { bool config_value_writer::begin_tuple(size_t size) {
return begin_sequence(size); return begin_sequence(size);
} }
bool settings_writer::end_tuple() { bool config_value_writer::end_tuple() {
return end_sequence(); return end_sequence();
} }
bool settings_writer::begin_key_value_pair() { bool config_value_writer::begin_key_value_pair() {
SCOPE(settings*); SCOPE(settings*);
auto [iter, added] = top->emplace("@tmp", config_value::list{}); auto [iter, added] = top->emplace("@tmp", config_value::list{});
if (!added) { if (!added) {
...@@ -202,7 +205,7 @@ bool settings_writer::begin_key_value_pair() { ...@@ -202,7 +205,7 @@ bool settings_writer::begin_key_value_pair() {
return true; return true;
} }
bool settings_writer::end_key_value_pair() { bool config_value_writer::end_key_value_pair() {
config_value::list tmp; config_value::list tmp;
/* lifetime scope of the list */ { /* lifetime scope of the list */ {
SCOPE(config_value::list*); SCOPE(config_value::list*);
...@@ -229,9 +232,16 @@ bool settings_writer::end_key_value_pair() { ...@@ -229,9 +232,16 @@ bool settings_writer::end_key_value_pair() {
return true; return true;
} }
bool settings_writer::begin_sequence(size_t) { bool config_value_writer::begin_sequence(size_t) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* val) {
// Morph the value into a list.
auto& ls = val->as_list();
ls.clear();
st_.top() = &ls;
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"cannot start sequence/tuple inside an object"); "cannot start sequence/tuple inside an object");
...@@ -261,16 +271,23 @@ bool settings_writer::begin_sequence(size_t) { ...@@ -261,16 +271,23 @@ bool settings_writer::begin_sequence(size_t) {
return visit(f, st_.top()); return visit(f, st_.top());
} }
bool settings_writer::end_sequence() { bool config_value_writer::end_sequence() {
SCOPE(config_value::list*); SCOPE(config_value::list*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::begin_associative_array(size_t) { bool config_value_writer::begin_associative_array(size_t) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
settings* inner = nullptr; settings* inner = nullptr;
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* val) {
// Morph the top element into a dictionary.
auto& dict = val->as_dictionary();
dict.clear();
st_.top() = &dict;
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields"); emplace_error(sec::runtime_error, "cannot write values outside fields");
return false; return false;
...@@ -316,45 +333,45 @@ bool settings_writer::begin_associative_array(size_t) { ...@@ -316,45 +333,45 @@ bool settings_writer::begin_associative_array(size_t) {
return false; return false;
} }
bool settings_writer::end_associative_array() { bool config_value_writer::end_associative_array() {
SCOPE(settings*); SCOPE(settings*);
st_.pop(); st_.pop();
return true; return true;
} }
bool settings_writer::value(bool x) { bool config_value_writer::value(bool x) {
return push(config_value{x}); return push(config_value{x});
} }
bool settings_writer::value(int8_t x) { bool config_value_writer::value(int8_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint8_t x) { bool config_value_writer::value(uint8_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int16_t x) { bool config_value_writer::value(int16_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint16_t x) { bool config_value_writer::value(uint16_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int32_t x) { bool config_value_writer::value(int32_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint32_t x) { bool config_value_writer::value(uint32_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(int64_t x) { bool config_value_writer::value(int64_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(uint64_t x) { bool config_value_writer::value(uint64_t x) {
auto max_val = std::numeric_limits<config_value::integer>::max(); auto max_val = std::numeric_limits<config_value::integer>::max();
if (x > static_cast<uint64_t>(max_val)) { if (x > static_cast<uint64_t>(max_val)) {
emplace_error(sec::runtime_error, "integer overflow"); emplace_error(sec::runtime_error, "integer overflow");
...@@ -363,41 +380,45 @@ bool settings_writer::value(uint64_t x) { ...@@ -363,41 +380,45 @@ bool settings_writer::value(uint64_t x) {
return push(config_value{static_cast<config_value::integer>(x)}); return push(config_value{static_cast<config_value::integer>(x)});
} }
bool settings_writer::value(float x) { bool config_value_writer::value(float x) {
return push(config_value{double{x}}); return push(config_value{double{x}});
} }
bool settings_writer::value(double x) { bool config_value_writer::value(double x) {
return push(config_value{x}); return push(config_value{x});
} }
bool settings_writer::value(long double x) { bool config_value_writer::value(long double x) {
return push(config_value{std::to_string(x)}); return push(config_value{std::to_string(x)});
} }
bool settings_writer::value(string_view x) { bool config_value_writer::value(string_view x) {
return push(config_value{to_string(x)}); return push(config_value{to_string(x)});
} }
bool settings_writer::value(const std::u16string&) { bool config_value_writer::value(const std::u16string&) {
emplace_error(sec::runtime_error, "u16string support not implemented yet"); emplace_error(sec::runtime_error, "u16string support not implemented yet");
return false; return false;
} }
bool settings_writer::value(const std::u32string&) { bool config_value_writer::value(const std::u32string&) {
emplace_error(sec::runtime_error, "u32string support not implemented yet"); emplace_error(sec::runtime_error, "u32string support not implemented yet");
return false; return false;
} }
bool settings_writer::value(span<const byte> x) { bool config_value_writer::value(span<const byte> x) {
std::string str; std::string str;
detail::append_hex(str, x.data(), x.size()); detail::append_hex(str, x.data(), x.size());
return push(config_value{std::move(str)}); return push(config_value{std::move(str)});
} }
bool settings_writer::push(config_value&& x) { bool config_value_writer::push(config_value&& x) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
auto f = detail::make_overload( auto f = detail::make_overload(
[&x](config_value* val) {
*val = std::move(x);
return true;
},
[this](settings*) { [this](settings*) {
emplace_error(sec::runtime_error, "cannot write values outside fields"); emplace_error(sec::runtime_error, "cannot write values outside fields");
return false; return false;
......
...@@ -24,11 +24,10 @@ ...@@ -24,11 +24,10 @@
#include "caf/optional.hpp" #include "caf/optional.hpp"
#define DEFAULT_META(type, parse_fun) \ #define DEFAULT_META(type, parse_fun) \
config_option::meta_state \ config_option::meta_state type##_meta_state{ \
type##_meta_state{default_config_option_check<type>, \ default_config_option_check<type>, default_config_option_store<type>, \
default_config_option_store<type>, get_impl<type>, \ get_impl<type>, parse_fun, \
parse_fun, \ detail::config_value_access_t<type>::type_name()};
select_config_value_access_t<type>::type_name()};
using std::string; using std::string;
...@@ -78,7 +77,7 @@ config_value bool_get_neg(const void* ptr) { ...@@ -78,7 +77,7 @@ config_value bool_get_neg(const void* ptr) {
meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg, meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg,
nullptr, nullptr,
select_config_value_access_t<bool>::type_name()}; detail::config_value_access_t<bool>::type_name()};
error check_timespan(const config_value& x) { error check_timespan(const config_value& x) {
if (holds_alternative<timespan>(x)) if (holds_alternative<timespan>(x))
...@@ -100,11 +99,11 @@ config_value get_timespan(const void* ptr) { ...@@ -100,11 +99,11 @@ config_value get_timespan(const void* ptr) {
meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>, meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>,
nullptr, nullptr,
select_config_value_access_t<timespan>::type_name()}; detail::config_value_access_t<timespan>::type_name()};
meta_state ms_res_meta{check_timespan, store_timespan<1000000>, meta_state ms_res_meta{check_timespan, store_timespan<1000000>,
get_timespan<1000000>, nullptr, get_timespan<1000000>, nullptr,
select_config_value_access_t<timespan>::type_name()}; detail::config_value_access_t<timespan>::type_name()};
} // namespace } // namespace
...@@ -114,17 +113,15 @@ config_option make_negated_config_option(bool& storage, string_view category, ...@@ -114,17 +113,15 @@ config_option make_negated_config_option(bool& storage, string_view category,
return {category, name, description, &bool_neg_meta, &storage}; return {category, name, description, &bool_neg_meta, &storage};
} }
config_option make_us_resolution_config_option(size_t& storage, config_option
string_view category, make_us_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view name, string_view description) {
string_view description) {
return {category, name, description, &us_res_meta, &storage}; return {category, name, description, &us_res_meta, &storage};
} }
config_option make_ms_resolution_config_option(size_t& storage, config_option
string_view category, make_ms_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view name, string_view description) {
string_view description) {
return {category, name, description, &ms_res_meta, &storage}; return {category, name, description, &ms_res_meta, &storage};
} }
......
...@@ -439,3 +439,58 @@ CAF_TEST(conversion to std::unordered_multimap) { ...@@ -439,3 +439,58 @@ CAF_TEST(conversion to std::unordered_multimap) {
CAF_REQUIRE(ys); CAF_REQUIRE(ys);
CAF_CHECK_EQUAL(*ys, map_type({{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}})); CAF_CHECK_EQUAL(*ys, map_type({{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}}));
} }
namespace {
struct point_3d {
int32_t x;
int32_t y;
int32_t z;
};
[[maybe_unused]] bool operator==(const point_3d& x, const point_3d& y) {
return std::tie(x.x, x.y, x.z) == std::tie(y.x, y.y, y.z);
}
template <class Inspector>
bool inspect(Inspector& f, point_3d& x) {
return f.object(x).fields(f.field("x", x.x), f.field("y", x.y),
f.field("z", x.z));
}
struct line {
point_3d p1;
point_3d p2;
};
[[maybe_unused]] bool operator==(const line& x, const line& y) {
return std::tie(x.p1, x.p2) == std::tie(y.p1, y.p2);
}
template <class Inspector>
bool inspect(Inspector& f, line& x) {
return f.object(x).fields(f.field("p1", x.p1), f.field("p2", x.p2));
}
} // namespace
CAF_TEST(config values pick up user defined inspect overloads) {
config_value x;
CAF_MESSAGE("fill config value with the fields necessary for a 'line'");
{
auto& dict = x.as_dictionary();
put(dict, "p1.x", 1);
put(dict, "p1.y", 2);
put(dict, "p1.z", 3);
put(dict, "p2.x", 10);
put(dict, "p2.y", 20);
put(dict, "p2.z", 30);
}
CAF_MESSAGE("read 'line' via get_if and verify the object");
{
auto l = get_if<line>(&x);
CAF_CHECK_NOT_EQUAL(l, none);
if (l)
CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}}));
}
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE config_value_adaptor
#include "caf/config_value_adaptor.hpp"
#include "core-test.hpp"
#include "caf/config_value_adaptor_access.hpp"
using namespace caf;
namespace {
// We want to configure this type as follows:
// my-duration = {
// count = 1
// resolution = "s"
// }
struct my_duration {
public:
constexpr my_duration() noexcept : ns_(0) {
// nop
}
constexpr my_duration(const my_duration&) noexcept = default;
my_duration& operator=(const my_duration&) noexcept = default;
int64_t ns() const {
return ns_;
}
int64_t us() const {
return ns() / 1000;
}
int64_t ms() const {
return us() / 1000;
}
int64_t s() const {
return ms() / 1000;
}
static my_duration from_ns(int64_t count) {
my_duration result;
result.ns_ = count;
return result;
}
static my_duration from_us(int64_t count) {
return from_ns(count * 1000);
}
static my_duration from_ms(int64_t count) {
return from_us(count * 1000);
}
static my_duration from_s(int64_t count) {
return from_ms(count * 1000);
}
private:
int64_t ns_;
};
bool operator==(my_duration x, my_duration y) {
return x.ns() == y.ns();
}
std::string to_string(my_duration x) {
return std::to_string(x.ns()) + "ns";
}
struct my_duration_adaptor {
using value_type = my_duration;
using tuple_type = std::tuple<int64_t, std::string>;
static std::string type_name() noexcept {
return "my-duration";
}
static bool resolution_valid(const std::string& str) {
static constexpr string_view whitelist[] = {"s", "ms", "us", "ns"};
auto matches = [&](string_view x) { return str == x; };
return std::any_of(std::begin(whitelist), std::end(whitelist), matches);
}
static config_value_adaptor<int64_t, std::string>& adaptor_ref() {
static auto singleton = make_config_value_adaptor(
make_config_value_adaptor_field<int64_t>("count"),
make_config_value_adaptor_field<std::string>("resolution", none,
resolution_valid));
return singleton;
}
static void convert(const value_type& src, tuple_type& dst) {
int count = src.ns();
if (count / 1000 != 0) {
dst = std::tie(count, "ns");
return;
}
count /= 1000;
if (count / 1000 != 0) {
dst = std::tie(count, "us");
return;
}
count /= 1000;
if (count / 1000 != 0) {
dst = std::tie(count, "ms");
return;
}
count /= 1000;
dst = std::tie(count, "s");
}
static void convert(const tuple_type& src, value_type& dst) {
auto count = std::get<0>(src);
const auto& resolution = std::get<1>(src);
if (resolution == "ns")
dst = my_duration::from_ns(count);
else if (resolution == "us")
dst = my_duration::from_us(count);
else if (resolution == "ms")
dst = my_duration::from_ms(count);
else
dst = my_duration::from_s(count);
}
};
struct fixture {
config_option_set opts;
template <class T>
expected<T> read(std::vector<std::string> args) {
settings cfg;
auto res = opts.parse(cfg, args);
if (res.first != pec::success)
return make_error(res.first, *res.second);
auto x = get_if<T>(&cfg, "value");
if (x == none)
return sec::invalid_argument;
return *x;
}
};
} // namespace
namespace caf {
template <>
struct config_value_access<my_duration>
: config_value_adaptor_access<my_duration_adaptor> {};
} // namespace caf
CAF_TEST_FIXTURE_SCOPE(config_value_adaptor_tests, fixture)
CAF_TEST(holds_alternative) {
auto make_value = [](int64_t count, std::string resolution) {
settings x;
put(x, "count", count);
put(x, "resolution", std::move(resolution));
return config_value{std::move(x)};
};
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "s")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "ms")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "us")));
CAF_CHECK(holds_alternative<my_duration>(make_value(1, "ns")));
CAF_CHECK(!holds_alternative<my_duration>(make_value(1, "foo")));
}
CAF_TEST(access from dictionary) {
settings x;
put(x, "value.count", 42);
put(x, "value.resolution", "s");
auto value = x["value"];
CAF_REQUIRE(holds_alternative<my_duration>(value));
CAF_CHECK_EQUAL(get_if<my_duration>(&value), my_duration::from_s(42));
CAF_CHECK_EQUAL(get<my_duration>(value), my_duration::from_s(42));
}
namespace {
constexpr const char* config_text = R"__(
max-delay = {
count = 123
resolution = "s"
}
)__";
struct test_config : actor_system_config {
test_config() {
opt_group{custom_options_, "global"}.add(max_delay, "max-delay,m",
"maximum delay");
}
my_duration max_delay;
};
} // namespace
CAF_TEST(adaptor access from actor system config - file input) {
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(0, nullptr, in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.max_delay, my_duration::from_s(123));
}
CAF_TEST(adaptor access from actor system config - file input and arguments) {
std::vector<std::string> args{
"--max-delay={count = 20, resolution = ms}",
};
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(std::move(args), in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.max_delay, my_duration::from_ms(20));
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -16,15 +16,17 @@ ...@@ -16,15 +16,17 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#define CAF_SUITE settings_reader #define CAF_SUITE config_value_reader
#include "caf/settings_reader.hpp" #include "caf/config_value_reader.hpp"
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
#include "inspector-tests.hpp" #include "inspector-tests.hpp"
#include "caf/settings_writer.hpp" #include "caf/config_value.hpp"
#include "caf/config_value_writer.hpp"
#include "caf/inspector_access.hpp"
using namespace caf; using namespace caf;
...@@ -44,12 +46,17 @@ struct fixture { ...@@ -44,12 +46,17 @@ struct fixture {
settings xs; settings xs;
template <class T> template <class T>
void deserialize(const settings& src, T& value) { void deserialize(const config_value& src, T& value) {
settings_reader reader{&src}; config_value_reader reader{&src};
if (!inspect_object(reader, value)) if (!detail::load_value(reader, value))
CAF_FAIL("failed to deserialize from settings: " << reader.get_error()); CAF_FAIL("failed to deserialize from settings: " << reader.get_error());
} }
template <class T>
void deserialize(const settings& src, T& value) {
deserialize(config_value{src}, value);
}
template <class T> template <class T>
void deserialize(T& value) { void deserialize(T& value) {
return deserialize(xs, value); return deserialize(xs, value);
...@@ -70,7 +77,14 @@ struct fixture { ...@@ -70,7 +77,14 @@ struct fixture {
} // namespace } // namespace
CAF_TEST_FIXTURE_SCOPE(settings_reader_tests, fixture) CAF_TEST_FIXTURE_SCOPE(config_value_reader_tests, fixture)
CAF_TEST(readers deserialize builtin types from config values) {
std::string value;
put(xs, "foo", "bar");
deserialize(xs["foo"], value);
CAF_CHECK_EQUAL(value, "bar");
}
CAF_TEST(readers deserialize simple objects from configs) { CAF_TEST(readers deserialize simple objects from configs) {
put(xs, "foo", "hello"); put(xs, "foo", "hello");
...@@ -124,9 +138,13 @@ CAF_TEST(readers deserialize objects from the output of writers) { ...@@ -124,9 +138,13 @@ CAF_TEST(readers deserialize objects from the output of writers) {
CAF_MESSAGE("serialize the 'line' object"); CAF_MESSAGE("serialize the 'line' object");
{ {
line l{{10, 20, 30}, {70, 60, 50}}; line l{{10, 20, 30}, {70, 60, 50}};
settings_writer writer{&xs}; config_value tmp;
if (!inspect_object(writer, l)) config_value_writer writer{&tmp};
if (!detail::save_value(writer, l))
CAF_FAIL("failed two write to settings: " << writer.get_error()); CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(tmp))
CAF_FAIL("writer failed to produce a dictionary");
xs = std::move(caf::get<settings>(tmp));
} }
CAF_MESSAGE("serialize and verify the 'line' object"); CAF_MESSAGE("serialize and verify the 'line' object");
{ {
......
...@@ -16,14 +16,16 @@ ...@@ -16,14 +16,16 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#define CAF_SUITE settings_writer #define CAF_SUITE config_value_writer
#include "caf/settings_writer.hpp" #include "caf/config_value_writer.hpp"
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
#include "inspector-tests.hpp" #include "inspector-tests.hpp"
#include "caf/inspector_access.hpp"
using namespace caf; using namespace caf;
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
...@@ -43,9 +45,13 @@ struct fixture { ...@@ -43,9 +45,13 @@ struct fixture {
template <class T> template <class T>
void set(const T& value) { void set(const T& value) {
settings_writer writer{&xs}; config_value val;
if (!inspect_object(writer, value)) config_value_writer writer{&val};
if (!detail::save_value(writer, value))
CAF_FAIL("failed two write to settings: " << writer.get_error()); CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(val))
CAF_FAIL("serializing T did not result in a dictionary");
xs = std::move(caf::get<settings>(val));
} }
template <class T> template <class T>
...@@ -63,7 +69,7 @@ struct fixture { ...@@ -63,7 +69,7 @@ struct fixture {
} // namespace } // namespace
CAF_TEST_FIXTURE_SCOPE(settings_writer_tests, fixture) CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture)
CAF_TEST(structs become dictionaries) { CAF_TEST(structs become dictionaries) {
set(foobar{"hello", "world"}); set(foobar{"hello", "world"});
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE make_config_value_field
#include "caf/make_config_value_field.hpp"
#include "core-test.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/config_option_set.hpp"
#include "caf/config_value_object_access.hpp"
using namespace caf;
namespace {
struct foobar {
int foo = 0;
std::string bar;
foobar() = default;
foobar(int foo, std::string bar) : foo(foo), bar(std::move(bar)) {
// nop
}
};
std::string to_string(const foobar& x) {
return deep_to_string(std::forward_as_tuple(x.foo, x.bar));
}
bool operator==(const foobar& x, const foobar& y) {
return x.foo == y.foo && x.bar == y.bar;
}
bool foo_valid(const int& x) {
return x >= 0;
}
int get_foo_fun(foobar x) {
return x.foo;
}
void set_foo_fun(foobar& x, const int& value) {
x.foo = value;
}
struct get_foo_t {
int operator()(const foobar& x) const noexcept {
return x.foo;
}
};
struct set_foo_t {
int& operator()(foobar& x, int value) const noexcept {
x.foo = value;
return x.foo;
}
};
struct foobar_trait {
using object_type = foobar;
static std::string type_name() {
return "foobar";
}
static span<config_value_field<object_type>*> fields() {
static auto singleton = make_config_value_field_storage(
make_config_value_field("foo", &foobar::foo, 123),
make_config_value_field("bar", &foobar::bar));
return singleton.fields();
}
};
struct foobar_foobar {
foobar x;
foobar y;
foobar_foobar() = default;
foobar_foobar(foobar x, foobar y) : x(x), y(y) {
// nop
}
};
std::string to_string(const foobar_foobar& x) {
return deep_to_string(std::forward_as_tuple(x.x, x.y));
}
bool operator==(const foobar_foobar& x, const foobar_foobar& y) {
return x.x == y.x && x.y == y.y;
}
struct foobar_foobar_trait {
using object_type = foobar_foobar;
static std::string type_name() {
return "foobar-foobar";
}
static span<config_value_field<object_type>*> fields() {
static auto singleton = make_config_value_field_storage(
make_config_value_field("x", &foobar_foobar::x),
make_config_value_field("y", &foobar_foobar::y));
return singleton.fields();
}
};
struct fixture {
get_foo_t get_foo;
set_foo_t set_foo;
config_option_set opts;
void test_foo_field(config_value_field<foobar>& foo_field) {
foobar x;
CAF_CHECK_EQUAL(foo_field.name(), "foo");
CAF_REQUIRE(foo_field.has_default());
CAF_CHECK_EQUAL(foo_field.get(x), config_value(0));
foo_field.set_default(x);
CAF_CHECK_EQUAL(foo_field.get(x), config_value(42));
CAF_CHECK(!foo_field.valid_input(config_value(1.)));
CAF_CHECK(!foo_field.valid_input(config_value(-1)));
CAF_CHECK(!foo_field.set(x, config_value(-1)));
string_view input = "123";
string_parser_state ps{input.begin(), input.end()};
foo_field.parse_cli(ps, x, false);
CAF_CHECK_EQUAL(ps.code, pec::success);
CAF_CHECK_EQUAL(foo_field.get(x), config_value(123));
}
template <class T>
expected<T> read(std::vector<std::string> args) {
settings cfg;
auto res = opts.parse(cfg, args);
if (res.first != pec::success)
return make_error(res.first, *res.second);
auto x = get_if<T>(&cfg, "value");
if (x == none)
return sec::invalid_argument;
return *x;
}
};
} // namespace
namespace caf {
template <>
struct config_value_access<foobar> : config_value_object_access<foobar_trait> {
};
template <>
struct config_value_access<foobar_foobar>
: config_value_object_access<foobar_foobar_trait> {};
} // namespace caf
CAF_TEST_FIXTURE_SCOPE(make_config_value_field_tests, fixture)
CAF_TEST(construction from pointer to member) {
make_config_value_field("foo", &foobar::foo);
make_config_value_field("foo", &foobar::foo, none);
make_config_value_field("foo", &foobar::foo, none, nullptr);
make_config_value_field("foo", &foobar::foo, 42);
make_config_value_field("foo", &foobar::foo, 42, nullptr);
make_config_value_field("foo", &foobar::foo, 42, foo_valid);
make_config_value_field("foo", &foobar::foo, 42,
[](const int& x) { return x != 0; });
}
CAF_TEST(pointer to member access) {
auto foo_field = make_config_value_field("foo", &foobar::foo, 42, foo_valid);
test_foo_field(foo_field);
}
CAF_TEST(construction from getter and setter) {
auto get_foo_lambda = [](const foobar& x) { return x.foo; };
auto set_foo_lambda = [](foobar& x, int value) { x.foo = value; };
make_config_value_field("foo", get_foo, set_foo);
make_config_value_field("foo", get_foo_fun, set_foo);
make_config_value_field("foo", get_foo_fun, set_foo_fun);
make_config_value_field("foo", get_foo_lambda, set_foo_lambda);
}
CAF_TEST(getter and setter access) {
auto foo_field = make_config_value_field("foo", get_foo, set_foo, 42,
foo_valid);
test_foo_field(foo_field);
}
CAF_TEST(object access from dictionary - foobar) {
settings x;
put(x, "my-value.bar", "hello");
CAF_MESSAGE("without foo member");
{
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none);
auto fb = get<foobar>(x, "my-value");
CAF_CHECK_EQUAL(fb.foo, 123);
CAF_CHECK_EQUAL(fb.bar, "hello");
}
CAF_MESSAGE("with foo member");
put(x, "my-value.foo", 42);
{
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none);
auto fb = get<foobar>(x, "my-value");
CAF_CHECK_EQUAL(fb.foo, 42);
CAF_CHECK_EQUAL(fb.bar, "hello");
}
}
CAF_TEST(object access from dictionary - foobar_foobar) {
settings x;
put(x, "my-value.x.foo", 1);
put(x, "my-value.x.bar", "hello");
put(x, "my-value.y.bar", "world");
CAF_REQUIRE(holds_alternative<foobar_foobar>(x, "my-value"));
CAF_REQUIRE(get_if<foobar_foobar>(&x, "my-value") != caf::none);
auto fbfb = get<foobar_foobar>(x, "my-value");
CAF_CHECK_EQUAL(fbfb.x.foo, 1);
CAF_CHECK_EQUAL(fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(fbfb.y.foo, 123);
CAF_CHECK_EQUAL(fbfb.y.bar, "world");
}
CAF_TEST(object access from CLI arguments - foobar) {
opts.add<foobar>("value,v", "some value");
CAF_CHECK_EQUAL(read<foobar>({"--value={foo = 1, bar = hello}"}),
foobar(1, "hello"));
CAF_CHECK_EQUAL(read<foobar>({"-v{bar = \"hello\"}"}), foobar(123, "hello"));
CAF_CHECK_EQUAL(read<foobar>({"-v", "{foo = 1, bar =hello ,}"}),
foobar(1, "hello"));
}
CAF_TEST(object access from CLI arguments - foobar_foobar) {
using fbfb = foobar_foobar;
opts.add<fbfb>("value,v", "some value");
CAF_CHECK_EQUAL(read<fbfb>({"-v{x={bar = hello},y={foo=1,bar=world!},}"}),
fbfb({123, "hello"}, {1, "world!"}));
}
namespace {
constexpr const char* config_text = R"__(
arg1 = {
foo = 42
bar = "Don't panic!"
}
arg2 = {
x = {
foo = 1
bar = "hello"
}
y = {
foo = 2
bar = "world"
}
}
)__";
struct test_config : actor_system_config {
test_config() {
opt_group{custom_options_, "global"}
.add(fb, "arg1,1", "some foobar")
.add(fbfb, "arg2,2", "somme foobar-foobar");
}
foobar fb;
foobar_foobar fbfb;
};
} // namespace
CAF_TEST(object access from actor system config - file input) {
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(0, nullptr, in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.fb.foo, 42);
CAF_CHECK_EQUAL(cfg.fb.bar, "Don't panic!");
CAF_CHECK_EQUAL(cfg.fbfb.x.foo, 1);
CAF_CHECK_EQUAL(cfg.fbfb.y.foo, 2);
CAF_CHECK_EQUAL(cfg.fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(cfg.fbfb.y.bar, "world");
}
CAF_TEST(object access from actor system config - file input and arguments) {
std::vector<std::string> args{
"-2",
"{y = {bar = CAF, foo = 20}, x = {foo = 10, bar = hello}}",
};
test_config cfg;
std::istringstream in{config_text};
if (auto err = cfg.parse(std::move(args), in))
CAF_FAIL("cfg.parse failed: " << err);
CAF_CHECK_EQUAL(cfg.fb.foo, 42);
CAF_CHECK_EQUAL(cfg.fb.bar, "Don't panic!");
CAF_CHECK_EQUAL(cfg.fbfb.x.foo, 10);
CAF_CHECK_EQUAL(cfg.fbfb.y.foo, 20);
CAF_CHECK_EQUAL(cfg.fbfb.x.bar, "hello");
CAF_CHECK_EQUAL(cfg.fbfb.y.bar, "CAF");
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment