Commit 44c0ca24 authored by Sebastian Woelke's avatar Sebastian Woelke

Fix missing actor_system_config custom type

parent ff2ed53e
......@@ -89,6 +89,7 @@
_Pragma("clang diagnostic ignored \"-Wunused-parameter\"") \
_Pragma("clang diagnostic ignored \"-Wnested-anon-types\"") \
_Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") \
_Pragma("clang diagnostic ignored \"-Wconstant-conversion\"") \
_Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"") \
_Pragma("clang diagnostic ignored \"-Wused-but-marked-unused\"") \
_Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"")
......
......@@ -28,6 +28,7 @@
#include <functional>
#include "caf/atom.hpp"
#include "caf/error.hpp"
#include "caf/message.hpp"
#include "caf/variant.hpp"
#include "caf/config_value.hpp"
......@@ -38,10 +39,18 @@
namespace caf {
extern const char* type_name_visitor_tbl[];
/// Helper class to generate config readers for different input types.
class config_option {
public:
using config_reader_sink = std::function<void (size_t, config_value&)>;
using config_reader_sink = std::function<void (size_t, config_value&,
optional<std::ostream&>)>;
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>;
config_option(const char* category, const char* name, const char* explanation);
......@@ -76,42 +85,64 @@ public:
virtual message::cli_arg to_cli_arg(bool use_caf_prefix = false) = 0;
/// Returns a human-readable type name for the visited type.
struct type_name_visitor : static_visitor<const char*> {
const char* operator()(const std::string&) const;
const char* operator()(double) const;
const char* operator()(int64_t) const;
const char* operator()(size_t) const;
const char* operator()(uint16_t) const;
const char* operator()(bool) const;
const char* operator()(atom_value) const;
};
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)];
}
protected:
// 32-bit platforms
private:
template <class T>
static typename std::enable_if<sizeof(T) == sizeof(uint32_t), bool>::type
unsigned_assign_in_range(T&, int64_t& x) {
return x <= std::numeric_limits<T>::max();
static constexpr int idx(std::false_type /* is_integer */) {
return detail::tl_index_of<legal_types, T>::value;
}
// 64-bit platforms
template <class T>
static typename std::enable_if<sizeof(T) == sizeof(uint64_t), bool>::type
unsigned_assign_in_range(T&, int64_t&) {
return true;
static constexpr int idx(std::true_type /* is_integer */) {
using squashed = detail::squashed_int_t<T>;
return detail::tl_index_of<legal_types, squashed>::value;
}
};
protected:
template <class T, class U>
static bool assign_config_value(T& x, U& y) {
x = std::move(y);
return true;
}
static bool assign_config_value(size_t& x, int64_t& y);
template <class T>
static bool assign_config_value(T& x, int64_t& y) {
if (y < static_cast<int64_t>(std::numeric_limits<T>::lowest())
|| y > static_cast<int64_t>(std::numeric_limits<T>::max()))
return false;
x = static_cast<T>(y);
return true;
}
static bool assign_config_value(uint16_t& x, int64_t& y);
static bool assign_config_value(uint64_t& x, int64_t& y) {
if (y < 0)
return false;
x = static_cast<uint64_t>(y);
return true;
}
void report_type_error(size_t line, config_value& x, const char* expected);
static bool assign_config_value(float& x, double& y) {
if (y < static_cast<double>(std::numeric_limits<float>::lowest())
|| y > static_cast<double>(std::numeric_limits<float>::max()))
return false;
x = static_cast<float>(y);
return true;
}
void report_type_error(size_t line, config_value& x, const char* expected,
optional<std::ostream&> errors);
private:
const char* category_;
......@@ -151,18 +182,23 @@ public:
}
config_reader_sink to_sink() override {
return [=](size_t ln, config_value& x) {
return [=](size_t ln, config_value& x, optional<std::ostream&> errors) {
// the INI parser accepts all integers as int64_t
// and all floating point numbers as doubles
using cfg_type =
typename std::conditional<
std::is_integral<T>::value && !std::is_same<bool, T>::value,
int64_t,
typename std::conditional<
std::is_floating_point<T>::value,
double,
T
>::type
>::type;
if (get<cfg_type>(&x) && assign_config_value(ref_, get<cfg_type>(x)))
return;
type_name_visitor tnv;
report_type_error(ln, x, tnv(ref_));
report_type_error(ln, x, tnv(ref_), errors);
};
}
......@@ -202,14 +238,19 @@ public:
}
config_reader_sink to_sink() override {
return [=](size_t ln, config_value& x) {
return [=](size_t ln, config_value& x, optional<std::ostream&> errors) {
// the INI parser accepts all integers as int64_t
// and all floating point numbers as doubles
using cfg_type =
typename std::conditional<
std::is_integral<value_type>::value
&& !std::is_same<bool, value_type>::value,
std::is_integral<value_type>::value &&
!std::is_same<bool, value_type>::value,
int64_t,
typename std::conditional<
std::is_floating_point<value_type>::value,
double,
value_type
>::type
>::type;
value_type tmp;
if (get<cfg_type>(&x) && assign_config_value(tmp, get<cfg_type>(x))) {
......@@ -217,7 +258,7 @@ public:
return;
}
type_name_visitor tnv;
report_type_error(ln, x, tnv(tmp));
report_type_error(ln, x, tnv(tmp), errors);
};
}
......
......@@ -33,8 +33,11 @@ namespace caf {
namespace detail {
struct parse_ini_t {
/// Denotes an optional error output stream
using opt_err = optional<std::ostream&>;
/// Denotes a callback for consuming configuration values.
using config_consumer = std::function<void (size_t, std::string, config_value&)>;
using config_consumer = std::function<void (size_t, std::string,
config_value&, opt_err)>;
/// Parse the given input stream as INI formatted data and
/// calls the consumer with every key-value pair.
......@@ -43,7 +46,7 @@ struct parse_ini_t {
/// @param consumer Callback consuming generated key-value pairs.
void operator()(std::istream& raw_data,
config_consumer consumer,
optional<std::ostream&> errors = none) const;
opt_err errors = none) const;
};
......
......@@ -35,7 +35,8 @@ using option_vector = actor_system_config::option_vector;
class actor_system_config_reader {
public:
using sink = std::function<void (size_t, config_value&)>;
using sink = std::function<void (size_t, config_value&,
optional<std::ostream&>)>;
actor_system_config_reader(option_vector& xs, option_vector& ys) {
add_opts(xs);
......@@ -50,7 +51,7 @@ public:
void operator()(size_t ln, std::string name, config_value& cv) {
auto i = sinks_.find(name);
if (i != sinks_.end())
(i->second)(ln, cv);
(i->second)(ln, cv, none);
else
std::cerr << "error in line " << ln
<< ": unrecognized parameter name \"" << name << "\"";
......@@ -213,7 +214,8 @@ actor_system_config& actor_system_config::parse(message& args,
// (2) content of the INI file overrides hard-coded defaults
if (ini.good()) {
actor_system_config_reader consumer{options_, custom_options_};
auto f = [&](size_t ln, std::string str, config_value& x) {
auto f = [&](size_t ln, std::string str,
config_value& x, optional<std::ostream&>) {
consumer(ln, std::move(str), x);
};
detail::parse_ini(ini, f, std::cerr);
......@@ -310,7 +312,7 @@ actor_system_config& actor_system_config::set(const char* cn, config_value cv) {
full_name += x->name();
if (full_name == cn) {
auto f = x->to_sink();
f(0, cv);
f(0, cv, none);
}
}
return *this;
......
......@@ -23,6 +23,22 @@
namespace caf {
const char* type_name_visitor_tbl[] {
"a boolean",
"a float",
"a double",
"a string",
"an atom_value",
"an 8-bit integer",
"an 8-bit unsigned integer",
"a 16-bit integer",
"a 16-bit unsigned integer",
"a 32-bit integer",
"a 32-bit unsigned integer",
"a 64-bit integer",
"a 64-bit unsigned integer"
};
config_option::config_option(const char* cat, const char* nm, const char* expl)
: category_(cat),
name_(nm),
......@@ -55,48 +71,15 @@ std::string config_option::full_name() const {
return res;
}
const char* config_option::type_name_visitor::operator()(const std::string&) const {
return "a string";
}
const char* config_option::type_name_visitor::operator()(double) const {
return "a double";
}
const char* config_option::type_name_visitor::operator()(int64_t) const {
return "an integer";
}
const char* config_option::type_name_visitor::operator()(size_t) const {
return "an unsigned integer";
}
const char* config_option::type_name_visitor::operator()(uint16_t) const {
return "an unsigned short integer";
}
const char* config_option::type_name_visitor::operator()(bool) const {
return "a boolean";
}
const char* config_option::type_name_visitor::operator()(atom_value) const {
return "an atom";
}
bool config_option::assign_config_value(size_t& x, int64_t& y) {
if (y < 0 || !unsigned_assign_in_range(x, y))
return false;
x = static_cast<size_t>(y);
return true;
}
bool config_option::assign_config_value(uint16_t& x, int64_t& y) {
if (y < 0 || y > std::numeric_limits<uint16_t>::max())
return false;
x = static_cast<uint16_t>(y);
return true;
}
void config_option::report_type_error(size_t ln, config_value& x,
const char* expected) {
const char* expected,
optional<std::ostream&> out) {
if (!out)
return;
type_name_visitor tnv;
std::cerr << "error in line " << ln << ": expected "
*out << "error in line " << ln << ": expected "
<< expected << " found "
<< apply_visitor(tnv, x) << std::endl;
<< apply_visitor(tnv, x) << '\n';
}
} // namespace caf
......@@ -28,10 +28,10 @@ namespace caf {
namespace detail {
void parse_ini_t::operator()(std::istream& input, config_consumer consumer_fun,
optional<std::ostream&> errors) const {
opt_err errors) const {
// wraps a temporary into an (lvalue) config_value and calls `consumer_fun`
auto consumer = [&](size_t ln, std::string name, config_value x) {
consumer_fun(ln, std::move(name), x);
consumer_fun(ln, std::move(name), x, errors);
};
std::string group;
std::string line;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* 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.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
//#elif defined(CAF_CLANG)
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wconstant-conversion"
#endif
using namespace caf;
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 T zero() { return 0; }
template<class T, class U, class V>
constexpr T overflow() {
// +2 is necessary as after an overflow unsigned integral numbers
// must differ from zero() in the tests
return static_cast<V>(std::numeric_limits<U>::max()) + 2;
}
template <class T, class U>
std::tuple<T, std::string> run_config_option(const T& init_value,
const U& test_value) {
std::stringstream ostr;
T output_value = init_value;
auto cv = config_value(test_value);
auto co = make_config_option(output_value, category, name, explanation);
auto f = co->to_sink();
f(line, cv, static_cast<std::ostream&>(ostr));
return std::make_tuple(output_value, ostr.str());
}
// only works with an integral types and double
template <class T, class U>
void check_num_general_usage() {
// check positive numbers
std::string error_str;
U test_value = 5;
T result;
std::tie(result, error_str) = run_config_option(zero<T>(), test_value);
CAF_CHECK_EQUAL(result, static_cast<T>(test_value));
CAF_CHECK(error_str.empty());
// check negative numbers
test_value = -5;
std::tie(result, error_str) = run_config_option(zero<T>(), test_value);
if (std::numeric_limits<T>::is_signed) {
CAF_CHECK_EQUAL(result, static_cast<T>(test_value));
CAF_CHECK(error_str.empty());
} else {
CAF_CHECK_EQUAL(result, zero<T>());
CAF_CHECK(!error_str.empty());
}
// check vector<T>
test_value = 5;
std::vector<T> vec_result;
std::tie(vec_result, error_str) =
run_config_option(std::vector<T>{}, test_value);
CAF_CHECK(!vec_result.empty());
if (!vec_result.empty()) {
CAF_CHECK_EQUAL(*vec_result.begin(), static_cast<T>(test_value));
}
}
// only works with an integral types (no doubles)
template <class T, class U>
void check_num_boundary_usage() {
std::string error_str;
T result;
U boundary_check = overflow<U,T,U>();
std::tie(result, error_str) = run_config_option(zero<T>(), boundary_check);
T tmp = overflow<T, T, U>();
CAF_CHECK_NOT_EQUAL(result, tmp);
CAF_CHECK_EQUAL(result, zero<T>());
CAF_CHECK(!error_str.empty());
}
// only works with an integral types (no doubles)
template <class T, class U>
void check_num_general_and_boundary_usage() {
check_num_general_usage<T, U>();
check_num_boundary_usage<T, U>();
}
// intended for atoms, strings, and bools
template <class T>
void check_non_num_general_usage(const T& init_value, const T& test_value) {
// general check
std::string error_str;
T result;
std::tie(result, error_str) = run_config_option(init_value, test_value);
CAF_CHECK_EQUAL(result, test_value);
// vector<T> check
std::vector<T> vec_result;
std::tie(vec_result, error_str) =
run_config_option(std::vector<T>{}, test_value);
CAF_CHECK(!vec_result.empty());
if (!vec_result.empty()) {
CAF_CHECK_EQUAL(*vec_result.begin(), test_value);
}
}
void check_non_num_general_usage(bool init_value, bool test_value) {
// general check
std::string error_str;
bool result;
std::tie(result, error_str) = run_config_option(init_value, test_value);
CAF_CHECK_EQUAL(result, test_value);
// vector<T> check
// emplace_back() in class cli_arg do not support <bool> until C++14
}
}
CAF_TEST(type_bool) {
check_non_num_general_usage(false, true);
}
CAF_TEST(type_int8_t) {
check_num_general_and_boundary_usage<int8_t, int64_t>();
}
CAF_TEST(type_uint8_t) {
check_num_general_and_boundary_usage<uint8_t, int64_t>();
}
CAF_TEST(type_int16_t) {
check_num_general_and_boundary_usage<int16_t, int64_t>();
}
CAF_TEST(type_uint16_t) {
check_num_general_and_boundary_usage<uint16_t, int64_t>();
}
CAF_TEST(type_int32_t) {
check_num_general_and_boundary_usage<int32_t, int64_t>();
}
CAF_TEST(type_uint32_t) {
check_num_general_and_boundary_usage<uint32_t, int64_t>();
}
CAF_TEST(type_uint64_t) {
check_num_general_usage<uint64_t, int64_t>();
}
CAF_TEST(type_int64_t) {
check_num_general_usage<int64_t, int64_t>();
}
CAF_TEST(type_float) {
check_num_general_usage<float, double>();
// check boundaries
std::string error_str;
float result;
float init_value = 0;
// *2 is required as +2 does not change the variable at this size anymore
double boundary_check = static_cast<double>(
std::numeric_limits<float>::max()) * 2;
std::tie(result, error_str) = run_config_option(init_value, boundary_check);
float float_inf = std::numeric_limits<float>::infinity();
// Unit test does not compare inf values correct until now
bool tmp = float_inf == result;
CAF_CHECK_NOT_EQUAL(tmp, true);
CAF_CHECK_EQUAL(result, init_value);
CAF_CHECK_EQUAL(error_str.empty(), false);
}
CAF_TEST(type_double) {
check_num_general_usage<double, double>();
}
CAF_TEST(type_string) {
check_non_num_general_usage<std::string>("", "test string");
}
CAF_TEST(type_atom) {
// TODO: in class cli_arg std::istringstream do not support atom_value
// check_non_num_general_usage<atom_value>(atom(""), atom("test atom"));
}
template <class T>
std::string v(const T& x) {
config_option::type_name_visitor v;
return v(x);
}
CAF_TEST(type_names) {
CAF_CHECK_EQUAL(v(true), "a boolean");
CAF_CHECK_EQUAL(v(atom("")), "an atom_value");
CAF_CHECK_EQUAL(v(std::string{}), "a string");
CAF_CHECK_EQUAL(v(zero<float>()), "a float");
CAF_CHECK_EQUAL(v(zero<double>()), "a double");
CAF_CHECK_EQUAL(v(zero<int8_t>()), "an 8-bit integer");
CAF_CHECK_EQUAL(v(zero<uint8_t>()), "an 8-bit unsigned integer");
CAF_CHECK_EQUAL(v(zero<int16_t>()), "a 16-bit integer");
CAF_CHECK_EQUAL(v(zero<uint16_t>()), "a 16-bit unsigned integer");
CAF_CHECK_EQUAL(v(zero<int32_t>()), "a 32-bit integer");
CAF_CHECK_EQUAL(v(zero<uint32_t>()), "a 32-bit unsigned integer");
CAF_CHECK_EQUAL(v(zero<int64_t>()), "a 64-bit integer");
CAF_CHECK_EQUAL(v(zero<uint64_t>()), "a 64-bit unsigned integer");
}
......@@ -110,7 +110,7 @@ struct fixture {
std::stringstream ss;
std::stringstream err;
ss << str;
detail::parse_ini(ss, consumer, err);
detail::parse_ini(ss, consumer, static_cast<std::ostream&>(err));
split(errors, err.str(), is_any_of("\n"), token_compress_on);
}
......@@ -125,7 +125,8 @@ struct fixture {
},
ERROR_HANDLER
);
auto consume = [&](size_t, std::string key, config_value& value) {
auto consume = [&](size_t, std::string key, config_value& value,
optional<std::ostream&>) {
message_visitor mv;
anon_send(config_server, put_atom::value,
std::move(key), apply_visitor(mv, value));
......@@ -134,7 +135,8 @@ struct fixture {
}
void load(const char* str) {
auto consume = [&](size_t, std::string key, config_value& value) {
auto consume = [&](size_t, std::string key, config_value& value,
optional<std::ostream&>) {
values.emplace(std::move(key), std::move(value));
};
load_impl(consume, str);
......
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