Unverified Commit dbdf9b26 authored by Noir's avatar Noir Committed by GitHub

Merge pull request #1194

Re-implement config_option using get_as
parents 3f59e8d4 f4c56936
...@@ -16,6 +16,18 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -16,6 +16,18 @@ is based on [Keep a Changelog](https://keepachangelog.com).
and `caf.middleman.serialization-time`. and `caf.middleman.serialization-time`.
- The macro `CAF_ADD_TYPE_ID` now accepts an optional third parameter for - The macro `CAF_ADD_TYPE_ID` now accepts an optional third parameter for
allowing users to override the default type name. allowing users to override the default type name.
- The new function pair `get_as` and `get_or` model type conversions on a
`config_value`. For example, `get_as<int>(x)` would convert the content of `x`
to an `int` by either casting numeric values to `int` (with bound checks) or
trying to parse the input of `x` if it contains a string. The function
`get_or` already existed for `settings`, but we have added new overloads for
generalizing the function to `config_value` as well.
### Deprecated
- The new `get_as` and `get_or` function pair makes type conversions on a
`config_value` via `get`, `get_if`, etc. obsolete. We will retain the
STL-style interface for treating a `config_value` as a `variant`-like type.
### Changed ### Changed
...@@ -23,9 +35,15 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -23,9 +35,15 @@ is based on [Keep a Changelog](https://keepachangelog.com).
i.e., `caf-application.conf`. i.e., `caf-application.conf`.
- Simplify the type inspection API by removing the distinction between - Simplify the type inspection API by removing the distinction between
`apply_object` and `apply_value`. Instead, inspectors only offer `apply` and `apply_object` and `apply_value`. Instead, inspectors only offer `apply` and
users may now also call `map`, `list`, `tuple` and `value` for unboxing simple users may now also call `map`, `list`, and `tuple` for unboxing simple wrapper
wrapper types. Furthermore, CAF no longer automatically serializes enumeration types. Furthermore, CAF no longer automatically serializes enumeration types
types using their underlying value because this is fundamentally unsafe. using their underlying value because this is fundamentally unsafe.
- CAF no longer parses the input to string options on the command line. For
example, `my_app '--msg="hello"'` results in CAF storing `"hello"` (including
the quotes) for the config option `msg`. Previously, CAF tried to parse any
string input on the command-line that starts with quotes in the same way it
would parse strings from a config file, leading to very unintuitive results in
some cases (#1113).
### Fixed ### Fixed
......
...@@ -325,6 +325,13 @@ private: ...@@ -325,6 +325,13 @@ private:
/// Returns all user-provided configuration parameters. /// Returns all user-provided configuration parameters.
CAF_CORE_EXPORT const settings& content(const actor_system_config& cfg); CAF_CORE_EXPORT const settings& content(const actor_system_config& cfg);
/// Returns whether `xs` associates a value of type `T` to `name`.
/// @relates actor_system_config
template <class T>
bool holds_alternative(const actor_system_config& cfg, string_view name) {
return holds_alternative<T>(content(cfg), name);
}
/// Tries to retrieve the value associated to `name` from `cfg`. /// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T> template <class T>
...@@ -339,29 +346,20 @@ T get(const actor_system_config& cfg, string_view name) { ...@@ -339,29 +346,20 @@ T get(const actor_system_config& cfg, string_view name) {
return get<T>(content(cfg), name); return get<T>(content(cfg), name);
} }
/// Retrieves the value associated to `name` from `cfg` or returns /// Retrieves the value associated to `name` from `cfg` or returns `fallback`.
/// `default_value`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T, class = typename std::enable_if< template <class To = get_or_auto_deduce, class Fallback>
!std::is_pointer<T>::value auto get_or(const actor_system_config& cfg, string_view name,
&& !std::is_convertible<T, string_view>::value>::type> Fallback&& fallback) {
T get_or(const actor_system_config& cfg, string_view name, T default_value) { return get_or<To>(content(cfg), name, std::forward<Fallback>(fallback));
return get_or(content(cfg), name, std::move(default_value));
} }
/// Retrieves the value associated to `name` from `cfg` or returns /// Tries to retrieve the value associated to `name` from `cfg` as an instance
/// `default_value`. /// of type `T`.
/// @relates actor_system_config
inline std::string get_or(const actor_system_config& cfg, string_view name,
string_view default_value) {
return get_or(content(cfg), name, default_value);
}
/// Returns whether `xs` associates a value of type `T` to `name`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T> template <class T>
bool holds_alternative(const actor_system_config& cfg, string_view name) { expected<T> get_as(const actor_system_config& cfg, string_view name) {
return holds_alternative<T>(content(cfg), name); return get_as<T>(content(cfg), name);
} }
} // namespace caf } // namespace caf
...@@ -36,22 +36,19 @@ public: ...@@ -36,22 +36,19 @@ public:
/// Custom vtable-like struct for delegating to type-specific functions and /// Custom vtable-like struct for delegating to type-specific functions and
/// storing type-specific information shared by several config options. /// storing type-specific information shared by several config options.
struct meta_state { struct meta_state {
/// Checks whether a value matches the option's type. /// Tries to perform this sequence of steps:
error (*check)(const config_value&); /// - Convert the config value to the type of the config option.
/// - Assign the converted value back to the config value to synchronize
/// Stores a value in the given location. /// conversions back to the caller.
void (*store)(void*, const config_value&); /// - Store the converted value in the pointer unless it is `nullptr`.
error (*sync)(void*, config_value&);
/// Tries to extract a value from the given location. Exists for backward /// Tries to extract a value from the given location. Exists for backward
/// compatibility only and will get removed with CAF 0.17. /// compatibility only and will get removed with CAF 0.17.
config_value (*get)(const void*); config_value (*get)(const void*);
/// Tries to parse an input string. Stores and returns the parsed value on
/// success, returns an error otherwise.
expected<config_value> (*parse)(void*, string_view);
/// Human-readable name of the option's type. /// Human-readable name of the option's type.
std::string type_name; string_view type_name;
}; };
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
...@@ -89,12 +86,19 @@ public: ...@@ -89,12 +86,19 @@ public:
/// Returns the full name for this config option as "<category>.<long name>". /// Returns the full name for this config option as "<category>.<long name>".
string_view full_name() const noexcept; string_view full_name() const noexcept;
/// Checks whether `x` holds a legal value for this option. /// Synchronizes the value of this config option with `x` and vice versa.
error check(const config_value& x) const; ///
/// Tries to perform this sequence of steps:
/// - Convert the config value to the type of the config option.
/// - Assign the converted value back to the config value to synchronize
/// conversions back to the caller.
/// - Store the converted value unless this option is stateless.
error sync(config_value& x) const;
/// Stores `x` in this option unless it is stateless. [[deprecated("use sync instead")]] error store(const config_value& x) const;
/// @pre `check(x) == none`.
void store(const config_value& x) const; [[deprecated("use sync instead")]] expected<config_value>
parse(string_view input) const;
/// Returns a human-readable representation of this option's expected type. /// Returns a human-readable representation of this option's expected type.
string_view type_name() const noexcept; string_view type_name() const noexcept;
...@@ -105,10 +109,6 @@ public: ...@@ -105,10 +109,6 @@ public:
/// Returns whether the category is optional for CLI options. /// Returns whether the category is optional for CLI options.
bool has_flat_cli_name() const noexcept; bool has_flat_cli_name() const noexcept;
/// Tries to parse an input string. Stores and returns the parsed value on
/// success, returns an error otherwise.
expected<config_value> parse(string_view input) const;
/// @private /// @private
// TODO: remove with CAF 0.17 // TODO: remove with CAF 0.17
optional<config_value> get() const; optional<config_value> get() const;
......
...@@ -52,14 +52,10 @@ public: ...@@ -52,14 +52,10 @@ public:
config_option_adder& config_option_adder&
add_neg(bool& ref, string_view name, string_view description); add_neg(bool& ref, string_view name, string_view description);
/// For backward compatibility only. Do not use for new code! [[deprecated("use timespan options instead")]] config_option_adder&
/// @private
config_option_adder&
add_us(size_t& ref, string_view name, string_view description); add_us(size_t& ref, string_view name, string_view description);
/// For backward compatibility only. Do not use for new code! [[deprecated("use timespan options instead")]] config_option_adder&
/// @private
config_option_adder&
add_ms(size_t& ref, string_view name, string_view description); add_ms(size_t& ref, string_view name, string_view description);
private: private:
......
This diff is collapsed.
...@@ -36,6 +36,8 @@ class CAF_CORE_EXPORT config_list_consumer { ...@@ -36,6 +36,8 @@ class CAF_CORE_EXPORT config_list_consumer {
public: public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
config_list_consumer() = default;
config_list_consumer(const config_option_set* options, config_list_consumer(const config_option_set* options,
config_consumer* parent); config_consumer* parent);
...@@ -60,18 +62,20 @@ public: ...@@ -60,18 +62,20 @@ public:
template <class T> template <class T>
void value(T&& x) { void value(T&& x) {
xs_.emplace_back(std::forward<T>(x)); result.emplace_back(std::forward<T>(x));
} }
config_value::list result;
std::string qualified_key(); std::string qualified_key();
private: private:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
const config_option_set* options_ = nullptr; const config_option_set* options_ = nullptr;
variant<config_consumer*, config_list_consumer*, config_value_consumer*> variant<none_t, config_consumer*, config_list_consumer*,
config_value_consumer*>
parent_; parent_;
config_value::list xs_;
}; };
/// Consumes a series of key-value pairs from an application configuration. /// Consumes a series of key-value pairs from an application configuration.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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::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 caf::detail
...@@ -101,21 +101,25 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x); ...@@ -101,21 +101,25 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x);
// -- CAF types ---------------------------------------------------------------- // -- CAF types ----------------------------------------------------------------
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_address& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_address&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_subnet& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_subnet&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_endpoint& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_endpoint&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_address& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_address&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_subnet& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_subnet&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_endpoint& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_endpoint&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, uri& x); CAF_CORE_EXPORT void parse(string_parser_state&, uri&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, config_value& x); CAF_CORE_EXPORT void parse(string_parser_state&, config_value&);
CAF_CORE_EXPORT void parse(string_parser_state&, std::vector<config_value>&);
CAF_CORE_EXPORT void parse(string_parser_state&, dictionary<config_value>&);
// -- variadic utility --------------------------------------------------------- // -- variadic utility ---------------------------------------------------------
...@@ -209,102 +213,6 @@ void parse(string_parser_state& ps, ...@@ -209,102 +213,6 @@ void parse(string_parser_state& ps,
x = value_type{since_epoch}; x = value_type{since_epoch};
} }
// -- container types ----------------------------------------------------------
CAF_CORE_EXPORT void parse_element(string_parser_state& ps, std::string& x,
const char* char_blacklist);
template <class T>
enable_if_t<!is_pair<T>::value>
parse_element(string_parser_state& ps, T& x, const char*);
template <class First, class Second, size_t N>
void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp,
const char (&char_blacklist)[N]);
struct require_opening_char_t {};
constexpr auto require_opening_char = require_opening_char_t{};
struct allow_omitting_opening_char_t {};
constexpr auto allow_omitting_opening_char = allow_omitting_opening_char_t{};
template <class T, class Policy = allow_omitting_opening_char_t>
enable_if_tt<is_iterable<T>>
parse(string_parser_state& ps, T& xs, Policy = {}) {
using value_type = deconst_kvp_t<typename T::value_type>;
static constexpr auto is_map_type = is_pair<value_type>::value;
static constexpr auto opening_char = is_map_type ? '{' : '[';
static constexpr auto closing_char = is_map_type ? '}' : ']';
auto out = std::inserter(xs, xs.end());
// List/map using [] or {} notation.
if (ps.consume(opening_char)) {
char char_blacklist[] = {closing_char, ',', '\0'};
do {
if (ps.consume(closing_char)) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
return;
}
value_type tmp;
parse_element(ps, tmp, char_blacklist);
if (ps.code > pec::trailing_character)
return;
*out++ = std::move(tmp);
} while (ps.consume(','));
if (ps.consume(closing_char)) {
ps.skip_whitespaces();
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
} else {
ps.code = pec::unexpected_character;
}
return;
}
if constexpr (std::is_same<Policy, require_opening_char_t>::value) {
ps.code = pec::unexpected_character;
} else {
// An empty string simply results in an empty list/map.
if (ps.at_end())
return;
// List/map without [] or {}.
do {
char char_blacklist[] = {',', '\0'};
value_type tmp;
parse_element(ps, tmp, char_blacklist);
if (ps.code > pec::trailing_character)
return;
*out++ = std::move(tmp);
} while (ps.consume(','));
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
}
template <class T>
enable_if_t<!is_pair<T>::value>
parse_element(string_parser_state& ps, T& x, const char*) {
parse(ps, x);
}
template <class First, class Second, size_t N>
void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp,
const char (&char_blacklist)[N]) {
static_assert(N > 0, "empty array");
// TODO: consider to guard the blacklist computation with
// `if constexpr (is_same_v<First, string>)` when switching to C++17.
char key_blacklist[N + 1];
if (N > 1)
memcpy(key_blacklist, char_blacklist, N - 1);
key_blacklist[N - 1] = '=';
key_blacklist[N] = '\0';
parse_element(ps, kvp.first, key_blacklist);
if (ps.code > pec::trailing_character)
return;
if (!ps.consume('=')) {
ps.code = pec::unexpected_character;
return;
}
parse_element(ps, kvp.second, char_blacklist);
}
// -- convenience functions ---------------------------------------------------- // -- convenience functions ----------------------------------------------------
CAF_CORE_EXPORT CAF_CORE_EXPORT
......
...@@ -102,6 +102,28 @@ void read_config_list(State& ps, Consumer&& consumer) { ...@@ -102,6 +102,28 @@ void read_config_list(State& ps, Consumer&& consumer) {
// clang-format on // clang-format on
} }
// Like read_config_list, but without surrounding '[]'.
template <class State, class Consumer>
void lift_config_list(State& ps, Consumer&& consumer) {
// clang-format off
start();
state(init) {
epsilon(before_value)
}
term_state(before_value) {
transition(before_value, " \t\n")
fsm_epsilon(read_config_comment(ps, consumer), before_value, '#')
fsm_epsilon(read_config_value(ps, consumer, std::true_type{}), after_value)
}
term_state(after_value) {
transition(after_value, " \t\n")
transition(before_value, ',')
fsm_epsilon(read_config_comment(ps, consumer), after_value, '#')
}
fin();
// clang-format on
}
template <bool Nested = true, class State, class Consumer> template <bool Nested = true, class State, class Consumer>
void read_config_map(State& ps, Consumer&& consumer) { void read_config_map(State& ps, Consumer&& consumer) {
std::string key; std::string key;
...@@ -140,6 +162,7 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -140,6 +162,7 @@ void read_config_map(State& ps, Consumer&& consumer) {
unstable_state(after_value) { unstable_state(after_value) {
transition(after_value, " \t") transition(after_value, " \t")
transition(had_newline, "\n") transition(had_newline, "\n")
transition_if(!Nested, after_comma, ',')
transition(await_key_name, ',') transition(await_key_name, ',')
transition_if(Nested, done, '}', consumer.end_map()) transition_if(Nested, done, '}', consumer.end_map())
fsm_epsilon(read_config_comment(ps, consumer), had_newline, '#') fsm_epsilon(read_config_comment(ps, consumer), had_newline, '#')
...@@ -157,6 +180,9 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -157,6 +180,9 @@ void read_config_map(State& ps, Consumer&& consumer) {
epsilon_if(!Nested, done) epsilon_if(!Nested, done)
epsilon(unexpected_end_of_input) epsilon(unexpected_end_of_input)
} }
term_state(after_comma) {
epsilon(await_key_name)
}
state(unexpected_end_of_input) { state(unexpected_end_of_input) {
// no transitions, only needed for the unstable states // no transitions, only needed for the unstable states
} }
......
...@@ -189,6 +189,13 @@ public: ...@@ -189,6 +189,13 @@ public:
return true; return true;
} else if constexpr (std::is_same<T, char>::value) { } else if constexpr (std::is_same<T, char>::value) {
return value(string_view{x, strlen(x)}); return value(string_view{x, strlen(x)});
} else if constexpr (std::is_same<T, void>::value) {
sep();
result_ += "*<";
auto addr = reinterpret_cast<intptr_t>(x);
result_ += std::to_string(addr);
result_ += '>';
return true;
} else { } else {
sep(); sep();
result_ += '*'; result_ += '*';
......
...@@ -685,6 +685,44 @@ public: ...@@ -685,6 +685,44 @@ public:
static constexpr bool value = sfinae_type::value; static constexpr bool value = sfinae_type::value;
}; };
template <class T>
struct has_reserve {
private:
template <class List>
static auto sfinae(List* l) -> decltype(l->reserve(10), std::true_type());
template <class U>
static auto sfinae(...) -> std::false_type;
using sfinae_type = decltype(sfinae<T>(nullptr));
public:
static constexpr bool value = sfinae_type::value;
};
template <class T>
constexpr bool has_reserve_v = has_reserve<T>::value;
template <class T>
struct has_emplace_back {
private:
template <class List>
static auto sfinae(List* l)
-> decltype(l->emplace_back(std::declval<typename List::value_type>()),
std::true_type());
template <class U>
static auto sfinae(...) -> std::false_type;
using sfinae_type = decltype(sfinae<T>(nullptr));
public:
static constexpr bool value = sfinae_type::value;
};
template <class T>
constexpr bool has_emplace_back_v = has_emplace_back<T>::value;
template <class T> template <class T>
class has_call_error_handler { class has_call_error_handler {
private: private:
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
...@@ -30,62 +29,41 @@ ...@@ -30,62 +29,41 @@
#include "caf/pec.hpp" #include "caf/pec.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
namespace caf { namespace caf::detail {
namespace detail {
template <class T>
error check_impl(const config_value& x) {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
}
template <class T>
void store_impl(void* ptr, const config_value& x) {
*static_cast<T*>(ptr) = get<T>(x);
}
template <class T>
config_value get_impl(const void* ptr) {
using trait = detail::config_value_access_t<T>;
return config_value{trait::convert(*reinterpret_cast<const T*>(ptr))};
}
template <class T> template <class T>
expected<config_value> parse_impl(T* ptr, string_view str) { error sync_impl(void* ptr, config_value& x) {
if (!ptr) { if (auto val = get_as<T>(x)) {
T tmp; if (auto err = x.assign(*val); !err) {
return parse_impl(&tmp, str); if (ptr)
*static_cast<T*>(ptr) = std::move(*val);
return none;
} else {
return err;
}
} else {
return std::move(val.error());
} }
if constexpr (detail::has_clear_member<T>::value)
ptr->clear();
using trait = detail::config_value_access_t<T>;
string_parser_state ps{str.begin(), str.end()};
trait::parse_cli(ps, *ptr, top_level_cli_parsing);
if (ps.code != pec::success)
return make_error(ps);
return config_value{trait::convert(*ptr)};
} }
CAF_CORE_EXPORT expected<config_value>
parse_impl(std::string* ptr, string_view str);
template <class T> template <class T>
expected<config_value> parse_impl_delegate(void* ptr, string_view str) { config_value get_impl(const void* ptr) {
return parse_impl(reinterpret_cast<T*>(ptr), str); config_value result;
auto err = result.assign(*static_cast<const T*>(ptr));
static_cast<void>(err); // Safe to discard because sync() fails otherwise.
return result;
} }
template <class T> template <class T>
config_option::meta_state* option_meta_state_instance() { config_option::meta_state* option_meta_state_instance() {
using trait = detail::config_value_access_t<T>; static config_option::meta_state obj{sync_impl<T>, get_impl<T>,
static config_option::meta_state obj{check_impl<T>, store_impl<T>, config_value::mapped_type_name<T>()};
get_impl<T>, parse_impl_delegate<T>,
trait::type_name()};
return &obj; return &obj;
} }
} // namespace detail } // namespace caf::detail
namespace caf {
/// Creates a config option that synchronizes with `storage`. /// Creates a config option that synchronizes with `storage`.
template <class T> template <class T>
...@@ -110,12 +88,12 @@ make_negated_config_option(bool& storage, string_view category, ...@@ -110,12 +88,12 @@ make_negated_config_option(bool& storage, string_view category,
string_view name, string_view description); string_view name, string_view description);
// Reads timespans, but stores an integer representing microsecond resolution. // Reads timespans, but stores an integer representing microsecond resolution.
CAF_CORE_EXPORT config_option [[deprecated("use timespan options instead")]] CAF_CORE_EXPORT config_option
make_us_resolution_config_option(size_t& storage, string_view category, make_us_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view description); string_view name, string_view description);
// Reads timespans, but stores an integer representing millisecond resolution. // Reads timespans, but stores an integer representing millisecond resolution.
CAF_CORE_EXPORT config_option [[deprecated("use timespan options instead")]] CAF_CORE_EXPORT config_option
make_ms_resolution_config_option(size_t& storage, string_view category, make_ms_resolution_config_option(size_t& storage, string_view category,
string_view name, string_view description); string_view name, string_view description);
......
...@@ -58,6 +58,10 @@ struct parser_state { ...@@ -58,6 +58,10 @@ struct parser_state {
e = last; e = last;
} }
parser_state(const parser_state&) noexcept = default;
parser_state& operator=(const parser_state&) noexcept = default;
/// Returns the null terminator when reaching the end of the string, /// Returns the null terminator when reaching the end of the string,
/// otherwise the next character. /// otherwise the next character.
char next() noexcept { char next() noexcept {
......
...@@ -170,6 +170,8 @@ enum class sec : uint8_t { ...@@ -170,6 +170,8 @@ enum class sec : uint8_t {
/// An operation failed because the callee does not implement this /// An operation failed because the callee does not implement this
/// functionality. /// functionality.
unsupported_operation, unsupported_operation,
/// A key lookup failed.
no_such_key = 65,
}; };
/// @relates sec /// @relates sec
......
...@@ -38,8 +38,8 @@ CAF_CORE_EXPORT std::string to_string(const settings& xs); ...@@ -38,8 +38,8 @@ CAF_CORE_EXPORT std::string to_string(const settings& xs);
/// Tries to retrieve the value associated to `name` from `xs`. /// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value /// @relates config_value
CAF_CORE_EXPORT const config_value* CAF_CORE_EXPORT const config_value* get_if(const settings* xs,
get_if(const settings* xs, string_view name); string_view name);
/// Tries to retrieve the value associated to `name` from `xs`. /// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value /// @relates config_value
...@@ -54,12 +54,14 @@ auto get_if(const settings* xs, string_view name) { ...@@ -54,12 +54,14 @@ 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 = 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 holds_alternative<T>(*value);
return false; else
return false;
} }
/// Retrieves the value associated to `name` from `xs`.
/// @relates actor_system_config
template <class T> template <class T>
T get(const settings& xs, string_view name) { T get(const settings& xs, string_view name) {
auto result = get_if<T>(&xs, name); auto result = get_if<T>(&xs, name);
...@@ -67,27 +69,39 @@ T get(const settings& xs, string_view name) { ...@@ -67,27 +69,39 @@ T get(const settings& xs, string_view name) {
return detail::move_if_not_ptr(result); return detail::move_if_not_ptr(result);
} }
template <class T, class = typename std::enable_if< /// Retrieves the value associated to `name` from `xs` or returns `fallback`.
!std::is_pointer<T>::value /// @relates actor_system_config
&& !std::is_convertible<T, string_view>::value>::type> template <class To = get_or_auto_deduce, class Fallback>
T get_or(const settings& xs, string_view name, T default_value) { auto get_or(const settings& xs, string_view name, Fallback&& fallback) {
auto result = get_if<T>(&xs, name); if (auto ptr = get_if(&xs, name)) {
if (result) return get_or<To>(*ptr, std::forward<Fallback>(fallback));
return std::move(*result); } else if constexpr (std::is_same<To, get_or_auto_deduce>::value) {
return default_value; using guide = get_or_deduction_guide<std::decay_t<Fallback>>;
return guide::convert(std::forward<Fallback>(fallback));
} else {
return To{std::forward<Fallback>(fallback)};
}
} }
CAF_CORE_EXPORT std::string /// Tries to retrieve the value associated to `name` from `xs` as an instance of
get_or(const settings& xs, string_view name, string_view default_value); /// type `T`.
/// @relates actor_system_config
template <class T>
expected<T> get_as(const settings& xs, string_view name) {
if (auto ptr = get_if(&xs, name))
return get_as<T>(*ptr);
else
return {sec::no_such_key};
}
/// @private /// @private
CAF_CORE_EXPORT config_value& CAF_CORE_EXPORT config_value& put_impl(settings& dict,
put_impl(settings& dict, const std::vector<string_view>& path, const std::vector<string_view>& path,
config_value& value); config_value& value);
/// @private /// @private
CAF_CORE_EXPORT config_value& CAF_CORE_EXPORT config_value& put_impl(settings& dict, string_view key,
put_impl(settings& dict, string_view key, config_value& value); config_value& value);
/// Converts `value` to a `config_value` and assigns it to `key`. /// Converts `value` to a `config_value` and assigns it to `key`.
/// @param dict Dictionary of key-value pairs. /// @param dict Dictionary of key-value pairs.
...@@ -118,7 +132,7 @@ CAF_CORE_EXPORT config_value::list& put_list(settings& xs, std::string name); ...@@ -118,7 +132,7 @@ CAF_CORE_EXPORT config_value::list& put_list(settings& xs, std::string name);
/// Inserts a new list named `name` into the dictionary `xs` and returns /// Inserts a new list named `name` into the dictionary `xs` and returns
/// a reference to it. Overrides existing entries with the same name. /// a reference to it. Overrides existing entries with the same name.
CAF_CORE_EXPORT config_value::dictionary& CAF_CORE_EXPORT config_value::dictionary& put_dictionary(settings& xs,
put_dictionary(settings& xs, std::string name); std::string name);
} // namespace caf } // namespace caf
...@@ -128,7 +128,7 @@ private: ...@@ -128,7 +128,7 @@ private:
return false; return false;
for (const auto& lbl : labels) { for (const auto& lbl : labels) {
if (auto ptr = get_if<settings>(cfg, lbl.str())) { if (auto ptr = get_if<settings>(cfg, lbl.str())) {
if (auto bounds = get_if<std::vector<value_type>>(ptr, "buckets")) { if (auto bounds = get_as<std::vector<value_type>>(*ptr, "buckets")) {
std::sort(bounds->begin(), bounds->end()); std::sort(bounds->begin(), bounds->end());
bounds->erase(std::unique(bounds->begin(), bounds->end()), bounds->erase(std::unique(bounds->begin(), bounds->end()),
bounds->end()); bounds->end());
......
...@@ -367,7 +367,7 @@ public: ...@@ -367,7 +367,7 @@ public:
if (auto grp = get_if<settings>(config_, prefix)) { if (auto grp = get_if<settings>(config_, prefix)) {
if (sub_settings = get_if<settings>(grp, name); if (sub_settings = get_if<settings>(grp, name);
sub_settings != nullptr) { sub_settings != nullptr) {
if (auto lst = get_if<upper_bounds_list>(sub_settings, "buckets")) { if (auto lst = get_as<upper_bounds_list>(*sub_settings, "buckets")) {
std::sort(lst->begin(), lst->end()); std::sort(lst->begin(), lst->end());
lst->erase(std::unique(lst->begin(), lst->end()), lst->end()); lst->erase(std::unique(lst->begin(), lst->end()), lst->end());
if (!lst->empty()) if (!lst->empty())
......
...@@ -295,10 +295,10 @@ actor_system::actor_system(actor_system_config& cfg) ...@@ -295,10 +295,10 @@ actor_system::actor_system(actor_system_config& cfg)
hook->init(*this); hook->init(*this);
// Cache some configuration parameters for faster lookups at runtime. // Cache some configuration parameters for faster lookups at runtime.
using string_list = std::vector<std::string>; using string_list = std::vector<std::string>;
if (auto lst = get_if<string_list>(&cfg, if (auto lst = get_as<string_list>(cfg,
"caf.metrics-filters.actors.includes")) "caf.metrics-filters.actors.includes"))
metrics_actors_includes_ = std::move(*lst); metrics_actors_includes_ = std::move(*lst);
if (auto lst = get_if<string_list>(&cfg, if (auto lst = get_as<string_list>(cfg,
"caf.metrics-filters.actors.excludes")) "caf.metrics-filters.actors.excludes"))
metrics_actors_excludes_ = std::move(*lst); metrics_actors_excludes_ = std::move(*lst);
if (!metrics_actors_includes_.empty()) if (!metrics_actors_includes_.empty())
......
...@@ -340,11 +340,10 @@ actor_system_config& actor_system_config::set_impl(string_view name, ...@@ -340,11 +340,10 @@ actor_system_config& actor_system_config::set_impl(string_view name,
if (opt == nullptr) { if (opt == nullptr) {
std::cerr << "*** failed to set config parameter " << name std::cerr << "*** failed to set config parameter " << name
<< ": invalid name" << std::endl; << ": invalid name" << std::endl;
} else if (auto err = opt->check(value)) { } else if (auto err = opt->sync(value)) {
std::cerr << "*** failed to set config parameter " << name << ": " std::cerr << "*** failed to set config parameter " << name << ": "
<< to_string(err) << std::endl; << to_string(err) << std::endl;
} else { } else {
opt->store(value);
auto category = opt->category(); auto category = opt->category();
if (category == "global") if (category == "global")
content[opt->long_name()] = std::move(value); content[opt->long_name()] = std::move(value);
...@@ -413,12 +412,13 @@ error actor_system_config::extract_config_file_path(string_list& args) { ...@@ -413,12 +412,13 @@ error actor_system_config::extract_config_file_path(string_list& args) {
args.erase(i); args.erase(i);
return make_error(pec::missing_argument, std::move(str)); return make_error(pec::missing_argument, std::move(str));
} }
auto evalue = ptr->parse(path); config_value val{path};
if (!evalue) if (auto err = ptr->sync(val); !err) {
return std::move(evalue.error()); put(content, "config-file", std::move(val));
put(content, "config-file", *evalue); return none;
ptr->store(*evalue); } else {
return none; return err;
}
} }
const settings& content(const actor_system_config& cfg) { const settings& content(const actor_system_config& cfg) {
......
...@@ -128,16 +128,13 @@ string_view config_option::full_name() const noexcept { ...@@ -128,16 +128,13 @@ string_view config_option::full_name() const noexcept {
return buf_slice(buf_[0] == '?' ? 1 : 0, long_name_separator_); return buf_slice(buf_[0] == '?' ? 1 : 0, long_name_separator_);
} }
error config_option::check(const config_value& x) const { error config_option::sync(config_value& x) const {
CAF_ASSERT(meta_->check != nullptr); return meta_->sync(value_, x);
return meta_->check(x);
} }
void config_option::store(const config_value& x) const { error config_option::store(const config_value& x) const {
if (value_ != nullptr) { auto cpy = x;
CAF_ASSERT(meta_->store != nullptr); return sync(cpy);
meta_->store(value_, x);
}
} }
string_view config_option::type_name() const noexcept { string_view config_option::type_name() const noexcept {
...@@ -145,7 +142,7 @@ string_view config_option::type_name() const noexcept { ...@@ -145,7 +142,7 @@ string_view config_option::type_name() const noexcept {
} }
bool config_option::is_flag() const noexcept { bool config_option::is_flag() const noexcept {
return type_name() == "boolean"; return type_name() == "bool";
} }
bool config_option::has_flat_cli_name() const noexcept { bool config_option::has_flat_cli_name() const noexcept {
...@@ -153,7 +150,11 @@ bool config_option::has_flat_cli_name() const noexcept { ...@@ -153,7 +150,11 @@ bool config_option::has_flat_cli_name() const noexcept {
} }
expected<config_value> config_option::parse(string_view input) const { expected<config_value> config_option::parse(string_view input) const {
return meta_->parse(value_, input); config_value val{input};
if (auto err = sync(val))
return {std::move(err)};
else
return {std::move(val)};
} }
optional<config_value> config_option::get() const { optional<config_value> config_option::get() const {
......
...@@ -18,8 +18,11 @@ ...@@ -18,8 +18,11 @@
#include "caf/config_option_adder.hpp" #include "caf/config_option_adder.hpp"
#include "caf/config.hpp"
#include "caf/config_option_set.hpp" #include "caf/config_option_set.hpp"
CAF_PUSH_DEPRECATED_WARNING
namespace caf { namespace caf {
config_option_adder::config_option_adder(config_option_set& target, config_option_adder::config_option_adder(config_option_set& target,
...@@ -53,3 +56,5 @@ config_option_adder& config_option_adder::add_impl(config_option&& opt) { ...@@ -53,3 +56,5 @@ config_option_adder& config_option_adder::add_impl(config_option&& opt) {
} }
} // namespace caf } // namespace caf
CAF_POP_WARNINGS
...@@ -137,6 +137,12 @@ auto config_option_set::parse(settings& config, argument_iterator first, ...@@ -137,6 +137,12 @@ auto config_option_set::parse(settings& config, argument_iterator first,
// Parses an argument. // Parses an argument.
using iter = string::const_iterator; using iter = string::const_iterator;
auto consume = [&](const config_option& opt, iter arg_begin, iter arg_end) { auto consume = [&](const config_option& opt, iter arg_begin, iter arg_end) {
auto to_pec_code = [](const error& err) {
if (err.category() == type_id_v<pec>)
return static_cast<pec>(err.code());
else
return pec::invalid_argument;
};
// Extract option name and category. // Extract option name and category.
auto opt_name = opt.long_name(); auto opt_name = opt.long_name();
auto opt_ctg = opt.category(); auto opt_ctg = opt.category();
...@@ -144,27 +150,31 @@ auto config_option_set::parse(settings& config, argument_iterator first, ...@@ -144,27 +150,31 @@ auto config_option_set::parse(settings& config, argument_iterator first,
auto& entry = opt_ctg == "global" ? config : select_entry(config, opt_ctg); auto& entry = opt_ctg == "global" ? config : select_entry(config, opt_ctg);
// Flags only consume the current element. // Flags only consume the current element.
if (opt.is_flag()) { if (opt.is_flag()) {
if (arg_begin != arg_end) if (arg_begin == arg_end) {
config_value cfg_true{true};
if (auto err = opt.sync(cfg_true); !err) {
entry[opt_name] = cfg_true;
return pec::success;
} else {
return to_pec_code(err);
}
} else {
return pec::invalid_argument; return pec::invalid_argument;
config_value cfg_true{true}; }
opt.store(cfg_true);
entry[opt_name] = cfg_true;
} else { } else {
if (arg_begin == arg_end) if (arg_begin != arg_end) {
auto arg_size = static_cast<size_t>(std::distance(arg_begin, arg_end));
config_value val{string_view{std::addressof(*arg_begin), arg_size}};
if (auto err = opt.sync(val); !err) {
entry[opt_name] = std::move(val);
return pec::success;
} else {
return to_pec_code(err);
}
} else {
return pec::missing_argument; return pec::missing_argument;
auto slice_size = static_cast<size_t>(std::distance(arg_begin, arg_end));
string_view slice{&*arg_begin, slice_size};
auto val = opt.parse(slice);
if (!val) {
auto& err = val.error();
if (err.category() == type_id_v<pec>)
return static_cast<pec>(err.code());
return pec::invalid_argument;
} }
opt.store(*val);
entry[opt_name] = std::move(*val);
} }
return pec::success;
}; };
// We loop over the first N-1 values, because we always consider two // We loop over the first N-1 values, because we always consider two
// arguments at once. // arguments at once.
......
...@@ -62,6 +62,11 @@ auto no_conversions() { ...@@ -62,6 +62,11 @@ auto no_conversions() {
return detail::make_overload(no_conversion<To, From>()...); return detail::make_overload(no_conversion<To, From>()...);
} }
template <class T>
constexpr ptrdiff_t signed_index_of() {
return detail::tl_index_of<typename config_value::types, T>::value;
}
} // namespace } // namespace
// -- constructors, destructors, and assignment operators ---------------------- // -- constructors, destructors, and assignment operators ----------------------
...@@ -153,6 +158,10 @@ const char* config_value::type_name_at_index(size_t index) noexcept { ...@@ -153,6 +158,10 @@ const char* config_value::type_name_at_index(size_t index) noexcept {
return type_names[index]; return type_names[index];
} }
ptrdiff_t config_value::signed_index() const noexcept {
return static_cast<ptrdiff_t>(data_.index());
}
// -- utility ------------------------------------------------------------------ // -- utility ------------------------------------------------------------------
type_id_t config_value::type_id() const noexcept { type_id_t config_value::type_id() const noexcept {
...@@ -321,6 +330,16 @@ expected<timespan> config_value::to_timespan() const { ...@@ -321,6 +330,16 @@ expected<timespan> config_value::to_timespan() const {
return visit(f, data_); return visit(f, data_);
} }
expected<uri> config_value::to_uri() const {
using result_type = expected<uri>;
auto f = detail::make_overload(
no_conversions<uri, none_t, bool, integer, real, timespan,
config_value::list, config_value::dictionary>(),
[](const uri& x) { return result_type{x}; },
[](const std::string& x) { return make_uri(x); });
return visit(f, data_);
}
expected<config_value::list> config_value::to_list() const { expected<config_value::list> config_value::to_list() const {
using result_type = expected<list>; using result_type = expected<list>;
auto dict_to_list = [](const dictionary& dict, list& result) { auto dict_to_list = [](const dictionary& dict, list& result) {
...@@ -335,18 +354,17 @@ expected<config_value::list> config_value::to_list() const { ...@@ -335,18 +354,17 @@ expected<config_value::list> config_value::to_list() const {
auto f = detail::make_overload( auto f = detail::make_overload(
no_conversions<list, none_t, bool, integer, real, timespan, uri>(), no_conversions<list, none_t, bool, integer, real, timespan, uri>(),
[dict_to_list](const std::string& x) { [dict_to_list](const std::string& x) {
// Check whether we can parse the string as a list. If that fails, try // Check whether we can parse the string as a list. However, we also
// whether we can parse it as a dictionary instead (and then convert that // accept dictionaries that we convert to lists of key-value pairs. We
// to a list). // need to try converting to dictionary *first*, because detail::parse for
config_value::list tmp; // the list otherwise produces a list with one dictionary.
if (detail::parse(x, tmp, detail::require_opening_char) == none) if (config_value::dictionary dict; detail::parse(x, dict) == none) {
return result_type{std::move(tmp)}; config_value::list tmp;
config_value::dictionary dict;
if (detail::parse(x, dict, detail::require_opening_char) == none) {
tmp.clear();
dict_to_list(dict, tmp); dict_to_list(dict, tmp);
return result_type{std::move(tmp)}; return result_type{std::move(tmp)};
} }
if (config_value::list tmp; detail::parse(x, tmp) == none)
return result_type{std::move(tmp)};
std::string msg = "cannot convert "; std::string msg = "cannot convert ";
detail::print_escaped(msg, x); detail::print_escaped(msg, x);
msg += " to a list"; msg += " to a list";
...@@ -383,12 +401,11 @@ expected<config_value::dictionary> config_value::to_dictionary() const { ...@@ -383,12 +401,11 @@ expected<config_value::dictionary> config_value::to_dictionary() const {
return result_type{std::move(err)}; return result_type{std::move(err)};
} }
}, },
[](const std::string& x) { [this](const std::string& x) {
if (dictionary tmp; detail::parse(x, tmp) == none) { if (dictionary tmp; detail::parse(x, tmp) == none)
return result_type{std::move(tmp)}; return result_type{std::move(tmp)};
} if (auto lst = to_list()) {
if (list tmp; detail::parse(x, tmp) == none) { config_value ls{std::move(*lst)};
config_value ls{std::move(tmp)};
if (auto res = ls.to_dictionary()) if (auto res = ls.to_dictionary())
return res; return res;
} }
...@@ -453,14 +470,66 @@ config_value::parse_msg_impl(string_view str, ...@@ -453,14 +470,66 @@ config_value::parse_msg_impl(string_view str,
// -- related free functions --------------------------------------------------- // -- related free functions ---------------------------------------------------
bool operator<(double x, const config_value& y) {
return config_value{x} < y;
}
bool operator<=(double x, const config_value& y) {
return config_value{x} <= y;
}
bool operator==(double x, const config_value& y) {
return config_value{x} == y;
}
bool operator>(double x, const config_value& y) {
return config_value{x} > y;
}
bool operator>=(double x, const config_value& y) {
return config_value{x} >= y;
}
bool operator<(const config_value& x, double y) {
return x < config_value{y};
}
bool operator<=(const config_value& x, double y) {
return x <= config_value{y};
}
bool operator==(const config_value& x, double y) {
return x == config_value{y};
}
bool operator>(const config_value& x, double y) {
return x > config_value{y};
}
bool operator>=(const config_value& x, double y) {
return x >= config_value{y};
}
bool operator<(const config_value& x, const config_value& y) { bool operator<(const config_value& x, const config_value& y) {
return x.get_data() < y.get_data(); return x.get_data() < y.get_data();
} }
bool operator<=(const config_value& x, const config_value& y) {
return x.get_data() <= y.get_data();
}
bool operator==(const config_value& x, const config_value& y) { bool operator==(const config_value& x, const config_value& y) {
return x.get_data() == y.get_data(); return x.get_data() == y.get_data();
} }
bool operator>(const config_value& x, const config_value& y) {
return x.get_data() > y.get_data();
}
bool operator>=(const config_value& x, const config_value& y) {
return x.get_data() >= y.get_data();
}
namespace { namespace {
void to_string_impl(std::string& str, const config_value& x); void to_string_impl(std::string& str, const config_value& x);
......
...@@ -428,7 +428,7 @@ bool pull(config_value_reader& reader, T& x) { ...@@ -428,7 +428,7 @@ bool pull(config_value_reader& reader, T& x) {
auto& top = reader.top(); auto& top = reader.top();
if (holds_alternative<const config_value*>(top)) { if (holds_alternative<const config_value*>(top)) {
auto ptr = get<const config_value*>(top); auto ptr = get<const config_value*>(top);
if (auto val = get_if<internal_type>(ptr)) { if (auto val = get_as<internal_type>(*ptr)) {
assign(*val); assign(*val);
reader.pop(); reader.pop();
return true; return true;
...@@ -446,7 +446,7 @@ bool pull(config_value_reader& reader, T& x) { ...@@ -446,7 +446,7 @@ bool pull(config_value_reader& reader, T& x) {
return false; return false;
} }
auto ptr = std::addressof(seq.current()); auto ptr = std::addressof(seq.current());
if (auto val = get_if<internal_type>(ptr)) { if (auto val = get_as<internal_type>(*ptr)) {
assign(*val); assign(*val);
seq.advance(); seq.advance();
return true; return true;
......
...@@ -268,11 +268,12 @@ bool config_value_writer::begin_associative_array(size_t) { ...@@ -268,11 +268,12 @@ 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) { [this, &inner](config_value* val) {
// Morph the top element into a dictionary. // Morph the top element into a dictionary.
auto& dict = val->as_dictionary(); auto& dict = val->as_dictionary();
dict.clear(); dict.clear();
st_.top() = &dict; st_.top() = &dict;
inner = &dict;
return true; return true;
}, },
[this](settings*) { [this](settings*) {
......
...@@ -43,14 +43,14 @@ config_list_consumer::config_list_consumer(config_value_consumer* parent) ...@@ -43,14 +43,14 @@ config_list_consumer::config_list_consumer(config_value_consumer* parent)
} }
pec config_list_consumer::end_list() { pec config_list_consumer::end_list() {
auto f = make_overload( auto f = make_overload([](none_t) { return pec::success; },
[this](config_consumer* ptr) { [this](config_consumer* ptr) {
return ptr->value(config_value{std::move(xs_)}); return ptr->value(config_value{std::move(result)});
}, },
[this](auto* ptr) { [this](auto* ptr) {
ptr->value(config_value{std::move(xs_)}); ptr->value(config_value{std::move(result)});
return pec::success; return pec::success;
}); });
return visit(f, parent_); return visit(f, parent_);
} }
...@@ -59,7 +59,8 @@ config_consumer config_list_consumer::begin_map() { ...@@ -59,7 +59,8 @@ config_consumer config_list_consumer::begin_map() {
} }
std::string config_list_consumer::qualified_key() { std::string config_list_consumer::qualified_key() {
auto f = make_overload([](config_value_consumer*) { return std::string{}; }, auto f = make_overload([](none_t) { return std::string{}; },
[](config_value_consumer*) { return std::string{}; },
[](auto* ptr) { return ptr->qualified_key(); }); [](auto* ptr) { return ptr->qualified_key(); });
return visit(f, parent_); return visit(f, parent_);
} }
...@@ -156,16 +157,11 @@ void merge_into_place(settings& src, settings& dst) { ...@@ -156,16 +157,11 @@ void merge_into_place(settings& src, settings& dst) {
} // namespace } // namespace
pec config_consumer::value_impl(config_value&& x) { pec config_consumer::value_impl(config_value&& x) {
// See whether there's a config_option associated to this key and perform a // Sync with config option object if available.
// type check if necessary. if (options_ != nullptr)
const config_option* opt; if (auto opt = options_->qualified_name_lookup(category_, current_key_))
if (options_ == nullptr) { if (auto err = opt->sync(x))
opt = nullptr; return pec::type_mismatch;
} else {
opt = options_->qualified_name_lookup(category_, current_key_);
if (opt && opt->check(x) != none)
return pec::type_mismatch;
}
// Insert / replace value in the map. // Insert / replace value in the map.
if (auto dict = get_if<settings>(&x)) { if (auto dict = get_if<settings>(&x)) {
// Merge values into the destination, because it can already contain any // Merge values into the destination, because it can already contain any
...@@ -178,13 +174,6 @@ pec config_consumer::value_impl(config_value&& x) { ...@@ -178,13 +174,6 @@ pec config_consumer::value_impl(config_value&& x) {
} else { } else {
cfg_->insert_or_assign(current_key_, std::move(x)); cfg_->insert_or_assign(current_key_, std::move(x));
} }
// Sync with config option if needed.
if (opt) {
if (auto i = cfg_->find(current_key_); i != cfg_->end())
opt->store(i->second);
else
return pec::invalid_state;
}
return pec::success; return pec::success;
} }
......
...@@ -165,17 +165,62 @@ void parse(string_parser_state& ps, uri& x) { ...@@ -165,17 +165,62 @@ void parse(string_parser_state& ps, uri& x) {
void parse(string_parser_state& ps, config_value& x) { void parse(string_parser_state& ps, config_value& x) {
ps.skip_whitespaces(); ps.skip_whitespaces();
if (ps.at_end()) { if (!ps.at_end()) {
detail::config_value_consumer f;
parser::read_config_value(ps, f);
if (ps.code <= pec::trailing_character)
x = std::move(f.result);
} else {
ps.code = pec::unexpected_eof;
}
}
void parse(string_parser_state& ps, std::vector<config_value>& x) {
ps.skip_whitespaces();
if (!ps.at_end()) {
detail::config_list_consumer f;
auto fallback = ps;
if (ps.consume('[')) {
parser::read_config_list(ps, f);
if (ps.code == pec::success) {
x = std::move(f.result);
} else {
// Rewind parser state and try parsing again as a list without
// surrounding '[]' characters. This catches edge cases like
// '[1, 2], [3, 4]'. On error, we restore the parser state after the
// first error.
auto first_failure = ps;
ps = fallback;
f.result.clear();
parser::lift_config_list(ps, f);
if (ps.code <= pec::trailing_character)
x = std::move(f.result);
else
ps = first_failure;
}
} else {
// If the string isn't surrounded by '[]' in the first place, we call
// lift_config_list and keep it's result in any case.
parser::lift_config_list(ps, f);
if (ps.code == pec::success)
x = std::move(f.result);
}
} else {
ps.code = pec::unexpected_eof;
}
}
void parse(string_parser_state& ps, dictionary<config_value>& x) {
ps.skip_whitespaces();
if (!ps.at_end()) {
detail::config_consumer f{x};
if (ps.consume('{'))
parser::read_config_map(ps, f);
else
parser::read_config_map<false>(ps, f);
} else {
ps.code = pec::unexpected_eof; ps.code = pec::unexpected_eof;
return;
} }
// Safe the string as fallback.
string_view str{ps.i, ps.e};
// Dispatch to parser.
detail::config_value_consumer f;
parser::read_config_value(ps, f);
if (ps.code <= pec::trailing_character)
x = std::move(f.result);
} }
PARSE_IMPL(ipv4_address, ipv4_address) PARSE_IMPL(ipv4_address, ipv4_address)
......
...@@ -310,7 +310,7 @@ void logger::init(actor_system_config& cfg) { ...@@ -310,7 +310,7 @@ void logger::init(actor_system_config& cfg) {
return CAF_LOG_LEVEL_QUIET; return CAF_LOG_LEVEL_QUIET;
}; };
auto read_filter = [&cfg](string_list& var, string_view key) { auto read_filter = [&cfg](string_list& var, string_view key) {
if (auto lst = get_if<string_list>(&cfg, key)) if (auto lst = get_as<string_list>(cfg, key))
var = std::move(*lst); var = std::move(*lst);
}; };
cfg_.file_verbosity = get_verbosity("caf.logger.file.verbosity"); cfg_.file_verbosity = get_verbosity("caf.logger.file.verbosity");
......
...@@ -23,73 +23,42 @@ ...@@ -23,73 +23,42 @@
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#define DEFAULT_META(type, parse_fun) \
config_option::meta_state type##_meta_state{ \
default_config_option_check<type>, default_config_option_store<type>, \
get_impl<type>, parse_fun, \
detail::config_value_access_t<type>::type_name()};
using std::string;
namespace caf { namespace caf {
namespace detail {
expected<config_value> parse_impl(std::string* ptr, string_view str) {
// Parse quoted strings, otherwise consume the entire string.
auto e = str.end();
auto i = std::find_if(str.begin(), e, [](char c) { return !isspace(c); });
if (i == e) {
if (ptr != nullptr)
ptr->assign(i, e);
return config_value{std::string{i, e}};
}
if (*i == '"') {
if (ptr == nullptr) {
std::string tmp;
if (auto err = parse(str, tmp))
return err;
return config_value{std::move(tmp)};
} else {
if (auto err = parse(str, *ptr))
return err;
return config_value{*ptr};
}
}
if (ptr != nullptr)
ptr->assign(str.begin(), str.end());
return config_value{std::string{str.begin(), str.end()}};
}
} // namespace detail
namespace { namespace {
using meta_state = config_option::meta_state; using meta_state = config_option::meta_state;
void bool_store_neg(void* ptr, const config_value& x) { error bool_sync_neg(void* ptr, config_value& x) {
*static_cast<bool*>(ptr) = !get<bool>(x); if (auto val = get_as<bool>(x)) {
x = config_value{*val};
if (ptr)
*static_cast<bool*>(ptr) = !*val;
return none;
} else {
return std::move(val.error());
}
} }
config_value bool_get_neg(const void* ptr) { config_value bool_get_neg(const void* ptr) {
return config_value{!*static_cast<const bool*>(ptr)}; return config_value{!*static_cast<const bool*>(ptr)};
} }
meta_state bool_neg_meta{detail::check_impl<bool>, bool_store_neg, bool_get_neg, meta_state bool_neg_meta{bool_sync_neg, bool_get_neg, "bool"};
nullptr,
detail::config_value_access_t<bool>::type_name()};
error check_timespan(const config_value& x) { template <uint64_t Denominator>
if (holds_alternative<timespan>(x)) error sync_timespan(void* ptr, config_value& x) {
if (auto val = get_as<timespan>(x)) {
x = config_value{*val};
if (ptr)
*static_cast<size_t*>(ptr) = static_cast<size_t>(get<timespan>(x).count())
/ Denominator;
return none; return none;
return make_error(pec::type_mismatch); } else {
return std::move(val.error());
}
} }
template <uint64_t Denominator>
void store_timespan(void* ptr, const config_value& x) {
*static_cast<size_t*>(ptr) = static_cast<size_t>(get<timespan>(x).count())
/ Denominator;
}
template <uint64_t Denominator> template <uint64_t Denominator>
config_value get_timespan(const void* ptr) { config_value get_timespan(const void* ptr) {
auto ival = static_cast<int64_t>(*static_cast<const size_t*>(ptr)); auto ival = static_cast<int64_t>(*static_cast<const size_t*>(ptr));
...@@ -97,13 +66,10 @@ config_value get_timespan(const void* ptr) { ...@@ -97,13 +66,10 @@ config_value get_timespan(const void* ptr) {
return config_value{val}; return config_value{val};
} }
meta_state us_res_meta{check_timespan, store_timespan<1000>, get_timespan<1000>, meta_state us_res_meta{sync_timespan<1000>, get_timespan<1000>, "timespan"};
nullptr,
detail::config_value_access_t<timespan>::type_name()};
meta_state ms_res_meta{check_timespan, store_timespan<1000000>, meta_state ms_res_meta{sync_timespan<1000000>, get_timespan<1000000>,
get_timespan<1000000>, nullptr, "timespan"};
detail::config_value_access_t<timespan>::type_name()};
} // namespace } // namespace
......
...@@ -249,10 +249,8 @@ void abstract_coordinator::init(actor_system_config& cfg) { ...@@ -249,10 +249,8 @@ void abstract_coordinator::init(actor_system_config& cfg) {
namespace sr = defaults::scheduler; namespace sr = defaults::scheduler;
max_throughput_ = get_or(cfg, "caf.scheduler.max-throughput", max_throughput_ = get_or(cfg, "caf.scheduler.max-throughput",
sr::max_throughput); sr::max_throughput);
if (auto num_workers = get_if<size_t>(&cfg, "caf.scheduler.max-threads")) num_workers_ = get_or(cfg, "caf.scheduler.max-threads",
num_workers_ = *num_workers; default_thread_count());
else
num_workers_ = default_thread_count();
} }
actor_system::module::id_t abstract_coordinator::id() const { actor_system::module::id_t abstract_coordinator::id() const {
......
...@@ -146,6 +146,8 @@ std::string to_string(sec x) { ...@@ -146,6 +146,8 @@ std::string to_string(sec x) {
return "type_clash"; return "type_clash";
case sec::unsupported_operation: case sec::unsupported_operation:
return "unsupported_operation"; return "unsupported_operation";
case sec::no_such_key:
return "no_such_key";
}; };
} }
...@@ -345,6 +347,9 @@ bool from_string(string_view in, sec& out) { ...@@ -345,6 +347,9 @@ bool from_string(string_view in, sec& out) {
} else if (in == "unsupported_operation") { } else if (in == "unsupported_operation") {
out = sec::unsupported_operation; out = sec::unsupported_operation;
return true; return true;
} else if (in == "no_such_key") {
out = sec::no_such_key;
return true;
} else { } else {
return false; return false;
} }
...@@ -421,6 +426,7 @@ bool from_integer(std::underlying_type_t<sec> in, ...@@ -421,6 +426,7 @@ bool from_integer(std::underlying_type_t<sec> in,
case sec::connection_closed: case sec::connection_closed:
case sec::type_clash: case sec::type_clash:
case sec::unsupported_operation: case sec::unsupported_operation:
case sec::no_such_key:
out = result; out = result;
return true; return true;
}; };
......
...@@ -43,12 +43,12 @@ const config_value* get_if(const settings* xs, string_view name) { ...@@ -43,12 +43,12 @@ const config_value* get_if(const settings* xs, string_view name) {
name.substr(pos + 1)); name.substr(pos + 1));
} }
std::string get_or(const settings& xs, string_view name, expected<std::string> get_or(const settings& xs, string_view name,
string_view default_value) { const char* fallback) {
auto result = get_if<std::string>(&xs, name); if (auto ptr = get_if(&xs, name))
if (result) return get_as<std::string>(*ptr);
return std::move(*result); else
return std::string{default_value.begin(), default_value.end()}; return {std::string{fallback}};
} }
config_value& put_impl(settings& dict, const std::vector<string_view>& path, config_value& put_impl(settings& dict, const std::vector<string_view>& path,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <vector> #include <vector>
using namespace caf; using namespace caf;
using namespace caf::literals;
using namespace std::string_literals; using namespace std::string_literals;
...@@ -161,12 +162,19 @@ CAF_TEST(file input overrides defaults but CLI args always win) { ...@@ -161,12 +162,19 @@ CAF_TEST(file input overrides defaults but CLI args always win) {
#define CHECK_SYNCED(var, value) \ #define CHECK_SYNCED(var, value) \
do { \ do { \
CAF_CHECK_EQUAL(var, value); \ CAF_CHECK_EQUAL(var, value); \
CAF_CHECK_EQUAL(get<decltype(var)>(cfg, #var), value); \ if (auto maybe_val = get_as<decltype(var)>(cfg, #var)) { \
CAF_CHECK_EQUAL(*maybe_val, value); \
} else { \
auto cv = get_if(std::addressof(cfg.content), #var); \
CAF_ERROR("expected type " \
<< config_value::mapped_type_name<decltype(var)>() \
<< ", got: " << cv->type_name()); \
} \
} while (false) } while (false)
// Checks whether an entry in content(cfg) is equal to `value`. // Checks whether an entry in content(cfg) is equal to `value`.
#define CHECK_TEXT_ONLY(type, var, value) \ #define CHECK_TEXT_ONLY(type, var, value) \
CAF_CHECK_EQUAL(get<type>(cfg, #var), value) CAF_CHECK_EQUAL(get_as<type>(cfg, #var), value)
#define ADD(var) add(var, #var, "...") #define ADD(var) add(var, #var, "...")
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "core-test.hpp" #include "core-test.hpp"
#include <sstream>
#include "caf/make_config_option.hpp" #include "caf/make_config_option.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
...@@ -32,6 +34,155 @@ using std::string; ...@@ -32,6 +34,155 @@ using std::string;
namespace { namespace {
struct state;
struct baseline {
std::vector<std::string> cli;
std::string conf;
settings res;
std::function<bool(const state&)> predicate;
};
struct request_pair {
my_request first;
my_request second;
};
template <class Inspector>
bool inspect(Inspector& f, request_pair& x) {
return f.object(x).fields(f.field("first", x.first),
f.field("second", x.second));
}
struct state {
s1 my_app_s1;
std::vector<int32_t> my_app_vector;
level my_app_severity = level::trace;
my_request my_app_request;
request_pair my_app_request_pair;
config_option_set options;
state() {
config_option_adder{options, "?my.app"}
.add(my_app_s1, "s1", "")
.add(my_app_vector, "vector,v", "")
.add(my_app_severity, "severity,s", "")
.add(my_app_request, "request,r", "")
.add(my_app_request_pair, "request-pair,R", "");
config_option_adder{options, "sys"}
.add<std::string>("query,q", "")
.add<int8_t>("threads,tTd", "");
}
void run(baseline& x, size_t index) {
settings res;
std::istringstream src{x.conf};
if (auto parsed = actor_system_config::parse_config(src, options)) {
res = std::move(*parsed);
} else {
CAF_ERROR("failed to parse baseline at index " << index << ": "
<< parsed.error());
return;
}
auto [code, pos] = options.parse(res, x.cli);
if (pos != x.cli.end()) {
CAF_ERROR("failed to parse all arguments for baseline at index "
<< index << ", stopped at: " << *pos << " (" << code << ')');
return;
}
if (code != pec::success) {
CAF_ERROR("CLI arguments for baseline at index "
<< index << " failed to parse: " << code);
return;
}
if (!x.predicate(*this)) {
CAF_ERROR("predicate for baseline at index " << index << "failed! ");
return;
}
MESSAGE("all checks for baseline at index " << index << " passed");
}
};
struct fixture {
std::vector<baseline> baselines;
template <class Predicate>
void add_test(std::vector<std::string> cli, std::string conf, settings res,
Predicate f) {
baselines.emplace_back(baseline{
std::move(cli),
std::move(conf),
std::move(res),
f,
});
}
template <class Predicate>
void add_test(std::vector<std::string> cli, std::string conf, std::string res,
Predicate f) {
config_value cv_res{res};
if (auto parsed = get_as<settings>(cv_res))
add_test(std::move(cli), std::move(conf), std::move(*parsed),
std::move(f));
else
CAF_FAIL("failed to parse result settings: " << parsed.error()
<< "\nINPUT:\n"
<< res << '\n');
}
template <class Res>
void add_test(std::vector<std::string> cli, std::string conf, Res&& res) {
return add_test(std::move(cli), std::move(conf), std::forward<Res>(res),
[](const state&) { return true; });
}
fixture() {
using ivec = std::vector<int32_t>;
add_test({"-s", "error"}, "", R"_(my { app { severity = "error" } })_",
[](auto& st) {
return CHECK_EQ(st.my_app_severity, level::error);
});
add_test({"-v", "1, 2, 3"}, "", R"_(my { app { vector = [1, 2, 3] } })_",
[](auto& st) {
return CHECK_EQ(st.my_app_vector, ivec({1, 2, 3}));
});
add_test({"-v", "[1, 2, 3]"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-v[1, 2, 3]"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-v1, 2, 3,"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-r", R"_({"a":1,"b":2})_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({"-r", R"_(a=1,b=2)_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({R"_(--my.app.request={a=1,b=2})_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({R"_(--my.app.request=a=1,b=2,)_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({"-R",
R"_({"first": {"a": 1, "b": 2}, "second": {"a": 3, "b": 4}})_"},
"",
R"_(my { app { request-pair { first { a = 1, b = 2 },
second { a = 3, b = 4 } } } })_");
add_test({}, "sys{threads=2}", R"_(sys { threads = 2 })_");
add_test({"-t", "1"}, "sys{threads=2}", R"_(sys { threads = 1 })_");
add_test({"-T", "1"}, "sys{threads=2}", R"_(sys { threads = 1 })_");
add_test({"-d", "1"}, "sys{threads=2}", R"_(sys { threads = 1 })_");
add_test({"--sys.threads=1"}, "sys{threads=2}", R"_(sys { threads = 1 })_");
add_test({"--sys.query=foo"}, "", R"_(sys { query = "foo" })_");
add_test({"-q", "\"a\" in b"}, "", R"_(sys { query = "\"a\" in b" })_");
}
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO("options on the CLI override config files that override defaults") {
for (size_t index = 0; index < baselines.size(); ++index) {
state st;
st.run(baselines[index], index);
}
}
constexpr string_view category = "category"; constexpr string_view category = "category";
constexpr string_view name = "name"; constexpr string_view name = "name";
constexpr string_view explanation = "explanation"; constexpr string_view explanation = "explanation";
...@@ -48,14 +199,13 @@ constexpr int64_t underflow() { ...@@ -48,14 +199,13 @@ constexpr int64_t underflow() {
template <class T> template <class T>
optional<T> read(string_view arg) { optional<T> read(string_view arg) {
auto co = make_config_option<T>(category, name, explanation); auto result = T{};
auto res = co.parse(arg); auto co = make_config_option<T>(result, category, name, explanation);
if (res && holds_alternative<T>(*res)) { config_value val{arg};
if (co.check(*res) != none) if (auto err = co.sync(val); !err)
CAF_ERROR("co.parse() produced the wrong type!"); return {std::move(result)};
return get<T>(*res); else
} return none;
return none;
} }
// Unsigned integers. // Unsigned integers.
...@@ -98,8 +248,6 @@ void compare(const config_option& lhs, const config_option& rhs) { ...@@ -98,8 +248,6 @@ void compare(const config_option& lhs, const config_option& rhs) {
CAF_CHECK_EQUAL(lhs.full_name(), rhs.full_name()); CAF_CHECK_EQUAL(lhs.full_name(), rhs.full_name());
} }
} // namespace
CAF_TEST(copy constructor) { CAF_TEST(copy constructor) {
auto one = make_config_option<int>("cat1", "one", "option 1"); auto one = make_config_option<int>("cat1", "one", "option 1");
auto two = one; auto two = one;
...@@ -166,12 +314,12 @@ CAF_TEST(type double) { ...@@ -166,12 +314,12 @@ CAF_TEST(type double) {
CAF_CHECK_EQUAL(unbox(read<double>("-1.0")), -1.0); CAF_CHECK_EQUAL(unbox(read<double>("-1.0")), -1.0);
CAF_CHECK_EQUAL(unbox(read<double>("-0.1")), -0.1); CAF_CHECK_EQUAL(unbox(read<double>("-0.1")), -0.1);
CAF_CHECK_EQUAL(read<double>("0"), 0.); CAF_CHECK_EQUAL(read<double>("0"), 0.);
CAF_CHECK_EQUAL(read<double>("\"0.1\""), none); CAF_CHECK_EQUAL(read<double>("\"0.1\""), none);
} }
CAF_TEST(type string) { CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo"); CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo"); CAF_CHECK_EQUAL(unbox(read<string>(R"_("foo")_")), R"_("foo")_");
} }
CAF_TEST(type timespan) { CAF_TEST(type timespan) {
...@@ -231,3 +379,5 @@ CAF_TEST(find by long opt) { ...@@ -231,3 +379,5 @@ CAF_TEST(find by long opt) {
// No options to look through. // No options to look through.
check({}, false, false); check({}, false, false);
} }
END_FIXTURE_SCOPE()
...@@ -53,10 +53,11 @@ struct fixture { ...@@ -53,10 +53,11 @@ struct fixture {
settings cfg; settings cfg;
auto res = opts.parse(cfg, std::move(args)); auto res = opts.parse(cfg, std::move(args));
if (res.first != pec::success) if (res.first != pec::success)
return res.first; return {res.first};
if (auto x = get_if<T>(&cfg, key)) else if (auto x = get_as<T>(cfg, key))
return detail::move_if_not_ptr(x); return {std::move(*x)};
return sec::invalid_argument; else
return {sec::invalid_argument};
} }
std::string key = "value"; std::string key = "value";
...@@ -101,7 +102,7 @@ CAF_TEST(parse with ref syncing) { ...@@ -101,7 +102,7 @@ CAF_TEST(parse with ref syncing) {
settings cfg; settings cfg;
vector<string> args{"-i42", vector<string> args{"-i42",
"-f", "-f",
"1e12", "1e2",
"-shello", "-shello",
"--bar.l=[\"hello\", \"world\"]", "--bar.l=[\"hello\", \"world\"]",
"-d", "-d",
...@@ -114,34 +115,20 @@ CAF_TEST(parse with ref syncing) { ...@@ -114,34 +115,20 @@ CAF_TEST(parse with ref syncing) {
CAF_FAIL("parser stopped at: " << *res.second); CAF_FAIL("parser stopped at: " << *res.second);
CAF_MESSAGE("verify referenced values"); CAF_MESSAGE("verify referenced values");
CAF_CHECK_EQUAL(foo_i, 42); CAF_CHECK_EQUAL(foo_i, 42);
CAF_CHECK_EQUAL(foo_f, 1e12); CAF_CHECK_EQUAL(foo_f, 1e2);
CAF_CHECK_EQUAL(foo_b, true); CAF_CHECK_EQUAL(foo_b, true);
CAF_CHECK_EQUAL(bar_s, "hello"); CAF_CHECK_EQUAL(bar_s, "hello");
CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"})); CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"}));
CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}})); CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}}));
CAF_MESSAGE("verify dictionary content"); CAF_MESSAGE("verify dictionary content");
CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42); CAF_CHECK_EQUAL(get_as<int>(cfg, "foo.i"), 42);
} }
CAF_TEST(string parameters) { CAF_TEST(string parameters) {
opts.add<std::string>("value,v", "some value"); opts.add<std::string>("value,v", "some value");
CAF_MESSAGE("test string option with and without quotes");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"foo\\tbar\""}), "foo\tbar");
CAF_CHECK_EQUAL(read<std::string>({"--value=foobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"--value=foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "foobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"-v", "foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-vfoobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"-vfoobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value='abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"--value=123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v123"}), "123");
} }
CAF_TEST(flat CLI options) { CAF_TEST(flat CLI options) {
...@@ -202,18 +189,19 @@ CAF_TEST(CLI arguments override defaults) { ...@@ -202,18 +189,19 @@ CAF_TEST(CLI arguments override defaults) {
CAF_MESSAGE("test integer lists"); CAF_MESSAGE("test integer lists");
ints = int_list{1, 2, 3}; ints = int_list{1, 2, 3};
cfg["bar"] = config_value{ints}; cfg["bar"] = config_value{ints};
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({1, 2, 3})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none); CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none);
CAF_CHECK_EQUAL(ints, int_list({10, 20, 30})); CAF_CHECK_EQUAL(ints, int_list({10, 20, 30}));
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({10, 20, 30})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({10, 20, 30}));
CAF_MESSAGE("test string lists"); CAF_MESSAGE("test string lists");
strings = string_list{"one", "two", "three"}; strings = string_list{"one", "two", "three"};
cfg["foo"] = config_value{strings}; cfg["foo"] = config_value{strings};
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"one", "two", "three"})); string_list({"one", "two", "three"}));
CAF_CHECK_EQUAL(read<string_list>(cfg, {"--foo=[hello, world]"}), none); CAF_CHECK_EQUAL(read<string_list>(cfg, {R"_(--foo=["hello", "world"])_"}),
none);
CAF_CHECK_EQUAL(strings, string_list({"hello", "world"})); CAF_CHECK_EQUAL(strings, string_list({"hello", "world"}));
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"hello", "world"})); string_list({"hello", "world"}));
} }
SUBTEST("without ref syncing") { SUBTEST("without ref syncing") {
...@@ -223,15 +211,16 @@ CAF_TEST(CLI arguments override defaults) { ...@@ -223,15 +211,16 @@ CAF_TEST(CLI arguments override defaults) {
opts.add<int_list>("global", "bar,b", "some list"); opts.add<int_list>("global", "bar,b", "some list");
CAF_MESSAGE("test integer lists"); CAF_MESSAGE("test integer lists");
cfg["bar"] = config_value{int_list{1, 2, 3}}; cfg["bar"] = config_value{int_list{1, 2, 3}};
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({1, 2, 3})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none); CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none);
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({10, 20, 30})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({10, 20, 30}));
CAF_MESSAGE("test string lists"); CAF_MESSAGE("test string lists");
cfg["foo"] = config_value{string_list{"one", "two", "three"}}; cfg["foo"] = config_value{string_list{"one", "two", "three"}};
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"one", "two", "three"})); string_list({"one", "two", "three"}));
CAF_CHECK_EQUAL(read<string_list>(cfg, {"--foo=[hello, world]"}), none); CAF_CHECK_EQUAL(read<string_list>(cfg, {R"_(--foo=["hello", "world"])_"}),
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), none);
CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"hello", "world"})); string_list({"hello", "world"}));
} }
} }
...@@ -240,8 +229,7 @@ CAF_TEST(CLI arguments may use custom types) { ...@@ -240,8 +229,7 @@ CAF_TEST(CLI arguments may use custom types) {
settings cfg; settings cfg;
opts.add<foobar>("global", "foobar,f", "test option"); opts.add<foobar>("global", "foobar,f", "test option");
CAF_CHECK_EQUAL(read<foobar>(cfg, {"-f{foo=\"hello\",bar=\"world\"}"}), none); CAF_CHECK_EQUAL(read<foobar>(cfg, {"-f{foo=\"hello\",bar=\"world\"}"}), none);
auto fb = get_if<foobar>(&cfg, "foobar"); if (auto fb = get_as<foobar>(cfg, "foobar"); CAF_CHECK(fb))
if (CAF_CHECK(fb))
CAF_CHECK_EQUAL(*fb, foobar("hello", "world")); CAF_CHECK_EQUAL(*fb, foobar("hello", "world"));
} }
......
This diff is collapsed.
...@@ -42,6 +42,7 @@ using i64_list = std::vector<i64>; ...@@ -42,6 +42,7 @@ using i64_list = std::vector<i64>;
struct fixture { struct fixture {
config_value x; config_value x;
settings dummy;
template <class T> template <class T>
void set(const T& value) { void set(const T& value) {
...@@ -50,19 +51,11 @@ struct fixture { ...@@ -50,19 +51,11 @@ struct fixture {
CAF_FAIL("failed two write to settings: " << writer.get_error()); CAF_FAIL("failed two write to settings: " << writer.get_error());
} }
template <class T> const settings& xs() const {
optional<T> get(const settings& cfg, string_view key) { if (auto* ptr = get_if<settings>(&x))
if (auto ptr = get_if<T>(&cfg, key))
return *ptr; return *ptr;
return none;
}
template <class T>
optional<T> get(string_view key) {
if (auto* xs = get_if<settings>(&x))
return get<T>(*xs, key);
else else
CAF_FAIL("fixture does not contain a dictionary"); return dummy;
} }
}; };
...@@ -72,18 +65,18 @@ CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture) ...@@ -72,18 +65,18 @@ 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"});
CAF_CHECK_EQUAL(get<std::string>("foo"), "hello"s); CAF_CHECK_EQUAL(get_as<std::string>(xs(), "foo"), "hello"s);
CAF_CHECK_EQUAL(get<std::string>("bar"), "world"s); CAF_CHECK_EQUAL(get_as<std::string>(xs(), "bar"), "world"s);
} }
CAF_TEST(nested structs become nested dictionaries) { CAF_TEST(nested structs become nested dictionaries) {
set(line{{10, 20, 30}, {70, 60, 50}}); set(line{{10, 20, 30}, {70, 60, 50}});
CAF_CHECK_EQUAL(get<i64>("p1.x"), 10_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.x"), 10_i64);
CAF_CHECK_EQUAL(get<i64>("p1.y"), 20_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.y"), 20_i64);
CAF_CHECK_EQUAL(get<i64>("p1.z"), 30_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.z"), 30_i64);
CAF_CHECK_EQUAL(get<i64>("p2.x"), 70_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.x"), 70_i64);
CAF_CHECK_EQUAL(get<i64>("p2.y"), 60_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.y"), 60_i64);
CAF_CHECK_EQUAL(get<i64>("p2.z"), 50_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.z"), 50_i64);
} }
CAF_TEST(empty types and maps become dictionaries) { CAF_TEST(empty types and maps become dictionaries) {
...@@ -100,36 +93,36 @@ CAF_TEST(empty types and maps become dictionaries) { ...@@ -100,36 +93,36 @@ CAF_TEST(empty types and maps become dictionaries) {
tst.v7["two"] = 2; tst.v7["two"] = 2;
tst.v7["three"] = 3; tst.v7["three"] = 3;
set(tst); set(tst);
CAF_CHECK_EQUAL(get<settings>("v1"), settings{}); CAF_CHECK_EQUAL(get_as<settings>(xs(), "v1"), settings{});
CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v2"), 42_i64);
CAF_CHECK_EQUAL(get<i64_list>("v3"), i64_list({-1, -2, -3, -4})); CAF_CHECK_EQUAL(get_as<i64_list>(xs(), "v3"), i64_list({-1, -2, -3, -4}));
if (auto v4 = get<config_value::list>("v4"); if (auto v4 = get_as<config_value::list>(xs(), "v4");
CAF_CHECK_EQUAL(v4->size(), 2u)) { CAF_CHECK(v4 && v4->size() == 2u)) {
if (auto v1 = v4->front(); CAF_CHECK(holds_alternative<settings>(v1))) { if (auto v1 = v4->front(); CAF_CHECK(holds_alternative<settings>(v1))) {
auto& v1_xs = caf::get<settings>(v1); auto& v1_xs = get<settings>(v1);
CAF_CHECK_EQUAL(get<double>(v1_xs, "content"), 0.0); CAF_CHECK_EQUAL(get<double>(v1_xs, "content"), 0.0);
CAF_CHECK_EQUAL(get<std::string>(v1_xs, "@content-type"), CAF_CHECK_EQUAL(get<std::string>(v1_xs, "@content-type"),
to_string(type_name_v<double>)); to_string(type_name_v<double>));
} }
if (auto v2 = v4->back(); CAF_CHECK(holds_alternative<settings>(v2))) { if (auto v2 = v4->back(); CAF_CHECK(holds_alternative<settings>(v2))) {
auto& v2_xs = caf::get<settings>(v2); auto& v2_xs = get<settings>(v2);
CAF_CHECK_EQUAL(get<double>(v2_xs, "content"), 1.0); CAF_CHECK_EQUAL(get<double>(v2_xs, "content"), 1.0);
CAF_CHECK_EQUAL(get<std::string>(v2_xs, "@content-type"), CAF_CHECK_EQUAL(get<std::string>(v2_xs, "@content-type"),
to_string(type_name_v<double>)); to_string(type_name_v<double>));
} }
} }
CAF_CHECK_EQUAL(get<i64_list>("v5"), i64_list({10, 20})); CAF_CHECK_EQUAL(get_as<i64_list>(xs(), "v5"), i64_list({10, 20}));
// TODO: check v6 // TODO: check v6
CAF_CHECK_EQUAL(get<i64>("v7.one"), 1_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.one"), 1_i64);
CAF_CHECK_EQUAL(get<i64>("v7.two"), 2_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.two"), 2_i64);
CAF_CHECK_EQUAL(get<i64>("v7.three"), 3_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.three"), 3_i64);
CAF_CHECK_EQUAL(get<config_value::list>("v8"), config_value::list()); CAF_CHECK_EQUAL(get_as<config_value::list>(xs(), "v8"), config_value::list());
} }
CAF_TEST(custom inspect overloads may produce single values) { CAF_TEST(custom inspect overloads may produce single values) {
auto tue = weekday::tuesday; auto tue = weekday::tuesday;
set(tue); set(tue);
CAF_CHECK_EQUAL(x, "tuesday"s); CAF_CHECK_EQUAL(get_as<std::string>(x), "tuesday"s);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -4,6 +4,51 @@ ...@@ -4,6 +4,51 @@
#include "core-test.hpp" #include "core-test.hpp"
std::string to_string(level lvl) {
switch (lvl) {
case level::all:
return "all";
case level::trace:
return "trace";
case level::debug:
return "debug";
case level::warning:
return "warning";
case level::error:
return "error";
default:
return "???";
}
}
bool from_string(caf::string_view str, level& lvl) {
auto set = [&](level value) {
lvl = value;
return true;
};
if (str == "all")
return set(level::all);
else if (str == "trace")
return set(level::trace);
else if (str == "debug")
return set(level::debug);
else if (str == "warning")
return set(level::warning);
else if (str == "error")
return set(level::error);
else
return false;
}
bool from_integer(uint8_t val, level& lvl) {
if (val < 5) {
lvl = static_cast<level>(val);
return true;
} else {
return false;
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
using namespace caf; using namespace caf;
init_global_meta_objects<id_block::core_test>(); init_global_meta_objects<id_block::core_test>();
......
...@@ -150,7 +150,7 @@ struct s1 { ...@@ -150,7 +150,7 @@ struct s1 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s1& x) { bool inspect(Inspector& f, s1& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct s2 { struct s2 {
...@@ -159,7 +159,7 @@ struct s2 { ...@@ -159,7 +159,7 @@ struct s2 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s2& x) { bool inspect(Inspector& f, s2& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct s3 { struct s3 {
...@@ -171,7 +171,7 @@ struct s3 { ...@@ -171,7 +171,7 @@ struct s3 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s3& x) { bool inspect(Inspector& f, s3& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct test_array { struct test_array {
...@@ -267,21 +267,17 @@ bool inspect(Inspector& f, dummy_enum_class& x) { ...@@ -267,21 +267,17 @@ bool inspect(Inspector& f, dummy_enum_class& x) {
return f.apply(get, set); return f.apply(get, set);
} }
enum class level { all, trace, debug, warning, error }; enum class level : uint8_t { all, trace, debug, warning, error };
std::string to_string(level);
bool from_string(caf::string_view, level&);
bool from_integer(uint8_t, level&);
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, level& x) { bool inspect(Inspector& f, level& x) {
using integer_type = std::underlying_type_t<level>; return caf::default_enum_inspect(f, x);
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
if (val >= 0 && val <= 4) {
x = static_cast<level>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
} }
enum dummy_enum { de_foo, de_bar }; enum dummy_enum { de_foo, de_bar };
......
...@@ -82,12 +82,11 @@ CAF_TEST(config_consumer) { ...@@ -82,12 +82,11 @@ CAF_TEST(config_consumer) {
detail::parser::read_config(res, consumer); detail::parser::read_config(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success); CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(string_view(res.i, res.e), string_view()); CAF_CHECK_EQUAL(string_view(res.i, res.e), string_view());
CAF_CHECK_EQUAL(get<bool>(config, "is_server"), true); CAF_CHECK_EQUAL(get_as<bool>(config, "is_server"), true);
CAF_CHECK_EQUAL(get<uint16_t>(config, "port"), 4242u); CAF_CHECK_EQUAL(get_as<uint16_t>(config, "port"), 4242u);
CAF_CHECK_EQUAL(get<ls>(config, "nodes"), ls({"sun", "venus"})); CAF_CHECK_EQUAL(get_as<ls>(config, "nodes"), ls({"sun", "venus"}));
CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.conf"); CAF_CHECK_EQUAL(get_as<string>(config, "logger.file-name"), "foobar.conf");
CAF_MESSAGE(config); CAF_CHECK_EQUAL(get_as<timespan>(config, "scheduler.timing"), timespan(2000));
CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000));
} }
CAF_TEST(simplified syntax) { CAF_TEST(simplified syntax) {
......
...@@ -171,41 +171,14 @@ CAF_TEST(strings) { ...@@ -171,41 +171,14 @@ CAF_TEST(strings) {
CAF_CHECK_EQUAL(read<std::string>(" \" foo\t\" "), " foo\t"); CAF_CHECK_EQUAL(read<std::string>(" \" foo\t\" "), " foo\t");
} }
CAF_TEST(lists) {
using int_list = std::vector<int>;
using string_list = std::vector<std::string>;
CAF_CHECK_EQUAL(read<int_list>("1"), int_list({1}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<string_list>("a, b , \" c \""),
string_list({"a", "b", " c "}));
}
CAF_TEST(maps) {
using int_map = std::map<std::string, int>;
CAF_CHECK_EQUAL(read<int_map>(R"(a=1, "b" = 42)"),
int_map({{"a", 1}, {"b", 42}}));
CAF_CHECK_EQUAL(read<int_map>(R"({ a = 1 , b = 42 ,} )"),
int_map({{"a", 1}, {"b", 42}}));
}
CAF_TEST(uris) { CAF_TEST(uris) {
using uri_list = std::vector<uri>; if (auto x_res = read<uri>("foo:bar")) {
auto x_res = read<uri>("foo:bar"); auto x = *x_res;
if (x_res == none) { CAF_CHECK_EQUAL(x.scheme(), "foo");
CAF_CHECK_EQUAL(x.path(), "bar");
} else {
CAF_ERROR("my:path not recognized as URI"); CAF_ERROR("my:path not recognized as URI");
return;
} }
auto x = *x_res;
CAF_CHECK_EQUAL(x.scheme(), "foo");
CAF_CHECK_EQUAL(x.path(), "bar");
auto ls = unbox(read<uri_list>("foo:bar, <http://actor-framework.org/doc>"));
CAF_REQUIRE_EQUAL(ls.size(), 2u);
CAF_CHECK_EQUAL(ls[0].scheme(), "foo");
CAF_CHECK_EQUAL(ls[0].path(), "bar");
CAF_CHECK_EQUAL(ls[1].scheme(), "http");
CAF_CHECK_EQUAL(ls[1].authority().host, std::string{"actor-framework.org"});
CAF_CHECK_EQUAL(ls[1].path(), "doc");
} }
CAF_TEST(IPv4 address) { CAF_TEST(IPv4 address) {
......
...@@ -48,7 +48,8 @@ struct bool_parser { ...@@ -48,7 +48,8 @@ struct bool_parser {
detail::parser::read_bool(res, f); detail::parser::read_bool(res, f);
if (res.code == pec::success) if (res.code == pec::success)
return f.x; return f.x;
return res.code; else
return res.code;
} }
}; };
...@@ -61,23 +62,23 @@ struct fixture { ...@@ -61,23 +62,23 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(read_bool_tests, fixture) CAF_TEST_FIXTURE_SCOPE(read_bool_tests, fixture)
CAF_TEST(valid booleans) { CAF_TEST(valid booleans) {
CAF_CHECK_EQUAL(p("true"), true); CAF_CHECK_EQUAL(p("true"), res_t{true});
CAF_CHECK_EQUAL(p("false"), false); CAF_CHECK_EQUAL(p("false"), res_t{false});
} }
CAF_TEST(invalid booleans) { CAF_TEST(invalid booleans) {
CAF_CHECK_EQUAL(p(""), pec::unexpected_eof); CAF_CHECK_EQUAL(p(""), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("t"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("t"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("tr"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("tr"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("tru"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("tru"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p(" true"), pec::unexpected_character); CAF_CHECK_EQUAL(p(" true"), res_t{pec::unexpected_character});
CAF_CHECK_EQUAL(p("f"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("f"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fa"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fa"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fal"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fal"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fals"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fals"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p(" false"), pec::unexpected_character); CAF_CHECK_EQUAL(p(" false"), res_t{pec::unexpected_character});
CAF_CHECK_EQUAL(p("tr\nue"), pec::unexpected_newline); CAF_CHECK_EQUAL(p("tr\nue"), res_t{pec::unexpected_newline});
CAF_CHECK_EQUAL(p("trues"), pec::trailing_character); CAF_CHECK_EQUAL(p("trues"), res_t{pec::trailing_character});
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -409,9 +409,10 @@ end object)_"); ...@@ -409,9 +409,10 @@ end object)_");
CAF_TEST(load inspectors support variant fields with fallbacks) { CAF_TEST(load inspectors support variant fields with fallbacks) {
fallback_dummy_message d; fallback_dummy_message d;
using content_type = decltype(d.content);
d.content = std::string{"hello world"}; d.content = std::string{"hello world"};
CAF_CHECK(inspect(f, d)); CAF_CHECK(inspect(f, d));
CAF_CHECK_EQUAL(d.content, 42.0); CAF_CHECK_EQUAL(d.content, content_type{42.0});
CAF_CHECK_EQUAL(f.log, R"_( CAF_CHECK_EQUAL(f.log, R"_(
begin object fallback_dummy_message begin object fallback_dummy_message
begin optional variant field content begin optional variant field content
......
...@@ -120,12 +120,12 @@ CAF_TEST(to_string converts messages to strings) { ...@@ -120,12 +120,12 @@ CAF_TEST(to_string converts messages to strings) {
CAF_CHECK_EQUAL(msg_as_string(R"__(this is a "test")__"), teststr); CAF_CHECK_EQUAL(msg_as_string(R"__(this is a "test")__"), teststr);
CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5), CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5),
"message([1, 2, 3], 4, 5)"); "message([1, 2, 3], 4, 5)");
CAF_CHECK_EQUAL(msg_as_string(s1{}), "message(s1([10, 20, 30]))"); CAF_CHECK_EQUAL(msg_as_string(s1{}), "message([10, 20, 30])");
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
CAF_CHECK_EQUAL(msg_as_string(s2{}), CAF_CHECK_EQUAL(msg_as_string(s2{}),
"message(s2([[1, 10], [2, 20], [3, 30], [4, 40]]))"); "message([[1, 10], [2, 20], [3, 30], [4, 40]])");
CAF_CHECK_EQUAL(msg_as_string(s3{}), "message(s3([1, 2, 3, 4]))"); CAF_CHECK_EQUAL(msg_as_string(s3{}), "message([1, 2, 3, 4])");
} }
CAF_TEST(match_elements exposes element types) { CAF_TEST(match_elements exposes element types) {
......
...@@ -120,7 +120,8 @@ CAF_TEST(message_lifetime_in_scoped_actor) { ...@@ -120,7 +120,8 @@ CAF_TEST(message_lifetime_in_scoped_actor) {
self->send(self, msg); self->send(self, msg);
CAF_CHECK_EQUAL(msg.cdata().get_reference_count(), 2u); CAF_CHECK_EQUAL(msg.cdata().get_reference_count(), 2u);
self->receive([&](int& value) { self->receive([&](int& value) {
CAF_CHECK_NOT_EQUAL(&value, msg.cdata().at(0)); auto addr = static_cast<void*>(&value);
CAF_CHECK_NOT_EQUAL(addr, msg.cdata().at(0));
value = 10; value = 10;
}); });
CAF_CHECK_EQUAL(msg.get_as<int>(0), 42); CAF_CHECK_EQUAL(msg.get_as<int>(0), 42);
......
...@@ -93,7 +93,7 @@ CAF_TEST(requests without result) { ...@@ -93,7 +93,7 @@ CAF_TEST(requests without result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((void), from(server).to(client)); expect((void), from(server).to(client));
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
SUBTEST("request.await") { SUBTEST("request.await") {
auto client = sys.spawn([=](event_based_actor* self) { auto client = sys.spawn([=](event_based_actor* self) {
...@@ -102,13 +102,13 @@ CAF_TEST(requests without result) { ...@@ -102,13 +102,13 @@ CAF_TEST(requests without result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((void), from(server).to(client)); expect((void), from(server).to(client));
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
SUBTEST("request.receive") { SUBTEST("request.receive") {
auto res_hdl = self->request(server, infinite, 1, 2); auto res_hdl = self->request(server, infinite, 1, 2);
run(); run();
res_hdl.receive([&] { *result = unit; }, ERROR_HANDLER); res_hdl.receive([&] { *result = unit; }, ERROR_HANDLER);
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
} }
...@@ -121,7 +121,7 @@ CAF_TEST(requests with integer result) { ...@@ -121,7 +121,7 @@ CAF_TEST(requests with integer result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int), from(server).to(client).with(3)); expect((int), from(server).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
SUBTEST("request.await") { SUBTEST("request.await") {
auto client = sys.spawn([=](event_based_actor* self) { auto client = sys.spawn([=](event_based_actor* self) {
...@@ -130,13 +130,13 @@ CAF_TEST(requests with integer result) { ...@@ -130,13 +130,13 @@ CAF_TEST(requests with integer result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int), from(server).to(client).with(3)); expect((int), from(server).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
SUBTEST("request.receive") { SUBTEST("request.receive") {
auto res_hdl = self->request(server, infinite, 1, 2); auto res_hdl = self->request(server, infinite, 1, 2);
run(); run();
res_hdl.receive([&](int x) { *result = x; }, ERROR_HANDLER); res_hdl.receive([&](int x) { *result = x; }, ERROR_HANDLER);
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
} }
...@@ -150,7 +150,7 @@ CAF_TEST(delegated request with integer result) { ...@@ -150,7 +150,7 @@ CAF_TEST(delegated request with integer result) {
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int, int), from(client).to(worker).with(1, 2)); expect((int, int), from(client).to(worker).with(1, 2));
expect((int), from(worker).to(client).with(3)); expect((int), from(worker).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
CAF_TEST(requesters support fan_out_request) { CAF_TEST(requesters support fan_out_request) {
......
...@@ -61,23 +61,22 @@ struct fixture { ...@@ -61,23 +61,22 @@ struct fixture {
} }
}; };
const config_value& unpack(const settings& x, string_view key) { config_value unpack(const settings& x, string_view key) {
auto i = x.find(key); if (auto i = x.find(key); i != x.end())
if (i == x.end()) return i->second;
CAF_FAIL("key not found in dictionary: " << key); else
return i->second; return {};
} }
template <class... Ts> template <class... Ts>
const config_value& unpack(const settings& x, string_view key, config_value
const char* next_key, Ts... keys) { unpack(const settings& x, string_view key, const char* next_key, Ts... keys) {
auto i = x.find(key); if (auto i = x.find(key); i == x.end())
if (i == x.end()) return {};
CAF_FAIL("key not found in dictionary: " << key); else if (auto ptr = get_if<settings>(std::addressof(i->second)))
if (!holds_alternative<settings>(i->second)) return unpack(*ptr, {next_key, strlen(next_key)}, keys...);
CAF_FAIL("value is not a dictionary: " << key); else
return unpack(get<settings>(i->second), {next_key, strlen(next_key)}, return {};
keys...);
} }
struct foobar { struct foobar {
...@@ -85,45 +84,12 @@ struct foobar { ...@@ -85,45 +84,12 @@ struct foobar {
int bar = 0; int bar = 0;
}; };
} // namespace template <class Inspector>
bool inspect(Inspector& f, foobar& x) {
namespace caf { return f.object(x).fields(f.field("foo", x.foo), f.field("bar", x.bar));
}
// Enable users to configure foobar's like this:
// my-value {
// foo = 42
// bar = 23
// }
template <>
struct config_value_access<foobar> {
static bool is(const config_value& x) {
auto dict = caf::get_if<config_value::dictionary>(&x);
if (dict != nullptr) {
return caf::get_if<int>(dict, "foo") != none
&& caf::get_if<int>(dict, "bar") != none;
}
return false;
}
static optional<foobar> get_if(const config_value* x) {
foobar result;
if (!is(*x))
return none;
const auto& dict = caf::get<config_value::dictionary>(*x);
result.foo = caf::get<int>(dict, "foo");
result.bar = caf::get<int>(dict, "bar");
return result;
}
static foobar get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
};
} // namespace caf } // namespace
CAF_TEST_FIXTURE_SCOPE(settings_tests, fixture) CAF_TEST_FIXTURE_SCOPE(settings_tests, fixture)
...@@ -135,11 +101,11 @@ CAF_TEST(put) { ...@@ -135,11 +101,11 @@ CAF_TEST(put) {
CAF_CHECK(x.contains("foo")); CAF_CHECK(x.contains("foo"));
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK(x.contains("one")); CAF_CHECK(x.contains("one"));
CAF_CHECK_EQUAL(unpack(x, "foo"), "bar"s); CAF_CHECK_EQUAL(unpack(x, "foo"), config_value{"bar"s});
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), "four"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), config_value{"four"s});
put(x, "logger.console", "trace"); put(x, "logger.console", "trace");
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "trace"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"trace"s});
} }
CAF_TEST(put missing) { CAF_TEST(put missing) {
...@@ -150,11 +116,11 @@ CAF_TEST(put missing) { ...@@ -150,11 +116,11 @@ CAF_TEST(put missing) {
CAF_CHECK(x.contains("foo")); CAF_CHECK(x.contains("foo"));
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK(x.contains("one")); CAF_CHECK(x.contains("one"));
CAF_CHECK_EQUAL(unpack(x, "foo"), "bar"s); CAF_CHECK_EQUAL(unpack(x, "foo"), config_value{"bar"s});
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), "four"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), config_value{"four"s});
put_missing(x, "logger.console", "trace"); put_missing(x, "logger.console", "trace");
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
} }
CAF_TEST(put list) { CAF_TEST(put list) {
...@@ -170,11 +136,12 @@ CAF_TEST(put list) { ...@@ -170,11 +136,12 @@ CAF_TEST(put list) {
CAF_TEST(put dictionary) { CAF_TEST(put dictionary) {
put_dictionary(x, "logger").emplace("console", "none"); put_dictionary(x, "logger").emplace("console", "none");
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
put_dictionary(x, "foo.bar").emplace("value", 42); put_dictionary(x, "foo.bar").emplace("value", 42);
CAF_CHECK_EQUAL(unpack(x, "foo", "bar", "value"), 42); CAF_CHECK_EQUAL(unpack(x, "foo", "bar", "value"), config_value{42});
put_dictionary(x, "one.two.three").emplace("four", "five"); put_dictionary(x, "one.two.three").emplace("four", "five");
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three", "four"), "five"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three", "four"),
config_value{"five"s});
} }
CAF_TEST(get and get_if) { CAF_TEST(get and get_if) {
...@@ -186,8 +153,8 @@ CAF_TEST(get and get_if) { ...@@ -186,8 +153,8 @@ CAF_TEST(get and get_if) {
CAF_CHECK(get<std::string>(x, "logger.console") == "none"s); CAF_CHECK(get<std::string>(x, "logger.console") == "none"s);
CAF_CHECK(get_if(&x, "one.two.three") != nullptr); CAF_CHECK(get_if(&x, "one.two.three") != nullptr);
CAF_CHECK(get_if<std::string>(&x, "one.two.three") == nullptr); CAF_CHECK(get_if<std::string>(&x, "one.two.three") == nullptr);
CAF_REQUIRE(get_if<int>(&x, "one.two.three") != none); if (CAF_CHECK(get_if<int64_t>(&x, "one.two.three") != nullptr))
CAF_CHECK(get<int>(x, "one.two.three") == 4); CAF_CHECK(get<int64_t>(x, "one.two.three") == 4);
} }
CAF_TEST(get_or) { CAF_TEST(get_or) {
...@@ -199,11 +166,10 @@ CAF_TEST(get_or) { ...@@ -199,11 +166,10 @@ CAF_TEST(get_or) {
CAF_TEST(custom type) { CAF_TEST(custom type) {
put(x, "my-value.foo", 42); put(x, "my-value.foo", 42);
put(x, "my-value.bar", 24); put(x, "my-value.bar", 24);
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value")); if (auto fb = get_as<foobar>(x, "my-value"); CAF_CHECK(fb)) {
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none); CAF_CHECK_EQUAL(fb->foo, 42);
auto fb = get<foobar>(x, "my-value"); CAF_CHECK_EQUAL(fb->bar, 24);
CAF_CHECK_EQUAL(fb.foo, 42); }
CAF_CHECK_EQUAL(fb.bar, 24);
} }
CAF_TEST(read_config accepts the to_string output of settings) { CAF_TEST(read_config accepts the to_string output of settings) {
......
...@@ -86,10 +86,10 @@ CAF_TEST(subspans) { ...@@ -86,10 +86,10 @@ CAF_TEST(subspans) {
CAF_TEST(free iterator functions) { CAF_TEST(free iterator functions) {
auto xs = make_span(chars); auto xs = make_span(chars);
CAF_CHECK_EQUAL(xs.begin(), begin(xs)); CAF_CHECK(xs.begin() == begin(xs));
CAF_CHECK_EQUAL(xs.cbegin(), cbegin(xs)); CAF_CHECK(xs.cbegin() == cbegin(xs));
CAF_CHECK_EQUAL(xs.end(), end(xs)); CAF_CHECK(xs.end() == end(xs));
CAF_CHECK_EQUAL(xs.cend(), cend(xs)); CAF_CHECK(xs.cend() == cend(xs));
} }
CAF_TEST(as bytes) { CAF_TEST(as bytes) {
...@@ -108,10 +108,10 @@ CAF_TEST(make_span) { ...@@ -108,10 +108,10 @@ CAF_TEST(make_span) {
CAF_CHECK(std::equal(xs.begin(), xs.end(), chars.begin())); CAF_CHECK(std::equal(xs.begin(), xs.end(), chars.begin()));
CAF_CHECK(std::equal(ys.begin(), ys.end(), chars.begin())); CAF_CHECK(std::equal(ys.begin(), ys.end(), chars.begin()));
CAF_CHECK(std::equal(zs.begin(), zs.end(), chars.begin())); CAF_CHECK(std::equal(zs.begin(), zs.end(), chars.begin()));
CAF_CHECK_EQUAL(end(xs), end(ys)); CAF_CHECK(end(xs) == end(ys));
CAF_CHECK_EQUAL(end(ys), end(zs)); CAF_CHECK(end(ys) == end(zs));
CAF_CHECK_EQUAL(begin(xs), begin(ys)); CAF_CHECK(begin(xs) == begin(ys));
CAF_CHECK_EQUAL(begin(ys), begin(zs)); CAF_CHECK(begin(ys) == begin(zs));
} }
CAF_TEST(spans are convertible from compatible containers) { CAF_TEST(spans are convertible from compatible containers) {
......
...@@ -81,8 +81,7 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10, ...@@ -81,8 +81,7 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
do { \ do { \
using type = std::decay_t<decltype(y)>; \ using type = std::decay_t<decltype(y)>; \
auto&& tmp = x; \ auto&& tmp = x; \
CAF_CHECK(holds_alternative<type>(tmp)); \ if (CAF_CHECK(holds_alternative<type>(tmp))) \
if (holds_alternative<type>(tmp)) \
CAF_CHECK_EQUAL(get<type>(tmp), y); \ CAF_CHECK_EQUAL(get<type>(tmp), y); \
} while (false) } while (false)
...@@ -102,9 +101,6 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10, ...@@ -102,9 +101,6 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
CAF_TEST(copying_moving_roundtrips) { CAF_TEST(copying_moving_roundtrips) {
actor_system_config cfg; actor_system_config cfg;
actor_system sys{cfg}; actor_system sys{cfg};
// default construction
variant<none_t> x1;
CAF_CHECK_EQUAL(x1, none);
variant<int, none_t> x2; variant<int, none_t> x2;
VARIANT_EQ(x2, 0); VARIANT_EQ(x2, 0);
v20 x3; v20 x3;
...@@ -129,11 +125,10 @@ CAF_TEST(constructors) { ...@@ -129,11 +125,10 @@ CAF_TEST(constructors) {
variant<float, int, std::string> b{"bar"s}; variant<float, int, std::string> b{"bar"s};
variant<int, std::string, double> c{123}; variant<int, std::string, double> c{123};
variant<bool, uint8_t> d{uint8_t{252}}; variant<bool, uint8_t> d{uint8_t{252}};
CAF_CHECK_EQUAL(a, 42); VARIANT_EQ(a, 42);
CAF_CHECK_EQUAL(b, "bar"s); VARIANT_EQ(b, "bar"s);
CAF_CHECK_EQUAL(c, 123); VARIANT_EQ(c, 123);
CAF_CHECK_NOT_EQUAL(c, "123"s); VARIANT_EQ(d, uint8_t{252});
CAF_CHECK_EQUAL(d, uint8_t{252});
} }
CAF_TEST(n_ary_visit) { CAF_TEST(n_ary_visit) {
...@@ -152,8 +147,7 @@ CAF_TEST(get_if) { ...@@ -152,8 +147,7 @@ CAF_TEST(get_if) {
CAF_CHECK_EQUAL(get_if<int>(&b), nullptr); CAF_CHECK_EQUAL(get_if<int>(&b), nullptr);
CAF_CHECK_NOT_EQUAL(get_if<std::string>(&b), nullptr); CAF_CHECK_NOT_EQUAL(get_if<std::string>(&b), nullptr);
CAF_MESSAGE("test get_if via unit test framework"); CAF_MESSAGE("test get_if via unit test framework");
CAF_CHECK_NOT_EQUAL(b, 42); VARIANT_EQ(b, "foo"s);
CAF_CHECK_EQUAL(b, "foo"s);
} }
CAF_TEST(less_than) { CAF_TEST(less_than) {
......
...@@ -46,7 +46,7 @@ instance::instance(abstract_broker* parent, callee& lstnr) ...@@ -46,7 +46,7 @@ instance::instance(abstract_broker* parent, callee& lstnr)
: tbl_(parent), this_node_(parent->system().node()), callee_(lstnr) { : tbl_(parent), this_node_(parent->system().node()), callee_(lstnr) {
CAF_ASSERT(this_node_ != none); CAF_ASSERT(this_node_ != none);
size_t workers; size_t workers;
if (auto workers_cfg = get_if<size_t>(&config(), "caf.middleman.workers")) if (auto workers_cfg = get_as<size_t>(config(), "caf.middleman.workers"))
workers = *workers_cfg; workers = *workers_cfg;
else else
workers = std::min(3u, std::thread::hardware_concurrency() / 4u) + 1; workers = std::min(3u, std::thread::hardware_concurrency() / 4u) + 1;
...@@ -250,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf, ...@@ -250,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
auto writer = make_callback([&](binary_serializer& sink) { auto writer = make_callback([&](binary_serializer& sink) {
using string_list = std::vector<std::string>; using string_list = std::vector<std::string>;
string_list app_ids; string_list app_ids;
if (auto ids = get_if<string_list>(&config(), if (auto ids = get_as<string_list>(config(),
"caf.middleman.app-identifiers")) "caf.middleman.app-identifiers"))
app_ids = std::move(*ids); app_ids = std::move(*ids);
else else
...@@ -349,7 +349,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -349,7 +349,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
} }
// Check the application ID. // Check the application ID.
string_list whitelist; string_list whitelist;
if (auto ls = get_if<string_list>(&config(), if (auto ls = get_as<string_list>(config(),
"caf.middleman.app-identifiers")) "caf.middleman.app-identifiers"))
whitelist = std::move(*ls); whitelist = std::move(*ls);
else else
......
...@@ -130,7 +130,7 @@ public: ...@@ -130,7 +130,7 @@ public:
bool start(const config_value::dictionary& cfg) override { bool start(const config_value::dictionary& cfg) override {
// Read port, address and reuse flag from the config. // Read port, address and reuse flag from the config.
uint16_t port = 0; uint16_t port = 0;
if (auto cfg_port = get_if<uint16_t>(&cfg, "port")) { if (auto cfg_port = get_as<uint16_t>(cfg, "port")) {
port = *cfg_port; port = *cfg_port;
} else { } else {
return false; return false;
......
...@@ -46,3 +46,8 @@ ...@@ -46,3 +46,8 @@
#define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs) #define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs)
#define MESSAGE(what) CAF_MESSAGE(what) #define MESSAGE(what) CAF_MESSAGE(what)
#define BEGIN_FIXTURE_SCOPE(fixture_class) \
CAF_TEST_FIXTURE_SCOPE(CAF_UNIFYN(tests), fixture_class)
#define END_FIXTURE_SCOPE() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -483,6 +483,11 @@ bool check(test* parent, const char* file, size_t line, const char* expr, ...@@ -483,6 +483,11 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return result; return result;
} }
bool check_un(bool result, const char* file, size_t line, const char* expr);
bool check_bin(bool result, const char* file, size_t line, const char* expr,
const std::string& lhs, const std::string& rhs);
} // namespace detail } // namespace detail
} // namespace caf::test } // namespace caf::test
...@@ -546,18 +551,13 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -546,18 +551,13 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
} while (false) } while (false)
#define CAF_CHECK(...) \ #define CAF_CHECK(...) \
([](bool expr_result) { \ [](bool expr_result) { \
auto caf_check_res \ return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__, \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \ #__VA_ARGS__); \
__FILE__, __LINE__, #__VA_ARGS__, false, \ }(static_cast<bool>(__VA_ARGS__))
expr_result); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
return caf_check_res; \
})(static_cast<bool>(__VA_ARGS__))
#define CAF_CHECK_FUNC(func, x_expr, y_expr) \ #define CAF_CHECK_FUNC(func, x_expr, y_expr) \
([](auto&& x_val, auto&& y_val) { \ [](auto&& x_val, auto&& y_val) { \
func comparator; \ func comparator; \
auto caf_check_res \ auto caf_check_res \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \ = ::caf::test::detail::check(::caf::test::engine::current_test(), \
...@@ -567,7 +567,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -567,7 +567,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
::caf::test::engine::last_check_file(__FILE__); \ ::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \ ::caf::test::engine::last_check_line(__LINE__); \
return caf_check_res; \ return caf_check_res; \
})(x_expr, y_expr) }(x_expr, y_expr)
#define CAF_CHECK_FAIL(...) \ #define CAF_CHECK_FAIL(...) \
do { \ do { \
...@@ -642,32 +642,81 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -642,32 +642,81 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
// -- CAF_CHECK* predicate family ---------------------------------------------- // -- CAF_CHECK* predicate family ----------------------------------------------
#define CAF_CHECK_EQUAL(x, y) CAF_CHECK_FUNC(::caf::test::equal_to, x, y) #define CAF_CHECK_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
#define CAF_CHECK_NOT_EQUAL(x, y) \ return ::caf::test::detail::check_bin(x_val == y_val, __FILE__, __LINE__, \
CAF_CHECK_FUNC(::caf::test::not_equal_to, x, y) #x_expr " == " #y_expr, \
caf::deep_to_string(x_val), \
#define CAF_CHECK_LESS(x, y) CAF_CHECK_FUNC(::caf::test::less_than, x, y) caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS(x, y) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than>, x, y) #define CAF_CHECK_NOT_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
#define CAF_CHECK_LESS_OR_EQUAL(x, y) \ return ::caf::test::detail::check_bin(x_val != y_val, __FILE__, __LINE__, \
CAF_CHECK_FUNC(::caf::test::less_than_or_equal, x, y) #x_expr " != " #y_expr, \
caf::deep_to_string(x_val), \
#define CAF_CHECK_NOT_LESS_OR_EQUAL(x, y) \ caf::deep_to_string(y_val)); \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y) }(x_expr, y_expr)
#define CAF_CHECK_GREATER(x, y) CAF_CHECK_FUNC(::caf::test::greater_than, x, y) #define CAF_CHECK_LESS(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
#define CAF_CHECK_NOT_GREATER(x, y) \ return ::caf::test::detail::check_bin(x_val < y_val, __FILE__, __LINE__, \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y) #x_expr " < " #y_expr, \
caf::deep_to_string(x_val), \
#define CAF_CHECK_GREATER_OR_EQUAL(x, y) \ caf::deep_to_string(y_val)); \
CAF_CHECK_FUNC(::caf::test::greater_than_or_equal, x, y) }(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER_OR_EQUAL(x, y) \ #define CAF_CHECK_NOT_LESS(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>, x, y) [](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val < y_val), __FILE__, __LINE__, "not " #x_expr " < " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_LESS_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val <= y_val, __FILE__, __LINE__, \
#x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val <= y_val), __FILE__, __LINE__, "not " #x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_GREATER(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val > y_val, __FILE__, __LINE__, \
#x_expr " > " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val > y_val), __FILE__, __LINE__, "not " #x_expr " > " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_GREATER_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val >= y_val, __FILE__, __LINE__, \
#x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val >= y_val), __FILE__, __LINE__, "not " #x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
// -- CAF_CHECK* predicate family ---------------------------------------------- // -- CAF_CHECK* predicate family ----------------------------------------------
......
...@@ -165,6 +165,51 @@ bool check(test* parent, const char* file, size_t line, const char* expr, ...@@ -165,6 +165,51 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return result; return result;
} }
bool check_un(bool result, const char* file, size_t line, const char* expr) {
string_view rel_up = "../";
while (strncmp(file, rel_up.data(), rel_up.size()) == 0)
file += rel_up.size();
auto parent = engine::current_test();
auto out = logger::instance().massive();
if (result) {
out << term::green << "** " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::reset << "passed" << '\n';
parent->pass();
} else {
out << term::red << "!! " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::red
<< "check failed: " << expr << term::reset << '\n';
parent->fail(false);
}
engine::last_check_file(file);
engine::last_check_line(line);
return result;
}
bool check_bin(bool result, const char* file, size_t line, const char* expr,
const std::string& lhs, const std::string& rhs) {
string_view rel_up = "../";
while (strncmp(file, rel_up.data(), rel_up.size()) == 0)
file += rel_up.size();
auto parent = engine::current_test();
auto out = logger::instance().massive();
if (result) {
out << term::green << "** " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::reset << "passed" << '\n';
parent->pass();
} else {
out << term::red << "!! " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::red
<< "check failed: " << expr << term::reset << '\n'
<< " lhs: " << lhs << '\n'
<< " rhs: " << rhs << '\n';
parent->fail(false);
}
engine::last_check_file(file);
engine::last_check_line(line);
return result;
}
} // namespace detail } // namespace detail
logger::stream::stream(logger& parent, logger::level lvl) logger::stream::stream(logger& parent, logger::level lvl)
......
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