Commit 64324ccf authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Integrate new parser infrastructure

parent 042548ff
......@@ -37,6 +37,7 @@ set(LIBCAF_CORE_SRCS
src/blocking_behavior.cpp
src/concatenated_tuple.cpp
src/config_option.cpp
src/config_option_set.cpp
src/config_value.cpp
src/decorated_tuple.cpp
src/default_attachable.cpp
......@@ -47,7 +48,6 @@ set(LIBCAF_CORE_SRCS
src/downstream_messages.cpp
src/duration.cpp
src/dynamic_message_data.cpp
src/ec.cpp
src/error.cpp
src/event_based_actor.cpp
src/execution_unit.cpp
......@@ -61,6 +61,7 @@ set(LIBCAF_CORE_SRCS
src/group_manager.cpp
src/group_module.cpp
src/inbound_path.cpp
src/ini_consumer.cpp
src/invoke_result_visitor.cpp
src/local_actor.cpp
src/logger.cpp
......@@ -77,6 +78,7 @@ set(LIBCAF_CORE_SRCS
src/node_id.cpp
src/outbound_path.cpp
src/parse_ini.cpp
src/pec.cpp
src/pretty_type_name.cpp
src/private_thread.cpp
src/proxy_registry.cpp
......
......@@ -31,6 +31,7 @@
#include "caf/thread_hook.hpp"
#include "caf/config_value.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_set.hpp"
#include "caf/actor_factory.hpp"
#include "caf/is_typed_actor.hpp"
#include "caf/type_erased_value.hpp"
......@@ -74,31 +75,37 @@ public:
using error_renderer_map = hash_map<atom_value, error_renderer>;
using option_ptr = std::unique_ptr<config_option>;
using option_vector = std::vector<option_ptr>;
using group_module_factory = std::function<group_module* ()>;
using group_module_factory_vector = std::vector<group_module_factory>;
using config_map = std::map<std::string, config_value::dictionary>;
using string_list = std::vector<std::string>;
// -- nested classes ---------------------------------------------------------
using named_actor_config_map = hash_map<std::string, named_actor_config>;
class opt_group {
public:
opt_group(option_vector& xs, const char* category);
opt_group(config_option_set& xs, const char* category);
template <class T>
opt_group& add(T& storage, const char* name, const char* description) {
xs_.add(storage, category_, name, description);
return *this;
}
template <class T>
opt_group& add(T& storage, const char* name, const char* explanation) {
xs_.emplace_back(make_config_option(storage, cat_, name, explanation));
opt_group& add(const char* name, const char* description) {
xs_.add<T>(category_, name, description);
return *this;
}
private:
option_vector& xs_;
const char* cat_;
config_option_set& xs_;
const char* category_;
};
// -- constructors, destructors, and assignment operators --------------------
......@@ -112,18 +119,29 @@ public:
actor_system_config(const actor_system_config&) = delete;
actor_system_config& operator=(const actor_system_config&) = delete;
// -- properties -------------------------------------------------------------
/// @private
std::map<std::string, config_value::dictionary> content;
/// Sets a config by using its INI name `config_name` to `config_value`.
template <class T>
actor_system_config& set(const char* name, T&& value) {
return set_impl(name, config_value{std::forward<T>(value)});
}
// -- modifiers --------------------------------------------------------------
/// Parses `args` as tuple of strings containing CLI options
/// and `ini_stream` as INI formatted input stream.
actor_system_config& parse(string_list args, std::istream& ini);
/// Parses `args` as tuple of strings containing CLI options and tries to
/// open `ini_file_cstr` as INI formatted config file. The parsers tries to
/// open `caf-application.ini` if `ini_file_cstr` is `nullptr`.
actor_system_config& parse(message& args,
actor_system_config& parse(string_list args,
const char* ini_file_cstr = nullptr);
/// Parses `args` as tuple of strings containing CLI options
/// and `ini_stream` as INI formatted input stream.
actor_system_config& parse(message& args, std::istream& ini);
/// Parses the CLI options `{argc, argv}` and
/// `ini_stream` as INI formatted input stream.
actor_system_config& parse(int argc, char** argv, std::istream& ini);
......@@ -134,6 +152,11 @@ public:
actor_system_config& parse(int argc, char** argv,
const char* ini_file_cstr = nullptr);
actor_system_config&
parse(message& args, const char* ini_file_cstr = nullptr) CAF_DEPRECATED;
actor_system_config& parse(message& args, std::istream& ini) CAF_DEPRECATED;
/// Allows other nodes to spawn actors created by `fun`
/// dynamically by using `name` as identifier.
/// @experimental
......@@ -237,12 +260,6 @@ public:
return *this;
}
/// Sets a config by using its INI name `config_name` to `config_value`.
template <class T>
actor_system_config& set(const char* cn, T&& x) {
return set_impl(cn, config_value{std::forward<T>(x)});
}
// -- parser and CLI state ---------------------------------------------------
/// Stores whether the help text was printed. If set to `true`, the
......@@ -385,16 +402,6 @@ public:
/// default.
std::string config_file_path;
// -- convenience functions --------------------------------------------------
template <class F>
void for_each_option(F f) const {
const option_vector* all_options[] = { &options_, &custom_options_ };
for (auto& opt_vec : all_options)
for (auto& opt : *opt_vec)
f(*opt);
}
// -- utility for caf-run ----------------------------------------------------
// Config parameter for individual actor types.
......@@ -405,7 +412,7 @@ public:
protected:
virtual std::string make_help_text(const std::vector<message::cli_arg>&);
option_vector custom_options_;
config_option_set custom_options_;
private:
template <class T>
......@@ -416,15 +423,15 @@ private:
&make_type_erased_value<T>);
}
actor_system_config& set_impl(const char* cn, config_value cv);
actor_system_config& set_impl(const char* name, config_value value);
static std::string render_sec(uint8_t, atom_value, const message&);
static std::string render_exit_reason(uint8_t, atom_value, const message&);
void extract_config_file_path(message& args);
void extract_config_file_path(string_list& args);
option_vector options_;
config_option_set options_;
};
} // namespace caf
......
......@@ -18,179 +18,75 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <cstdint>
#include <cstring>
#include <functional>
#include "caf/atom.hpp"
#include "caf/config_value.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/error.hpp"
#include "caf/message.hpp"
#include "caf/static_visitor.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
#include "caf/fwd.hpp"
extern const char* type_name_visitor_tbl[];
namespace caf {
/// Helper class to generate config readers for different input types.
/// Defines a configuration option for the application.
class config_option {
public:
using config_reader_sink = std::function<bool (size_t, config_value&,
optional<std::ostream&>)>;
// -- member types -----------------------------------------------------------
/// Custom vtable-like struct for delegating to type-specific functions.
struct vtbl_type {
error (*check)(const config_value&);
void (*store)(void*, const config_value&);
};
using legal_types = detail::type_list<bool, float, double, std::string,
atom_value, int8_t, uint8_t, int16_t,
uint16_t, int32_t, uint32_t, int64_t,
uint64_t, timespan>;
// -- constructors, destructors, and assignment operators --------------------
config_option(const char* cat, const char* nm, const char* expl);
/// Constructs a config option.
config_option(std::string category, const char* name, std::string description,
bool is_flag, const vtbl_type& vtbl, void* storage = nullptr);
virtual ~config_option();
config_option(config_option&&) = default;
inline const char* name() const {
return name_.c_str();
}
config_option& operator=(config_option&&) = default;
inline char short_name() const {
return short_name_;
}
// -- properties -------------------------------------------------------------
inline const char* category() const {
inline const std::string& category() const noexcept {
return category_;
}
inline const char* explanation() const {
return explanation_;
/// Returns the
inline const std::string& long_name() const noexcept {
return long_name_;
}
/// Returns the full name for this config option as "<category>.<long name>".
std::string full_name() const;
/// Returns the held value as string.
virtual std::string to_string() const = 0;
/// Returns a sink function for config readers.
virtual config_reader_sink to_sink() = 0;
/// Returns a CLI argument parser.
virtual message::cli_arg to_cli_arg(bool use_caf_prefix = false) = 0;
/// Returns a human-readable type name for the visited type.
class type_name_visitor : public static_visitor<const char*> {
public:
template <class T>
const char* operator()(const T&) const {
static constexpr bool is_int = std::is_integral<T>::value
&& !std::is_same<bool, T>::value;
static constexpr std::integral_constant<bool, is_int> tk{};
static constexpr int index = idx<T>(tk);
static_assert(index >= 0, "illegal type in name visitor");
return type_name_visitor_tbl[static_cast<size_t>(index)];
}
template <class U>
const char* operator()(const std::vector<U>&) {
return "a list";
}
template <class K, class V>
const char* operator()(const std::map<K, V>&) {
return "a dictionary";
}
const char* operator()(const timespan&) {
return "a timespan";
}
private:
// Catches non-integer types.
template <class T>
static constexpr int idx(std::false_type) {
return detail::tl_index_of<legal_types, T>::value;
}
// Catches integer types.
template <class T>
static constexpr int idx(std::true_type) {
using squashed = detail::squashed_int_t<T>;
return detail::tl_index_of<legal_types, squashed>::value;
}
};
protected:
void report_type_error(size_t ln, config_value& x, const char* expected,
optional<std::ostream&> out);
private:
const char* category_;
std::string name_;
const char* explanation_;
char short_name_;
};
template <class T>
class config_option_impl : public config_option {
public:
config_option_impl(T& ref, const char* ctg, const char* nm, const char* xp)
: config_option(ctg, nm, xp),
ref_(ref) {
// nop
inline const std::string& short_names() const noexcept {
return short_names_;
}
std::string to_string() const override {
return deep_to_string(ref_);
inline const std::string& description() const noexcept {
return description_;
}
message::cli_arg to_cli_arg(bool use_caf_prefix) override {
std::string argname;
if (use_caf_prefix)
argname = "caf#";
if (strcmp(category(), "global") != 0) {
argname += category();
argname += ".";
}
argname += name();
if (short_name() != '\0') {
argname += ',';
argname += short_name();
}
return {std::move(argname), explanation(), ref_};
inline bool is_flag() const noexcept {
return is_flag_;
}
config_reader_sink to_sink() override {
return [=](size_t ln, config_value& x, optional<std::ostream&> errors) {
auto res = get_if<T>(&x);
if (res) {
ref_ = *res;
return true;
}
type_name_visitor tnv;
report_type_error(ln, x, tnv(ref_), errors);
return false;
};
}
/// Returns the full name for this config option as "<category>.<long name>".
std::string full_name() const;
private:
T& ref_;
};
/// Checks whether `x` holds a legal value for this option.
error check(const config_value& x) const;
/// Stores `x` in this option unless it is stateless.
/// @pre `check(x) == none`.
void store(const config_value& x) const;
template <class T>
std::unique_ptr<config_option>
make_config_option(T& storage, const char* category,
const char* name, const char* explanation) {
auto ptr = new config_option_impl<T>(storage, category, name, explanation);
return std::unique_ptr<config_option>{ptr};
}
private:
std::string category_;
std::string long_name_;
std::string short_names_;
std::string description_;
bool is_flag_;
vtbl_type vtbl_;
mutable void* value_;
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "caf/config_option.hpp"
#include "caf/fwd.hpp"
#include "caf/make_config_option.hpp"
#include "caf/pec.hpp"
namespace caf {
/// A set of `config_option` objects that parses CLI arguments into a
/// `config_value::dictionary`.
class config_option_set {
public:
// -- member types -----------------------------------------------------------
/// An iterator over CLI arguments.
using argument_iterator = std::vector<std::string>::const_iterator;
/// The result of `parse` member functions.
using parse_result = std::pair<pec, argument_iterator>;
/// List of config options.
using option_vector = std::vector<config_option>;
/// Pointer to a config option.
using option_pointer = const config_option*;
/// An iterator over ::config_option unique pointers.
using iterator = option_vector::iterator;
/// Maps string keys to arbitrary (config) values.
using dictionary = std::map<std::string, config_value>;
/// Categorized settings.
using config_map = std::map<std::string, dictionary>;
// -- constructors, destructors, and assignment operators --------------------
config_option_set();
// -- properties -------------------------------------------------------------
/// Returns the first `config_option` that matches the CLI name.
/// @param name Config option name formatted as
/// `<prefix>#<category>.<long-name>`. Users can omit `<prefix>`
/// for options that have an empty prefix and `<category>`
/// if the category is "global".
option_pointer cli_long_name_lookup(const std::string& name) const;
/// Returns the first `config_option` that matches the CLI short option name.
option_pointer cli_short_name_lookup(char short_name) const;
/// Returns the first `config_option` that matches category and long name.
option_pointer qualified_name_lookup(const std::string& category,
const std::string& long_name) const;
/// Returns the first `config_option` that matches the qualified name.
/// @param name Config option name formatted as `<category>.<long-name>`.
option_pointer qualified_name_lookup(const std::string& name) const;
/// Returns the number of stored config options.
inline size_t size() const noexcept {
return opts_.size();
}
/// Returns an iterator to the first ::config_option object.
inline iterator begin() noexcept {
return opts_.begin();
}
/// Returns the past-the-end iterator.
inline iterator end() noexcept {
return opts_.end();
}
/// Adds a config option to the set.
template <class T>
config_option_set& add(const char* category, const char* name,
const char* description = "") {
opts_.emplace_back(make_config_option<T>(category, name, description));
return *this;
}
/// Adds a config option to the set that synchronizes its value with `ref`.
template <class T>
config_option_set& add(T& ref, const char* category, const char* name,
const char* description = "") {
opts_.emplace_back(make_config_option<T>(ref, category, name, description));
return *this;
}
// -- parsing ----------------------------------------------------------------
/// Parses a given range as CLI arguments into `config`.
parse_result parse(config_map& config, argument_iterator begin,
argument_iterator end) const;
/// Parses a given range as CLI arguments into `config`.
parse_result parse(config_map& config,
const std::vector<std::string>& args) const;
private:
// -- member variables -------------------------------------------------------
option_vector opts_;
};
} // namespace caf
......@@ -25,15 +25,11 @@
#include <vector>
#include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/default_sum_type_access.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/optional.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/sum_type.hpp"
#include "caf/sum_type_access.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
......@@ -44,10 +40,6 @@ namespace caf {
/// contain lists of themselves.
class config_value {
public:
// -- friends ----------------------------------------------------------------
friend struct default_sum_type_access<config_value>;
// -- member types -----------------------------------------------------------
using integer = int64_t;
......@@ -452,6 +444,67 @@ T get_or(const config_value::dictionary& xs, const std::string& name,
return default_value;
}
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
optional<T> get_if(const std::map<std::string, config_value::dictionary>* xs,
const std::string& name) {
// Get the category.
auto pos = name.find('.');
if (pos == std::string::npos)
return none;
auto i = xs->find(name.substr(0, pos));
if (i == xs->end())
return none;
return get_if<T>(&i->second, name.substr(pos + 1));
}
/// Retrieves the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
T get(const std::map<std::string, config_value::dictionary>& xs,
const std::string& name) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
CAF_RAISE_ERROR("invalid type or name found");
}
/// Retrieves the value associated to `name` from `xs` or returns
/// `default_value`.
/// @relates config_value
template <class T>
T get_or(const std::map<std::string, config_value::dictionary>& xs,
const std::string& name, const T& default_value) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
return default_value;
}
/// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
optional<T> get_if(const actor_system_config* cfg, const std::string& name) {
return get_if<T>(&content(*cfg), name);
}
/// Retrieves the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
T get(const actor_system_config& cfg, const std::string& name) {
return get<T>(content(cfg), name);
}
/// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`.
/// @relates config_value
template <class T>
T get_or(const actor_system_config& cfg, const std::string& name,
const T& default_value) {
return get_or(content(cfg), name, default_value);
}
/// @relates config_value
bool operator<(const config_value& x, const config_value& y);
......
......@@ -85,6 +85,49 @@ container_view<F, Container> make_container_view(Container& x) {
return {x};
}
/// Like `std::find`, but takes a range instead of an iterator pair and returns
/// a pointer to the found object on success instead of returning an iterator.
template <class T>
typename T::value_type* ptr_find(T& xs, const typename T::value_type& x) {
for (auto& y : xs)
if (y == x)
return &y;
return nullptr;
}
/// Like `std::find`, but takes a range instead of an iterator pair and returns
/// a pointer to the found object on success instead of returning an iterator.
template <class T>
const typename T::value_type* ptr_find(const T& xs,
const typename T::value_type& x) {
for (auto& y : xs)
if (y == x)
return &y;
return nullptr;
}
/// Like `std::find_if`, but takes a range instead of an iterator pair and
/// returns a pointer to the found object on success instead of returning an
/// iterator.
template <class T, class Predicate>
typename T::value_type* ptr_find_if(T& xs, Predicate pred) {
for (auto& x : xs)
if (pred(x))
return &x;
return nullptr;
}
/// Like `std::find_if`, but takes a range instead of an iterator pair and
/// returns a pointer to the found object on success instead of returning an
/// iterator.
template <class T, class Predicate>
const typename T::value_type* ptr_find_if(const T& xs, Predicate pred) {
for (auto& x : xs)
if (pred(x))
return &x;
return nullptr;
}
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 <map>
#include <string>
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
namespace caf {
namespace detail {
class ini_consumer;
class ini_list_consumer;
class ini_map_consumer;
class abstract_ini_consumer {
public:
// -- constructors, destructors, and assignment operators --------------------
explicit abstract_ini_consumer(abstract_ini_consumer* parent = nullptr);
virtual ~abstract_ini_consumer();
// -- properties -------------------------------------------------------------
virtual void value_impl(config_value&& x) = 0;
template <class T>
void value(T&& x) {
value_impl(config_value{std::forward<T>(x)});
}
inline abstract_ini_consumer* parent() {
return parent_;
}
ini_map_consumer begin_map();
ini_list_consumer begin_list();
protected:
// -- member variables -------------------------------------------------------
abstract_ini_consumer* parent_;
};
class ini_map_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
using map_type = config_value::dictionary;
using iterator = map_type::iterator;
// -- constructors, destructors, and assignment operators --------------------
ini_map_consumer(abstract_ini_consumer* ptr);
ini_map_consumer(ini_map_consumer&& other);
~ini_map_consumer() override;
// -- properties -------------------------------------------------------------
void end_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
map_type xs_;
iterator i_;
};
class ini_list_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
ini_list_consumer(abstract_ini_consumer* ptr);
ini_list_consumer(ini_list_consumer&& other);
// -- properties -------------------------------------------------------------
void end_list();
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
config_value::list xs_;
};
/// Consumes a single value from an INI parser.
class ini_value_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
explicit ini_value_consumer(abstract_ini_consumer* parent = nullptr);
// -- properties -------------------------------------------------------------
void value_impl(config_value&& x) override;
// -- member variables -------------------------------------------------------
config_value result;
};
/// Consumes a config category.
class ini_category_consumer : public abstract_ini_consumer {
public:
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
// -- constructors, destructors, and assignment operators --------------------
ini_category_consumer(ini_consumer* parent, std::string category);
ini_category_consumer(ini_category_consumer&&) = default;
// -- properties -------------------------------------------------------------
void end_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- properties -------------------------------------------------------------
ini_consumer* dparent();
// -- member variables -------------------------------------------------------
std::string category_;
config_value::dictionary xs_;
std::string current_key;
};
/// Consumes a series of dictionaries forming a application configuration.
class ini_consumer : public abstract_ini_consumer {
public:
// -- friends ----------------------------------------------------------------
friend class ini_category_consumer;
// -- member types -----------------------------------------------------------
using super = abstract_ini_consumer;
using config_map = std::map<std::string, config_value::dictionary>;
// -- constructors, destructors, and assignment operators --------------------
ini_consumer(config_option_set& options, config_map& cfg);
ini_consumer(ini_consumer&&) = default;
// -- properties -------------------------------------------------------------
ini_category_consumer begin_map();
void key(std::string name);
void value_impl(config_value&& x) override;
private:
// -- member variables -------------------------------------------------------
config_option_set& options_;
config_map& cfg_;
std::string current_key;
std::vector<error> warnings_;
};
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* *
* 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/detail/type_traits.hpp"
namespace caf {
namespace detail {
/// Moves the value from `x` if it is not a pointer (e.g., `optional` or
/// `expected`), returns `*x` otherwise.
template <class T>
T& move_if_not_ptr(T* x) {
return *x;
}
/// Moves the value from `x` if it is not a pointer (e.g., `optional` or
/// `expected`), returns `*x` otherwise.
template <class T, class E = enable_if_t<!std::is_pointer<T>::value>>
T&& move_if_not_ptr(T& x) {
return std::move(*x);
}
} // namespace detail
} // namespace caf
......@@ -30,6 +30,8 @@
namespace caf {
namespace detail {
struct parse_ini_t {
/// Denotes an optional error output stream
using opt_err = optional<std::ostream&>;
......
......@@ -21,6 +21,7 @@
#include <cstring>
#include "caf/detail/pp.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -55,8 +56,8 @@ extern const char octal_chars[9];
} // namespace caf
#define CAF_FSM_EVAL_MISMATCH_EC \
if (mismatch_ec == ec::unexpected_character) \
ps.code = ch != '\n' ? mismatch_ec : ec::unexpected_newline; \
if (mismatch_ec == caf::pec::unexpected_character) \
ps.code = ch != '\n' ? mismatch_ec : caf::pec::unexpected_newline; \
else \
ps.code = mismatch_ec; \
return;
......@@ -67,17 +68,17 @@ extern const char octal_chars[9];
goto s_init; \
s_unexpected_eof: \
__attribute__((unused)); \
ps.code = ec::unexpected_eof; \
ps.code = caf::pec::unexpected_eof; \
return; \
{ \
static constexpr ec mismatch_ec = ec::unexpected_character
static constexpr auto mismatch_ec = caf::pec::unexpected_character
/// Defines a non-terminal state in the FSM.
#define state(name) \
CAF_FSM_EVAL_MISMATCH_EC \
} \
{ \
static constexpr ec mismatch_ec = ec::unexpected_character; \
static constexpr auto mismatch_ec = caf::pec::unexpected_character; \
s_##name : __attribute__((unused)); \
if (ch == '\0') \
goto s_unexpected_eof; \
......@@ -88,7 +89,7 @@ extern const char octal_chars[9];
CAF_FSM_EVAL_MISMATCH_EC \
} \
{ \
static constexpr ec mismatch_ec = ec::trailing_character; \
static constexpr auto mismatch_ec = caf::pec::trailing_character; \
s_##name : __attribute__((unused)); \
if (ch == '\0') \
goto s_fin; \
......@@ -99,7 +100,7 @@ extern const char octal_chars[9];
CAF_FSM_EVAL_MISMATCH_EC \
} \
s_fin: \
ps.code = ec::success; \
ps.code = caf::pec::success; \
return;
#define CAF_TRANSITION_IMPL1(target) \
......@@ -181,7 +182,7 @@ extern const char octal_chars[9];
#define CAF_FSM_TRANSITION_IMPL2(fsm_call, target) \
ps.next(); \
fsm_call; \
if (ps.code > ec::trailing_character) \
if (ps.code > caf::pec::trailing_character) \
return; \
ch = ps.current(); \
goto s_##target;
......@@ -218,7 +219,7 @@ extern const char octal_chars[9];
#define CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
fsm_call; \
if (ps.code > ec::trailing_character) \
if (ps.code > caf::pec::trailing_character) \
return; \
ch = ps.current(); \
goto s_##target;
......
......@@ -23,13 +23,11 @@
#include <string>
#include "caf/atom.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -52,7 +50,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
return true;
};
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character)
if (ps.code <= pec::trailing_character)
consumer.value(atom(buf));
});
start();
......@@ -62,7 +60,7 @@ void read_atom(state<Iterator, Sentinel>& ps, Consumer& consumer) {
}
state(read_chars) {
transition(done, '\'')
transition(read_chars, is_legal, append(ch), ec::too_many_characters)
transition(read_chars, is_legal, append(ch), pec::too_many_characters)
}
term_state(done) {
transition(done, " \t")
......
......@@ -21,12 +21,11 @@
#include <cstdint>
#include <string>
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -37,7 +36,7 @@ template <class Iterator, class Sentinel, class Consumer>
void read_bool(state<Iterator, Sentinel>& ps, Consumer& consumer) {
bool res = false;
auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character)
if (ps.code <= pec::trailing_character)
consumer.value(res);
});
start();
......
......@@ -22,13 +22,13 @@
#include <stack>
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_number_or_timespan.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -52,7 +52,7 @@ namespace parser {
//
template <class Iterator, class Sentinel, class Consumer>
void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) {
void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&&) {
start();
term_state(init) {
transition(done, '\n')
......@@ -65,7 +65,7 @@ void read_ini_comment(state<Iterator, Sentinel>& ps, Consumer&) {
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer);
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer);
template <class Iterator, class Sentinel, class Consumer>
void read_ini_list(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
......@@ -136,7 +136,7 @@ void read_ini_map(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
}
template <class Iterator, class Sentinel, class Consumer>
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) {
void read_ini_value(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
start();
state(init) {
fsm_epsilon(read_string(ps, consumer), done, '"')
......@@ -155,57 +155,26 @@ void read_ini_value(state<Iterator, Sentinel>& ps, Consumer& consumer) {
/// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) {
void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using std::swap;
std::string tmp;
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
};
bool in_section = false;
auto emit_key = [&] {
std::string key;
swap(tmp, key);
consumer.key(std::move(key));
};
auto begin_section = [&] {
if (in_section)
consumer.end_map();
else
in_section = true;
emit_key();
consumer.begin_map();
};
auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character && in_section)
consumer.end_map();
if (ps.code <= pec::trailing_character)
consumer.end_map();
});
start();
// Scanning for first section.
// Dispatches to read sections, comments, or key/value pairs.
term_state(init) {
transition(init, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(start_section, '[')
}
// Read the section key after reading an '['.
state(start_section) {
transition(start_section, " \t")
transition(read_section_name, alphabetic_chars, tmp = ch)
}
// Reads a section name such as "[foo]".
state(read_section_name) {
transition(read_section_name, alnum_or_dash, tmp += ch)
epsilon(close_section)
}
// Wait for the closing ']', preceded by any number of whitespaces.
state(close_section) {
transition(close_section, " \t")
transition(dispatch, ']', begin_section())
}
// Dispatches to read sections, comments, or key/value pairs.
term_state(dispatch) {
transition(dispatch, " \t\n")
transition(read_section_name, '[')
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';')
transition(read_key_name, alphanumeric_chars, tmp = ch)
}
// Reads a key of a "key=value" line.
......@@ -226,8 +195,47 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// Waits for end-of-line after reading a value
term_state(await_eol) {
transition(await_eol, " \t")
fsm_epsilon(read_ini_comment(ps, consumer), dispatch, ';')
transition(dispatch, '\n')
fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(init, '\n')
}
fin();
}
/// Reads an INI formatted input.
template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using std::swap;
std::string tmp;
auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_';
};
auto begin_section = [&]() -> decltype(consumer.begin_map()) {
std::string key;
swap(tmp, key);
consumer.key(std::move(key));
return consumer.begin_map();
};
start();
// Scanning for first section.
term_state(init) {
transition(init, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(start_section, '[')
}
// Read the section key after reading an '['.
state(start_section) {
transition(start_section, " \t")
transition(read_section_name, alphabetic_chars, tmp = ch)
}
// Reads a section name such as "[foo]".
state(read_section_name) {
transition(read_section_name, alnum_or_dash, tmp += ch)
epsilon(close_section)
}
// Wait for the closing ']', preceded by any number of whitespaces.
state(close_section) {
transition(close_section, " \t")
fsm_transition(read_ini_section(ps, begin_section()), init, ']')
}
fin();
}
......
......@@ -20,15 +20,14 @@
#include <cstdint>
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -53,7 +52,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
int64_t int_res = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character) {
if (ps.code <= pec::trailing_character) {
if (result_type == integer) {
consumer.value(int_res);
return;
......@@ -63,11 +62,11 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
exp += dec_exp;
// 2) Check whether exponent is in valid range.
if (exp < -max_double_exponent) {
ps.code = ec::exponent_underflow;
ps.code = pec::exponent_underflow;
return;
}
if (exp > max_double_exponent) {
ps.code = ec::exponent_overflow;
ps.code = pec::exponent_overflow;
return;
}
// 3) Scale result.
......@@ -140,13 +139,13 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_bin)
}
term_state(pos_bin) {
transition(pos_bin, "01", add_ascii<2>(int_res, ch), ec::integer_overflow)
transition(pos_bin, "01", add_ascii<2>(int_res, ch), pec::integer_overflow)
}
state(start_neg_bin) {
epsilon(neg_bin)
}
term_state(neg_bin) {
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), ec::integer_underflow)
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), pec::integer_underflow)
}
// Octal integers.
state(start_pos_oct) {
......@@ -154,14 +153,14 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
}
term_state(pos_oct) {
transition(pos_oct, octal_chars, add_ascii<8>(int_res, ch),
ec::integer_overflow)
pec::integer_overflow)
}
state(start_neg_oct) {
epsilon(neg_oct)
}
term_state(neg_oct) {
transition(neg_oct, octal_chars, sub_ascii<8>(int_res, ch),
ec::integer_underflow)
pec::integer_underflow)
}
// Hexal integers.
state(start_pos_hex) {
......@@ -169,32 +168,32 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
}
term_state(pos_hex) {
transition(pos_hex, hexadecimal_chars, add_ascii<16>(int_res, ch),
ec::integer_overflow)
pec::integer_overflow)
}
state(start_neg_hex) {
epsilon(neg_hex)
}
term_state(neg_hex) {
transition(neg_hex, hexadecimal_chars, sub_ascii<16>(int_res, ch),
ec::integer_underflow)
pec::integer_underflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) {
transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch),
ec::integer_overflow)
pec::integer_overflow)
transition(has_e, "eE", ch_res(positive_double))
transition(trailing_dot, '.', ch_res(positive_double))
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) {
transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch),
ec::integer_underflow)
pec::integer_underflow)
transition(has_e, "eE", ch_res(negative_double))
transition(trailing_dot, '.', ch_res(negative_double))
}
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal.
state(leading_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow)
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
}
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
......@@ -202,7 +201,7 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), ec::exponent_underflow)
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
transition(has_e, "eE")
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
......@@ -211,25 +210,25 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
pec::exponent_overflow)
}
state(has_plus_after_e) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
pec::exponent_overflow)
}
state(has_minus_after_e) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
pec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
ec::exponent_overflow)
pec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
pec::exponent_underflow)
}
fin();
}
......
......@@ -22,18 +22,16 @@
#include <cstdint>
#include <string>
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/pec.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp"
namespace caf {
namespace detail {
......@@ -60,7 +58,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
auto has_dbl = [&] { return holds_alternative<double>(ic.interim); };
auto get_int = [&] { return get<int64_t>(ic.interim); };
auto g = make_scope_guard([&] {
if (ps.code <= ec::trailing_character) {
if (ps.code <= pec::trailing_character) {
if (res != none) {
consumer.value(*res);
} else if (!holds_alternative<none_t>(ic.interim)) {
......@@ -80,7 +78,7 @@ void read_number_or_timespan(state<Iterator, Sentinel>& ps,
epsilon_if(has_dbl(), has_double)
}
term_state(has_double) {
error_transition(ec::fractional_timespan, "unms")
error_transition(pec::fractional_timespan, "unms")
}
term_state(has_integer) {
transition(have_u, 'u')
......
......@@ -23,10 +23,10 @@
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -38,7 +38,7 @@ template <class Iterator, class Sentinel, class Consumer>
void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
std::string res;
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character)
if (ps.code <= pec::trailing_character)
consumer.value(std::move(res));
});
start();
......@@ -49,7 +49,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
state(read_chars) {
transition(escape, '\\')
transition(done, '"')
error_transition(ec::unexpected_newline, '\n')
error_transition(pec::unexpected_newline, '\n')
transition(read_chars, any_char, res += ch)
}
state(escape) {
......@@ -58,7 +58,7 @@ void read_string(state<Iterator, Sentinel>& ps, Consumer& consumer) {
transition(read_chars, 't', res += '\t')
transition(read_chars, '\\', res += '\\')
transition(read_chars, '"', res += '"')
error_transition(ec::illegal_escape_sequence)
error_transition(pec::illegal_escape_sequence)
}
term_state(done) {
transition(done, " \t")
......
......@@ -20,7 +20,7 @@
#include <cstdint>
#include "caf/detail/parser/ec.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
......@@ -30,7 +30,7 @@ template <class Iterator, class Sentinel = Iterator>
struct state {
Iterator i;
Sentinel e;
ec code;
pec code;
int32_t line = 1;
int32_t column = 1;
......
......@@ -19,6 +19,7 @@
#pragma once
#include <cstdint>
#include <map>
#include <memory>
#include <tuple>
......@@ -138,6 +139,12 @@ struct prohibit_top_level_spawn_marker;
enum class stream_priority;
enum class atom_value : uint64_t;
// -- functions ----------------------------------------------------------------
/// @relates actor_system_config
const std::map<std::string, std::map<std::string, config_value>>&
content(const actor_system_config&);
// -- aliases ------------------------------------------------------------------
using actor_id = uint64_t;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/pec.hpp"
namespace caf {
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(const char* category, const char* name,
const char* description) {
config_option::vtbl_type vtbl{
[](const config_value& x) -> error {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
},
nullptr
};
return {category, name, description, std::is_same<T, bool>::value, vtbl};
}
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(T& storage, const char* category,
const char* name, const char* description) {
config_option::vtbl_type vtbl{
[](const config_value& x) -> error {
if (holds_alternative<T>(x))
return none;
return make_error(pec::type_mismatch);
},
[](void* ptr, const config_value& x) {
*static_cast<T*>(ptr) = get<T>(x);
}
};
return {category, name, description, std::is_same<T, bool>::value,
vtbl, &storage};
}
} // namespace caf
......@@ -23,14 +23,14 @@
#include "caf/fwd.hpp"
namespace caf {
namespace detail {
namespace parser {
enum class ec : uint8_t {
/// PEC stands for "Parser Error Code". This enum contains error codes used by
/// various CAF parsers.
enum class pec : uint8_t {
/// Not-an-error.
success,
success = 0,
/// Parser succeeded but found trailing character(s).
trailing_character,
trailing_character = 1,
/// Parser stopped after reaching the end while still expecting input.
unexpected_eof,
/// Parser stopped after reading an unexpected character.
......@@ -38,7 +38,7 @@ enum class ec : uint8_t {
/// Parsed integer exceeds the number of available bits of a `timespan`.
timespan_overflow,
/// Tried constructing a `timespan` with from a floating point number.
fractional_timespan,
fractional_timespan = 5,
/// Too many characters for an atom.
too_many_characters,
/// Unrecognized character after escaping `\`.
......@@ -48,19 +48,27 @@ enum class ec : uint8_t {
/// Parsed positive integer exceeds the number of available bits.
integer_overflow,
/// Parsed negative integer exceeds the number of available bits.
integer_underflow,
integer_underflow = 10,
/// Exponent of parsed double is less than the minimum supported exponent.
exponent_underflow,
/// Exponent of parsed double is greater than the maximum supported exponent.
exponent_overflow,
/// Parsed type does not match the expected type.
type_mismatch,
/// Stopped at an unrecognized option name.
not_an_option,
/// Stopped at an unparseable argument.
illegal_argument = 15,
/// Stopped because an argument was omitted.
missing_argument,
/// Stopped because the key of a category was taken.
illegal_category,
};
error make_error(ec code);
error make_error(pec code);
const char* to_string(ec x);
error make_error(pec code, size_t line, size_t column);
const char* to_string(pec x);
} // namespace parser
} // namespace detail
} // namespace caf
......@@ -27,8 +27,8 @@
namespace caf {
/// SEC stands for "System Error Code". This enum contains
/// error codes used internally by CAF.
/// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules.
enum class sec : uint8_t {
/// No error.
none = 0,
......
This diff is collapsed.
......@@ -20,6 +20,28 @@
#include <iostream>
#include "caf/config.hpp"
#include "caf/error.hpp"
using std::move;
using std::string;
namespace {
string get_long_name(const char* name) {
auto i= strchr(name, ',');
return i != nullptr ? string{name, i} : string{name};
}
string get_short_names(const char* name) {
auto substr = strchr(name, ',');
if (substr != nullptr)
return ++substr;
return string{};
}
} // namespace <anonymous>
namespace caf {
const char* type_name_visitor_tbl[] {
......@@ -39,49 +61,36 @@ const char* type_name_visitor_tbl[] {
"a duration"
};
config_option::config_option(const char* cat, const char* nm, const char* expl)
: category_(cat),
name_(nm),
explanation_(expl),
short_name_('\0') {
auto last = name_.end();
auto comma = std::find(name_.begin(), last, ',');
if (comma != last) {
auto i = comma;
++i;
if (i != last)
short_name_ = *i;
name_.erase(comma, last);
}
config_option::config_option(string category, const char* name,
string description, bool is_flag,
const vtbl_type& vtbl, void* value)
: category_(move(category)),
long_name_(get_long_name(name)),
short_names_(get_short_names(name)),
description_(move(description)),
is_flag_(is_flag),
vtbl_(vtbl),
value_(value) {
// nop
}
config_option::~config_option() {
// nop
string config_option::full_name() const {
std::string result = category();
result += '.';
result += long_name_;
return result;
}
std::string config_option::full_name() const {
std::string res = category();
res += '.';
auto name_begin = name();
const char* name_end = strchr(name(), ',');
if (name_end != nullptr)
res.insert(res.end(), name_begin, name_end);
else
res += name();
return res;
error config_option::check(const config_value& x) const {
CAF_ASSERT(vtbl_.check != nullptr);
return vtbl_.check(x);
}
void config_option::report_type_error(size_t ln, config_value& x,
const char* expected,
optional<std::ostream&> out) {
if (!out)
return;
/*
type_name_visitor tnv;
*out << "error in line " << ln << ": expected "
<< expected << " found "
<< visit(tnv, x) << '\n';
*/
void config_option::store(const config_value& x) const {
if (value_ != nullptr) {
CAF_ASSERT(vtbl_.store != nullptr);
vtbl_.store(value_, x);
}
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config_option_set.hpp"
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/algorithms.hpp"
#include "caf/expected.hpp"
using std::string;
namespace caf {
config_option_set::config_option_set() {
// nop
}
auto config_option_set::parse(config_map& config, argument_iterator first,
argument_iterator last) const
-> std::pair<pec, argument_iterator> {
// Sanity check.
if (first == last)
return {pec::success, last};
// Parses an argument.
using iter = string::const_iterator;
auto consume = [&](const config_option& opt, iter arg_begin, iter arg_end) {
// Extract option name and category.
auto opt_name = opt.long_name();
string opt_ctg = opt.category();
// Try inserting a new submap into the config or fill existing one.
auto& submap = config[opt_ctg];
// Flags only consume the current element.
if (opt.is_flag()) {
if (arg_begin != arg_end)
return pec::illegal_argument;
config_value cfg_true{true};
opt.store(cfg_true);
submap[opt_name] = cfg_true;
} else {
if (arg_begin == arg_end)
return pec::missing_argument;
auto val = config_value::parse(arg_begin, arg_end);
if (!val)
return pec::illegal_argument;
if (opt.check(*val) != none)
return pec::type_mismatch;
opt.store(*val);
submap[opt_name] = std::move(*val);
}
return pec::success;
};
// We loop over the first N-1 values, because we always consider two
// arguments at once.
for (auto i = first; i != last;) {
if (i->size() < 2)
return {pec::not_an_option, i};
if (*i== "--")
return {pec::success, std::next(first)};
if (i->compare(0, 2, "--") == 0) {
// Long options use the syntax "--<name>=<value>" and consume only a
// single argument.
auto assign_op = i->find('=');//std::find(i->begin(), i->end(), '=');
auto name = i->substr(2, assign_op - 2);
auto opt = cli_long_name_lookup(name);
if (opt == nullptr)
return {pec::not_an_option, i};
auto code = consume(*opt, i->begin() + assign_op + 1, i->end());
if (code != pec::success)
return {code, i};
++i;
} else if (i->front() == '-') {
// Short options have three possibilities.
auto opt = cli_short_name_lookup((*i)[1]);
if (opt == nullptr)
return {pec::not_an_option, i};
if (opt->is_flag()) {
// 1) "-f" for flags, consumes one argument
auto code = consume(*opt, i->begin() + 2, i->end());
if (code != pec::success)
return {code, i};
++i;
} else {
if (i->size() == 2) {
// 2) "-k <value>", consumes both arguments
auto j = std::next(i);
if (j == last) {
return {pec::missing_argument, j};
}
auto code = consume(*opt, j->begin(), j->end());
if (code != pec::success)
return {code, i};
std::advance(i, 2);
} else {
// 3) "-k<value>" (no space), consumes one argument
auto code = consume(*opt, i->begin() + 2, i->end());
if (code != pec::success)
return {code, i};
++i;
}
}
} else {
// No leading '-' found on current position.
return {pec::not_an_option, i};
}
}
return {pec::success, last};
}
config_option_set::parse_result
config_option_set::parse(config_map& config,
const std::vector<string>& args) const {
return parse(config, args.begin(), args.end());
}
config_option_set::option_pointer
config_option_set::cli_long_name_lookup(const string& name) const {
// We accept "caf#" prefixes for backward compatibility, but ignore them.
size_t offset = name.compare(0, 4, "caf#") != 0 ? 0u : 4u;
// Extract category and long name.
string category;
string long_name;
auto sep = name.find('.', offset);
if (sep == string::npos) {
category = "global";
if (offset == 0)
long_name = name;
else
long_name = name.substr(offset);
} else {
category = name.substr(offset, sep);
long_name = name.substr(sep + 1);
}
// Scan all options for a match.
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
});
}
config_option_set::option_pointer
config_option_set::cli_short_name_lookup(char short_name) const {
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.short_names().find(short_name) != string::npos;
});
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& category,
const std::string& long_name) const {
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
});
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& name) const {
auto sep = name.find('.');
if (sep == string::npos)
return nullptr;
return qualified_name_lookup(name.substr(0, sep), name.substr(sep + 1));
}
} // namespace caf
......@@ -19,10 +19,11 @@
#include "caf/config_value.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/ini_consumer.hpp"
#include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp"
#include "caf/pec.hpp"
namespace caf {
......@@ -49,133 +50,24 @@ config_value::~config_value() {
// -- parsing ------------------------------------------------------------------
namespace {
struct abstract_consumer {
virtual ~abstract_consumer() {
// nop
}
template <class T>
void value(T&& x) {
value_impl(config_value{std::forward<T>(x)});
}
virtual void value_impl(config_value&& x) = 0;
};
struct list_consumer;
struct map_consumer : abstract_consumer {
using map_type = config_value::dictionary;
using iterator = map_type::iterator;
map_consumer begin_map() {
return {this};
}
void end_map() {
parent->value_impl(config_value{std::move(xs)});
}
list_consumer begin_list();
void key(std::string name) {
i = xs.emplace_hint(xs.end(), std::make_pair(std::move(name), config_value{}));
}
void value_impl(config_value&& x) override {
i->second = std::move(x);
}
map_consumer(abstract_consumer* ptr) : i(xs.end()), parent(ptr) {
// nop
}
map_consumer(map_consumer&& other)
: xs(std::move(other.xs)),
i(xs.end()),
parent(other.parent) {
// nop
}
map_type xs;
iterator i;
abstract_consumer* parent;
};
struct list_consumer : abstract_consumer {
void end_list() {
parent->value_impl(config_value{std::move(xs)});
}
map_consumer begin_map() {
return {this};
}
list_consumer begin_list() {
return {this};
}
void value_impl(config_value&& x) override {
xs.emplace_back(std::move(x));
}
list_consumer(abstract_consumer* ptr) : parent(ptr) {
// nop
}
list_consumer(list_consumer&& other)
: xs(std::move(other.xs)),
parent(other.parent) {
// nop
}
config_value::list xs;
abstract_consumer* parent;
};
list_consumer map_consumer::begin_list() {
return {this};
}
struct consumer : abstract_consumer {
config_value result;
map_consumer begin_map() {
return {this};
}
list_consumer begin_list() {
return {this};
}
void value_impl(config_value&& x) override {
result = std::move(x);
}
};
} // namespace <anonymous>
expected<config_value> config_value::parse(std::string::const_iterator first,
std::string::const_iterator last) {
using namespace detail;
auto i = first;
// Sanity check.
if (i == last)
return make_error(parser::ec::unexpected_eof);
return make_error(pec::unexpected_eof);
// Skip to beginning of the argument.
while (isspace(*i))
if (++i == last)
return make_error(parser::ec::unexpected_eof);
return make_error(pec::unexpected_eof);
// Dispatch to parser.
parser::state<std::string::const_iterator> res;
consumer f;
detail::ini_value_consumer f;
res.i = i;
res.e = last;
parser::read_ini_value(res, f);
if (res.code == detail::parser::ec::success)
if (res.code == pec::success)
return std::move(f.result);
// Assume an unescaped string unless the first character clearly indicates
// otherwise.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/ini_consumer.hpp"
#include "caf/pec.hpp"
namespace caf {
namespace detail {
// -- abstract_ini_consumer ----------------------------------------------------
abstract_ini_consumer::abstract_ini_consumer(abstract_ini_consumer* parent)
: parent_(parent) {
// nop
}
abstract_ini_consumer::~abstract_ini_consumer() {
// nop
}
ini_map_consumer abstract_ini_consumer::begin_map() {
return {this};
}
ini_list_consumer abstract_ini_consumer::begin_list() {
return {this};
}
// -- map_consumer -------------------------------------------------------------
ini_map_consumer::ini_map_consumer(abstract_ini_consumer* parent)
: super(parent),
i_(xs_.end()) {
CAF_ASSERT(parent != nullptr);
}
ini_map_consumer::ini_map_consumer(ini_map_consumer&& other)
: super(other.parent()) {
}
ini_map_consumer::~ini_map_consumer() {
// nop
}
void ini_map_consumer::end_map() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_map_consumer::key(std::string name) {
i_ = xs_.emplace(std::move(name), config_value{}).first;
}
void ini_map_consumer::value_impl(config_value&& x) {
CAF_ASSERT(i_ != xs_.end());
i_->second = std::move(x);
}
// -- ini_list_consumer --------------------------------------------------------
ini_list_consumer::ini_list_consumer(abstract_ini_consumer* parent)
: super(parent) {
CAF_ASSERT(parent != nullptr);
}
ini_list_consumer::ini_list_consumer(ini_list_consumer&& other)
: super(other.parent()),
xs_(std::move(other.xs_)) {
// nop
}
void ini_list_consumer::end_list() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_list_consumer::value_impl(config_value&& x) {
xs_.emplace_back(std::move(x));
}
// -- ini_value_consumer -------------------------------------------------------
ini_value_consumer::ini_value_consumer(abstract_ini_consumer* parent)
: super(parent) {
// nop
}
void ini_value_consumer::value_impl(config_value&& x) {
result = std::move(x);
}
// -- ini_category_consumer ----------------------------------------------------
ini_category_consumer::ini_category_consumer(ini_consumer* parent,
std::string category)
: super(parent),
category_(std::move(category)) {
CAF_ASSERT(parent != nullptr);
}
void ini_category_consumer::end_map() {
parent_->value_impl(config_value{std::move(xs_)});
}
void ini_category_consumer::key(std::string name) {
current_key = std::move(name);
}
void ini_category_consumer::value_impl(config_value&& x) {
// See whether there's an config_option associated to this category and key.
auto opt = dparent()->options_.qualified_name_lookup(category_, current_key);
if (opt == nullptr) {
// Simply store in config if no option was found.
xs_.emplace(std::move(current_key), std::move(x));
} else {
// Perform a type check to see whether this is a valid input.
if (opt->check(x) == none) {
opt->store(x);
xs_.emplace(std::move(current_key), std::move(x));
} else {
dparent()->warnings_.emplace_back(make_error(pec::type_mismatch));
}
}
}
ini_consumer* ini_category_consumer::dparent() {
// Safe, because our constructor only accepts ini_consumer parents.
return static_cast<ini_consumer*>(parent());
}
// -- ini_consumer -------------------------------------------------------------
ini_consumer::ini_consumer(config_option_set& options, config_map& cfg)
: options_(options),
cfg_(cfg) {
// nop
}
ini_category_consumer ini_consumer::begin_map() {
return {this, current_key};
}
void ini_consumer::key(std::string name) {
current_key = std::move(name);
}
void ini_consumer::value_impl(config_value&& x) {
auto dict = get_if<config_value::dictionary>(&x);
if (dict != nullptr && !dict->empty())
cfg_.emplace(std::move(current_key), std::move(*dict));
}
} // namespace detail
} // namespace caf
......@@ -16,9 +16,10 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/parser/ec.hpp"
#include "caf/pec.hpp"
#include "caf/error.hpp"
#include "caf/make_message.hpp"
namespace {
......@@ -37,22 +38,28 @@ constexpr const char* tbl[] = {
"exponent_underflow",
"exponent_overflow",
"type_mismatch",
"not_an_option",
"illegal_argument",
"missing_argument",
"illegal_category",
};
} // namespace <anonymous>
namespace caf {
namespace detail {
namespace parser {
error make_error(ec code) {
error make_error(pec code) {
return {static_cast<uint8_t>(code), atom("parser")};
}
const char* to_string(ec x) {
error make_error(pec code, size_t line, size_t column) {
return {static_cast<uint8_t>(code), atom("parser"),
make_message(static_cast<uint32_t>(line),
static_cast<uint32_t>(column))};
}
const char* to_string(pec x) {
return tbl[static_cast<uint8_t>(x)];
}
} // namespace parser
} // namespace detail
} // namespace caf
......@@ -16,23 +16,12 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE config_option
#include "caf/test/unit_test.hpp"
#include "caf/config_option.hpp"
#include "caf/actor_system_config.hpp"
// turn off several flags for overflows / sign conversion
#ifdef CAF_CLANG
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wconstant-conversion"
#elif defined(CAF_GCC)
#pragma GCC diagnostic ignored "-Woverflow"
#endif
using namespace caf;
using std::string;
......@@ -42,7 +31,6 @@ namespace {
constexpr const char* category = "category";
constexpr const char* name = "name";
constexpr const char* explanation = "explanation";
constexpr size_t line = 0;
template<class T>
constexpr int64_t overflow() {
......@@ -55,36 +43,39 @@ constexpr int64_t underflow() {
}
template <class T>
optional<T> read(config_value test_value) {
T output_value{};
auto co = make_config_option(output_value, category, name, explanation);
auto f = co->to_sink();
if (f(line, test_value, none))
return output_value;
optional<T> read(const std::string& arg) {
auto co = make_config_option<T>(category, name, explanation);
auto res = config_value::parse(arg);
if (res && holds_alternative<T>(*res)) {
CAF_CHECK_EQUAL(co.check(*res), none);
return get<T>(*res);
}
return none;
}
// Unsigned integers.
template <class T>
void check_integer_options(std::true_type) {
using std::to_string;
// Run tests for positive integers.
T xzero = 0;
T xmax = std::numeric_limits<T>::max();
CAF_CHECK_EQUAL(read<T>(config_value{xzero}), xzero);
CAF_CHECK_EQUAL(read<T>(config_value{xmax}), xmax);
CAF_CHECK_EQUAL(read<T>(config_value{overflow<T>()}), none);
CAF_CHECK_EQUAL(read<T>(to_string(xzero)), xzero);
CAF_CHECK_EQUAL(read<T>(to_string(xmax)), xmax);
CAF_CHECK_EQUAL(read<T>(to_string(overflow<T>())), none);
}
// Signed integers.
template <class T>
void check_integer_options(std::false_type) {
using std::to_string;
// Run tests for positive integers.
std::true_type tk;
check_integer_options<T>(tk);
// Run tests for negative integers.
auto xmin = std::numeric_limits<T>::min();
CAF_CHECK_EQUAL(read<T>(config_value{xmin}), xmin);
CAF_CHECK_EQUAL(read<T>(config_value{underflow<T>()}), none);
CAF_CHECK_EQUAL(read<T>(to_string(xmin)), xmin);
CAF_CHECK_EQUAL(read<T>(to_string(underflow<T>())), none);
}
// only works with an integral types and double
......@@ -104,10 +95,10 @@ T unbox(optional<T> x) {
} // namespace <anonymous>
CAF_TEST(type_bool) {
CAF_CHECK_EQUAL(read<bool>(config_value{true}), true);
CAF_CHECK_EQUAL(read<bool>(config_value{false}), false);
CAF_CHECK_EQUAL(read<bool>(config_value{0}), none);
CAF_CHECK_EQUAL(read<bool>(config_value{1}), none);
CAF_CHECK_EQUAL(read<bool>("true"), true);
CAF_CHECK_EQUAL(read<bool>("false"), false);
CAF_CHECK_EQUAL(read<bool>("0"), none);
CAF_CHECK_EQUAL(read<bool>("1"), none);
}
CAF_TEST(type int8_t) {
......@@ -135,66 +126,41 @@ CAF_TEST(type uint32_t) {
}
CAF_TEST(type uint64_t) {
CAF_CHECK_EQUAL(unbox(read<uint64_t>(config_value{0})), 0u);
CAF_CHECK_EQUAL(read<uint64_t>(config_value{-1}), none);
CAF_CHECK_EQUAL(unbox(read<uint64_t>("0")), 0u);
CAF_CHECK_EQUAL(read<uint64_t>("-1"), none);
}
CAF_TEST(type int64_t) {
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{-1})), -1);
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{0})), 0);
CAF_CHECK_EQUAL(unbox(read<int64_t>(config_value{1})), 1);
CAF_CHECK_EQUAL(unbox(read<int64_t>("-1")), -1);
CAF_CHECK_EQUAL(unbox(read<int64_t>("0")), 0);
CAF_CHECK_EQUAL(unbox(read<int64_t>("1")), 1);
}
CAF_TEST(type float) {
CAF_CHECK_EQUAL(unbox(read<float>(config_value{-1.0})), -1.0f);
CAF_CHECK_EQUAL(unbox(read<float>(config_value{-0.1})), -0.1f);
CAF_CHECK_EQUAL(read<float>(config_value{0}), none);
CAF_CHECK_EQUAL(read<float>(config_value{"0.1"}), none);
CAF_CHECK_EQUAL(unbox(read<float>("-1.0")), -1.0f);
CAF_CHECK_EQUAL(unbox(read<float>("-0.1")), -0.1f);
CAF_CHECK_EQUAL(read<float>("0"), none);
CAF_CHECK_EQUAL(read<float>("\"0.1\""), none);
}
CAF_TEST(type double) {
CAF_CHECK_EQUAL(unbox(read<double>(config_value{-1.0})), -1.0);
CAF_CHECK_EQUAL(unbox(read<double>(config_value{-0.1})), -0.1);
CAF_CHECK_EQUAL(read<double>(config_value{0}), none);
CAF_CHECK_EQUAL(read<double>(config_value{"0.1"}), none);
CAF_CHECK_EQUAL(unbox(read<double>("-1.0")), -1.0);
CAF_CHECK_EQUAL(unbox(read<double>("-0.1")), -0.1);
CAF_CHECK_EQUAL(read<double>("0"), none);
CAF_CHECK_EQUAL(read<double>("\"0.1\""), none);
}
CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>(config_value{"foo"})), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
}
CAF_TEST(type atom) {
auto foo = atom("foo");
CAF_CHECK_EQUAL(unbox(read<atom_value>(config_value{foo})), foo);
CAF_CHECK_EQUAL(read<atom_value>(config_value{"bar"}), none);
CAF_CHECK_EQUAL(unbox(read<atom_value>("'foo'")), atom("foo"));
CAF_CHECK_EQUAL(read<atom_value>("bar"), none);
}
CAF_TEST(type timespan) {
timespan dur{500};
CAF_CHECK_EQUAL(unbox(read<timespan>(config_value{dur})), dur);
}
template <class T>
std::string name_of() {
T x{};
config_option::type_name_visitor v;
return v(x);
}
CAF_TEST(type_names) {
CAF_CHECK_EQUAL((name_of<std::map<int, int>>()), "a dictionary");
CAF_CHECK_EQUAL(name_of<atom_value>(), "an atom_value");
CAF_CHECK_EQUAL(name_of<bool>(), "a boolean");
CAF_CHECK_EQUAL(name_of<double>(), "a double");
CAF_CHECK_EQUAL(name_of<float>(), "a float");
CAF_CHECK_EQUAL(name_of<int16_t>(), "a 16-bit integer");
CAF_CHECK_EQUAL(name_of<int32_t>(), "a 32-bit integer");
CAF_CHECK_EQUAL(name_of<int64_t>(), "a 64-bit integer");
CAF_CHECK_EQUAL(name_of<int8_t>(), "an 8-bit integer");
CAF_CHECK_EQUAL(name_of<std::vector<int>>(), "a list");
CAF_CHECK_EQUAL(name_of<string>(), "a string");
CAF_CHECK_EQUAL(name_of<uint16_t>(), "a 16-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint32_t>(), "a 32-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint64_t>(), "a 64-bit unsigned integer");
CAF_CHECK_EQUAL(name_of<uint8_t>(), "an 8-bit unsigned integer");
CAF_CHECK_EQUAL(unbox(read<timespan>("500ns")), dur);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <map>
#include <string>
#include <vector>
#include "caf/config.hpp"
#define CAF_SUITE config_option_set
#include "caf/test/dsl.hpp"
#include "caf/config_option_set.hpp"
using std::string;
using std::vector;
using namespace caf;
namespace {
struct fixture {
config_option_set opts;
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(config_option_set_tests, fixture)
CAF_TEST(lookup) {
opts.add<int>("global", "opt1,1", "test option 1")
.add<float>("test", "opt2,2", "test option 2")
.add<bool>("test", "flag,fl3", "test flag");
CAF_CHECK_EQUAL(opts.size(), 3u);
CAF_MESSAGE("lookup by long name");
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("opt1"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("global.opt1"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("test.opt2"), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_long_name_lookup("test.flag"), nullptr);
CAF_MESSAGE("lookup by short name");
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('1'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('2'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('f'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('l'), nullptr);
CAF_CHECK_NOT_EQUAL(opts.cli_short_name_lookup('3'), nullptr);
}
CAF_TEST(parse with ref syncing) {
using ls = vector<string>; // list of strings
using ds = std::map<string, string>; // dictionary of strings
auto foo_i = 0;
auto foo_f = 0.f;
auto foo_b = false;
auto bar_s = string{};
auto bar_l = ls{};
auto bar_d = ds{};
opts.add<int>(foo_i, "foo", "i,i", "")
.add<float>(foo_f, "foo", "f,f", "")
.add<bool>(foo_b, "foo", "b,b", "")
.add<string>(bar_s, "bar", "s,s", "")
.add<vector<string>>(bar_l, "bar", "l,l", "")
.add<std::map<string, string>>(bar_d, "bar", "d,d", "");
std::map<std::string, config_value::dictionary> cfg;
vector<string> args{"-i42",
"-f",
"1e12",
"-shello",
"--bar.l=[\"hello\", \"world\"]",
"-d",
"{a=\"a\",b=\"b\"}",
"-b"};
CAF_MESSAGE("parse arguments");
auto res = opts.parse(cfg, args);
CAF_CHECK_EQUAL(res.first, pec::success);
if (res.second != args.end())
CAF_FAIL("parser stopped at: " << *res.second);
CAF_MESSAGE("verify referenced values");
CAF_CHECK_EQUAL(foo_i, 42);
CAF_CHECK_EQUAL(foo_f, 1e12);
CAF_CHECK_EQUAL(foo_b, true);
CAF_CHECK_EQUAL(bar_s, "hello");
CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"}));
CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}}));
CAF_MESSAGE("verify dictionary content");
CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -28,8 +28,8 @@
#include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/none.hpp"
#include "caf/pec.hpp"
#include "caf/variant.hpp"
using namespace std;
......@@ -236,11 +236,10 @@ CAF_TEST(unsuccessful parsing) {
CAF_FAIL("assumed an error but got a result");
return std::move(x.error());
};
using detail::parser::ec;
CAF_CHECK_EQUAL(parse("10msb"), ec::trailing_character);
CAF_CHECK_EQUAL(parse("10foo"), ec::trailing_character);
CAF_CHECK_EQUAL(parse("[1,"), ec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=,"), ec::unexpected_character);
CAF_CHECK_EQUAL(parse("{a=1,"), ec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=1 b=2}"), ec::unexpected_character);
CAF_CHECK_EQUAL(parse("10msb"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("10foo"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("[1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=,"), pec::unexpected_character);
CAF_CHECK_EQUAL(parse("{a=1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=1 b=2}"), pec::unexpected_character);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE ini_consumer
#include "caf/test/dsl.hpp"
#include "caf/detail/ini_consumer.hpp"
#include "caf/detail/parser/read_ini.hpp"
using std::string;
using namespace caf;
// List-of-strings.
using ls = std::vector<std::string>;
namespace {
const char test_ini[] = R"(
[global]
is_server=true
port=4242
nodes=["sun", "venus", ]
[logger]
file-name = "foobar.ini" ; our file name
[scheduler] ; more settings
timing = 2us ; using microsecond resolution
impl = 'foo';some atom
)";
struct fixture {
detail::parser::state<std::string::const_iterator> res;
config_option_set options;
config_option_set::config_map config;
fixture() {
options.add<bool>("global", "is_server")
.add<uint16_t>("global", "port")
.add<ls>("global", "nodes")
.add<string>("logger", "file-name")
.add<int>("scheduler", "padding")
.add<timespan>("scheduler", "timing")
.add<atom_value>("scheduler", "impl");
}
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(ini_consumer_tests, fixture)
CAF_TEST(ini_value_consumer) {
std::string str = R"("hello world")";
detail::ini_value_consumer consumer;
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini_value(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<string>(consumer.result), "hello world");
}
CAF_TEST(ini_consumer) {
std::string str = test_ini;
detail::ini_consumer consumer{options, config};
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<bool>(config, "global.is_server"), true);
CAF_CHECK_EQUAL(get<uint16_t>(config, "global.port"), 4242u);
CAF_CHECK_EQUAL(get<ls>(config, "global.nodes"), ls({"sun", "venus"}));
CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.ini");
CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000));
CAF_CHECK_EQUAL(get<atom_value>(config, "scheduler.impl"), atom("foo"));
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -28,8 +28,6 @@
using namespace caf;
using detail::parser::ec;
namespace {
struct atom_parser_consumer {
......@@ -39,7 +37,7 @@ struct atom_parser_consumer {
}
};
using res_t = variant<ec, atom_value>;
using res_t = variant<pec, atom_value>;
struct atom_parser {
res_t operator()(std::string str) {
......@@ -48,7 +46,7 @@ struct atom_parser {
res.i = str.begin();
res.e = str.end();
detail::parser::read_atom(res, f);
if (res.code == ec::success)
if (res.code == pec::success)
return f.x;
return res.code;
}
......@@ -79,11 +77,11 @@ CAF_TEST(non-empty atom) {
}
CAF_TEST(invalid atoms) {
CAF_CHECK_EQUAL(p("'abc"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("'ab\nc'"), ec::unexpected_newline);
CAF_CHECK_EQUAL(p("abc"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("'abc' def"), ec::trailing_character);
CAF_CHECK_EQUAL(p("'12345678901'"), ec::too_many_characters);
CAF_CHECK_EQUAL(p("'abc"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("'ab\nc'"), pec::unexpected_newline);
CAF_CHECK_EQUAL(p("abc"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("'abc' def"), pec::trailing_character);
CAF_CHECK_EQUAL(p("'12345678901'"), pec::too_many_characters);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -28,8 +28,6 @@
using namespace caf;
using detail::parser::ec;
namespace {
struct bool_parser_consumer {
......@@ -39,7 +37,7 @@ struct bool_parser_consumer {
}
};
using res_t = variant<ec, bool>;
using res_t = variant<pec, bool>;
struct bool_parser {
res_t operator()(std::string str) {
......@@ -48,7 +46,7 @@ struct bool_parser {
res.i = str.begin();
res.e = str.end();
detail::parser::read_bool(res, f);
if (res.code == ec::success)
if (res.code == pec::success)
return f.x;
return res.code;
}
......@@ -68,18 +66,18 @@ CAF_TEST(valid booleans) {
}
CAF_TEST(invalid booleans) {
CAF_CHECK_EQUAL(p(""), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("t"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("tr"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("tru"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p(" true"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("f"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fa"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fal"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("fals"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p(" false"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("tr\nue"), ec::unexpected_newline);
CAF_CHECK_EQUAL(p("trues"), ec::trailing_character);
CAF_CHECK_EQUAL(p(""), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("t"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("tr"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("tru"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p(" true"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("f"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fa"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fal"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("fals"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p(" false"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("tr\nue"), pec::unexpected_newline);
CAF_CHECK_EQUAL(p("trues"), pec::trailing_character);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -35,6 +35,10 @@ using log_type = std::vector<std::string>;
struct test_consumer {
log_type log;
test_consumer() = default;
test_consumer(const test_consumer&) = delete;
test_consumer& operator=(const test_consumer&) = delete;
test_consumer& begin_map() {
log.emplace_back("{");
return *this;
......@@ -83,7 +87,7 @@ struct fixture {
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini(res, f);
if (res.code == detail::parser::ec::success != expect_success)
if (res.code == pec::success != expect_success)
CAF_MESSAGE("unexpected parser result state: " << res.code);
return std::move(f.log);
}
......
......@@ -28,8 +28,6 @@
using namespace caf;
using detail::parser::ec;
namespace {
struct numbers_parser_consumer {
......@@ -43,7 +41,7 @@ struct numbers_parser_consumer {
};
struct res_t {
variant<ec, double, int64_t> val;
variant<pec, double, int64_t> val;
template <class T>
res_t(T&& x) : val(std::forward<T>(x)) {
// nop
......@@ -57,8 +55,8 @@ bool operator==(const res_t& x, const res_t& y) {
caf::test::equal_to f;
using caf::get;
using caf::holds_alternative;
if (holds_alternative<ec>(x.val))
return f(get<ec>(x.val), get<ec>(y.val));
if (holds_alternative<pec>(x.val))
return f(get<pec>(x.val), get<pec>(y.val));
if (holds_alternative<double>(x.val))
return f(get<double>(x.val), get<double>(y.val));
return f(get<int64_t>(x.val), get<int64_t>(y.val));
......@@ -71,7 +69,7 @@ struct numbers_parser {
res.i = str.begin();
res.e = str.end();
detail::parser::read_number(res, f);
if (res.code == ec::success)
if (res.code == pec::success)
return f.x;
return res.code;
}
......@@ -126,7 +124,7 @@ CAF_TEST(octal numbers) {
CHECK_NUMBER(-00);
CHECK_NUMBER(-0123);
// invalid numbers
CAF_CHECK_EQUAL(p("018"), ec::trailing_character);
CAF_CHECK_EQUAL(p("018"), pec::trailing_character);
}
CAF_TEST(decimal numbers) {
......@@ -147,11 +145,11 @@ CAF_TEST(hexadecimal numbers) {
CHECK_NUMBER(-0x123);
CHECK_NUMBER(-0xaf01);
// invalid numbers
CAF_CHECK_EQUAL(p("0xFG"), ec::trailing_character);
CAF_CHECK_EQUAL(p("0xFG"), pec::trailing_character);
CAF_CHECK_EQUAL(p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_overflow);
pec::integer_overflow);
CAF_CHECK_EQUAL(p("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_underflow);
pec::integer_underflow);
}
CAF_TEST(floating point numbers) {
......@@ -196,8 +194,8 @@ CAF_TEST(integer mantissa with negative exponent) {
CHECK_NUMBER(1e-5);
CHECK_NUMBER(1e-6);
// invalid numbers
CAF_CHECK_EQUAL(p("-9.9999e-e511"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("-9.9999e-511"), ec::exponent_underflow);
CAF_CHECK_EQUAL(p("-9.9999e-e511"), pec::unexpected_character);
CAF_CHECK_EQUAL(p("-9.9999e-511"), pec::exponent_underflow);
}
CAF_TEST(fractional mantissa with positive exponent) {
......
......@@ -29,8 +29,6 @@
using namespace caf;
using namespace std::chrono;
using detail::parser::ec;
namespace {
struct number_or_timespan_parser_consumer {
......@@ -42,7 +40,7 @@ struct number_or_timespan_parser_consumer {
};
struct res_t {
variant<ec, double, int64_t, timespan> val;
variant<pec, double, int64_t, timespan> val;
template <class T>
res_t(T&& x) : val(std::forward<T>(x)) {
// nop
......@@ -60,8 +58,8 @@ bool operator==(const res_t& x, const res_t& y) {
caf::test::equal_to f;
using caf::get;
using caf::holds_alternative;
if (holds_alternative<ec>(x.val))
return f(get<ec>(x.val), get<ec>(y.val));
if (holds_alternative<pec>(x.val))
return f(get<pec>(x.val), get<pec>(y.val));
if (holds_alternative<double>(x.val))
return f(get<double>(x.val), get<double>(y.val));
if (holds_alternative<int64_t>(x.val))
......@@ -76,7 +74,7 @@ struct number_or_timespan_parser {
res.i = str.begin();
res.e = str.end();
detail::parser::read_number_or_timespan(res, f);
if (res.code == ec::success)
if (res.code == pec::success)
return f.x;
return res.code;
}
......@@ -117,20 +115,20 @@ CAF_TEST(valid numbers and timespans) {
}
CAF_TEST(invalid timespans) {
CAF_CHECK_EQUAL(p("12.3s"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3m"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ms"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3mi"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3min"), ec::fractional_timespan);
CAF_CHECK_EQUAL(p("123ss"), ec::trailing_character);
CAF_CHECK_EQUAL(p("123m"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("123mi"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("123u"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("123n"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("12.3s"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3m"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ms"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3n"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3ns"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3mi"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("12.3min"), pec::fractional_timespan);
CAF_CHECK_EQUAL(p("123ss"), pec::trailing_character);
CAF_CHECK_EQUAL(p("123m"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123mi"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123u"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("123n"), pec::unexpected_eof);
}
CAF_TEST_FIXTURE_SCOPE_END()
......
......@@ -28,8 +28,6 @@
using namespace caf;
using detail::parser::ec;
namespace {
struct string_parser_consumer {
......@@ -39,7 +37,7 @@ struct string_parser_consumer {
}
};
using res_t = variant<ec, std::string>;
using res_t = variant<pec, std::string>;
struct string_parser {
res_t operator()(std::string str) {
......@@ -48,7 +46,7 @@ struct string_parser {
res.i = str.begin();
res.e = str.end();
detail::parser::read_string(res, f);
if (res.code == ec::success)
if (res.code == pec::success)
return f.x;
return res.code;
}
......@@ -93,11 +91,11 @@ CAF_TEST(string with escaped characters) {
}
CAF_TEST(invalid strings) {
CAF_CHECK_EQUAL(p(R"("abc)"), ec::unexpected_eof);
CAF_CHECK_EQUAL(p("\"ab\nc\""), ec::unexpected_newline);
CAF_CHECK_EQUAL(p(R"("foo \i bar")"), ec::illegal_escape_sequence);
CAF_CHECK_EQUAL(p(R"(foo)"), ec::unexpected_character);
CAF_CHECK_EQUAL(p(R"("abc" def)"), ec::trailing_character);
CAF_CHECK_EQUAL(p(R"("abc)"), pec::unexpected_eof);
CAF_CHECK_EQUAL(p("\"ab\nc\""), pec::unexpected_newline);
CAF_CHECK_EQUAL(p(R"("foo \i bar")"), pec::illegal_escape_sequence);
CAF_CHECK_EQUAL(p(R"(foo)"), pec::unexpected_character);
CAF_CHECK_EQUAL(p(R"("abc" def)"), pec::trailing_character);
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment