Commit ea4fb677 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Support lists and durations in config_value

parent 2ac0ae32
...@@ -37,6 +37,7 @@ set(LIBCAF_CORE_SRCS ...@@ -37,6 +37,7 @@ set(LIBCAF_CORE_SRCS
src/blocking_behavior.cpp src/blocking_behavior.cpp
src/concatenated_tuple.cpp src/concatenated_tuple.cpp
src/config_option.cpp src/config_option.cpp
src/config_value.cpp
src/decorated_tuple.cpp src/decorated_tuple.cpp
src/default_attachable.cpp src/default_attachable.cpp
src/deserializer.cpp src/deserializer.cpp
......
...@@ -238,7 +238,10 @@ public: ...@@ -238,7 +238,10 @@ public:
} }
/// Sets a config by using its INI name `config_name` to `config_value`. /// Sets a config by using its INI name `config_name` to `config_value`.
actor_system_config& set(const char* cn, config_value cv); 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 --------------------------------------------------- // -- parser and CLI state ---------------------------------------------------
...@@ -413,6 +416,8 @@ private: ...@@ -413,6 +416,8 @@ private:
&make_type_erased_value<T>); &make_type_erased_value<T>);
} }
actor_system_config& set_impl(const char* cn, config_value cv);
static std::string render_sec(uint8_t, atom_value, const message&); static std::string render_sec(uint8_t, atom_value, const message&);
static std::string render_exit_reason(uint8_t, atom_value, const message&); static std::string render_exit_reason(uint8_t, atom_value, const message&);
......
...@@ -48,7 +48,7 @@ public: ...@@ -48,7 +48,7 @@ public:
using legal_types = detail::type_list<bool, float, double, std::string, using legal_types = detail::type_list<bool, float, double, std::string,
atom_value, int8_t, uint8_t, int16_t, atom_value, int8_t, uint8_t, int16_t,
uint16_t, int32_t, uint32_t, int64_t, uint16_t, int32_t, uint32_t, int64_t,
uint64_t>; uint64_t, duration>;
config_option(const char* cat, const char* nm, const char* expl); config_option(const char* cat, const char* nm, const char* expl);
...@@ -95,6 +95,18 @@ public: ...@@ -95,6 +95,18 @@ public:
return type_name_visitor_tbl[static_cast<size_t>(index)]; return type_name_visitor_tbl[static_cast<size_t>(index)];
} }
const char* operator()(const std::vector<config_value>&) {
return "a list";
}
const char* operator()(const std::map<std::string, config_value>&) {
return "a map";
}
const char* operator()(const timespan&) {
return "a timespan";
}
private: private:
// Catches non-integer types. // Catches non-integer types.
template <class T> template <class T>
...@@ -147,6 +159,12 @@ protected: ...@@ -147,6 +159,12 @@ protected:
return true; return true;
} }
template <class T>
static bool assign_config_value(std::vector<T>&,
std::vector<config_value>&) {
// TODO: implement me
}
void report_type_error(size_t ln, config_value& x, const char* expected, void report_type_error(size_t ln, config_value& x, const char* expected,
optional<std::ostream&> out); optional<std::ostream&> out);
...@@ -250,13 +268,13 @@ public: ...@@ -250,13 +268,13 @@ public:
// and all floating point numbers as doubles // and all floating point numbers as doubles
using cfg_type = using cfg_type =
typename std::conditional< typename std::conditional<
std::is_integral<value_type>::value && std::is_integral<value_type>::value &&
!std::is_same<bool, value_type>::value, !std::is_same<bool, value_type>::value,
int64_t, int64_t,
typename std::conditional< typename std::conditional<
std::is_floating_point<value_type>::value, std::is_floating_point<value_type>::value,
double, double,
value_type value_type
>::type >::type
>::type; >::type;
value_type tmp; value_type tmp;
......
...@@ -18,16 +18,222 @@ ...@@ -18,16 +18,222 @@
#pragma once #pragma once
#include <string> #include <chrono>
#include <cstdint> #include <cstdint>
#include <map>
#include <string>
#include <vector>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/timestamp.hpp"
#include "caf/variant.hpp" #include "caf/variant.hpp"
#include "caf/detail/comparable.hpp"
namespace caf { namespace caf {
/// A variant type for config parameters. /// A type for config parameters with similar interface to a `variant`. This
using config_value = variant<std::string, double, int64_t, bool, atom_value>; /// type is not implemented as a simple variant alias because variants cannot
/// contain lists of themselves.
class config_value {
public:
using T0 = int64_t;
using T1 = bool;
using T2 = double;
using T3 = atom_value;
using T4 = timespan;
using T5 = std::string;
using T6 = std::vector<config_value>;
using T7 = std::map<std::string, config_value>;
using type0 = T0;
using types = detail::type_list<T0, T1, T2, T3, T4, T5, T6, T7>;
using variant_type = variant<T0, T1, T2, T3, T4, T5, T6, T7>;
config_value() = default;
config_value(config_value&& other) = default;
config_value(const config_value& other) = default;
template <class T, class E = detail::enable_if_t<
!std::is_same<detail::decay_t<T>, config_value>::value>>
explicit config_value(T&& x) {
set(std::forward<T>(x));
}
config_value& operator=(config_value&& other) = default;
config_value& operator=(const config_value& other) = default;
template <class T, class E = detail::enable_if_t<
!std::is_same<detail::decay_t<T>, config_value>::value>>
config_value& operator=(T&& x) {
set(std::forward<T>(x));
return *this;
}
~config_value();
/// Converts the value to a list with one element. Does nothing if the value
/// already is a list.
void convert_to_list();
/// Appends `x` to a list. Converts this config value to a list first by
/// calling `convert_to_list` if needed.
void append(config_value x);
inline size_t index() const {
return data_.index();
}
inline bool valueless_by_exception() const {
return data_.valueless_by_exception();
}
template <class Visitor>
auto apply(Visitor&& visitor) -> decltype(visitor(std::declval<T0&>())) {
return data_.apply(visitor);
}
template <class Visitor>
auto apply(Visitor&& visitor) const
-> decltype(visitor(std::declval<const T0&>())) {
return data_.apply(visitor);
}
template <int Pos>
const typename detail::tl_at<types, Pos>::type&
get(std::integral_constant<int, Pos> token) const {
return data_.get(token);
}
template <int Pos>
typename detail::tl_at<types, Pos>::type&
get(std::integral_constant<int, Pos> token) {
return data_.get(token);
}
inline variant_type& data() {
return data_;
}
inline const variant_type& data() const {
return data_;
}
/// @endcond
private:
// -- auto conversion of related types ---------------------------------------
inline void set(bool x) {
data_ = x;
}
inline void set(const char* x) {
data_ = std::string{x};
}
template <class T>
detail::enable_if_t<
detail::is_one_of<detail::decay_t<T>, T2, T3, T4, T5, T6, T7>::value>
set(T&& x) {
data_ = std::forward<T>(x);
}
template <class T>
detail::enable_if_t<std::is_integral<T>::value> set(T x) {
data_ = static_cast<int64_t>(x);
}
inline void convert(timespan x) {
data_ = x;
}
template <class Rep, class Ratio>
void convert(std::chrono::duration<Rep, Ratio> x) {
data_ = std::chrono::duration_cast<timespan>(x);
}
// -- member variables -------------------------------------------------------
variant_type data_;
};
/// @relates config_value
bool operator<(const config_value& x, const config_value& y);
/// @relates config_value
bool operator==(const config_value& x, const config_value& y);
/// @relates config_value
inline bool operator>=(const config_value& x, const config_value& y) {
return !(x < y);
}
/// @relates config_value
inline bool operator!=(const config_value& x, const config_value& y) {
return !(x == y);
}
/// @relates config_value
template <class Visitor>
auto visit(Visitor&& f, config_value& x)
-> decltype(f(std::declval<int64_t&>())) {
return visit(std::forward<Visitor>(f), x.data());
}
/// @relates config_value
template <class Visitor>
auto visit(Visitor&& f, const config_value& x)
-> decltype(f(std::declval<const int64_t&>())) {
return visit(std::forward<Visitor>(f), x.data());
}
/// @relates config_value
template <class T>
bool holds_alternative(const config_value& x) {
return holds_alternative<T>(x.data());
}
/// @relates config_value
template <class T>
T& get(config_value& x) {
return get<T>(x.data());
}
/// @relates config_value
template <class T>
const T& get(const config_value& x) {
return get<T>(x.data());
}
/// @relates config_value
template <class T>
T* get_if(config_value* x) {
return x != nullptr ? get_if<T>(&(x->data())) : nullptr;
}
/// @relates config_value
template <class T>
const T* get_if(const config_value* x) {
return x != nullptr ? get_if<T>(&(x->data())) : nullptr;
}
/// @relates config_value
inline std::string to_string(const config_value& x) {
return deep_to_string(x.data());
}
/// @relates config_value
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, config_value& x) {
return f(meta::type_name("config_value"), x.data());
}
} // namespace caf } // namespace caf
...@@ -419,7 +419,8 @@ actor_system_config::add_error_category(atom_value x, error_renderer y) { ...@@ -419,7 +419,8 @@ actor_system_config::add_error_category(atom_value x, error_renderer y) {
return *this; return *this;
} }
actor_system_config& actor_system_config::set(const char* cn, config_value cv) { actor_system_config& actor_system_config::set_impl(const char* cn,
config_value cv) {
auto e = options_.end(); auto e = options_.end();
auto i = std::find_if(options_.begin(), e, [cn](const option_ptr& ptr) { auto i = std::find_if(options_.begin(), e, [cn](const option_ptr& ptr) {
return ptr->full_name() == cn; return ptr->full_name() == cn;
......
...@@ -35,7 +35,8 @@ const char* type_name_visitor_tbl[] { ...@@ -35,7 +35,8 @@ const char* type_name_visitor_tbl[] {
"a 32-bit integer", "a 32-bit integer",
"a 32-bit unsigned integer", "a 32-bit unsigned integer",
"a 64-bit integer", "a 64-bit integer",
"a 64-bit unsigned integer" "a 64-bit unsigned integer",
"a duration"
}; };
config_option::config_option(const char* cat, const char* nm, const char* expl) config_option::config_option(const char* cat, const char* nm, const char* expl)
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2017 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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_value.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace {
struct less_comparator {
template <class T, class U>
detail::enable_if_t<std::is_same<T, U>::value, bool>
operator()(const T& x, const U& y) const {
return x < y;
}
template <class T, class U>
detail::enable_if_t<!std::is_same<T, U>::value, bool>
operator()(const T&, const U&) const {
// Sort by type index when comparing different types.
using namespace detail;
using types = config_value::types;
return tl_index_of<types, T>::value < tl_index_of<types, U>::value;
}
};
struct equal_comparator {
template <class T, class U>
detail::enable_if_t<std::is_same<T, U>::value, bool>
operator()(const T& x, const U& y) const {
return x == y;
}
template <class T, class U>
detail::enable_if_t<!std::is_same<T, U>::value, bool>
operator()(const T&, const U&) const {
return false;
}
};
} // namespace <anonymous>
config_value::~config_value() {
// nop
}
void config_value::convert_to_list() {
if (holds_alternative<T6>(data_))
return;
config_value tmp{std::move(*this)};
set(T6{std::move(tmp)});
}
void config_value::append(config_value x) {
convert_to_list();
auto& xs = caf::get<T6>(data_);
xs.emplace_back(std::move(x));
}
bool operator<(const config_value& x, const config_value& y) {
less_comparator cmp;
return visit(cmp, x.data(), y.data());
}
bool operator==(const config_value& x, const config_value& y) {
equal_comparator cmp;
return visit(cmp, x.data(), y.data());
}
} // namespace caf
...@@ -26,12 +26,27 @@ ...@@ -26,12 +26,27 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
void parse_ini_t::operator()(std::istream& input, const config_consumer& consumer_fun, namespace {
// TODO: replace with generic lambda when switching to C++14
// Wraps a temporary into an (lvalue) config_value and calls `consumer_fun`.
struct consumer_t {
const parse_ini_t::config_consumer& consumer_fun;
parse_ini_t::opt_err& errors;
template <class T>
void operator()(size_t ln, std::string name, T&& x) {
config_value tmp{std::forward<T>(x)};
consumer_fun(ln, std::move(name), tmp, errors);
}
};
} // namespace <anonymous>
void parse_ini_t::operator()(std::istream& input,
const config_consumer& consumer_fun,
opt_err errors) const { opt_err errors) const {
// wraps a temporary into an (lvalue) config_value and calls `consumer_fun` consumer_t consumer{consumer_fun, errors};
auto consumer = [&](size_t ln, std::string name, config_value x) {
consumer_fun(ln, std::move(name), x, errors);
};
std::string group; std::string group;
std::string line; std::string line;
size_t ln = 0; // line number size_t ln = 0; // line number
...@@ -90,14 +105,12 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume ...@@ -90,14 +105,12 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume
// begin-of-value, ignoring whitespaces after '=' // begin-of-value, ignoring whitespaces after '='
auto bov = find_if_not(eqs + 1, eol, ::isspace); auto bov = find_if_not(eqs + 1, eol, ::isspace);
// auto-detect what we are dealing with // auto-detect what we are dealing with
constexpr const char* true_str = "true";
constexpr const char* false_str = "false";
auto icase_eq = [](char x, char y) { auto icase_eq = [](char x, char y) {
return tolower(x) == tolower(y); return tolower(x) == tolower(y);
}; };
if (std::equal(bov, eol, true_str, icase_eq)) { if (std::equal(bov, eol, "true", icase_eq)) {
consumer(ln, std::move(key), true); consumer(ln, std::move(key), true);
} else if (std::equal(bov, eol, false_str, icase_eq)) { } else if (std::equal(bov, eol, "false", icase_eq)) {
consumer(ln, std::move(key), false); consumer(ln, std::move(key), false);
} else if (*bov == '\'') { } else if (*bov == '\'') {
// end-of-atom iterator // end-of-atom iterator
...@@ -189,6 +202,7 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume ...@@ -189,6 +202,7 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume
else else
consumer(ln, std::move(key), is_neg ? -res : res); consumer(ln, std::move(key), is_neg ? -res : res);
}; };
// check for number prefixes (0 for octal, 0x for hex, 0b for binary)
if (*bov == '0' && std::distance(bov, eol) > 1) if (*bov == '0' && std::distance(bov, eol) > 1)
switch (*(bov + 1)) { switch (*(bov + 1)) {
case 'x': case 'x':
...@@ -205,10 +219,13 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume ...@@ -205,10 +219,13 @@ void parse_ini_t::operator()(std::istream& input, const config_consumer& consume
else else
set_dval(); set_dval();
} }
else if (all_of(bov, eol, ::isdigit)) else {
set_ival(10, 0, "invalid decimal value"); // no suffix, try parsing the line as integer or double
else if (all_of(bov, eol, ::isdigit))
set_dval(); set_ival(10, 0, "invalid decimal value");
else
set_dval();
}
} }
} }
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 config_value
#include "caf/test/unit_test.hpp"
#include <string>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/none.hpp"
#include "caf/variant.hpp"
using namespace std;
using namespace caf;
struct tostring_visitor : static_visitor<string> {
template <class T>
inline string operator()(const T& value) {
return to_string(value);
}
};
CAF_TEST(default_constructed) {
config_value x;
CAF_CHECK_EQUAL(holds_alternative<int64_t>(x), true);
CAF_CHECK_EQUAL(get<int64_t>(x), 0);
}
CAF_TEST(list) {
std::vector<config_value> xs;
xs.emplace_back(int64_t{1});
xs.emplace_back(atom("foo"));
xs.emplace_back(string("bar"));
config_value x{xs};
CAF_CHECK_EQUAL(to_string(x), "[1, 'foo', \"bar\"]");
auto ys = xs;
xs.emplace_back(std::move(ys));
x = xs;
CAF_CHECK_EQUAL(to_string(x), "[1, 'foo', \"bar\", [1, 'foo', \"bar\"]]");
}
CAF_TEST(convert_to_list) {
config_value x{int64_t{42}};
CAF_CHECK_EQUAL(to_string(x), "42");
x.convert_to_list();
CAF_CHECK_EQUAL(to_string(x), "[42]");
x.convert_to_list();
CAF_CHECK_EQUAL(to_string(x), "[42]");
}
CAF_TEST(append) {
config_value x{int64_t{1}};
CAF_CHECK_EQUAL(to_string(x), "1");
x.append(config_value{int64_t{2}});
CAF_CHECK_EQUAL(to_string(x), "[1, 2]");
x.append(config_value{atom("foo")});
CAF_CHECK_EQUAL(to_string(x), "[1, 2, 'foo']");
}
CAF_TEST(maps) {
std::map<std::string, config_value> xs;
xs["num"] = int64_t{42};
xs["atm"] = atom("hello");
xs["str"] = string{"foobar"};
xs["dur"] = timespan{100};
config_value x{xs};
CAF_CHECK_EQUAL(
to_string(x),
R"([("atm", 'hello'), ("dur", 100ns), ("num", 42), ("str", "foobar")])");
}
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