Commit e2981877 authored by Dominik Charousset's avatar Dominik Charousset

Add apply functions to inspectors, add docs

parent 5e8bd93a
......@@ -127,6 +127,12 @@ is based on [Keep a Changelog](https://keepachangelog.com).
*types*, but did not enforce it because the name was only used for logging.
Since the new metrics use this name for filtering now, we enforce static names
in order to help avoid hard-to-find issues with the filtering mechanism.
- The type inspection API received a complete overhaul. The new DSL for writing
`inspect` functions exposes the entire structure of an object to CAF. This
enables inspectors to read and write a wider range of data formats. In
particular human-readable, structured data such as configuration files, JSON,
XML, etc. The inspection API received too many changes to list them here.
Please refer to the manual section on type inspection instead.
### Removed
......@@ -183,7 +189,7 @@ is based on [Keep a Changelog](https://keepachangelog.com).
- Datagram servants of UDP socket managers were not added as children to their
parent broker on creation, which prevented proper system shutdown in some
cases. Adding all servants consistently to the broker should make sure UDP
brokers terminate correctly (#1133).
brokers terminate correctly (#1133).
## [0.17.6] - 2020-07-24
......
......@@ -94,14 +94,14 @@ void caf_main(actor_system& sys) {
binary_serializer::container_type buf;
// write f1 to buffer
binary_serializer sink{sys, buf};
if (!inspect_object(sink, f1)) {
if (!sink.apply_object(f1)) {
std::cerr << "*** failed to serialize foo2: " << to_string(sink.get_error())
<< '\n';
return;
}
// read f2 back from buffer
binary_deserializer source{sys, buf};
if (!inspect_object(source, f2)) {
if (!source.apply_object(f2)) {
std::cerr << "*** failed to deserialize foo2: "
<< to_string(source.get_error()) << '\n';
return;
......
......@@ -50,29 +50,28 @@ void copy_construct(void* ptr, const void* src) {
template <class T>
bool save_binary(binary_serializer& sink, const void* ptr) {
return inspect_object(sink, as_mutable_ref(*static_cast<const T*>(ptr)));
return sink.apply_object(*static_cast<const T*>(ptr));
}
template <class T>
bool load_binary(binary_deserializer& source, void* ptr) {
return inspect_object(source, *static_cast<T*>(ptr));
return source.apply_object(*static_cast<T*>(ptr));
}
template <class T>
bool save(serializer& sink, const void* ptr) {
return inspect_object(sink, as_mutable_ref(*static_cast<const T*>(ptr)));
return sink.apply_object(*static_cast<const T*>(ptr));
}
template <class T>
bool load(deserializer& source, void* ptr) {
return inspect_object(source, *static_cast<T*>(ptr));
return source.apply_object(*static_cast<T*>(ptr));
}
template <class T>
void stringify(std::string& buf, const void* ptr) {
stringification_inspector f{buf};
auto unused
= inspect_object(f, detail::as_mutable_ref(*static_cast<const T*>(ptr)));
auto unused = f.apply_object(*static_cast<const T*>(ptr));
static_cast<void>(unused);
}
......
......@@ -292,6 +292,7 @@ void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp,
// -- convenience functions ----------------------------------------------------
CAF_CORE_EXPORT
error parse_result(const string_parser_state& ps, string_view input);
template <class T>
......
......@@ -28,6 +28,7 @@
namespace caf::detail {
CAF_CORE_EXPORT
size_t print_timestamp(char* buf, size_t buf_size, time_t ts, size_t ms);
template <class Buffer>
......
......@@ -96,16 +96,16 @@ public:
template <class T>
size_t serialized_size(const T& x) {
serialized_size_inspector f;
auto inspection_res = inspect_object(f, detail::as_mutable_ref(x));
static_cast<void>(inspection_res); // Always true.
auto unused = f.apply_object(x);
static_cast<void>(unused); // Always true.
return f.result;
}
template <class T>
size_t serialized_size(actor_system& sys, const T& x) {
serialized_size_inspector f{sys};
auto inspection_res = inspect_object(f, detail::as_mutable_ref(x));
static_cast<void>(inspection_res); // Always true.
auto unused = f.apply_object(x);
static_cast<void>(unused); // Always true.
return f.result;
}
......
......@@ -161,7 +161,13 @@ public:
bool>
value(const T& x) {
auto str = to_string(x);
append(str);
if constexpr (std::is_convertible<decltype(str), const char*>::value) {
const char* cstr = str;
sep();
result_ += cstr;
} else {
append(str);
}
return true;
}
......
......@@ -156,9 +156,8 @@ public:
static T compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref;
fnv f;
auto inspect_result = (inspect_object(f, as_mutable_ref(xs)) && ...);
// Discard inspection result: always true.
static_cast<void>(inspect_result);
auto unused = f.apply_objects(xs...);
static_cast<void>(unused); // Always true.
return f.result;
}
......
......@@ -70,7 +70,7 @@ bool load_value(Inspector& f, T& x, inspector_access_type::specialization) {
}
template <class Inspector, class T>
bool load_object(Inspector& f, T& x, inspector_access_type::inspect_value) {
bool load_value(Inspector& f, T& x, inspector_access_type::inspect_value) {
return inspect_value(f, x);
}
......@@ -477,30 +477,6 @@ struct default_inspector_access : inspector_access_base<T> {
template <class T>
struct inspector_access;
/// Inspects `x` using the inspector `f`.
template <class Inspector, class T>
[[nodiscard]] bool inspect_object(Inspector& f, T& x) {
constexpr auto token = inspect_object_access_type<Inspector, T>();
if constexpr (Inspector::is_loading)
return detail::load_object(f, x, token);
else
return detail::save_object(f, x, token);
}
/// Inspects `x` using the inspector `f`.
template <class Inspector, class T>
[[nodiscard]] bool inspect_object(Inspector& f, const T& x) {
static_assert(!Inspector::is_loading);
constexpr auto token = inspect_object_access_type<Inspector, T>();
return detail::save_object(f, detail::as_mutable_ref(x), token);
}
/// Inspects all `xs` using the inspector `f`.
template <class Inspector, class... Ts>
[[nodiscard]] bool inspect_objects(Inspector& f, Ts&... xs) {
return (inspect_object(f, xs) && ...);
}
// -- inspection support for optional values -----------------------------------
template <class T>
......
......@@ -35,6 +35,10 @@ namespace caf {
/// for the DSL.
class CAF_CORE_EXPORT load_inspector {
public:
// -- member types -----------------------------------------------------------
using result_type = bool;
// -- constants --------------------------------------------------------------
/// Convenience constant to indicate success of a processing step.
......@@ -47,14 +51,6 @@ public:
/// Enables dispatching on the inspector type.
static constexpr bool is_loading = true;
// -- legacy API -------------------------------------------------------------
static constexpr bool reads_state = false;
static constexpr bool writes_state = true;
using result_type = bool;
// -- constructors, destructors, and assignment operators --------------------
virtual ~load_inspector();
......
......@@ -39,6 +39,18 @@ public:
// -- dispatching to load/load functions -------------------------------------
template <class T>
[[nodiscard]] bool apply_object(T& x) {
static_assert(!std::is_const<T>::value);
constexpr auto token = inspect_object_access_type<Subtype, T>();
return detail::load_object(dref(), x, token);
}
template <class... Ts>
[[nodiscard]] bool apply_objects(Ts&... xs) {
return (apply_object(xs) && ...);
}
template <class T>
bool apply_value(T& x) {
return detail::load_value(dref(), x);
......
......@@ -35,6 +35,10 @@ namespace caf {
/// for the DSL.
class CAF_CORE_EXPORT save_inspector {
public:
// -- member types -----------------------------------------------------------
using result_type = bool;
// -- constants --------------------------------------------------------------
/// Convenience constant to indicate success of a processing step.
......@@ -47,14 +51,6 @@ public:
/// Enables dispatching on the inspector type.
static constexpr bool is_loading = false;
// -- legacy API -------------------------------------------------------------
static constexpr bool reads_state = true;
static constexpr bool writes_state = false;
using result_type = bool;
// -- constructors, destructors, and assignment operators --------------------
virtual ~save_inspector();
......
......@@ -40,12 +40,29 @@ public:
// -- dispatching to load/save functions -------------------------------------
template <class T>
bool apply_value(T& x) {
[[nodiscard]] bool apply_object(T& x) {
constexpr auto token = inspect_object_access_type<Subtype, T>();
return detail::save_object(dref(), x, token);
}
template <class T>
[[nodiscard]] bool apply_object(const T& x) {
constexpr auto token = inspect_object_access_type<Subtype, T>();
return detail::save_object(dref(), detail::as_mutable_ref(x), token);
}
template <class... Ts>
[[nodiscard]] bool apply_objects(Ts&... xs) {
return (apply_object(xs) && ...);
}
template <class T>
[[nodiscard]] bool apply_value(T& x) {
return detail::save_value(dref(), x);
}
template <class Get, class Set>
bool apply_value(Get&& get, Set&&) {
[[nodiscard]] bool apply_value(Get&& get, Set&&) {
auto&& x = get();
return detail::save_value(dref(), x);
}
......
......@@ -231,8 +231,8 @@ CAF_CORE_EXPORT type_id_t query_type_id(string_view name);
return atom_text; \
} \
template <class Inspector> \
auto inspect(Inspector& f, atom_name&) { \
return f(caf::meta::type_name(#atom_namespace "::" #atom_name)); \
auto inspect(Inspector& f, atom_name& x) { \
return f.object(x).fields(); \
} \
} \
CAF_ADD_TYPE_ID(project_name, (atom_namespace::atom_name))
......
......@@ -54,12 +54,12 @@ CAF_TEST(serialization roundtrips go through the registry) {
CAF_MESSAGE("hdl.id: " << hdl->id());
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!inspect_object(sink, hdl))
if (!sink.apply_object(hdl))
CAF_FAIL("serialization failed: " << sink.get_error());
CAF_MESSAGE("buf: " << buf);
actor hdl2;
binary_deserializer source{sys, buf};
if (!inspect_object(source, hdl2))
if (!source.apply_object(hdl2))
CAF_FAIL("deserialization failed: " << source.get_error());
CAF_CHECK_EQUAL(hdl, hdl2);
anon_send_exit(hdl, exit_reason::user_shutdown);
......
......@@ -59,7 +59,7 @@ struct fixture {
template <class... Ts>
void load(const std::vector<byte>& buf, Ts&... xs) {
binary_deserializer source{nullptr, buf};
if (!inspect_objects(source, xs...))
if (!source.apply_objects(xs...))
CAF_FAIL("binary_deserializer failed to load: " << source.get_error());
}
......
......@@ -57,7 +57,7 @@ struct fixture {
auto save(const Ts&... xs) {
byte_buffer result;
binary_serializer sink{nullptr, result};
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
if (!sink.apply_objects(xs...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
return result;
}
......@@ -65,7 +65,7 @@ struct fixture {
template <class... Ts>
void save_to_buf(byte_buffer& data, const Ts&... xs) {
binary_serializer sink{nullptr, data};
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
if (!sink.apply_objects(xs...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
}
};
......
......@@ -39,7 +39,7 @@ struct fixture : test_coordinator_fixture<> {
size_t actual_size(const Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!inspect_objects(sink, detail::as_mutable_ref(xs)...))
if (!sink.apply_objects(xs...))
CAF_FAIL("failed to serialize data: " << sink.get_error());
return buf.size();
}
......
......@@ -54,11 +54,11 @@ struct fixture {
T roundtrip(T x) {
byte_buffer buf;
binary_serializer sink(sys, buf);
if (!inspect_object(sink, x))
if (!sink.apply_object(x))
CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf));
T y;
if (!inspect_object(source, y))
if (!source.apply_object(y))
CAF_FAIL("deserialization failed: " << source.get_error());
return y;
}
......
......@@ -54,11 +54,11 @@ struct fixture {
T roundtrip(T x) {
byte_buffer buf;
binary_serializer sink(sys, buf);
if (!inspect_object(sink, x))
if (!sink.apply_object(x))
CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf));
T y;
if (!inspect_object(source, y))
if (!source.apply_object(y))
CAF_FAIL("serialization failed: " << source.get_error());
return y;
}
......
......@@ -54,22 +54,10 @@ struct testee : deserializer {
log.insert(log.end(), indent, ' ');
}
using deserializer::object;
bool fetch_next_object_type(type_id_t&) override {
return false;
}
template <class T>
auto object(optional<T>&) {
return object_t<testee>{"optional", this};
}
template <class... Ts>
auto object(variant<Ts...>&) {
return object_t<testee>{"variant", this};
}
bool begin_object(string_view object_name) override {
new_line();
indent += 2;
......@@ -342,9 +330,9 @@ end object)_");
CAF_TEST(load inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(inspect_object(f, x), true);
CAF_CHECK_EQUAL(f.apply_object(x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object optional
begin object anonymous
begin optional field value
end field
end object)_");
......
......@@ -23,36 +23,29 @@ std::string to_string(weekday x);
bool parse(caf::string_view input, weekday& dest);
namespace caf {
template <>
struct inspector_access<weekday> : inspector_access_base<weekday> {
using default_impl = default_inspector_access<weekday>;
template <class Inspector>
static bool apply_object(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
f.object(x).fields(f.field("value", get, set));
} else {
return default_impl::apply_object(f, x);
}
template <class Inspector>
bool inspect(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
f.object(x).fields(f.field("value", get, set));
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_object(f, x);
}
}
template <class Inspector>
static bool apply_value(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
return f.apply_value(get, set);
} else {
return default_impl::apply_value(f, x);
}
template <class Inspector>
bool inspect_value(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
return f.apply_value(get, set);
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_value(f, x);
}
};
} // namespace caf
}
#define ADD_GET_SET_FIELD(type, name) \
private: \
......
......@@ -33,7 +33,7 @@ node_id roundtrip(node_id nid) {
byte_buffer buf;
{
binary_serializer sink{nullptr, buf};
if (!inspect_object(sink, nid))
if (!sink.apply_object(nid))
CAF_FAIL("serialization failed: " << sink.get_error());
}
if (buf.empty())
......@@ -41,7 +41,7 @@ node_id roundtrip(node_id nid) {
node_id result;
{
binary_deserializer source{nullptr, buf};
if (!inspect_object(source, result))
if (!source.apply_object(result))
CAF_FAIL("deserialization failed: " << source.get_error());
if (source.remaining() > 0)
CAF_FAIL("binary_serializer ignored part of its input");
......
......@@ -49,18 +49,6 @@ struct testee : serializer {
log.insert(log.end(), indent, ' ');
}
using serializer::object;
template <class T>
auto object(optional<T>&) {
return object_t<testee>{"optional", this};
}
template <class... Ts>
auto object(variant<Ts...>&) {
return object_t<testee>{"variant", this};
}
bool inject_next_object_type(type_id_t type) override {
new_line();
log += "next object type: ";
......@@ -448,9 +436,9 @@ end object)_");
CAF_TEST(save inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(inspect_object(f, x), true);
CAF_CHECK_EQUAL(f.apply_object(x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object optional
begin object anonymous
begin optional field value
end field
end object)_");
......
......@@ -111,7 +111,7 @@ struct fixture : test_coordinator_fixture<> {
byte_buffer serialize(const Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!inspect_objects(sink, xs...))
if (!sink.apply_objects(xs...))
CAF_FAIL("serialization failed: "
<< sink.get_error()
<< ", data: " << deep_to_string(std::forward_as_tuple(xs...)));
......@@ -121,7 +121,7 @@ struct fixture : test_coordinator_fixture<> {
template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf};
if (!inspect_objects(source, xs...))
if (!source.apply_objects(xs...))
CAF_FAIL("deserialization failed: " << source.get_error());
}
......
......@@ -44,7 +44,7 @@ struct fixture {
template <class T>
void set(const T& value) {
settings_writer writer{&xs};
if (!inspect_object(writer, value))
if (!writer.apply_object(value))
CAF_FAIL("failed two write to settings: " << writer.get_error());
}
......
......@@ -47,11 +47,11 @@ public:
}
bool serialize(serializer& sink) const override {
return inspect_object(sink, value);
return sink.apply_object(value);
}
bool serialize(binary_serializer& sink) const override {
return inspect_object(sink, value);
return sink.apply_object(value);
}
};
......@@ -72,7 +72,7 @@ private:
bool deserialize_impl(Deserializer& source,
std::unique_ptr<tracing_data>& dst) const {
string value;
if (!inspect_object(source, value))
if (!source.apply_object(value))
return false;
dst.reset(new dummy_tracing_data(std::move(value)));
return true;
......@@ -193,10 +193,10 @@ CAF_TEST(tracing data is serializable) {
byte_buffer buf;
binary_serializer sink{sys, buf};
tracing_data_ptr data{new dummy_tracing_data("iTrace")};
CAF_CHECK(inspect_object(sink, data));
CAF_CHECK(sink.apply_object(data));
binary_deserializer source{sys, buf};
tracing_data_ptr copy;
CAF_CHECK(inspect_object(source, copy));
CAF_CHECK(source.apply_object(copy));
CAF_REQUIRE_NOT_EQUAL(copy.get(), nullptr);
CAF_CHECK_EQUAL(dynamic_cast<dummy_tracing_data&>(*copy).value, "iTrace");
}
......
......@@ -133,7 +133,7 @@ struct fixture {
byte_buffer serialize(uri x) {
byte_buffer buf;
binary_serializer sink{nullptr, buf};
if (!inspect_objects(sink, x))
if (!sink.apply_objects(x))
CAF_FAIL("unable to serialize " << x << ": " << sink.get_error());
return buf;
}
......@@ -141,7 +141,7 @@ struct fixture {
uri deserialize(byte_buffer buf) {
uri result;
binary_deserializer source{nullptr, buf};
if (!inspect_objects(source, result))
if (!source.apply_objects(result))
CAF_FAIL("unable to deserialize from buffer: " << source.get_error());
return result;
}
......
......@@ -75,12 +75,12 @@ public:
if (dref.hdr_.operation == basp::message_type::routed_message) {
node_id src_node;
node_id dst_node;
if (!inspect_object(source, src_node)) {
if (!source.apply_object(src_node)) {
CAF_LOG_ERROR(
"failed to read source of routed message:" << source.get_error());
return;
}
if (!inspect_object(source, dst_node)) {
if (!source.apply_object(dst_node)) {
CAF_LOG_ERROR("failed to read destination of routed message:"
<< source.get_error());
return;
......@@ -104,11 +104,11 @@ public:
return;
}
// Get the remainder of the message.
if (!inspect_object(source, stages)) {
if (!source.apply_object(stages)) {
CAF_LOG_ERROR("failed to read stages:" << source.get_error());
return;
}
if (!inspect_objects(source, msg)) {
if (!source.apply_objects(msg)) {
CAF_LOG_ERROR("failed to read message content:" << source.get_error());
return;
}
......
......@@ -71,7 +71,7 @@ connection_state instance::handle(execution_unit* ctx, new_data_msg& dm,
}
} else {
binary_deserializer source{ctx, dm.buf};
if (!inspect_object(source, hdr)) {
if (!source.apply_object(hdr)) {
CAF_LOG_WARNING("failed to receive header:" << source.get_error());
return err(malformed_basp_message);
}
......@@ -183,7 +183,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
sender ? sender->id() : invalid_actor_id,
dest_actor};
auto writer = make_callback([&](binary_serializer& sink) { //
return inspect_objects(sink, forwarding_stack, msg);
return sink.apply_objects(forwarding_stack, msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
} else {
......@@ -197,8 +197,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
CAF_LOG_DEBUG("send routed message: "
<< CAF_ARG(source_node) << CAF_ARG(dest_node)
<< CAF_ARG(forwarding_stack) << CAF_ARG(msg));
return inspect_objects(sink, source_node, dest_node, forwarding_stack,
msg);
return sink.apply_objects(source_node, dest_node, forwarding_stack, msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
}
......@@ -222,7 +221,7 @@ void instance::write(execution_unit* ctx, byte_buffer& buf, header& hdr,
auto payload_len = buf.size() - (header_offset + basp::header_size);
hdr.payload_len = static_cast<uint32_t>(payload_len);
}
if (!inspect_objects(sink, hdr))
if (!sink.apply_objects(hdr))
CAF_LOG_ERROR(sink.get_error());
}
......@@ -251,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
aid = pa->first->id();
iface = pa->second;
}
return inspect_objects(sink, this_node_, app_ids, aid, iface);
return sink.apply_objects(this_node_, app_ids, aid, iface);
});
header hdr{message_type::server_handshake,
0,
......@@ -264,7 +263,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
void instance::write_client_handshake(execution_unit* ctx, byte_buffer& buf) {
auto writer = make_callback([&](binary_serializer& sink) { //
return inspect_objects(sink, this_node_);
return sink.apply_objects(this_node_);
});
header hdr{message_type::client_handshake,
0,
......@@ -279,7 +278,7 @@ void instance::write_monitor_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid));
auto writer = make_callback([&](binary_serializer& sink) { //
return inspect_objects(sink, this_node_, dest_node);
return sink.apply_objects(this_node_, dest_node);
});
header hdr{message_type::monitor_message, 0, 0, 0, invalid_actor_id, aid};
write(ctx, buf, hdr, &writer);
......@@ -290,7 +289,7 @@ void instance::write_down_message(execution_unit* ctx, byte_buffer& buf,
const error& rsn) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn));
auto writer = make_callback([&](binary_serializer& sink) { //
return inspect_objects(sink, this_node_, dest_node, rsn);
return sink.apply_objects(this_node_, dest_node, rsn);
});
header hdr{message_type::down_message, 0, 0, 0, aid, invalid_actor_id};
write(ctx, buf, hdr, &writer);
......@@ -326,7 +325,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
string_list app_ids;
actor_id aid = invalid_actor_id;
std::set<std::string> sigs;
if (!inspect_objects(source, source_node, app_ids, aid, sigs)) {
if (!source.apply_objects(source_node, app_ids, aid, sigs)) {
CAF_LOG_WARNING("unable to deserialize payload of server handshake:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -376,7 +375,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
// Deserialize payload.
binary_deserializer source{ctx, *payload};
node_id source_node;
if (!inspect_objects(source, source_node)) {
if (!source.apply_objects(source_node)) {
CAF_LOG_WARNING("unable to deserialize payload of client handshake:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -399,7 +398,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
binary_deserializer source{ctx, *payload};
node_id source_node;
node_id dest_node;
if (!inspect_objects(source, source_node, dest_node)) {
if (!source.apply_objects(source_node, dest_node)) {
CAF_LOG_WARNING(
"unable to deserialize source and destination for routed message:"
<< source.get_error());
......@@ -458,7 +457,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
binary_deserializer source{ctx, *payload};
node_id source_node;
node_id dest_node;
if (!inspect_objects(source, source_node, dest_node)) {
if (!source.apply_objects(source_node, dest_node)) {
CAF_LOG_WARNING("unable to deserialize payload of monitor message:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -475,7 +474,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
node_id source_node;
node_id dest_node;
error fail_state;
if (!inspect_objects(source, source_node, dest_node, fail_state)) {
if (!source.apply_objects(source_node, dest_node, fail_state)) {
CAF_LOG_WARNING("unable to deserialize payload of down message:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -512,12 +511,12 @@ void instance::forward(execution_unit* ctx, const node_id& dest_node,
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(hdr) << CAF_ARG(payload));
auto path = lookup(dest_node);
if (path) {
binary_serializer bs{ctx, callee_.get_buffer(path->hdl)};
if (!inspect_objects(bs, hdr)) {
CAF_LOG_ERROR("unable to serialize BASP header:" << bs.get_error());
binary_serializer sink{ctx, callee_.get_buffer(path->hdl)};
if (!sink.apply_object(hdr)) {
CAF_LOG_ERROR("unable to serialize BASP header:" << sink.get_error());
return;
}
bs.value(span<const byte>{payload.data(), payload.size()});
sink.value(span<const byte>{payload.data(), payload.size()});
flush(*path);
} else {
CAF_LOG_WARNING("cannot forward message, no route to destination");
......
......@@ -163,7 +163,7 @@ public:
uint32_t serialized_size(const message& msg) {
byte_buffer buf;
binary_serializer sink{mpx_, buf};
if (!inspect_objects(sink, msg))
if (!sink.apply_object(msg))
CAF_FAIL("failed to serialize message: " << sink.get_error());
return static_cast<uint32_t>(buf.size());
}
......@@ -221,7 +221,7 @@ public:
template <class... Ts>
void to_payload(byte_buffer& buf, const Ts&... xs) {
binary_serializer sink{mpx_, buf};
if (!inspect_objects(sink, xs...))
if (!sink.apply_objects(xs...))
CAF_FAIL("failed to serialize payload: " << sink.get_error());
}
......@@ -235,7 +235,7 @@ public:
auto pw = make_callback([&](binary_serializer& sink) {
if (writer != nullptr && !(*writer)(sink))
return false;
return inspect_objects(sink, x, xs...);
return sink.apply_objects(x, xs...);
});
to_buf(buf, hdr, &pw);
}
......@@ -243,7 +243,7 @@ public:
std::pair<basp::header, byte_buffer> from_buf(const byte_buffer& buf) {
basp::header hdr;
binary_deserializer source{mpx_, buf};
if (!inspect_object(source, hdr))
if (!source.apply_object(hdr))
CAF_FAIL("failed to deserialize header: " << source.get_error());
byte_buffer payload;
if (hdr.payload_len > 0) {
......@@ -307,7 +307,7 @@ public:
binary_deserializer source{mpx_, buf};
std::vector<strong_actor_ptr> stages;
message msg;
if (!inspect_objects(source, stages, msg))
if (!source.apply_objects(stages, msg))
CAF_FAIL("deserialization failed: " << source.get_error());
auto src = actor_cast<strong_actor_ptr>(registry_->get(hdr.source_actor));
auto dest = registry_->get(hdr.dest_actor);
......@@ -348,7 +348,7 @@ public:
basp::header hdr;
{ // lifetime scope of source
binary_deserializer source{this_->mpx(), ob};
if (!inspect_objects(source, hdr))
if (!source.apply_objects(hdr))
CAF_FAIL("failed to deserialize header: " << source.get_error());
}
byte_buffer payload;
......@@ -486,7 +486,7 @@ CAF_TEST(non_empty_server_handshake) {
std::set<std::string> ifs{"caf::replies_to<@u16>::with<@u16>"};
binary_serializer sink{nullptr, expected_payload};
auto id = self()->id();
if (!inspect_objects(sink, instance().this_node(), app_ids, id, ifs))
if (!sink.apply_objects(instance().this_node(), app_ids, id, ifs))
CAF_FAIL("serializing handshake failed: " << sink.get_error());
CAF_CHECK_EQUAL(hexstr(payload), hexstr(expected_payload));
}
......
......@@ -49,7 +49,7 @@ struct fixture : test_coordinator_fixture<> {
auto serialize(T& x, Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!inspect_objects(sink, x, xs...))
if (!sink.apply_objects(x, xs...))
CAF_FAIL("serialization failed: " << sink.get_error());
return buf;
}
......@@ -57,7 +57,7 @@ struct fixture : test_coordinator_fixture<> {
template <class Buffer, class T, class... Ts>
void deserialize(const Buffer& buf, T& x, Ts&... xs) {
binary_deserializer source{sys, buf};
if (!inspect_objects(source, x, xs...))
if (!source.apply_objects(x, xs...))
CAF_FAIL("serialization failed: " << source.get_error());
}
};
......
......@@ -109,7 +109,7 @@ CAF_TEST(deliver serialized message) {
std::vector<strong_actor_ptr> stages;
binary_serializer sink{sys, payload};
auto msg = make_message(ok_atom_v);
if (!inspect_objects(sink, stages, msg))
if (!sink.apply_objects(stages, msg))
CAF_FAIL("unable to serialize message: " << sink.get_error());
io::basp::header hdr{io::basp::message_type::direct_message,
0,
......
......@@ -843,7 +843,7 @@ public:
caf::byte_buffer serialize(const Ts&... xs) {
caf::byte_buffer buf;
caf::binary_serializer sink{sys, buf};
if (!inspect_objects(sink, xs...))
if (!sink.apply_objects(xs...))
CAF_FAIL("serialization failed: " << sink.get_error());
return buf;
}
......@@ -851,7 +851,7 @@ public:
template <class... Ts>
void deserialize(const caf::byte_buffer& buf, Ts&... xs) {
caf::binary_deserializer source{sys, buf};
if (!inspect_objects(source, xs...))
if (!source.apply_objects(xs...))
CAF_FAIL("deserialization failed: " << source.get_error());
}
......
......@@ -8,6 +8,8 @@ be serializable. Using a message type that is not serializable causes a compiler
error unless explicitly listed as unsafe message type by the user (see
:ref:`unsafe-message-type`).
.. _type-inspection-data-model:
Data Model
----------
......@@ -90,6 +92,92 @@ the inspector:
f.field("z", x.z));
}
Writing ``inspect_value`` Overloads
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The free function ``inspect`` models the object-level type inspection. As
mentioned in the section on the :ref:`data model <type-inspection-data-model>`,
objects are containers for fields that in turn contain a value. When providing
an ``inspect`` overload, CAF recursively visits a value as an object.
For example, consider the following ID type that simply wraps a string:
.. code-block:: C++
struct id { std::string value; };
template <class Inspector>
bool inspect(Inspector& f, id& x) {
return f.object(x).fields(f.field("value", x.value));
}
The type ``id`` is basically a *strong typedef* to improve type safety. To a
type inspector, ID objects look as follows:
.. code-block:: none
object(type: "id") {
field(name: "value") {
value(type: "string")
}
}
Now, this type has little use on its own. Usually, we would use such a type to
compose other types such as the following type ``person``:
.. code-block:: C++
struct person { std::string name; id key; };
template <class Inspector>
bool inspect(Inspector& f, person& x) {
return f.object(x).fields(f.field("name", x.name), f.field("key", x.key));
}
By providing the ``inspect`` overload for ID, inspectors can recursively visit
an ``id`` as an object. Hence, the above implementations work as expected. When
using ``person`` in human-readable data formats such as CAF configurations,
however, allowing CAF to look "inside" a strong typedef can simplify working
with types.
With the current implementation, we could read the key ``manager.ceo`` from a
configuration file with this content:
.. code-block:: none
manager {
ceo {
name = "Bob"
key = {
value = "TWFuIGlz"
}
}
}
This clearly is more verbose than it needs to be. By also providing an overload
for ``inspect_value``, we can teach CAF how to inspect an ID directly as a
*value* without having to recursively visit it as an object:
.. code-block:: C++
template <class Inspector>
bool inspect_value(Inspector& f, id& x) {
return f.apply_value(x.value);
}
With this overload in place, inspectors can now remove one level of indirection
and read or write strings whenever they encounter an ``id`` as value. This
allows us to simply our config file from before:
.. code-block:: none
manager {
ceo {
name = "Bob"
key = "TWFuIGlz"
}
}
Specializing ``inspector_access``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -128,9 +216,13 @@ The full interface of ``inspector_access`` looks as follows:
SetFallback& set_fallback);
};
For most types, we can implement ``apply_object`` and use default implementation
for the other member functions. For example, specializing ``inspector_access``
for our ``point_3d`` would look as follows:
The static member function ``apply_object`` has the same role as the free
``inspect`` function. Likewise, ``apply_value`` corresponds to the free
``inspect_value`` function.
For most types, we can implement only ``apply_object`` and use default
implementation for the other member functions. For example, specializing
``inspector_access`` for our ``point_3d`` would look as follows:
.. code-block:: C++
......@@ -170,9 +262,6 @@ from ``apply_value``. The latter customizes how CAF inspects a value inside a
field. By calling ``apply_object``, we simply recursively visit ``x`` as an
object again.
For a non-trivial use case of ``apply_value``, see
:ref:`has-human-readable-format`.
Types with Getter and Setter Access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -364,36 +453,29 @@ an ``enum class`` otherwise.
bool parse(std::string_view input, weekday& dest);
namespace caf {
template <>
struct inspector_access<weekday> : inspector_access_base<weekday> {
using default_impl = default_inspector_access<weekday>;
template <class Inspector>
static bool apply_object(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
f.object(x).fields(f.field("value", get, set));
} else {
return default_impl::apply_object(f, x);
}
template <class Inspector>
bool inspect(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
f.object(x).fields(f.field("value", get, set));
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_object(f, x);
}
}
template <class Inspector>
static bool apply_value(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
return f.apply_value(get, set);
} else {
return default_impl::apply_value(f, x);
}
template <class Inspector>
bool inspect_value(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, x); };
return f.apply_value(get, set);
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_value(f, x);
}
};
} // namespace caf
}
When inspecting an object of type ``weekday``, we treat it as if we were
inspecting an object with a single field named ``value``. However, usually we
......
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