Commit ddb7719e authored by Dominik Charousset's avatar Dominik Charousset

Allow treating config_value as a tuple

parent d771808b
...@@ -24,11 +24,13 @@ ...@@ -24,11 +24,13 @@
#include <iterator> #include <iterator>
#include <map> #include <map>
#include <string> #include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/bounds_checker.hpp" #include "caf/detail/bounds_checker.hpp"
#include "caf/detail/move_if_not_ptr.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/dictionary.hpp" #include "caf/dictionary.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -387,6 +389,77 @@ struct config_value_access<std::vector<T>> { ...@@ -387,6 +389,77 @@ struct config_value_access<std::vector<T>> {
} }
}; };
/// Implements automagic unboxing of `std::tuple<Ts...>` from a heterogeneous
///`config_value::list`.
/// @relates config_value
template <class... Ts>
struct config_value_access<std::tuple<Ts...>> {
using tuple_type = std::tuple<Ts...>;
static constexpr bool specialized = true;
static bool is(const config_value& x) {
if (auto lst = caf::get_if<config_value::list>(&x)) {
if (lst->size() != sizeof...(Ts))
return false;
return rec_is(*lst, detail::int_token<0>(), detail::type_list<Ts...>());
}
return false;
}
static optional<tuple_type> get_if(const config_value* x) {
if (auto lst = caf::get_if<config_value::list>(x)) {
if (lst->size() != sizeof...(Ts))
return none;
tuple_type result;
if (rec_get(*lst, result, detail::int_token<0>(),
detail::type_list<Ts...>()))
return result;
}
return none;
}
static tuple_type get(const config_value& x) {
if (auto result = get_if(&x))
return std::move(*result);
CAF_RAISE_ERROR("invalid type found");
}
private:
template <int Pos>
static bool rec_is(const config_value::list&, detail::int_token<Pos>,
detail::type_list<>) {
// End of recursion.
return true;
}
template <int Pos, class U, class... Us>
static bool rec_is(const config_value::list& xs, detail::int_token<Pos>,
detail::type_list<U, Us...>) {
if (!holds_alternative<U>(xs[Pos]))
return false;
return rec_is(xs, detail::int_token<Pos + 1>(), detail::type_list<Us...>());
}
template <int Pos>
static bool rec_get(const config_value::list&, tuple_type&,
detail::int_token<Pos>, detail::type_list<>) {
// End of recursion.
return true;
}
template <int Pos, class U, class... Us>
static bool rec_get(const config_value::list& xs, tuple_type& result,
detail::int_token<Pos>, detail::type_list<U, Us...>) {
if (auto value = caf::get_if<U>(&xs[Pos])) {
std::get<Pos>(result) = detail::move_if_not_ptr(value);
return rec_get(xs, result, detail::int_token<Pos + 1>(),
detail::type_list<Us...>());
}
return false;
}
};
/// Implements automagic unboxing of `dictionary<V>` from a homogeneous /// Implements automagic unboxing of `dictionary<V>` from a homogeneous
/// `config_value::dictionary`. /// `config_value::dictionary`.
/// @relates config_value /// @relates config_value
......
...@@ -129,7 +129,7 @@ CAF_TEST(timespan) { ...@@ -129,7 +129,7 @@ CAF_TEST(timespan) {
CAF_CHECK_NOT_EQUAL(get_if<timespan>(&x), nullptr); CAF_CHECK_NOT_EQUAL(get_if<timespan>(&x), nullptr);
} }
CAF_TEST(list) { CAF_TEST(homogeneous list) {
using integer_list = std::vector<int64_t>; using integer_list = std::vector<int64_t>;
auto xs = make_config_value_list(1, 2, 3); auto xs = make_config_value_list(1, 2, 3);
auto ys = config_value{integer_list{1, 2, 3}}; auto ys = config_value{integer_list{1, 2, 3}};
...@@ -141,6 +141,16 @@ CAF_TEST(list) { ...@@ -141,6 +141,16 @@ CAF_TEST(list) {
CAF_CHECK_EQUAL(get<integer_list>(xs), integer_list({1, 2, 3})); CAF_CHECK_EQUAL(get<integer_list>(xs), integer_list({1, 2, 3}));
} }
CAF_TEST(heterogeneous list) {
auto xs_value = make_config_value_list(1, "two", 3.0);
auto& xs = xs_value.as_list();
CAF_CHECK_EQUAL(xs_value.type_name(), "list"_s);
CAF_REQUIRE_EQUAL(xs.size(), 3u);
CAF_CHECK_EQUAL(xs[0], 1);
CAF_CHECK_EQUAL(xs[1], "two"_s);
CAF_CHECK_EQUAL(xs[2], 3.0);
}
CAF_TEST(convert_to_list) { CAF_TEST(convert_to_list) {
config_value x{int64_t{42}}; config_value x{int64_t{42}};
CAF_CHECK_EQUAL(x.type_name(), "integer"_s); CAF_CHECK_EQUAL(x.type_name(), "integer"_s);
...@@ -252,3 +262,24 @@ CAF_TEST(unsuccessful parsing) { ...@@ -252,3 +262,24 @@ CAF_TEST(unsuccessful parsing) {
CAF_CHECK_EQUAL(parse("{a=1,"), pec::unexpected_eof); CAF_CHECK_EQUAL(parse("{a=1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=1 b=2}"), pec::unexpected_character); CAF_CHECK_EQUAL(parse("{a=1 b=2}"), pec::unexpected_character);
} }
CAF_TEST(conversion to simple tuple) {
using tuple_type = std::tuple<size_t, std::string>;
config_value x{42};
x.as_list().emplace_back("hello world");
CAF_REQUIRE(holds_alternative<tuple_type>(x));
CAF_REQUIRE_NOT_EQUAL(get_if<tuple_type>(&x), none);
CAF_CHECK_EQUAL(get<tuple_type>(x),
std::make_tuple(size_t{42}, "hello world"_s));
}
CAF_TEST(conversion to nested tuple) {
using inner_tuple_type = std::tuple<int, int>;
using tuple_type = std::tuple<size_t, inner_tuple_type>;
config_value x{42};
x.as_list().emplace_back(make_config_value_list(2, 40));
CAF_REQUIRE(holds_alternative<tuple_type>(x));
CAF_REQUIRE_NOT_EQUAL(get_if<tuple_type>(&x), none);
CAF_CHECK_EQUAL(get<tuple_type>(x),
std::make_tuple(size_t{42}, std::make_tuple(2, 40)));
}
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