Commit 5071ccb3 authored by Dominik Charousset's avatar Dominik Charousset

Implement new config_value::parse_msg utility

parent d3b21231
...@@ -114,13 +114,21 @@ public: ...@@ -114,13 +114,21 @@ public:
// -- parsing ---------------------------------------------------------------- // -- parsing ----------------------------------------------------------------
/// Tries to parse a value from `str`. /// Tries to parse a value from given characters.
static expected<config_value> parse(string_view::iterator first, static expected<config_value> parse(string_view::iterator first,
string_view::iterator last); string_view::iterator last);
/// Tries to parse a value from `str`. /// Tries to parse a value from `str`.
static expected<config_value> parse(string_view str); static expected<config_value> parse(string_view str);
/// Tries to parse a config value (list) from `str` and then converting it to
/// an allowed input message type for `Handle`.
template <class Handle>
static optional<message> parse_msg(string_view str, const Handle&) {
auto allowed = Handle::allowed_inputs();
return parse_msg_impl(str, allowed);
}
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
/// Converts the value to a list with one element (unless the config value /// Converts the value to a list with one element (unless the config value
...@@ -228,6 +236,9 @@ private: ...@@ -228,6 +236,9 @@ private:
static const char* type_name_at_index(size_t index) noexcept; static const char* type_name_at_index(size_t index) noexcept;
static optional<message>
parse_msg_impl(string_view str, span<const type_id_list> allowed_types);
// -- auto conversion of related types --------------------------------------- // -- auto conversion of related types ---------------------------------------
void set(none_t) { void set(none_t) {
......
...@@ -88,6 +88,10 @@ public: ...@@ -88,6 +88,10 @@ public:
return begin() + size(); return begin() + size();
} }
/// Returns the number of bytes that a buffer needs to allocate for storing a
/// type-erased tuple for the element types stored in this list.
size_t data_size() const noexcept;
private: private:
pointer data_; pointer data_;
}; };
...@@ -110,3 +114,22 @@ constexpr type_id_list make_type_id_list() { ...@@ -110,3 +114,22 @@ constexpr type_id_list make_type_id_list() {
CAF_CORE_EXPORT std::string to_string(type_id_list xs); CAF_CORE_EXPORT std::string to_string(type_id_list xs);
} // namespace caf } // namespace caf
namespace caf::detail {
template <class F>
struct argument_type_id_list_factory;
template <class R, class... Ts>
struct argument_type_id_list_factory<R(Ts...)> {
static type_id_list make() {
return make_type_id_list<Ts...>();
}
};
template <class F>
type_id_list make_argument_type_id_list() {
return argument_type_id_list_factory<F>::make();
}
} // namespace caf::detail
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "caf/make_actor.hpp" #include "caf/make_actor.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
#include "caf/stateful_actor.hpp" #include "caf/stateful_actor.hpp"
#include "caf/type_id_list.hpp"
#include "caf/typed_actor_view_base.hpp" #include "caf/typed_actor_view_base.hpp"
#include "caf/typed_behavior.hpp" #include "caf/typed_behavior.hpp"
#include "caf/typed_response_promise.hpp" #include "caf/typed_response_promise.hpp"
...@@ -246,6 +247,10 @@ public: ...@@ -246,6 +247,10 @@ public:
x.ptr_.reset(); x.ptr_.reset();
} }
static std::array<type_id_list, sizeof...(Sigs)> allowed_inputs() {
return {{detail::make_argument_type_id_list<Sigs>()...}};
}
/// @endcond /// @endcond
private: private:
......
...@@ -422,6 +422,43 @@ bool config_value::can_convert_to_dictionary() const { ...@@ -422,6 +422,43 @@ bool config_value::can_convert_to_dictionary() const {
return visit(f, data_); return visit(f, data_);
} }
optional<message>
config_value::parse_msg_impl(string_view str,
span<const type_id_list> allowed_types) {
if (auto val = parse(str)) {
auto ls_size = val->as_list().size();
message result;
auto converts = [&val, &result, ls_size](type_id_list ls) {
if (ls.size() != ls_size)
return false;
config_value_reader reader{std::addressof(*val)};
auto unused = size_t{0};
reader.begin_sequence(unused);
CAF_ASSERT(unused == ls_size);
intrusive_ptr<detail::message_data> ptr;
if (auto vptr = malloc(sizeof(detail::message_data) + ls.data_size()))
ptr.reset(new (vptr) detail::message_data(ls), false);
else
return false;
auto pos = ptr->storage();
for (auto type : ls) {
auto meta = detail::global_meta_object(type);
CAF_ASSERT(meta != nullptr);
meta->default_construct(pos);
ptr->inc_constructed_elements();
if (!meta->load(reader, pos))
return false;
pos += meta->padded_size;
}
result.reset(ptr.release(), false);
return reader.end_sequence();
};
if (std::any_of(allowed_types.begin(), allowed_types.end(), converts))
return {std::move(result)};
}
return {};
}
// -- related free functions --------------------------------------------------- // -- related free functions ---------------------------------------------------
bool operator<(const config_value& x, const config_value& y) { bool operator<(const config_value& x, const config_value& y) {
......
...@@ -22,6 +22,15 @@ ...@@ -22,6 +22,15 @@
namespace caf { namespace caf {
size_t type_id_list::data_size() const noexcept {
auto result = size_t{0};
for (auto type : *this) {
auto meta_obj = detail::global_meta_object(type);
result += meta_obj->padded_size;
}
return result;
}
std::string to_string(type_id_list xs) { std::string to_string(type_id_list xs) {
if (!xs || xs.size() == 0) if (!xs || xs.size() == 0)
return "[]"; return "[]";
......
...@@ -745,6 +745,61 @@ SCENARIO("config values can convert lists of tuples to dictionaries") { ...@@ -745,6 +745,61 @@ SCENARIO("config values can convert lists of tuples to dictionaries") {
} }
} }
SCENARIO("config values can parse messages") {
using testee_t = typed_actor<result<void>(int16_t), //
result<void>(int32_t, int32_t), //
result<void>(my_request), //
result<void>(add_atom, int32_t, int32_t)>; //
auto parse = [](string_view str) {
testee_t testee;
return config_value::parse_msg(str, testee);
};
GIVEN("a typed actor handle and valid input strings") {
THEN("config_value::parse_msg generates matching message types") {
if (auto msg = parse("16000"); CHECK(msg)) {
if (CHECK((msg->match_elements<int16_t>()))) {
CHECK_EQ(msg->get_as<int16_t>(0), 16000);
}
}
if (auto msg = parse("[16000]"); CHECK(msg)) {
if (CHECK((msg->match_elements<int16_t>()))) {
CHECK_EQ(msg->get_as<int16_t>(0), 16000);
}
}
if (auto msg = parse("[1, 2]"); CHECK(msg)) {
if (CHECK((msg->match_elements<int32_t, int32_t>()))) {
CHECK_EQ(msg->get_as<int32_t>(0), 1);
CHECK_EQ(msg->get_as<int32_t>(1), 2);
}
}
if (auto msg = parse("{a = 1, b = 2}"); CHECK(msg)) {
if (CHECK((msg->match_elements<my_request>()))) {
CHECK_EQ(msg->get_as<my_request>(0), my_request(1, 2));
}
}
if (auto msg = parse("[{a = 1, b = 2}]"); CHECK(msg)) {
if (CHECK((msg->match_elements<my_request>()))) {
CHECK_EQ(msg->get_as<my_request>(0), my_request(1, 2));
}
}
if (auto msg = parse(R"_([{"@type" = "caf::add_atom"}, 1, 2])_");
CHECK(msg)) {
if (CHECK((msg->match_elements<add_atom, int32_t, int32_t>()))) {
CHECK_EQ(msg->get_as<int32_t>(1), 1);
CHECK_EQ(msg->get_as<int32_t>(2), 2);
}
}
}
}
GIVEN("a typed actor handle and invalid input strings") {
THEN("config_value::parse_msg returns nullopt") {
CHECK(!parse("65000"));
CHECK(!parse("[1, 2, 3]"));
CHECK(!parse("[{a = 1.1, b = 2.2}]"));
}
}
}
CAF_TEST(default_constructed) { CAF_TEST(default_constructed) {
config_value x; config_value x;
CAF_CHECK_EQUAL(holds_alternative<none_t>(x), true); CAF_CHECK_EQUAL(holds_alternative<none_t>(x), true);
......
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