Commit 8c8fb8be authored by Dominik Charousset's avatar Dominik Charousset

Simplify and streamline the type inspection API

parent b57ead03
......@@ -94,6 +94,27 @@ int main(int argc, char** argv) {
cerr << "empty enum name found\n";
return EXIT_FAILURE;
}
std::string case_label_prefix;
if (is_enum_class)
case_label_prefix = enum_name + "::";
// Read until hitting the closing '}'.
std::vector<std::string> enum_values;
for (;;) {
if (!getline(in, line)) {
cerr << "unable to read enum values\n";
return EXIT_FAILURE;
}
trim(line);
if (line.empty())
continue;
if (line[0] == '}')
break;
if (line[0] != '/') {
keep_alnum(line);
enum_values.emplace_back(line);
}
}
// Generate output file.
std::ofstream out{argv[2]};
if (!out) {
cerr << "unable to open output file: " << argv[2] << '\n';
......@@ -104,7 +125,8 @@ int main(int argc, char** argv) {
<< "// DO NOT EDIT: "
"this file is auto-generated by caf-generate-enum-strings.\n"
"// Run the target update-enum-strings if this file is out of sync.\n"
"#include \"caf/config.hpp\"\n\n"
"#include \"caf/config.hpp\"\n"
"#include \"caf/string_view.hpp\"\n\n"
"CAF_PUSH_DEPRECATED_WARNING\n\n"
<< "#include \"" << namespaces[0];
for (size_t i = 1; i < namespaces.size(); ++i)
......@@ -114,35 +136,44 @@ int main(int argc, char** argv) {
<< "namespace " << namespaces[0] << " {\n";
for (size_t i = 1; i < namespaces.size(); ++i)
out << "namespace " << namespaces[i] << " {\n";
out << "\nstd::string to_string(" << enum_name << " x) {\n"
out << '\n';
// Generate to_string implementation.
out << "std::string to_string(" << enum_name << " x) {\n"
<< " switch(x) {\n"
<< " default:\n"
<< " return \"???\";\n";
// Read until hitting the closing '}'.
std::string case_label_prefix;
if (is_enum_class)
case_label_prefix = enum_name + "::";
for (;;) {
if (!getline(in, line)) {
cerr << "unable to read enum values\n";
return EXIT_FAILURE;
}
trim(line);
if (line.empty())
continue;
if (line[0] == '}')
break;
if (line[0] != '/') {
keep_alnum(line);
out << " case " << case_label_prefix << line << ":\n"
<< " return \"" << line << "\";\n";
}
}
// Done. Print file footer and exit.
for (auto& val : enum_values)
out << " case " << case_label_prefix << val << ":\n"
<< " return \"" << val << "\";\n";
out << " };\n"
<< "}\n\n";
// Generate from_string implementation.
out << "bool from_string(string_view in, " << enum_name << "& out) {\n ";
for (auto& val : enum_values)
out << "if (in == \"" << val << "\") {\n"
<< " out = " << case_label_prefix << val << ";\n"
<< " return true;\n"
<< " } else ";
out << "{\n"
<< " return false;\n"
<< " }\n"
<< "}\n\n";
// Generate from_integer implementation.
out << "bool from_integer(std::underlying_type_t<" << enum_name << "> in,\n"
<< " " << enum_name << "& out) {\n"
<< " auto result = static_cast<" << enum_name << ">(in);\n"
<< " switch(result) {\n"
<< " default:\n"
<< " return false;\n";
for (auto& val : enum_values)
out << " case " << case_label_prefix << val << ":\n";
out << " out = result;\n"
<< " return true;\n"
<< " };\n"
<< "}\n\n";
// Done. Print file footer and exit.
for (auto i = namespaces.rbegin(); i != namespaces.rend(); ++i)
out << "} // namespace " << *i << '\n';
out << "\nCAF_POP_WARNINGS\n";
return EXIT_SUCCESS;
}
......@@ -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 (!sink.apply_object(f1)) {
if (!sink.apply(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 (!source.apply_object(f2)) {
if (!source.apply(f2)) {
std::cerr << "*** failed to deserialize foo2: "
<< to_string(source.get_error()) << '\n';
return;
......
......@@ -21,9 +21,11 @@
#include <cstddef>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/load_inspector_base.hpp"
......@@ -37,6 +39,10 @@ namespace caf {
class CAF_CORE_EXPORT binary_deserializer
: public load_inspector_base<binary_deserializer> {
public:
// -- member types -----------------------------------------------------------
using super = load_inspector_base<binary_deserializer>;
// -- constructors, destructors, and assignment operators --------------------
template <class Container>
......@@ -107,11 +113,11 @@ public:
bool fetch_next_object_type(type_id_t& type) noexcept;
constexpr bool begin_object(string_view) noexcept {
return ok;
return true;
}
constexpr bool end_object() noexcept {
return ok;
return true;
}
constexpr bool begin_field(string_view) noexcept {
......@@ -127,29 +133,29 @@ public:
span<const type_id_t> types, size_t& index) noexcept;
constexpr bool end_field() {
return ok;
return true;
}
constexpr bool begin_tuple(size_t) noexcept {
return ok;
return true;
}
constexpr bool end_tuple() noexcept {
return ok;
return true;
}
constexpr bool begin_key_value_pair() noexcept {
return ok;
return true;
}
constexpr bool end_key_value_pair() noexcept {
return ok;
return true;
}
bool begin_sequence(size_t& list_size) noexcept;
constexpr bool end_sequence() noexcept {
return ok;
return true;
}
bool begin_associative_array(size_t& size) noexcept {
......@@ -180,6 +186,17 @@ public:
bool value(uint64_t& x) noexcept;
template <class T>
std::enable_if_t<std::is_integral<T>::value, bool> value(T& x) noexcept {
auto tmp = detail::squashed_int_t<T>{0};
if (value(tmp)) {
x = static_cast<T>(tmp);
return true;
} else {
return false;
}
}
bool value(float& x) noexcept;
bool value(double& x) noexcept;
......
......@@ -20,11 +20,13 @@
#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp"
#include "caf/save_inspector_base.hpp"
#include "caf/span.hpp"
......@@ -37,6 +39,8 @@ class CAF_CORE_EXPORT binary_serializer
public:
// -- member types -----------------------------------------------------------
using super = save_inspector_base<binary_serializer>;
using container_type = byte_buffer;
using value_type = byte;
......@@ -94,15 +98,15 @@ public:
bool inject_next_object_type(type_id_t type);
constexpr bool begin_object(string_view) {
return ok;
return true;
}
constexpr bool end_object() {
return ok;
return true;
}
constexpr bool begin_field(string_view) noexcept {
return ok;
return true;
}
bool begin_field(string_view, bool is_present);
......@@ -113,29 +117,29 @@ public:
size_t index);
constexpr bool end_field() {
return ok;
return true;
}
constexpr bool begin_tuple(size_t) {
return ok;
return true;
}
constexpr bool end_tuple() {
return ok;
return true;
}
constexpr bool begin_key_value_pair() {
return ok;
return true;
}
constexpr bool end_key_value_pair() {
return ok;
return true;
}
bool begin_sequence(size_t list_size);
constexpr bool end_sequence() {
return ok;
return true;
}
bool begin_associative_array(size_t size) {
......@@ -166,6 +170,11 @@ public:
bool value(uint64_t x);
template <class T>
std::enable_if_t<std::is_integral<T>::value, bool> value(T x) {
return value(static_cast<detail::squashed_int_t<T>>(x));
}
bool value(float x);
bool value(double x);
......
......@@ -942,7 +942,7 @@ struct inspect_config_value_access {
static optional<T> get_if(const config_value* x) {
config_value_reader reader{x};
auto tmp = T{};
if (detail::load_value(reader, tmp))
if (detail::load(reader, tmp))
return optional<T>{std::move(tmp)};
return none;
}
......@@ -985,7 +985,7 @@ struct inspect_config_value_access {
static config_value convert(const T& x) {
config_value result;
config_value_writer writer{&result};
if (!detail::save_value(writer, x))
if (!detail::save(writer, x))
CAF_RAISE_ERROR("unable to convert type to a config_value");
return result;
}
......
......@@ -131,6 +131,8 @@ public:
bool end_associative_array() override;
bool value(byte& x) override;
bool value(bool& x) override;
bool value(int8_t& x) override;
......
......@@ -102,6 +102,8 @@ public:
bool end_associative_array() override;
bool value(byte x) override;
bool value(bool x) override;
bool value(int8_t x) override;
......
......@@ -136,19 +136,11 @@ struct inspector_access<cow_tuple<Ts...>> {
using value_type = cow_tuple<Ts...>;
template <class Inspector>
static bool apply_object(Inspector& f, value_type& x) {
static bool apply(Inspector& f, value_type& x) {
if constexpr (Inspector::is_loading)
return detail::load_object(f, x.unshared());
return f.tuple(x.unshared());
else
return detail::save_object(f, detail::as_mutable_ref(x.data()));
}
template <class Inspector>
static bool apply_value(Inspector& f, value_type& x) {
if constexpr (Inspector::is_loading)
return detail::load_value(f, x.unshared());
else
return detail::save_value(f, detail::as_mutable_ref(x.data()));
return f.tuple(x.data());
}
template <class Inspector>
......
......@@ -35,7 +35,7 @@ std::string deep_to_string(const T& x) {
using inspector_type = detail::stringification_inspector;
std::string result;
inspector_type f{result};
detail::save_value(f, detail::as_mutable_ref(x));
detail::save(f, detail::as_mutable_ref(x));
return result;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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. *
******************************************************************************/
#pragma once
#include <string>
#include <type_traits>
#include "caf/string_view.hpp"
namespace caf {
/// Convenience function for providing a default inspection scaffold for custom
/// enumeration types.
///
/// The enumeration type must provide the following interface based on free
/// functions:
///
/// ~~~(cpp)
/// enum class Enumeration : ... { ... };
/// std::string to_string(Enumeration);
/// bool from_string(string_view, Enumeration&);
/// bool from_integer(std::underlying_type_t<Enumeration>, Enumeration&);
/// ~~~
template <class Inspector, class Enumeration>
bool default_enum_inspect(Inspector& f, Enumeration& x) {
using integer_type = std::underlying_type_t<Enumeration>;
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](string_view str) { return from_string(str, x); };
return f.apply(get, set);
} else {
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) { return from_integer(val, x); };
return f.apply(get, set);
}
}
} // namespace caf
......@@ -26,6 +26,7 @@
#include "caf/byte.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp"
#include "caf/load_inspector_base.hpp"
#include "caf/span.hpp"
......@@ -37,6 +38,10 @@ namespace caf {
/// Technology-independent deserialization interface.
class CAF_CORE_EXPORT deserializer : public load_inspector_base<deserializer> {
public:
// -- member types -----------------------------------------------------------
using super = load_inspector_base<deserializer>;
// -- constructors, destructors, and assignment operators --------------------
explicit deserializer(actor_system& sys) noexcept;
......@@ -111,9 +116,12 @@ public:
/// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array();
/// Reads primitive value from the input.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
/// Reads `x` from the input.
/// @param x A reference to a builtin type.
/// @returns `true` on success, `false` otherwise.
virtual bool value(byte& x) = 0;
/// @copydoc value
virtual bool value(bool& x) = 0;
/// @copydoc value
......@@ -140,6 +148,17 @@ public:
/// @copydoc value
virtual bool value(uint64_t&) = 0;
/// @copydoc value
template <class T>
std::enable_if_t<std::is_integral<T>::value, bool> value(T& x) noexcept {
auto tmp = detail::squashed_int_t<T>{0};
if (value(tmp)) {
x = static_cast<T>(tmp);
return true;
} else {
return false;
}
}
/// @copydoc value
virtual bool value(float&) = 0;
......@@ -163,10 +182,12 @@ public:
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual bool value(span<byte> x) = 0;
using super::list;
/// Adds each boolean in `xs` to the output. Derived classes can override this
/// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format.
virtual bool value(std::vector<bool>& xs);
virtual bool list(std::vector<bool>& xs);
protected:
/// Provides access to the ::proxy_registry and to the ::actor_system.
......
......@@ -50,28 +50,28 @@ void copy_construct(void* ptr, const void* src) {
template <class T>
bool save_binary(binary_serializer& sink, const void* ptr) {
return sink.apply_object(*static_cast<const T*>(ptr));
return sink.apply(*static_cast<const T*>(ptr));
}
template <class T>
bool load_binary(binary_deserializer& source, void* ptr) {
return source.apply_object(*static_cast<T*>(ptr));
return source.apply(*static_cast<T*>(ptr));
}
template <class T>
bool save(serializer& sink, const void* ptr) {
return sink.apply_object(*static_cast<const T*>(ptr));
return sink.apply(*static_cast<const T*>(ptr));
}
template <class T>
bool load(deserializer& source, void* ptr) {
return source.apply_object(*static_cast<T*>(ptr));
return source.apply(*static_cast<T*>(ptr));
}
template <class T>
void stringify(std::string& buf, const void* ptr) {
stringification_inspector f{buf};
auto unused = f.apply_object(*static_cast<const T*>(ptr));
auto unused = f.apply(*static_cast<const T*>(ptr));
static_cast<void>(unused);
}
......
......@@ -58,6 +58,8 @@ public:
bool end_sequence() override;
bool value(byte x) override;
bool value(bool x) override;
bool value(int8_t x) override;
......@@ -90,13 +92,15 @@ public:
bool value(span<const byte> x) override;
bool value(const std::vector<bool>& xs) override;
using super::list;
bool list(const std::vector<bool>& xs) override;
};
template <class T>
size_t serialized_size(const T& x) {
serialized_size_inspector f;
auto unused = f.apply_object(x);
auto unused = f.apply(x);
static_cast<void>(unused); // Always true.
return f.result;
}
......@@ -104,7 +108,7 @@ size_t serialized_size(const T& x) {
template <class T>
size_t serialized_size(actor_system& sys, const T& x) {
serialized_size_inspector f{sys};
auto unused = f.apply_object(x);
auto unused = f.apply(x);
static_cast<void>(unused); // Always true.
return f.result;
}
......
......@@ -66,7 +66,7 @@ public:
this->inspector_.result = 0;
this->sampled_elements_ += x.xs_size;
for (auto& element : x.xs.get_as<std::vector<T>>(0))
detail::save_value(this->inspector_, element);
detail::save(this->inspector_, element);
this->sampled_total_size_
+= static_cast<int64_t>(this->inspector_.result);
}
......
......@@ -40,7 +40,7 @@ class CAF_CORE_EXPORT stringification_inspector
public:
// -- member types -----------------------------------------------------------
using super = save_inspector;
using super = save_inspector_base<stringification_inspector>;
// -- constructors, destructors, and assignment operators --------------------
......@@ -100,6 +100,8 @@ public:
return end_sequence();
}
bool value(byte x);
bool value(bool x);
template <class Integral>
......@@ -126,7 +128,11 @@ public:
bool value(const std::u32string& x);
bool value(const std::vector<bool>& xs);
bool value(span<const byte> x);
using super::list;
bool list(const std::vector<bool>& xs);
// -- builtin inspection to pick up to_string or provide nicer formatting ----
......@@ -146,14 +152,14 @@ public:
return true;
}
result_ += '{';
save_value(*this, i->first);
save(*this, i->first);
result_ += " = ";
save_value(*this, i->second);
save(*this, i->second);
while (++i != last) {
sep();
save_value(*this, i->first);
save(*this, i->first);
result_ += " = ";
save_value(*this, i->second);
save(*this, i->second);
}
result_ += '}';
return true;
......@@ -186,7 +192,7 @@ public:
} else {
sep();
result_ += '*';
save_value(*this, detail::as_mutable_ref(*x));
save(*this, detail::as_mutable_ref(*x));
return true;
}
}
......@@ -198,7 +204,7 @@ public:
result_ += "null";
} else {
result_ += '*';
save_value(*this, detail::as_mutable_ref(*x));
save(*this, detail::as_mutable_ref(*x));
}
return true;
}
......@@ -228,7 +234,7 @@ private:
sep();
result_ += '[';
while (first != sentinel)
save_value(*this, *first++);
save(*this, *first++);
result_ += ']';
}
......
......@@ -878,23 +878,6 @@ public:
static constexpr bool value = result_type::value;
};
/// Checks whether `T` provides an `inspect_value` overload for `Inspector`.
template <class Inspector, class T>
class has_inspect_value_overload {
private:
template <class U>
static auto sfinae(Inspector& x, U& y)
-> decltype(inspect_value(x, y), std::true_type{});
static std::false_type sfinae(Inspector&, ...);
using result_type
= decltype(sfinae(std::declval<Inspector&>(), std::declval<T&>()));
public:
static constexpr bool value = result_type::value;
};
/// Checks whether the inspector has a `builtin_inspect` overload for `T`.
template <class Inspector, class T>
class has_builtin_inspect {
......@@ -980,6 +963,48 @@ public:
static constexpr bool value = sfinae_result::value;
};
/// Checks whether `T` is primitive, i.e., either an arithmetic type or
/// convertible to one of STL's string types.
template <class T, bool IsLoading>
struct is_builtin_inspector_type {
static constexpr bool value = std::is_arithmetic<T>::value;
};
template <bool IsLoading>
struct is_builtin_inspector_type<byte, IsLoading> {
static constexpr bool value = true;
};
template <bool IsLoading>
struct is_builtin_inspector_type<span<byte>, IsLoading> {
static constexpr bool value = true;
};
template <bool IsLoading>
struct is_builtin_inspector_type<std::string, IsLoading> {
static constexpr bool value = true;
};
template <bool IsLoading>
struct is_builtin_inspector_type<std::u16string, IsLoading> {
static constexpr bool value = true;
};
template <bool IsLoading>
struct is_builtin_inspector_type<std::u32string, IsLoading> {
static constexpr bool value = true;
};
template <>
struct is_builtin_inspector_type<string_view, false> {
static constexpr bool value = true;
};
template <>
struct is_builtin_inspector_type<span<const byte>, false> {
static constexpr bool value = true;
};
} // namespace caf::detail
#undef CAF_HAS_MEMBER_TRAIT
......
......@@ -24,8 +24,11 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp"
namespace caf {
......@@ -55,6 +58,18 @@ enum class exit_reason : uint8_t {
/// @relates exit_reason
CAF_CORE_EXPORT std::string to_string(exit_reason);
/// @relates exit_reason
CAF_CORE_EXPORT bool from_string(string_view, exit_reason&);
/// @relates exit_reason
CAF_CORE_EXPORT bool from_integer(std::underlying_type_t<exit_reason>,
exit_reason&);
template <class Inspector>
bool inspect(Inspector& f, exit_reason& x) {
return default_enum_inspect(f, x);
}
} // namespace caf
CAF_ERROR_CODE_ENUM(exit_reason)
......@@ -196,7 +196,7 @@ enum class exit_reason : uint8_t;
enum class invoke_message_result;
enum class pec : uint8_t;
enum class sec : uint8_t;
enum class stream_priority;
enum class stream_priority : uint8_t;
// -- aliases ------------------------------------------------------------------
......
......@@ -43,6 +43,8 @@ class fnv : public save_inspector_base<fnv<T>> {
public:
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
using super = save_inspector_base<fnv<T>>;
constexpr fnv() noexcept : result(init()) {
// nop
}
......@@ -154,7 +156,7 @@ public:
static T compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref;
fnv f;
auto unused = f.apply_objects(xs...);
auto unused = (f.apply(xs) && ...);
static_cast<void>(unused); // Always true.
return f.result;
}
......
......@@ -37,6 +37,9 @@ public:
/// Hash size in bytes.
static constexpr size_t hash_size = 20;
/// Alias to the super types.
using super = save_inspector_base<sha1>;
/// Array type for storing a 160-bit hash.
using result_type = std::array<byte, hash_size>;
......@@ -152,7 +155,7 @@ public:
static result_type compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref;
sha1 f;
auto unused = f.apply_objects(xs...);
auto unused = f.apply(xs...);
static_cast<void>(unused); // Always true.
return f.result();
}
......
......@@ -73,137 +73,65 @@ constexpr bool assertion_failed_v = false;
// -- loading ------------------------------------------------------------------
template <class Inspector, class T>
bool load_value(Inspector& f, T& x);
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply_value(f, x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::inspect_value) {
return inspect_value(f, x);
bool load(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply(f, x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::inspect) {
bool load(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect(f, x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::integral) {
auto tmp = detail::squashed_int_t<T>{0};
if (f.value(tmp)) {
x = static_cast<T>(tmp);
return true;
}
return false;
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::builtin) {
return f.builtin_inspect(x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::trivial) {
bool load(Inspector& f, T& x, inspector_access_type::builtin) {
return f.value(x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::enumeration) {
auto tmp = detail::squashed_int_t<std::underlying_type_t<T>>{0};
if (f.value(tmp)) {
x = static_cast<T>(tmp);
return true;
}
return false;
bool load(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
return f.builtin_inspect(x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::empty) {
bool load(Inspector& f, T& x, inspector_access_type::empty) {
return f.object(x).fields();
}
template <class Inspector, class T>
bool load_value(Inspector& f, T&, inspector_access_type::unsafe) {
bool load(Inspector& f, T&, inspector_access_type::unsafe) {
f.emplace_error(sec::unsafe_type);
return false;
}
template <class Inspector, class T, size_t N>
bool load_value(Inspector& f, T (&xs)[N], inspector_access_type::array) {
if (!f.begin_tuple(N))
return false;
for (size_t index = 0; index < N; ++index)
if (!load_value(f, xs[index]))
return false;
return f.end_tuple();
}
template <class Inspector, class T, size_t... Ns>
bool load_tuple(Inspector& f, T& xs, std::index_sequence<Ns...>) {
return f.begin_tuple(sizeof...(Ns)) //
&& (load_value(f, get<Ns>(xs)) && ...) //
&& f.end_tuple();
bool load(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
return f.tuple(xs);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::tuple) {
return load_tuple(f, x,
std::make_index_sequence<std::tuple_size<T>::value>{});
bool load(Inspector& f, T& xs, inspector_access_type::tuple) {
return f.tuple(xs);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::map) {
x.clear();
size_t size = 0;
if (!f.begin_associative_array(size))
return false;
for (size_t i = 0; i < size; ++i) {
auto key = typename T::key_type{};
auto val = typename T::mapped_type{};
if (!(f.begin_key_value_pair() //
&& load_value(f, key) //
&& load_value(f, val) //
&& f.end_key_value_pair()))
return false;
// A multimap returns an iterator, a regular map returns a pair.
auto emplace_result = x.emplace(std::move(key), std::move(val));
if constexpr (is_pair<decltype(emplace_result)>::value) {
if (!emplace_result.second) {
f.emplace_error(sec::runtime_error, "multiple key definitions");
return false;
}
}
}
return f.end_associative_array();
bool load(Inspector& f, T& x, inspector_access_type::map) {
return f.map(x);
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::list) {
x.clear();
size_t size = 0;
if (!f.begin_sequence(size))
return false;
for (size_t i = 0; i < size; ++i) {
auto val = typename T::value_type{};
if (!detail::load_value(f, val))
return false;
x.insert(x.end(), std::move(val));
}
return f.end_sequence();
bool load(Inspector& f, T& x, inspector_access_type::list) {
return f.list(x);
}
template <class Inspector, class T>
std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool>
load_value(Inspector& f, T& x, inspector_access_type::none) {
load(Inspector& f, T& x, inspector_access_type::none) {
return f.opaque_value(x);
}
template <class Inspector, class T>
std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool>
load_value(Inspector&, T&, inspector_access_type::none) {
load(Inspector&, T&, inspector_access_type::none) {
static_assert(
detail::assertion_failed_v<T>,
"please provide an inspect overload for T or specialize inspector_access");
......@@ -211,28 +139,8 @@ load_value(Inspector&, T&, inspector_access_type::none) {
}
template <class Inspector, class T>
bool load_value(Inspector& f, T& x) {
return load_value(f, x, inspect_value_access_type<Inspector, T>());
}
template <class Inspector, class T>
bool load_object(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply_object(f, x);
}
template <class Inspector, class T>
bool load_object(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect(f, x);
}
template <class Inspector, class T, class Token>
bool load_object(Inspector& f, T& x, Token) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector, class T>
bool load_object(Inspector& f, T& x) {
return load_object(f, x, inspect_object_access_type<Inspector, T>());
bool load(Inspector& f, T& x) {
return load(f, x, inspect_access_type<Inspector, T>());
}
template <class Inspector, class T, class IsValid, class SyncValue>
......@@ -257,116 +165,65 @@ bool load_field(Inspector& f, string_view field_name, T& x, IsValid& is_valid,
// -- saving -------------------------------------------------------------------
template <class Inspector, class T>
bool save_value(Inspector& f, T& x);
template <class Inspector, class T>
bool save_value(Inspector& f, const T& x);
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply_value(f, x);
bool save(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply(f, x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::inspect_value) {
return inspect_value(f, x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::inspect) {
bool save(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect(f, x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::integral) {
auto tmp = static_cast<detail::squashed_int_t<T>>(x);
return f.value(tmp);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::builtin) {
return f.builtin_inspect(x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::trivial) {
bool save(Inspector& f, T& x, inspector_access_type::builtin) {
return f.value(x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::enumeration) {
auto tmp = static_cast<detail::squashed_int_t<std::underlying_type_t<T>>>(x);
return f.value(tmp);
bool save(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
return f.builtin_inspect(x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::empty) {
bool save(Inspector& f, T& x, inspector_access_type::empty) {
return f.object(x).fields();
}
template <class Inspector, class T>
bool save_value(Inspector& f, T&, inspector_access_type::unsafe) {
bool save(Inspector& f, T&, inspector_access_type::unsafe) {
f.emplace_error(sec::unsafe_type);
return false;
}
template <class Inspector, class T, size_t N>
bool save_value(Inspector& f, T (&xs)[N], inspector_access_type::array) {
if (!f.begin_tuple(N))
return false;
for (size_t index = 0; index < N; ++index)
if (!save_value(f, xs[index]))
return false;
return f.end_tuple();
}
template <class Inspector, class T, size_t... Ns>
bool save_tuple(Inspector& f, T& xs, std::index_sequence<Ns...>) {
return f.begin_tuple(sizeof...(Ns)) //
&& (save_value(f, get<Ns>(xs)) && ...) //
&& f.end_tuple();
bool save(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
return f.tuple(xs);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::tuple) {
return save_tuple(f, x,
std::make_index_sequence<std::tuple_size<T>::value>{});
bool save(Inspector& f, const T& xs, inspector_access_type::tuple) {
return f.tuple(xs);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::map) {
if (!f.begin_associative_array(x.size()))
return false;
for (auto&& kvp : x) {
if (!(f.begin_key_value_pair() //
&& save_value(f, kvp.first) //
&& save_value(f, kvp.second) //
&& f.end_key_value_pair()))
return false;
}
return f.end_associative_array();
bool save(Inspector& f, T& x, inspector_access_type::map) {
return f.map(x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::list) {
auto size = x.size();
if (!f.begin_sequence(size))
return false;
for (auto&& val : x)
if (!save_value(f, val))
return false;
return f.end_sequence();
bool save(Inspector& f, T& x, inspector_access_type::list) {
return f.list(x);
}
template <class Inspector, class T>
std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool>
save_value(Inspector& f, T& x, inspector_access_type::none) {
save(Inspector& f, T& x, inspector_access_type::none) {
return f.opaque_value(x);
}
template <class Inspector, class T>
std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool>
save_value(Inspector&, T&, inspector_access_type::none) {
save(Inspector&, T&, inspector_access_type::none) {
static_assert(
detail::assertion_failed_v<T>,
"please provide an inspect overload for T or specialize inspector_access");
......@@ -374,34 +231,13 @@ save_value(Inspector&, T&, inspector_access_type::none) {
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x) {
return save_value(f, x, inspect_value_access_type<Inspector, T>());
}
template <class Inspector, class T>
bool save_value(Inspector& f, const T& x) {
return save_value(f, as_mutable_ref(x),
inspect_value_access_type<Inspector, T>());
bool save(Inspector& f, T& x) {
return save(f, x, inspect_access_type<Inspector, T>());
}
template <class Inspector, class T>
bool save_object(Inspector& f, T& x, inspector_access_type::specialization) {
return inspector_access<T>::apply_object(f, x);
}
template <class Inspector, class T>
bool save_object(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect(f, x);
}
template <class Inspector, class T, class Token>
bool save_object(Inspector& f, T& x, Token) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector, class T>
bool save_object(Inspector& f, T& x) {
return save_object(f, x, inspect_object_access_type<Inspector, T>());
bool save(Inspector& f, const T& x) {
return save(f, as_mutable_ref(x), inspect_access_type<Inspector, T>());
}
template <class Inspector, class T>
......@@ -422,77 +258,13 @@ bool save_field(Inspector& f, string_view field_name, IsPresent& is_present,
return impl::save_field(f, field_name, is_present, get);
}
// -- dispatching to save or load using getter/setter API ----------------------
template <class Inspector, class T>
bool split_save_load(Inspector& f, T& x) {
if constexpr (Inspector::is_loading)
return load_value(f, x);
else
return save_value(f, x);
}
template <class Inspector, class Get, class Set>
bool split_save_load(Inspector& f, Get&& get, Set&& set) {
using value_type = std::decay_t<decltype(get())>;
if constexpr (Inspector::is_loading) {
auto tmp = value_type{};
if (load_value(f, tmp))
return set(std::move(tmp));
return false;
} else {
auto&& x = get();
return save_value(f, x);
}
}
} // namespace caf::detail
namespace caf {
/// Default implementation for @ref inspector_access.
template <class T>
struct default_inspector_access : inspector_access_base<T> {
// -- interface functions ----------------------------------------------------
/// Applies `x` as an object to `f`.
template <class Inspector>
[[nodiscard]] static bool apply_object(Inspector& f, T& x) {
// Dispatch to user-provided `inspect` overload or assume a trivial type.
if constexpr (std::is_empty<T>::value) {
return f.object(x).fields();
} else {
return f.object(x).fields(f.field("value", x));
}
}
/// Applies `x` as a single value to `f`.
template <class Inspector>
[[nodiscard]] static bool apply_value(Inspector& f, T& x) {
constexpr auto token = nested_inspect_value_access_type<Inspector, T>();
if constexpr (Inspector::is_loading)
return detail::load_value(f, x, token);
else
return detail::save_value(f, x, token);
}
// -- deprecated API ---------------------------------------------------------
template <class Inspector>
[[deprecated("inspect() overloads should return bool")]] //
[[nodiscard]] static bool
apply_deprecated(Inspector& f, T& x) {
if (auto err = inspect(f, x)) {
f.emplace_error(std::move(err));
return false;
}
return true;
}
};
// -- customization points -----------------------------------------------------
/// Customization point for adding support for a custom type. The default
/// implementation requires an `inspect` overload for `T` that is available via
/// ADL.
/// Customization point for adding support for a custom type.
template <class T>
struct inspector_access;
......@@ -559,15 +331,10 @@ struct optional_inspector_access {
using value_type = typename traits::value_type;
template <class Inspector>
[[nodiscard]] static bool apply_object(Inspector& f, container_type& x) {
[[nodiscard]] static bool apply(Inspector& f, container_type& x) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector>
[[nodiscard]] static bool apply_value(Inspector& f, container_type& x) {
return apply_object(f, x);
}
template <class Inspector>
static bool
save_field(Inspector& f, string_view field_name, container_type& x) {
......@@ -696,18 +463,13 @@ struct variant_inspector_access {
using traits = variant_inspector_traits<T>;
template <class Inspector>
[[nodiscard]] static bool apply_object(Inspector& f, value_type& x) {
[[nodiscard]] static bool apply(Inspector& f, value_type& x) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector>
[[nodiscard]] static bool apply_value(Inspector& f, value_type& x) {
return apply_object(f, x);
}
template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, value_type& x) {
auto g = [&f](auto& y) { return detail::save_value(f, y); };
auto g = [&f](auto& y) { return detail::save(f, y); };
return f.begin_field(field_name, make_span(traits::allowed_types),
traits::type_index(x)) //
&& traits::visit(g, x) //
......@@ -720,7 +482,7 @@ struct variant_inspector_access {
auto allowed_types = make_span(traits::allowed_types);
if (is_present()) {
auto&& x = get();
auto g = [&f](auto& y) { return detail::save_value(f, y); };
auto g = [&f](auto& y) { return detail::save(f, y); };
return f.begin_field(field_name, true, allowed_types,
traits::type_index(x)) //
&& traits::visit(g, x) //
......@@ -735,7 +497,7 @@ struct variant_inspector_access {
value_type& x, type_id_t runtime_type) {
auto res = false;
auto type_found = traits::load(runtime_type, [&](auto& tmp) {
if (!detail::load_value(f, tmp))
if (!detail::load(f, tmp))
return;
traits::assign(x, std::move(tmp));
res = true;
......@@ -876,12 +638,7 @@ struct inspector_access<std::chrono::duration<Rep, Period>>
using value_type = std::chrono::duration<Rep, Period>;
template <class Inspector>
static bool apply_object(Inspector& f, value_type& x) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector>
static bool apply_value(Inspector& f, value_type& x) {
static bool apply(Inspector& f, value_type& x) {
if (f.has_human_readable_format()) {
auto get = [&x] {
std::string str;
......@@ -892,14 +649,15 @@ struct inspector_access<std::chrono::duration<Rep, Period>>
auto err = detail::parse(str, x);
return !err;
};
return detail::split_save_load(f, get, set);
return f.apply(get, set);
} else {
auto get = [&x] { return x.count(); };
auto set = [&x](Rep value) {
x = std::chrono::duration<Rep, Period>{value};
return true;
};
return f.apply(get, set);
}
auto get = [&x] { return x.count(); };
auto set = [&x](Rep value) {
x = std::chrono::duration<Rep, Period>{value};
return true;
};
return detail::split_save_load(f, get, set);
}
};
......@@ -912,12 +670,7 @@ struct inspector_access<
= std::chrono::time_point<std::chrono::system_clock, Duration>;
template <class Inspector>
static bool apply_object(Inspector& f, value_type& x) {
return f.object(x).fields(f.field("value", x));
}
template <class Inspector>
static bool apply_value(Inspector& f, value_type& x) {
static bool apply(Inspector& f, value_type& x) {
if (f.has_human_readable_format()) {
auto get = [&x] {
std::string str;
......@@ -928,15 +681,16 @@ struct inspector_access<
auto err = detail::parse(str, x);
return !err;
};
return detail::split_save_load(f, get, set);
return f.apply(get, set);
} else {
using rep_type = typename Duration::rep;
auto get = [&x] { return x.time_since_epoch().count(); };
auto set = [&x](rep_type value) {
x = value_type{Duration{value}};
return true;
};
return f.apply(get, set);
}
using rep_type = typename Duration::rep;
auto get = [&x] { return x.time_since_epoch().count(); };
auto set = [&x](rep_type value) {
x = value_type{Duration{value}};
return true;
};
return detail::split_save_load(f, get, set);
}
};
......
......@@ -32,7 +32,7 @@ struct inspector_access_base {
template <class Inspector, class IsValid, class SyncValue>
static bool load_field(Inspector& f, string_view field_name, T& x,
IsValid& is_valid, SyncValue& sync_value) {
if (f.begin_field(field_name) && f.apply_value(x)) {
if (f.begin_field(field_name) && f.apply(x)) {
if (!is_valid(x)) {
f.emplace_error(sec::field_invariant_check_failed,
to_string(field_name));
......@@ -58,7 +58,7 @@ struct inspector_access_base {
if (!f.begin_field(field_name, is_present))
return false;
if (is_present) {
if (!f.apply_value(x))
if (!f.apply(x))
return false;
if (!is_valid(x)) {
f.emplace_error(sec::field_invariant_check_failed,
......@@ -80,7 +80,7 @@ struct inspector_access_base {
template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, T& x) {
return f.begin_field(field_name) //
&& f.apply_value(x) //
&& f.apply(x) //
&& f.end_field();
}
......@@ -91,7 +91,7 @@ struct inspector_access_base {
if (is_present()) {
auto&& x = get();
return f.begin_field(field_name, true) //
&& f.apply_value(x) //
&& f.apply(x) //
&& f.end_field();
}
return f.begin_field(field_name, false) && f.end_field();
......
......@@ -35,23 +35,14 @@ struct inspector_access_type {
/// Flags types that provide an `inspector_access` specialization.
struct specialization {};
/// Flags types that provide an `inspect_value()` overload.
struct inspect_value {};
/// Flags types that provide an `inspect()` overload.
struct inspect {};
/// Flags builtin integral types.
struct integral {};
/// Flags types with builtin support via `Inspector::builtin_inspect`.
/// Flags types with builtin support of the inspection API.
struct builtin {};
/// Flags types with builtin support via `Inspector::value`.
struct trivial {};
/// Flags `enum` types.
struct enumeration {};
/// Flags types with builtin support via `Inspector::builtin_inspect`.
struct builtin_inspect {};
/// Flags stateless message types.
struct empty {};
......@@ -59,15 +50,9 @@ struct inspector_access_type {
/// Flags allowed unsafe message types.
struct unsafe {};
/// Flags native C array types.
struct array {};
/// Flags types with `std::tuple`-like API.
struct tuple {};
/// Flags type that specialize `inspector_access_boxed_value_traits`.
struct boxed_value {};
/// Flags types with `std::map`-like API.
struct map {};
......@@ -80,98 +65,23 @@ struct inspector_access_type {
/// @relates inspector_access_type
template <class Inspector, class T>
constexpr auto inspect_object_access_type() {
constexpr auto inspect_access_type() {
using namespace detail;
// Order: unsafe (error) > C Array > builtin_inspect > inspector_access >
// inspect > trivial > defaults.
// Order: unsafe (error) > C Array > builtin > builtin_inspect
// > inspector_access > inspect > trivial > defaults.
if constexpr (is_allowed_unsafe_message_type_v<T>)
return inspector_access_type::unsafe{};
else if constexpr (std::is_array<T>::value)
return inspector_access_type::array{};
else if constexpr (has_builtin_inspect<Inspector, T>::value)
return inspector_access_type::builtin{};
else if constexpr (detail::is_complete<inspector_access<T>>)
return inspector_access_type::specialization{};
else if constexpr (has_inspect_overload<Inspector, T>::value)
return inspector_access_type::inspect{};
else if constexpr (is_trivial_inspector_value_v<Inspector::is_loading, T>)
return inspector_access_type::trivial{};
else if constexpr (std::is_integral<T>::value)
return inspector_access_type::integral{};
else if constexpr (std::is_enum<T>::value)
return inspector_access_type::enumeration{};
else if constexpr (std::is_empty<T>::value)
return inspector_access_type::empty{};
else if constexpr (is_stl_tuple_type_v<T>)
return inspector_access_type::tuple{};
else if constexpr (is_map_like_v<T>)
return inspector_access_type::map{};
else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{};
else
return inspector_access_type::none{};
}
/// @relates inspector_access_type
template <class Inspector, class T>
constexpr auto inspect_value_access_type() {
// Order: unsafe (error) > C Array > builtin_inspect > inspector_access >
// inspect_value > inspect > defaults.
// This is the same as in inspect_object_access_type, except that we pick up
// inspect_value overloads.
using namespace detail;
if constexpr (is_allowed_unsafe_message_type_v<T>)
return inspector_access_type::unsafe{};
else if constexpr (std::is_array<T>::value)
return inspector_access_type::array{};
else if constexpr (has_builtin_inspect<Inspector, T>::value)
else if constexpr (detail::is_builtin_inspector_type<
T, Inspector::is_loading>::value)
return inspector_access_type::builtin{};
else if constexpr (has_builtin_inspect<Inspector, T>::value)
return inspector_access_type::builtin_inspect{};
else if constexpr (detail::is_complete<inspector_access<T>>)
return inspector_access_type::specialization{};
else if constexpr (has_inspect_value_overload<Inspector, T>::value)
return inspector_access_type::inspect_value{};
else if constexpr (has_inspect_overload<Inspector, T>::value)
return inspector_access_type::inspect{};
else if constexpr (is_trivial_inspector_value_v<Inspector::is_loading, T>)
return inspector_access_type::trivial{};
else if constexpr (std::is_integral<T>::value)
return inspector_access_type::integral{};
else if constexpr (std::is_enum<T>::value)
return inspector_access_type::enumeration{};
else if constexpr (std::is_empty<T>::value)
return inspector_access_type::empty{};
else if constexpr (is_stl_tuple_type_v<T>)
return inspector_access_type::tuple{};
else if constexpr (is_map_like_v<T>)
return inspector_access_type::map{};
else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{};
else
return inspector_access_type::none{};
}
/// Same as `inspect_value_access_type`, but ignores specialization of
/// `inspector_access` as well as `inspect` and `inspect_value` overloads.
/// @relates inspector_access_type
template <class Inspector, class T>
constexpr auto nested_inspect_value_access_type() {
// Order: unsafe (error) > C Array > builtin_inspect > inspector_access >
// inspect_value > inspect > defaults.
// This is the same as in inspect_object_access_type, except that we pick up
// inspect_value overloads.
using namespace detail;
if constexpr (is_allowed_unsafe_message_type_v<T>)
return inspector_access_type::unsafe{};
else if constexpr (std::is_array<T>::value)
return inspector_access_type::array{};
else if constexpr (has_builtin_inspect<Inspector, T>::value)
return inspector_access_type::builtin{};
else if constexpr (is_trivial_inspector_value_v<Inspector::is_loading, T>)
return inspector_access_type::trivial{};
else if constexpr (std::is_integral<T>::value)
return inspector_access_type::integral{};
else if constexpr (std::is_enum<T>::value)
return inspector_access_type::enumeration{};
else if constexpr (std::is_empty<T>::value)
return inspector_access_type::empty{};
else if constexpr (is_stl_tuple_type_v<T>)
......
......@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT load_inspector {
public:
// -- member types -----------------------------------------------------------
using result_type = bool;
using result_type [[deprecated("inspectors always return bool")]] = bool;
// -- constants --------------------------------------------------------------
/// Convenience constant to indicate success of a processing step.
static constexpr bool ok = true;
/// Convenience constant to indicate that a processing step failed and no
/// further processing steps should take place.
static constexpr bool stop = false;
/// Enables dispatching on the inspector type.
static constexpr bool is_loading = true;
......
......@@ -18,6 +18,10 @@
#pragma once
#include <array>
#include <tuple>
#include <utility>
#include "caf/inspector_access.hpp"
#include "caf/load_inspector.hpp"
......@@ -37,35 +41,131 @@ public:
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()};
}
// -- dispatching to load/load functions -------------------------------------
template <class T>
bool list(T& xs) {
xs.clear();
auto size = size_t{0};
if (!dref().begin_sequence(size))
return false;
for (size_t i = 0; i < size; ++i) {
auto val = typename T::value_type{};
if (!detail::load(dref(), val))
return false;
xs.insert(xs.end(), std::move(val));
}
return dref().end_sequence();
}
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);
bool map(T& xs) {
xs.clear();
auto size = size_t{0};
if (!dref().begin_associative_array(size))
return false;
for (size_t i = 0; i < size; ++i) {
auto key = typename T::key_type{};
auto val = typename T::mapped_type{};
if (!(dref().begin_key_value_pair() //
&& detail::load(dref(), key) //
&& detail::load(dref(), val) //
&& dref().end_key_value_pair()))
return false;
// A multimap returns an iterator, a regular map returns a pair.
auto emplace_result = xs.emplace(std::move(key), std::move(val));
if constexpr (detail::is_pair<decltype(emplace_result)>::value) {
if (!emplace_result.second) {
dref().emplace_error(sec::runtime_error, "multiple key definitions");
return false;
}
}
}
return dref().end_associative_array();
}
template <class... Ts>
[[nodiscard]] bool apply_objects(Ts&... xs) {
return (apply_object(xs) && ...);
template <class T, size_t... Is>
bool tuple(T& xs, std::index_sequence<Is...>) {
return dref().begin_tuple(sizeof...(Is))
&& (detail::load(dref(), get<Is>(xs)) && ...) //
&& dref().end_tuple();
}
template <class T>
bool apply_value(T& x) {
return detail::load_value(dref(), x);
bool tuple(T& xs) {
return tuple(xs, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template <class T, size_t N>
bool tuple(T (&xs)[N]) {
if (!dref().begin_tuple(N))
return false;
for (size_t index = 0; index < N; ++index)
if (!detail::load(dref(), xs[index]))
return false;
return dref().end_tuple();
}
// -- dispatching to load/load functions -------------------------------------
template <class T>
[[nodiscard]] bool apply(T& x) {
static_assert(!std::is_const<T>::value);
return detail::load(dref(), x);
}
/// Deerializes a primitive value with getter / setter access.
template <class Get, class Set>
bool apply_value(Get&& get, Set&& set) {
using field_type = std::decay_t<decltype(get())>;
auto tmp = field_type{};
if (apply_value(tmp)) {
if (set(std::move(tmp)))
return true;
this->set_error(sec::load_callback_failed);
[[nodiscard]] bool apply(Get&& get, Set&& set) {
using value_type = std::decay_t<decltype(get())>;
auto tmp = value_type{};
using setter_result = decltype(set(std::move(tmp)));
if constexpr (std::is_same<setter_result, bool>::value) {
if (dref().value(tmp))
return set(std::move(tmp));
else
return false;
} else {
static_assert(std::is_convertible<setter_result, error>::value);
if (dref().value(tmp)) {
if (auto err = set(std::move(tmp)); !err) {
return true;
} else {
this->emplace_error(std::move(err));
return false;
}
} else {
return false;
}
}
}
// -- deprecated API: remove with CAF 0.19 -----------------------------------
template <class T>
[[deprecated("auto-conversion to underlying type is unsafe, add inspect")]] //
std::enable_if_t<std::is_enum<T>::value, bool>
opaque_value(T& val) {
auto tmp = std::underlying_type_t<T>{0};
if (dref().value(tmp)) {
val = static_cast<T>(tmp);
return true;
} else {
return false;
}
return false;
}
template <class T>
[[deprecated("use apply instead")]] bool apply_object(T& x) {
return detail::save(dref(), x);
}
template <class... Ts>
[[deprecated("use apply instead")]] bool apply_objects(Ts&... xs) {
return (apply(xs) && ...);
}
template <class T>
[[deprecated("use apply instead")]] bool apply_value(T& x) {
return detail::save(dref(), x);
}
private:
......
......@@ -229,28 +229,15 @@ constexpr message_id make_message_id(message_priority p) {
// -- inspection support -------------------------------------------------------
template <>
struct inspector_access<message_id> : inspector_access_base<message_id> {
template <class Inspector>
static bool apply_object(Inspector& f, message_id& x) {
auto get = [&x] { return x.integer_value(); };
auto set = [&x](uint64_t val) {
x = message_id{val};
return true;
};
return f.object(x).fields(f.field("value", get, set));
}
template <class Inspector>
static bool apply_value(Inspector& f, message_id& x) {
auto get = [&x] { return x.integer_value(); };
auto set = [&x](uint64_t val) {
x = message_id{val};
return true;
};
return detail::split_save_load(f, get, set);
}
};
template <class Inspector>
bool inspect(Inspector& f, message_id& x) {
auto get = [&x] { return x.integer_value(); };
auto set = [&x](uint64_t val) {
x = message_id{val};
return true;
};
return f.apply(get, set);
}
} // namespace caf
......
......@@ -20,7 +20,9 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp"
......@@ -80,8 +82,21 @@ enum class pec : uint8_t {
invalid_state,
};
/// @relates pec
CAF_CORE_EXPORT std::string to_string(pec);
/// @relates pec
CAF_CORE_EXPORT bool from_string(string_view, pec&);
/// @relates pec
CAF_CORE_EXPORT bool from_integer(std::underlying_type_t<pec>, pec&);
/// @relates pec
template <class Inspector>
bool inspect(Inspector& f, pec& x) {
return default_enum_inspect(f, x);
}
} // namespace caf
CAF_ERROR_CODE_ENUM(pec)
......@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT save_inspector {
public:
// -- member types -----------------------------------------------------------
using result_type = bool;
using result_type [[deprecated("inspectors always return bool")]] = bool;
// -- constants --------------------------------------------------------------
/// Convenience constant to indicate success of a processing step.
static constexpr bool ok = true;
/// Convenience constant to indicate that a processing step failed and no
/// further processing steps should take place.
static constexpr bool stop = false;
/// Enables dispatching on the inspector type.
static constexpr bool is_loading = false;
......
......@@ -30,41 +30,104 @@ public:
using super = save_inspector;
// -- DSL entry point --------------------------------------------------------
// -- DSL entry points -------------------------------------------------------
template <class T>
constexpr auto object(T&) noexcept {
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()};
}
// -- dispatching to load/save functions -------------------------------------
template <class T>
bool list(const T& xs) {
using value_type = typename T::value_type;
auto size = xs.size();
if (!dref().begin_sequence(size))
return false;
for (auto&& val : xs) {
using found_type = std::decay_t<decltype(val)>;
if constexpr (std::is_same<found_type, value_type>::value) {
if (!detail::save(dref(), val))
return false;
} else {
// Deals with atrocities like std::vector<bool>.
auto tmp = static_cast<value_type>(val);
if (!detail::save(dref(), tmp))
return false;
}
}
return dref().end_sequence();
}
template <class T>
[[nodiscard]] bool apply_object(T& x) {
constexpr auto token = inspect_object_access_type<Subtype, T>();
return detail::save_object(dref(), x, token);
bool map(const T& xs) {
if (!dref().begin_associative_array(xs.size()))
return false;
for (auto&& kvp : xs) {
if (!(dref().begin_key_value_pair() //
&& detail::save(dref(), kvp.first) //
&& detail::save(dref(), kvp.second) //
&& dref().end_key_value_pair()))
return false;
}
return dref().end_associative_array();
}
template <class T, size_t... Is>
bool tuple(const T& xs, std::index_sequence<Is...>) {
return dref().begin_tuple(sizeof...(Is)) //
&& (detail::save(dref(), get<Is>(xs)) && ...) //
&& dref().end_tuple();
}
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);
bool tuple(const T& xs) {
return tuple(xs, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template <class... Ts>
[[nodiscard]] bool apply_objects(Ts&&... xs) {
return (apply_object(xs) && ...);
template <class T, size_t N>
bool tuple(T (&xs)[N]) {
if (!dref().begin_tuple(N))
return false;
for (size_t index = 0; index < N; ++index)
if (!detail::save(dref(), xs[index]))
return false;
return dref().end_tuple();
}
// -- dispatching to load/save functions -------------------------------------
template <class T>
[[nodiscard]] bool apply_value(T& x) {
return detail::save_value(dref(), x);
[[nodiscard]] bool apply(const T& x) {
return detail::save(dref(), x);
}
template <class Get, class Set>
[[nodiscard]] bool apply_value(Get&& get, Set&&) {
auto&& x = get();
return detail::save_value(dref(), x);
[[nodiscard]] bool apply(Get&& get, Set&&) {
return detail::save(dref(), get());
}
// -- deprecated API: remove with CAF 0.19 -----------------------------------
template <class T>
[[deprecated("auto-conversion to underlying type is unsafe, add inspect")]] //
std::enable_if_t<std::is_enum<T>::value, bool>
opaque_value(T val) {
return dref().value(static_cast<std::underlying_type_t<T>>(val));
}
template <class T>
[[deprecated("use apply instead")]] bool apply_object(const T& x) {
return apply(x);
}
template <class... Ts>
[[deprecated("use apply instead")]] bool apply_objects(const Ts&... xs) {
return (apply(xs) && ...);
}
template <class T>
[[deprecated("use apply instead")]] bool apply_value(const T& x) {
return apply(x);
}
private:
......
......@@ -24,7 +24,9 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp"
......@@ -167,6 +169,18 @@ enum class sec : uint8_t {
/// @relates sec
CAF_CORE_EXPORT std::string to_string(sec);
/// @relates sec
CAF_CORE_EXPORT bool from_string(string_view, sec&);
/// @relates sec
CAF_CORE_EXPORT bool from_integer(std::underlying_type_t<sec>, sec&);
/// @relates sec
template <class Inssector>
bool inspect(Inssector& f, sec& x) {
return default_enum_inspect(f, x);
}
} // namespace caf
CAF_ERROR_CODE_ENUM(sec)
......@@ -22,10 +22,12 @@
#include <cstdint>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include "caf/byte.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
......@@ -40,6 +42,10 @@ namespace caf {
/// Technology-independent serialization interface.
class CAF_CORE_EXPORT serializer : public save_inspector_base<serializer> {
public:
// -- member types -----------------------------------------------------------
using super = save_inspector_base<serializer>;
// -- constructors, destructors, and assignment operators --------------------
explicit serializer(actor_system& sys) noexcept;
......@@ -116,9 +122,12 @@ public:
/// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array();
/// Adds the primitive type `x` to the output.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
/// Adds `x` to the output.
/// @param x A value for a builtin type.
/// @returns `true` on success, `false` otherwise.
virtual bool value(byte x) = 0;
/// @copydoc value
virtual bool value(bool x) = 0;
/// @copydoc value
......@@ -145,6 +154,12 @@ public:
/// @copydoc value
virtual bool value(uint64_t x) = 0;
/// @copydoc value
template <class T>
std::enable_if_t<std::is_integral<T>::value, bool> value(T x) {
return value(static_cast<detail::squashed_int_t<T>>(x));
}
/// @copydoc value
virtual bool value(float x) = 0;
......@@ -168,10 +183,12 @@ public:
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual bool value(span<const byte> x) = 0;
using super::list;
/// Adds each boolean in `xs` to the output. Derived classes can override this
/// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format.
virtual bool value(const std::vector<bool>& xs);
virtual bool list(const std::vector<bool>& xs);
protected:
/// Provides access to the ::proxy_registry and to the ::actor_system.
......
......@@ -18,14 +18,18 @@
#pragma once
#include <cstdint>
#include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
namespace caf {
/// Categorizes individual streams.
enum class stream_priority {
enum class stream_priority : uint8_t {
/// Denotes soft-realtime traffic.
very_high,
/// Denotes time-sensitive traffic.
......@@ -38,9 +42,20 @@ enum class stream_priority {
very_low
};
/// Stores the number of `stream_priority` classes.
static constexpr size_t stream_priorities = 5;
/// @relates stream_priority
CAF_CORE_EXPORT std::string to_string(stream_priority x);
/// @relates stream_priority
CAF_CORE_EXPORT bool from_string(string_view, stream_priority&);
/// @relates stream_priority
CAF_CORE_EXPORT bool from_integer(std::underlying_type_t<stream_priority>,
stream_priority&);
/// @relates stream_priority
template <class Inspector>
bool inspect(Inspector& f, stream_priority& x) {
return default_enum_inspect(f, x);
}
} // namespace caf
......@@ -260,35 +260,14 @@ CAF_CORE_EXPORT expected<uri> make_uri(string_view str);
template <>
struct inspector_access<uri> : inspector_access_base<uri> {
template <class Inspector>
static bool apply_object(Inspector& f, uri& x) {
static bool apply(Inspector& f, uri& x) {
if (f.has_human_readable_format()) {
// TODO: extend the inspector DSL to promote string_view to std::string
// automatically to avoid unnecessary to_string conversions.
auto get = [&x] { return to_string(x); };
// TODO: setters should be able to return the error directly to avoid loss
// of information.
auto set = [&x](std::string str) {
auto err = parse(str, x);
return err.empty();
return !err;
};
return f.object(x).fields(f.field("value", get, set));
} else {
if constexpr (Inspector::is_loading)
if (!x.impl_->unique())
x.impl_.reset(new uri::impl_type, false);
return inspect(f, *x.impl_);
}
}
template <class Inspector>
static bool apply_value(Inspector& f, uri& x) {
if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) {
auto err = parse(str, x);
return err.empty();
};
return detail::split_save_load(f, get, set);
return f.apply(get, set);
} else {
if constexpr (Inspector::is_loading)
if (!x.impl_->unique())
......
......@@ -139,7 +139,7 @@ bool binary_serializer::value(span<const byte> x) {
}
write_pos_ += x.size();
CAF_ASSERT(write_pos_ <= buf_.size());
return ok;
return true;
}
bool binary_serializer::value(byte x) {
......@@ -148,7 +148,7 @@ bool binary_serializer::value(byte x) {
else
buf_[write_pos_] = x;
++write_pos_;
return ok;
return true;
}
bool binary_serializer::value(bool x) {
......
......@@ -448,6 +448,17 @@ bool pull(config_value_reader& reader, T& x) {
} // namespace
bool config_value_reader::value(byte& x) {
CHECK_NOT_EMPTY();
auto tmp = uint8_t{0};
if (pull(*this, tmp)) {
x = static_cast<byte>(tmp);
return true;
} else {
return false;
}
}
bool config_value_reader::value(bool& x) {
CHECK_NOT_EMPTY();
return pull(*this, x);
......
......@@ -339,6 +339,10 @@ bool config_value_writer::end_associative_array() {
return true;
}
bool config_value_writer::value(byte x) {
return push(config_value{static_cast<config_value::integer>(x)});
}
bool config_value_writer::value(bool x) {
return push(config_value{x});
}
......
......@@ -51,7 +51,7 @@ bool deserializer::end_associative_array() {
return end_sequence();
}
bool deserializer::value(std::vector<bool>& x) {
bool deserializer::list(std::vector<bool>& x) {
x.clear();
size_t size = 0;
if (!begin_sequence(size))
......
......@@ -107,6 +107,11 @@ bool serialized_size_inspector::end_sequence() {
return true;
}
bool serialized_size_inspector::value(byte x) {
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(bool) {
result += sizeof(uint8_t);
return true;
......@@ -194,7 +199,7 @@ bool serialized_size_inspector::value(span<const byte> x) {
return true;
}
bool serialized_size_inspector::value(const std::vector<bool>& xs) {
bool serialized_size_inspector::list(const std::vector<bool>& xs) {
CAF_IGNORE_UNUSED(begin_sequence(xs.size()));
result += (xs.size() + static_cast<size_t>(xs.size() % 8 != 0)) / 8;
return end_sequence();
......
......@@ -57,7 +57,7 @@ bool stringification_inspector::begin_object(string_view name) {
} else {
in_string_object_ = true;
}
return ok;
return true;
}
bool stringification_inspector::end_object() {
......@@ -65,11 +65,11 @@ bool stringification_inspector::end_object() {
result_ += ')';
else
in_string_object_ = false;
return ok;
return true;
}
bool stringification_inspector::begin_field(string_view) {
return ok;
return true;
}
bool stringification_inspector::begin_field(string_view, bool is_present) {
......@@ -78,12 +78,12 @@ bool stringification_inspector::begin_field(string_view, bool is_present) {
result_ += "null";
else
result_ += '*';
return ok;
return true;
}
bool stringification_inspector::begin_field(string_view, span<const type_id_t>,
size_t) {
return ok;
return true;
}
bool stringification_inspector::begin_field(string_view, bool is_present,
......@@ -93,22 +93,26 @@ bool stringification_inspector::begin_field(string_view, bool is_present,
result_ += "null";
else
result_ += '*';
return ok;
return true;
}
bool stringification_inspector::end_field() {
return ok;
return true;
}
bool stringification_inspector::begin_sequence(size_t) {
sep();
result_ += '[';
return ok;
return true;
}
bool stringification_inspector::end_sequence() {
result_ += ']';
return ok;
return true;
}
bool stringification_inspector::value(byte x) {
return value(span<const byte>(&x, 1));
}
bool stringification_inspector::value(bool x) {
......@@ -228,7 +232,13 @@ bool stringification_inspector::int_value(uint64_t x) {
return true;
}
bool stringification_inspector::value(const std::vector<bool>& xs) {
bool stringification_inspector::value(span<const byte> x) {
sep();
detail::append_hex(result_, x.data(), x.size());
return true;
}
bool stringification_inspector::list(const std::vector<bool>& xs) {
begin_sequence(xs.size());
for (bool x : xs)
value(x);
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -34,6 +35,55 @@ std::string to_string(exit_reason x) {
};
}
bool from_string(string_view in, exit_reason& out) {
if (in == "normal") {
out = exit_reason::normal;
return true;
} else if (in == "unhandled_exception") {
out = exit_reason::unhandled_exception;
return true;
} else if (in == "unknown") {
out = exit_reason::unknown;
return true;
} else if (in == "out_of_workers") {
out = exit_reason::out_of_workers;
return true;
} else if (in == "user_shutdown") {
out = exit_reason::user_shutdown;
return true;
} else if (in == "kill") {
out = exit_reason::kill;
return true;
} else if (in == "remote_link_unreachable") {
out = exit_reason::remote_link_unreachable;
return true;
} else if (in == "unreachable") {
out = exit_reason::unreachable;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<exit_reason> in,
exit_reason& out) {
auto result = static_cast<exit_reason>(in);
switch(result) {
default:
return false;
case exit_reason::normal:
case exit_reason::unhandled_exception:
case exit_reason::unknown:
case exit_reason::out_of_workers:
case exit_reason::user_shutdown:
case exit_reason::kill:
case exit_reason::remote_link_unreachable:
case exit_reason::unreachable:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -25,6 +26,35 @@ std::string to_string(inbox_result x) {
};
}
bool from_string(string_view in, inbox_result& out) {
if (in == "success") {
out = inbox_result::success;
return true;
} else if (in == "unblocked_reader") {
out = inbox_result::unblocked_reader;
return true;
} else if (in == "queue_closed") {
out = inbox_result::queue_closed;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<inbox_result> in,
inbox_result& out) {
auto result = static_cast<inbox_result>(in);
switch(result) {
default:
return false;
case inbox_result::success:
case inbox_result::unblocked_reader:
case inbox_result::queue_closed:
out = result;
return true;
};
}
} // namespace intrusive
} // namespace caf
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -27,6 +28,39 @@ std::string to_string(task_result x) {
};
}
bool from_string(string_view in, task_result& out) {
if (in == "resume") {
out = task_result::resume;
return true;
} else if (in == "skip") {
out = task_result::skip;
return true;
} else if (in == "stop") {
out = task_result::stop;
return true;
} else if (in == "stop_all") {
out = task_result::stop_all;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<task_result> in,
task_result& out) {
auto result = static_cast<task_result>(in);
switch(result) {
default:
return false;
case task_result::resume:
case task_result::skip:
case task_result::stop:
case task_result::stop_all:
out = result;
return true;
};
}
} // namespace intrusive
} // namespace caf
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -24,6 +25,35 @@ std::string to_string(invoke_message_result x) {
};
}
bool from_string(string_view in, invoke_message_result& out) {
if (in == "consumed") {
out = invoke_message_result::consumed;
return true;
} else if (in == "skipped") {
out = invoke_message_result::skipped;
return true;
} else if (in == "dropped") {
out = invoke_message_result::dropped;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<invoke_message_result> in,
invoke_message_result& out) {
auto result = static_cast<invoke_message_result>(in);
switch(result) {
default:
return false;
case invoke_message_result::consumed:
case invoke_message_result::skipped:
case invoke_message_result::dropped:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -602,7 +602,7 @@ void logger::log_first_line() {
msg += to_string(system_.node());
msg += ", excluded-components = ";
detail::stringification_inspector f{msg};
detail::save_value(f, filter);
detail::save(f, filter);
return msg;
};
namespace lg = defaults::logger;
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -22,6 +23,31 @@ std::string to_string(message_priority x) {
};
}
bool from_string(string_view in, message_priority& out) {
if (in == "high") {
out = message_priority::high;
return true;
} else if (in == "normal") {
out = message_priority::normal;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<message_priority> in,
message_priority& out) {
auto result = static_cast<message_priority>(in);
switch(result) {
default:
return false;
case message_priority::high:
case message_priority::normal:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -64,6 +65,115 @@ std::string to_string(pec x) {
};
}
bool from_string(string_view in, pec& out) {
if (in == "success") {
out = pec::success;
return true;
} else if (in == "trailing_character") {
out = pec::trailing_character;
return true;
} else if (in == "unexpected_eof") {
out = pec::unexpected_eof;
return true;
} else if (in == "unexpected_character") {
out = pec::unexpected_character;
return true;
} else if (in == "timespan_overflow") {
out = pec::timespan_overflow;
return true;
} else if (in == "fractional_timespan") {
out = pec::fractional_timespan;
return true;
} else if (in == "too_many_characters") {
out = pec::too_many_characters;
return true;
} else if (in == "invalid_escape_sequence") {
out = pec::invalid_escape_sequence;
return true;
} else if (in == "unexpected_newline") {
out = pec::unexpected_newline;
return true;
} else if (in == "integer_overflow") {
out = pec::integer_overflow;
return true;
} else if (in == "integer_underflow") {
out = pec::integer_underflow;
return true;
} else if (in == "exponent_underflow") {
out = pec::exponent_underflow;
return true;
} else if (in == "exponent_overflow") {
out = pec::exponent_overflow;
return true;
} else if (in == "type_mismatch") {
out = pec::type_mismatch;
return true;
} else if (in == "not_an_option") {
out = pec::not_an_option;
return true;
} else if (in == "invalid_argument") {
out = pec::invalid_argument;
return true;
} else if (in == "missing_argument") {
out = pec::missing_argument;
return true;
} else if (in == "invalid_category") {
out = pec::invalid_category;
return true;
} else if (in == "invalid_field_name") {
out = pec::invalid_field_name;
return true;
} else if (in == "repeated_field_name") {
out = pec::repeated_field_name;
return true;
} else if (in == "missing_field") {
out = pec::missing_field;
return true;
} else if (in == "invalid_range_expression") {
out = pec::invalid_range_expression;
return true;
} else if (in == "invalid_state") {
out = pec::invalid_state;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<pec> in,
pec& out) {
auto result = static_cast<pec>(in);
switch(result) {
default:
return false;
case pec::success:
case pec::trailing_character:
case pec::unexpected_eof:
case pec::unexpected_character:
case pec::timespan_overflow:
case pec::fractional_timespan:
case pec::too_many_characters:
case pec::invalid_escape_sequence:
case pec::unexpected_newline:
case pec::integer_overflow:
case pec::integer_underflow:
case pec::exponent_underflow:
case pec::exponent_overflow:
case pec::type_mismatch:
case pec::not_an_option:
case pec::invalid_argument:
case pec::missing_argument:
case pec::invalid_category:
case pec::invalid_field_name:
case pec::repeated_field_name:
case pec::missing_field:
case pec::invalid_range_expression:
case pec::invalid_state:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -144,6 +145,275 @@ std::string to_string(sec x) {
};
}
bool from_string(string_view in, sec& out) {
if (in == "none") {
out = sec::none;
return true;
} else if (in == "unexpected_message") {
out = sec::unexpected_message;
return true;
} else if (in == "unexpected_response") {
out = sec::unexpected_response;
return true;
} else if (in == "request_receiver_down") {
out = sec::request_receiver_down;
return true;
} else if (in == "request_timeout") {
out = sec::request_timeout;
return true;
} else if (in == "no_such_group_module") {
out = sec::no_such_group_module;
return true;
} else if (in == "no_actor_published_at_port") {
out = sec::no_actor_published_at_port;
return true;
} else if (in == "unexpected_actor_messaging_interface") {
out = sec::unexpected_actor_messaging_interface;
return true;
} else if (in == "state_not_serializable") {
out = sec::state_not_serializable;
return true;
} else if (in == "unsupported_sys_key") {
out = sec::unsupported_sys_key;
return true;
} else if (in == "unsupported_sys_message") {
out = sec::unsupported_sys_message;
return true;
} else if (in == "disconnect_during_handshake") {
out = sec::disconnect_during_handshake;
return true;
} else if (in == "cannot_forward_to_invalid_actor") {
out = sec::cannot_forward_to_invalid_actor;
return true;
} else if (in == "no_route_to_receiving_node") {
out = sec::no_route_to_receiving_node;
return true;
} else if (in == "failed_to_assign_scribe_from_handle") {
out = sec::failed_to_assign_scribe_from_handle;
return true;
} else if (in == "failed_to_assign_doorman_from_handle") {
out = sec::failed_to_assign_doorman_from_handle;
return true;
} else if (in == "cannot_close_invalid_port") {
out = sec::cannot_close_invalid_port;
return true;
} else if (in == "cannot_connect_to_node") {
out = sec::cannot_connect_to_node;
return true;
} else if (in == "cannot_open_port") {
out = sec::cannot_open_port;
return true;
} else if (in == "network_syscall_failed") {
out = sec::network_syscall_failed;
return true;
} else if (in == "invalid_argument") {
out = sec::invalid_argument;
return true;
} else if (in == "invalid_protocol_family") {
out = sec::invalid_protocol_family;
return true;
} else if (in == "cannot_publish_invalid_actor") {
out = sec::cannot_publish_invalid_actor;
return true;
} else if (in == "cannot_spawn_actor_from_arguments") {
out = sec::cannot_spawn_actor_from_arguments;
return true;
} else if (in == "end_of_stream") {
out = sec::end_of_stream;
return true;
} else if (in == "no_context") {
out = sec::no_context;
return true;
} else if (in == "unknown_type") {
out = sec::unknown_type;
return true;
} else if (in == "no_proxy_registry") {
out = sec::no_proxy_registry;
return true;
} else if (in == "runtime_error") {
out = sec::runtime_error;
return true;
} else if (in == "remote_linking_failed") {
out = sec::remote_linking_failed;
return true;
} else if (in == "cannot_add_upstream") {
out = sec::cannot_add_upstream;
return true;
} else if (in == "upstream_already_exists") {
out = sec::upstream_already_exists;
return true;
} else if (in == "invalid_upstream") {
out = sec::invalid_upstream;
return true;
} else if (in == "cannot_add_downstream") {
out = sec::cannot_add_downstream;
return true;
} else if (in == "downstream_already_exists") {
out = sec::downstream_already_exists;
return true;
} else if (in == "invalid_downstream") {
out = sec::invalid_downstream;
return true;
} else if (in == "no_downstream_stages_defined") {
out = sec::no_downstream_stages_defined;
return true;
} else if (in == "stream_init_failed") {
out = sec::stream_init_failed;
return true;
} else if (in == "invalid_stream_state") {
out = sec::invalid_stream_state;
return true;
} else if (in == "unhandled_stream_error") {
out = sec::unhandled_stream_error;
return true;
} else if (in == "bad_function_call") {
out = sec::bad_function_call;
return true;
} else if (in == "feature_disabled") {
out = sec::feature_disabled;
return true;
} else if (in == "cannot_open_file") {
out = sec::cannot_open_file;
return true;
} else if (in == "socket_invalid") {
out = sec::socket_invalid;
return true;
} else if (in == "socket_disconnected") {
out = sec::socket_disconnected;
return true;
} else if (in == "socket_operation_failed") {
out = sec::socket_operation_failed;
return true;
} else if (in == "unavailable_or_would_block") {
out = sec::unavailable_or_would_block;
return true;
} else if (in == "incompatible_versions") {
out = sec::incompatible_versions;
return true;
} else if (in == "incompatible_application_ids") {
out = sec::incompatible_application_ids;
return true;
} else if (in == "malformed_basp_message") {
out = sec::malformed_basp_message;
return true;
} else if (in == "serializing_basp_payload_failed") {
out = sec::serializing_basp_payload_failed;
return true;
} else if (in == "redundant_connection") {
out = sec::redundant_connection;
return true;
} else if (in == "remote_lookup_failed") {
out = sec::remote_lookup_failed;
return true;
} else if (in == "no_tracing_context") {
out = sec::no_tracing_context;
return true;
} else if (in == "all_requests_failed") {
out = sec::all_requests_failed;
return true;
} else if (in == "field_invariant_check_failed") {
out = sec::field_invariant_check_failed;
return true;
} else if (in == "field_value_synchronization_failed") {
out = sec::field_value_synchronization_failed;
return true;
} else if (in == "invalid_field_type") {
out = sec::invalid_field_type;
return true;
} else if (in == "unsafe_type") {
out = sec::unsafe_type;
return true;
} else if (in == "save_callback_failed") {
out = sec::save_callback_failed;
return true;
} else if (in == "load_callback_failed") {
out = sec::load_callback_failed;
return true;
} else if (in == "conversion_failed") {
out = sec::conversion_failed;
return true;
} else if (in == "connection_closed") {
out = sec::connection_closed;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<sec> in,
sec& out) {
auto result = static_cast<sec>(in);
switch(result) {
default:
return false;
case sec::none:
case sec::unexpected_message:
case sec::unexpected_response:
case sec::request_receiver_down:
case sec::request_timeout:
case sec::no_such_group_module:
case sec::no_actor_published_at_port:
case sec::unexpected_actor_messaging_interface:
case sec::state_not_serializable:
case sec::unsupported_sys_key:
case sec::unsupported_sys_message:
case sec::disconnect_during_handshake:
case sec::cannot_forward_to_invalid_actor:
case sec::no_route_to_receiving_node:
case sec::failed_to_assign_scribe_from_handle:
case sec::failed_to_assign_doorman_from_handle:
case sec::cannot_close_invalid_port:
case sec::cannot_connect_to_node:
case sec::cannot_open_port:
case sec::network_syscall_failed:
case sec::invalid_argument:
case sec::invalid_protocol_family:
case sec::cannot_publish_invalid_actor:
case sec::cannot_spawn_actor_from_arguments:
case sec::end_of_stream:
case sec::no_context:
case sec::unknown_type:
case sec::no_proxy_registry:
case sec::runtime_error:
case sec::remote_linking_failed:
case sec::cannot_add_upstream:
case sec::upstream_already_exists:
case sec::invalid_upstream:
case sec::cannot_add_downstream:
case sec::downstream_already_exists:
case sec::invalid_downstream:
case sec::no_downstream_stages_defined:
case sec::stream_init_failed:
case sec::invalid_stream_state:
case sec::unhandled_stream_error:
case sec::bad_function_call:
case sec::feature_disabled:
case sec::cannot_open_file:
case sec::socket_invalid:
case sec::socket_disconnected:
case sec::socket_operation_failed:
case sec::unavailable_or_would_block:
case sec::incompatible_versions:
case sec::incompatible_application_ids:
case sec::malformed_basp_message:
case sec::serializing_basp_payload_failed:
case sec::redundant_connection:
case sec::remote_lookup_failed:
case sec::no_tracing_context:
case sec::all_requests_failed:
case sec::field_invariant_check_failed:
case sec::field_value_synchronization_failed:
case sec::invalid_field_type:
case sec::unsafe_type:
case sec::save_callback_failed:
case sec::load_callback_failed:
case sec::conversion_failed:
case sec::connection_closed:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -51,7 +51,7 @@ bool serializer::end_associative_array() {
return end_sequence();
}
bool serializer::value(const std::vector<bool>& xs) {
bool serializer::list(const std::vector<bool>& xs) {
if (!begin_sequence(xs.size()))
return false;
for (bool x : xs)
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -28,6 +29,43 @@ std::string to_string(stream_priority x) {
};
}
bool from_string(string_view in, stream_priority& out) {
if (in == "very_high") {
out = stream_priority::very_high;
return true;
} else if (in == "high") {
out = stream_priority::high;
return true;
} else if (in == "normal") {
out = stream_priority::normal;
return true;
} else if (in == "low") {
out = stream_priority::low;
return true;
} else if (in == "very_low") {
out = stream_priority::very_low;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<stream_priority> in,
stream_priority& out) {
auto result = static_cast<stream_priority>(in);
switch(result) {
default:
return false;
case stream_priority::very_high:
case stream_priority::high:
case stream_priority::normal:
case stream_priority::low:
case stream_priority::very_low:
out = result;
return true;
};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -210,15 +210,15 @@ CAF_TEST(profilers record request / response messaging) {
"new: worker",
"new: server",
"new: client",
"client sends: message(int32_t(19), int32_t(23))",
"server got: message(int32_t(19), int32_t(23))",
"server sends: message(int32_t(19), int32_t(23))",
"client sends: message(19, 23)",
"server got: message(19, 23)",
"server sends: message(19, 23)",
"server consumed the message",
"delete: server",
"worker got: message(int32_t(19), int32_t(23))",
"worker sends: message(int32_t(42))",
"worker got: message(19, 23)",
"worker sends: message(42)",
"worker consumed the message",
"client got: message(int32_t(42))",
"client got: message(42)",
"client consumed the message",
"delete: worker",
"delete: client",
......
......@@ -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 (!sink.apply_object(hdl))
if (!sink.apply(hdl))
CAF_FAIL("serialization failed: " << sink.get_error());
CAF_MESSAGE("buf: " << buf);
actor hdl2;
binary_deserializer source{sys, buf};
if (!source.apply_object(hdl2))
if (!source.apply(hdl2))
CAF_FAIL("deserialization failed: " << source.get_error());
CAF_CHECK_EQUAL(hdl, hdl2);
anon_send_exit(hdl, exit_reason::user_shutdown);
......
......@@ -60,7 +60,7 @@ struct fixture {
template <class... Ts>
void load(const std::vector<byte>& buf, Ts&... xs) {
binary_deserializer source{nullptr, buf};
if (!source.apply_objects(xs...))
if (!(source.apply(xs) && ...))
CAF_FAIL("binary_deserializer failed to load: " << source.get_error());
}
......
......@@ -58,7 +58,7 @@ struct fixture {
auto save(const Ts&... xs) {
byte_buffer result;
binary_serializer sink{nullptr, result};
if (!sink.apply_objects(xs...))
if (!(sink.apply(xs) && ...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
return result;
}
......@@ -66,7 +66,7 @@ struct fixture {
template <class... Ts>
void save_to_buf(byte_buffer& data, const Ts&... xs) {
binary_serializer sink{nullptr, data};
if (!sink.apply_objects(xs...))
if (!(sink.apply(xs) && ...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
}
};
......
......@@ -43,13 +43,13 @@ constexpr i64 operator""_i64(unsigned long long int x) {
using i64_list = std::vector<i64>;
struct fixture {
settings xs;
config_value x;
template <class T>
void deserialize(const config_value& src, T& value) {
config_value_reader reader{&src};
if (!detail::load_value(reader, value))
CAF_FAIL("failed to deserialize from settings: " << reader.get_error());
if (!detail::load(reader, value))
CAF_FAIL("deserialization failed: " << reader.get_error());
}
template <class T>
......@@ -59,7 +59,7 @@ struct fixture {
template <class T>
void deserialize(T& value) {
return deserialize(xs, value);
return deserialize(x, value);
}
template <class T>
......@@ -71,7 +71,10 @@ struct fixture {
template <class T>
optional<T> get(string_view key) {
return get<T>(xs, key);
if (auto* xs = get_if<settings>(&x))
return get<T>(*xs, key);
else
CAF_FAIL("fixture does not contain a dictionary");
}
};
......@@ -81,12 +84,14 @@ CAF_TEST_FIXTURE_SCOPE(config_value_reader_tests, fixture)
CAF_TEST(readers deserialize builtin types from config values) {
std::string value;
auto& xs = x.as_dictionary();
put(xs, "foo", "bar");
deserialize(xs["foo"], value);
CAF_CHECK_EQUAL(value, "bar");
}
CAF_TEST(readers deserialize simple objects from configs) {
auto& xs = x.as_dictionary();
put(xs, "foo", "hello");
put(xs, "bar", "world");
foobar fb;
......@@ -97,6 +102,7 @@ CAF_TEST(readers deserialize simple objects from configs) {
CAF_TEST(readers deserialize complex objects from configs) {
CAF_MESSAGE("fill a dictionary with data for a 'basics' object");
auto& xs = x.as_dictionary();
put(xs, "v1", settings{});
put(xs, "v2", 42_i64);
put(xs, "v3", i64_list({1, 2, 3, 4}));
......@@ -140,11 +146,11 @@ CAF_TEST(readers deserialize objects from the output of writers) {
line l{{10, 20, 30}, {70, 60, 50}};
config_value tmp;
config_value_writer writer{&tmp};
if (!detail::save_value(writer, l))
if (!detail::save(writer, l))
CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(tmp))
CAF_FAIL("writer failed to produce a dictionary");
xs = std::move(caf::get<settings>(tmp));
x.as_dictionary() = std::move(caf::get<settings>(tmp));
}
CAF_MESSAGE("serialize and verify the 'line' object");
{
......
......@@ -41,17 +41,13 @@ constexpr i64 operator""_i64(unsigned long long int x) {
using i64_list = std::vector<i64>;
struct fixture {
settings xs;
config_value x;
template <class T>
void set(const T& value) {
config_value val;
config_value_writer writer{&val};
if (!detail::save_value(writer, value))
config_value_writer writer{&x};
if (!detail::save(writer, value))
CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(val))
CAF_FAIL("serializing T did not result in a dictionary");
xs = std::move(caf::get<settings>(val));
}
template <class T>
......@@ -63,7 +59,10 @@ struct fixture {
template <class T>
optional<T> get(string_view key) {
return get<T>(xs, key);
if (auto* xs = get_if<settings>(&x))
return get<T>(*xs, key);
else
CAF_FAIL("fixture does not contain a dictionary");
}
};
......@@ -101,7 +100,6 @@ CAF_TEST(empty types and maps become dictionaries) {
tst.v7["two"] = 2;
tst.v7["three"] = 3;
set(tst);
CAF_MESSAGE(xs);
CAF_CHECK_EQUAL(get<settings>("v1"), settings{});
CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64);
CAF_CHECK_EQUAL(get<i64_list>("v3"), i64_list({-1, -2, -3, -4}));
......@@ -128,4 +126,10 @@ CAF_TEST(empty types and maps become dictionaries) {
CAF_CHECK_EQUAL(get<config_value::list>("v8"), config_value::list());
}
CAF_TEST(custom inspect overloads may produce single values) {
auto tue = weekday::tuesday;
set(tue);
CAF_CHECK_EQUAL(x, "tuesday"s);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -200,6 +200,20 @@ enum class test_enum : int32_t {
// Implemented in serialization.cpp
std::string to_string(test_enum x);
template <class Inspector>
bool inspect(Inspector& f, test_enum& x) {
auto get = [&x] { return static_cast<int32_t>(x); };
auto set = [&x](int32_t val) {
if (val >= 0 && val <= 2) {
x = static_cast<test_enum>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
// Used in serializer.cpp and deserializer.cpp
struct test_data {
int32_t i32;
......@@ -231,10 +245,54 @@ enum class dummy_enum_class : short { foo, bar };
return x == dummy_enum_class::foo ? "foo" : "bar";
}
template <class Inspector>
bool inspect(Inspector& f, dummy_enum_class& x) {
auto get = [&x] { return static_cast<short>(x); };
auto set = [&x](short val) {
if (val >= 0 && val <= 1) {
x = static_cast<dummy_enum_class>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
enum class level { all, trace, debug, warning, error };
template <class Inspector>
bool inspect(Inspector& f, level& x) {
using integer_type = std::underlying_type_t<level>;
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
if (val >= 0 && val <= 4) {
x = static_cast<level>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
enum dummy_enum { de_foo, de_bar };
template <class Inspector>
bool inspect(Inspector& f, dummy_enum& x) {
using integer_type = std::underlying_type_t<dummy_enum>;
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
if (val >= 0 && val <= 1) {
x = static_cast<dummy_enum>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
// -- type IDs for for all unit test suites ------------------------------------
#define ADD_TYPE_ID(type) CAF_ADD_TYPE_ID(core_test, type)
......
......@@ -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 (!sink.apply_objects(xs...))
if (!(sink.apply(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 (!sink.apply_object(x))
if (!sink.apply(x))
CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf));
T y;
if (!source.apply_object(y))
if (!source.apply(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 (!sink.apply_object(x))
if (!sink.apply(x))
CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf));
T y;
if (!source.apply_object(y))
if (!source.apply(y))
CAF_FAIL("serialization failed: " << source.get_error());
return y;
}
......
......@@ -44,7 +44,7 @@ struct testee : deserializer {
bool load_field_failed(string_view, sec code) {
set_error(make_error(code));
return stop;
return false;
}
size_t indent = 0;
......@@ -63,14 +63,14 @@ struct testee : deserializer {
indent += 2;
log += "begin object ";
log.insert(log.end(), object_name.begin(), object_name.end());
return ok;
return true;
}
bool end_object() override {
indent -= 2;
new_line();
log += "end object";
return ok;
return true;
}
bool begin_field(string_view name) override {
......@@ -78,7 +78,7 @@ struct testee : deserializer {
indent += 2;
log += "begin field ";
log.insert(log.end(), name.begin(), name.end());
return ok;
return true;
}
bool begin_field(string_view name, bool& is_present) override {
......@@ -87,7 +87,7 @@ struct testee : deserializer {
log += "begin optional field ";
log.insert(log.end(), name.begin(), name.end());
is_present = false;
return ok;
return true;
}
bool begin_field(string_view name, span<const type_id_t>,
......@@ -97,7 +97,7 @@ struct testee : deserializer {
log += "begin variant field ";
log.insert(log.end(), name.begin(), name.end());
type_index = 0;
return ok;
return true;
}
bool begin_field(string_view name, bool& is_present, span<const type_id_t>,
......@@ -107,14 +107,14 @@ struct testee : deserializer {
log += "begin optional variant field ";
log.insert(log.end(), name.begin(), name.end());
is_present = false;
return ok;
return true;
}
bool end_field() override {
indent -= 2;
new_line();
log += "end field";
return ok;
return true;
}
bool begin_tuple(size_t size) override {
......@@ -122,28 +122,28 @@ struct testee : deserializer {
indent += 2;
log += "begin tuple of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_tuple() override {
indent -= 2;
new_line();
log += "end tuple";
return ok;
return true;
}
bool begin_key_value_pair() override {
new_line();
indent += 2;
log += "begin key-value pair";
return ok;
return true;
}
bool end_key_value_pair() override {
indent -= 2;
new_line();
log += "end key-value pair";
return ok;
return true;
}
bool begin_sequence(size_t& size) override {
......@@ -152,14 +152,14 @@ struct testee : deserializer {
indent += 2;
log += "begin sequence of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_sequence() override {
indent -= 2;
new_line();
log += "end sequence";
return ok;
return true;
}
bool begin_associative_array(size_t& size) override {
......@@ -168,21 +168,21 @@ struct testee : deserializer {
indent += 2;
log += "begin associative array of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_associative_array() override {
indent -= 2;
new_line();
log += "end associative array";
return ok;
return true;
}
bool value(bool& x) override {
new_line();
log += "bool value";
x = false;
return ok;
return true;
}
template <class T>
......@@ -192,7 +192,14 @@ struct testee : deserializer {
log.insert(log.end(), tn.begin(), tn.end());
log += " value";
x = T{};
return ok;
return true;
}
bool value(byte& x) override {
new_line();
log += "byte value";
x = byte{};
return true;
}
bool value(int8_t& x) override {
......@@ -256,7 +263,7 @@ struct testee : deserializer {
log += "caf::span<caf::byte> value";
for (auto& x : xs)
x = byte{0};
return ok;
return true;
}
};
......@@ -330,7 +337,7 @@ end object)_");
CAF_TEST(load inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(f.apply_object(x), true);
CAF_CHECK_EQUAL(f.apply(x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object anonymous
begin optional field value
......
......@@ -110,19 +110,16 @@ CAF_TEST(to_string converts messages to strings) {
CAF_CHECK_EQUAL(msg_as_string(), "message()");
CAF_CHECK_EQUAL(msg_as_string("hello", "world"),
R"__(message("hello", "world"))__");
CAF_CHECK_EQUAL(
msg_as_string(svec{"one", "two", "three"}),
R"__(message(std::vector<std::string>(["one", "two", "three"])))__");
CAF_CHECK_EQUAL(
msg_as_string(svec{"one", "two"}, "three", "four",
svec{"five", "six", "seven"}),
R"__(message(std::vector<std::string>(["one", "two"]), "three", "four", )__"
R"__(std::vector<std::string>(["five", "six", "seven"])))__");
CAF_CHECK_EQUAL(msg_as_string(svec{"one", "two", "three"}),
R"__(message(["one", "two", "three"]))__");
CAF_CHECK_EQUAL(msg_as_string(svec{"one", "two"}, "three", "four",
svec{"five", "six", "seven"}),
R"__(message(["one", "two"], "three", "four", )__"
R"__(["five", "six", "seven"]))__");
auto teststr = R"__(message("this is a \"test\""))__"; // fails inline on MSVC
CAF_CHECK_EQUAL(msg_as_string(R"__(this is a "test")__"), teststr);
CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5),
"message(std::tuple<int32_t, int32_t, int32_t>([1, 2, 3]), "
"int32_t(4), int32_t(5))");
"message([1, 2, 3], 4, 5)");
CAF_CHECK_EQUAL(msg_as_string(s1{}), "message(s1([10, 20, 30]))");
s2 tmp;
tmp.value[0][1] = 100;
......
......@@ -47,7 +47,7 @@ CAF_TEST(message builder can build messages incrermenetally) {
CAF_CHECK_EQUAL(builder.size(), 1u);
CAF_CHECK_EQUAL(msg.types(), make_type_id_list<int32_t>());
CAF_CHECK_EQUAL(to_string(msg.types()), "[int32_t]");
CAF_CHECK_EQUAL(to_string(msg), "message(int32_t(1))");
CAF_CHECK_EQUAL(to_string(msg), "message(1)");
}
STEP("after adding [2, 3], the message is (1, 2, 3)") {
std::vector<int32_t> xs{2, 3};
......@@ -57,15 +57,13 @@ CAF_TEST(message builder can build messages incrermenetally) {
CAF_CHECK_EQUAL(msg.types(),
(make_type_id_list<int32_t, int32_t, int32_t>()));
CAF_CHECK_EQUAL(to_string(msg.types()), "[int32_t, int32_t, int32_t]");
CAF_CHECK_EQUAL(to_string(msg),
"message(int32_t(1), int32_t(2), int32_t(3))");
CAF_CHECK_EQUAL(to_string(msg), "message(1, 2, 3)");
}
STEP("moving the content to a message produces the same message again") {
auto msg = builder.move_to_message();
CAF_CHECK_EQUAL(msg.types(),
(make_type_id_list<int32_t, int32_t, int32_t>()));
CAF_CHECK_EQUAL(to_string(msg.types()), "[int32_t, int32_t, int32_t]");
CAF_CHECK_EQUAL(to_string(msg),
"message(int32_t(1), int32_t(2), int32_t(3))");
CAF_CHECK_EQUAL(to_string(msg), "message(1, 2, 3)");
}
}
......@@ -28,22 +28,18 @@ 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); };
return f.object(x).fields(f.field("value", get, set));
return f.apply(get, set);
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_object(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);
auto get = [&x] { return static_cast<uint8_t>(x); };
auto set = [&x](uint8_t val) {
if (val < 7) {
x = static_cast<weekday>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
}
......
......@@ -33,7 +33,7 @@ node_id roundtrip(node_id nid) {
byte_buffer buf;
{
binary_serializer sink{nullptr, buf};
if (!sink.apply_object(nid))
if (!sink.apply(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 (!source.apply_object(result))
if (!source.apply(result))
CAF_FAIL("deserialization failed: " << source.get_error());
if (source.remaining() > 0)
CAF_FAIL("binary_serializer ignored part of its input");
......
......@@ -54,7 +54,7 @@ struct testee : serializer {
log += "next object type: ";
auto tn = detail::global_meta_object(type)->type_name;
log.insert(log.end(), tn.begin(), tn.end());
return ok;
return true;
}
bool begin_object(string_view object_name) override {
......@@ -62,7 +62,7 @@ struct testee : serializer {
indent += 2;
log += "begin object ";
log.insert(log.end(), object_name.begin(), object_name.end());
return ok;
return true;
}
bool end_object() override {
......@@ -71,7 +71,7 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end object";
return ok;
return true;
}
bool begin_field(string_view name) override {
......@@ -79,7 +79,7 @@ struct testee : serializer {
indent += 2;
log += "begin field ";
log.insert(log.end(), name.begin(), name.end());
return ok;
return true;
}
bool begin_field(string_view name, bool) override {
......@@ -87,7 +87,7 @@ struct testee : serializer {
indent += 2;
log += "begin optional field ";
log.insert(log.end(), name.begin(), name.end());
return ok;
return true;
}
bool begin_field(string_view name, span<const type_id_t>, size_t) override {
......@@ -95,7 +95,7 @@ struct testee : serializer {
indent += 2;
log += "begin variant field ";
log.insert(log.end(), name.begin(), name.end());
return ok;
return true;
}
bool begin_field(string_view name, bool, span<const type_id_t>,
......@@ -104,7 +104,7 @@ struct testee : serializer {
indent += 2;
log += "begin optional variant field ";
log.insert(log.end(), name.begin(), name.end());
return ok;
return true;
}
bool end_field() override {
......@@ -113,7 +113,7 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end field";
return ok;
return true;
}
bool begin_tuple(size_t size) override {
......@@ -121,7 +121,7 @@ struct testee : serializer {
indent += 2;
log += "begin tuple of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_tuple() override {
......@@ -130,14 +130,14 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end tuple";
return ok;
return true;
}
bool begin_key_value_pair() override {
new_line();
indent += 2;
log += "begin key-value pair";
return ok;
return true;
}
bool end_key_value_pair() override {
......@@ -146,7 +146,7 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end key-value pair";
return ok;
return true;
}
bool begin_sequence(size_t size) override {
......@@ -154,7 +154,7 @@ struct testee : serializer {
indent += 2;
log += "begin sequence of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_sequence() override {
......@@ -163,7 +163,7 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end sequence";
return ok;
return true;
}
bool begin_associative_array(size_t size) override {
......@@ -171,7 +171,7 @@ struct testee : serializer {
indent += 2;
log += "begin associative array of size ";
log += std::to_string(size);
return ok;
return true;
}
bool end_associative_array() override {
......@@ -180,103 +180,109 @@ struct testee : serializer {
indent -= 2;
new_line();
log += "end associative array";
return ok;
return true;
}
bool value(byte) override {
new_line();
log += "byte value";
return true;
}
bool value(bool) override {
new_line();
log += "bool value";
return ok;
return true;
}
bool value(int8_t) override {
new_line();
log += "int8_t value";
return ok;
return true;
}
bool value(uint8_t) override {
new_line();
log += "uint8_t value";
return ok;
return true;
}
bool value(int16_t) override {
new_line();
log += "int16_t value";
return ok;
return true;
}
bool value(uint16_t) override {
new_line();
log += "uint16_t value";
return ok;
return true;
}
bool value(int32_t) override {
new_line();
log += "int32_t value";
return ok;
return true;
}
bool value(uint32_t) override {
new_line();
log += "uint32_t value";
return ok;
return true;
}
bool value(int64_t) override {
new_line();
log += "int64_t value";
return ok;
return true;
}
bool value(uint64_t) override {
new_line();
log += "uint64_t value";
return ok;
return true;
}
bool value(float) override {
new_line();
log += "float value";
return ok;
return true;
}
bool value(double) override {
new_line();
log += "double value";
return ok;
return true;
}
bool value(long double) override {
new_line();
log += "long double value";
return ok;
return true;
}
bool value(string_view) override {
new_line();
log += "std::string value";
return ok;
return true;
}
bool value(const std::u16string&) override {
new_line();
log += "std::u16string value";
return ok;
return true;
}
bool value(const std::u32string&) override {
new_line();
log += "std::u32string value";
return ok;
return true;
}
bool value(span<const byte>) override {
new_line();
log += "byte_span value";
return ok;
return true;
}
};
......@@ -290,7 +296,7 @@ CAF_TEST_FIXTURE_SCOPE(load_inspector_tests, fixture)
CAF_TEST(save inspectors can visit C arrays) {
int32_t xs[] = {1, 2, 3};
CAF_CHECK_EQUAL(detail::save_value(f, xs), true);
CAF_CHECK_EQUAL(detail::save(f, xs), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin tuple of size 3
int32_t value
......@@ -436,7 +442,7 @@ end object)_");
CAF_TEST(save inspectors support optional) {
optional<int32_t> x;
CAF_CHECK_EQUAL(f.apply_object(x), true);
CAF_CHECK_EQUAL(f.apply(x), true);
CAF_CHECK_EQUAL(f.log, R"_(
begin object anonymous
begin optional field value
......@@ -717,21 +723,9 @@ begin object message
end field
begin field values
begin tuple of size 3
begin object int32_t
begin field value
int32_t value
end field
end object
begin object std::string
begin field value
std::string value
end field
end object
begin object double
begin field value
double value
end field
end object
int32_t value
std::string value
double value
end tuple
end field
end object)_");
......@@ -744,23 +738,11 @@ begin object message
begin field values
begin sequence of size 3
next object type: int32_t
begin object int32_t
begin field value
int32_t value
end field
end object
int32_t value
next object type: std::string
begin object std::string
begin field value
std::string value
end field
end object
std::string value
next object type: double
begin object double
begin field value
double value
end field
end object
double value
end sequence
end field
end object)_");
......
......@@ -30,6 +30,7 @@
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <set>
......@@ -52,17 +53,66 @@
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/stringification_inspector.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/message.hpp"
#include "caf/message_handler.hpp"
#include "caf/proxy_registry.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/serializer.hpp"
#include "caf/variant.hpp"
struct opaque {
int secret;
};
CAF_ALLOW_UNSAFE_MESSAGE_TYPE(opaque)
struct the_great_unknown {
int secret;
};
using namespace caf;
using bs = binary_serializer;
using si = detail::stringification_inspector;
using iat = inspector_access_type;
template <class Inspector, class T>
struct access_of {
template <class What>
static constexpr bool is
= std::is_same<decltype(inspect_access_type<Inspector, T>()), What>::value;
};
static_assert(access_of<bs, variant<int, double>>::is<iat::specialization>);
static_assert(access_of<bs, sec>::is<iat::inspect>);
static_assert(access_of<bs, int>::is<iat::builtin>);
static_assert(access_of<bs, dummy_tag_type>::is<iat::empty>);
static_assert(access_of<bs, opaque>::is<iat::unsafe>);
static_assert(access_of<bs, std::tuple<int, double>>::is<iat::tuple>);
static_assert(access_of<bs, std::map<int, int>>::is<iat::map>);
static_assert(access_of<bs, std::vector<bool>>::is<iat::list>);
static_assert(access_of<bs, the_great_unknown>::is<iat::none>);
// The stringification inspector picks up to_string via builtin_inspect.
static_assert(access_of<si, sec>::is<iat::builtin_inspect>);
static_assert(access_of<si, timespan>::is<iat::builtin_inspect>);
const char* test_enum_strings[] = {
"a",
"b",
......@@ -111,7 +161,7 @@ struct fixture : test_coordinator_fixture<> {
byte_buffer serialize(const Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!sink.apply_objects(xs...))
if (!(sink.apply(xs) && ...))
CAF_FAIL("serialization failed: "
<< sink.get_error()
<< ", data: " << deep_to_string(std::forward_as_tuple(xs...)));
......@@ -121,7 +171,7 @@ struct fixture : test_coordinator_fixture<> {
template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf};
if (!source.apply_objects(xs...))
if (!(source.apply(xs) && ...))
CAF_FAIL("deserialization failed: " << source.get_error());
}
......
......@@ -47,11 +47,11 @@ public:
}
bool serialize(serializer& sink) const override {
return sink.apply_object(value);
return sink.apply(value);
}
bool serialize(binary_serializer& sink) const override {
return sink.apply_object(value);
return sink.apply(value);
}
};
......@@ -72,7 +72,7 @@ private:
bool deserialize_impl(Deserializer& source,
std::unique_ptr<tracing_data>& dst) const {
string value;
if (!source.apply_object(value))
if (!source.apply(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(sink.apply_object(data));
CAF_CHECK(sink.apply(data));
binary_deserializer source{sys, buf};
tracing_data_ptr copy;
CAF_CHECK(source.apply_object(copy));
CAF_CHECK(source.apply(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 (!sink.apply_objects(x))
if (!sink.apply(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 (!source.apply_objects(result))
if (!source.apply(result))
CAF_FAIL("unable to deserialize from buffer: " << source.get_error());
return result;
}
......
......@@ -20,7 +20,9 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/io_export.hpp"
namespace caf::io::basp {
......@@ -76,6 +78,16 @@ enum class message_type : uint8_t {
CAF_IO_EXPORT std::string to_string(message_type);
CAF_IO_EXPORT bool from_string(string_view, message_type&);
CAF_IO_EXPORT bool from_integer(std::underlying_type_t<message_type>,
message_type&);
template <class Inssector>
bool inspect(Inssector& f, message_type& x) {
return default_enum_inspect(f, x);
}
/// @}
} // namespace caf::io::basp
......@@ -79,12 +79,12 @@ public:
if (dref.hdr_.operation == basp::message_type::routed_message) {
node_id src_node;
node_id dst_node;
if (!source.apply_object(src_node)) {
if (!source.apply(src_node)) {
CAF_LOG_ERROR(
"failed to read source of routed message:" << source.get_error());
return;
}
if (!source.apply_object(dst_node)) {
if (!source.apply(dst_node)) {
CAF_LOG_ERROR("failed to read destination of routed message:"
<< source.get_error());
return;
......@@ -108,13 +108,13 @@ public:
return;
}
// Get the remainder of the message.
if (!source.apply_object(stages)) {
if (!source.apply(stages)) {
CAF_LOG_ERROR("failed to read stages:" << source.get_error());
return;
}
auto& mm_metrics = ctx->system().middleman().metric_singletons;
auto t0 = telemetry::timer::clock_type::now();
if (!source.apply_objects(msg)) {
if (!source.apply(msg)) {
CAF_LOG_ERROR("failed to read message content:" << source.get_error());
return;
}
......
......@@ -40,10 +40,32 @@ inline std::string to_string(protocol::transport x) {
return x == protocol::tcp ? "TCP" : "UDP";
}
template <class Inspector>
bool inspect(Inspector& f, protocol::transport& x) {
using integer_type = std::underlying_type_t<protocol::transport>;
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
x = static_cast<protocol::transport>(val);
return true;
};
return f.apply(get, set);
}
inline std::string to_string(protocol::network x) {
return x == protocol::ipv4 ? "IPv4" : "IPv6";
}
template <class Inspector>
bool inspect(Inspector& f, protocol::network& x) {
using integer_type = std::underlying_type_t<protocol::network>;
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
x = static_cast<protocol::network>(val);
return true;
};
return f.apply(get, set);
}
template <class Inspector>
bool inspect(Inspector& f, protocol& x) {
return f.object(x).fields(f.field("trans", x.trans), f.field("net", x.net));
......
......@@ -73,7 +73,7 @@ connection_state instance::handle(execution_unit* ctx, new_data_msg& dm,
}
} else {
binary_deserializer source{ctx, dm.buf};
if (!source.apply_object(hdr)) {
if (!source.apply(hdr)) {
CAF_LOG_WARNING("failed to receive header:" << source.get_error());
return err(malformed_basp_message);
}
......@@ -185,7 +185,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 sink.apply_objects(forwarding_stack, msg);
return sink.apply(forwarding_stack) && sink.apply(msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
} else {
......@@ -199,7 +199,10 @@ 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 sink.apply_objects(source_node, dest_node, forwarding_stack, msg);
return sink.apply(source_node) //
&& sink.apply(dest_node) //
&& sink.apply(forwarding_stack) //
&& sink.apply(msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
}
......@@ -229,7 +232,7 @@ void instance::write(execution_unit* ctx, byte_buffer& buf, header& hdr,
mm_metrics.outbound_messages_size->observe(signed_payload_len);
hdr.payload_len = static_cast<uint32_t>(payload_len);
}
if (!sink.apply_objects(hdr))
if (!sink.apply(hdr))
CAF_LOG_ERROR(sink.get_error());
}
......@@ -258,7 +261,10 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
aid = pa->first->id();
iface = pa->second;
}
return sink.apply_objects(this_node_, app_ids, aid, iface);
return sink.apply(this_node_) //
&& sink.apply(app_ids) //
&& sink.apply(aid) //
&& sink.apply(iface);
});
header hdr{message_type::server_handshake,
0,
......@@ -271,7 +277,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 sink.apply_objects(this_node_);
return sink.apply(this_node_);
});
header hdr{message_type::client_handshake,
0,
......@@ -286,7 +292,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 sink.apply_objects(this_node_, dest_node);
return sink.apply(this_node_) && sink.apply(dest_node);
});
header hdr{message_type::monitor_message, 0, 0, 0, invalid_actor_id, aid};
write(ctx, buf, hdr, &writer);
......@@ -296,8 +302,8 @@ void instance::write_down_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid,
const error& rsn) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn));
auto writer = make_callback([&](binary_serializer& sink) { //
return sink.apply_objects(this_node_, dest_node, rsn);
auto writer = make_callback([&](binary_serializer& sink) {
return sink.apply(this_node_) && sink.apply(dest_node) && sink.apply(rsn);
});
header hdr{message_type::down_message, 0, 0, 0, aid, invalid_actor_id};
write(ctx, buf, hdr, &writer);
......@@ -333,7 +339,10 @@ 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 (!source.apply_objects(source_node, app_ids, aid, sigs)) {
if (!source.apply(source_node) //
|| !source.apply(app_ids) //
|| !source.apply(aid) //
|| !source.apply(sigs)) {
CAF_LOG_WARNING("unable to deserialize payload of server handshake:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -383,7 +392,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
// Deserialize payload.
binary_deserializer source{ctx, *payload};
node_id source_node;
if (!source.apply_objects(source_node)) {
if (!source.apply(source_node)) {
CAF_LOG_WARNING("unable to deserialize payload of client handshake:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -406,7 +415,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 (!source.apply_objects(source_node, dest_node)) {
if (!source.apply(source_node) || !source.apply(dest_node)) {
CAF_LOG_WARNING(
"unable to deserialize source and destination for routed message:"
<< source.get_error());
......@@ -465,7 +474,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 (!source.apply_objects(source_node, dest_node)) {
if (!source.apply(source_node) || !source.apply(dest_node)) {
CAF_LOG_WARNING("unable to deserialize payload of monitor message:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -482,7 +491,9 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
node_id source_node;
node_id dest_node;
error fail_state;
if (!source.apply_objects(source_node, dest_node, fail_state)) {
if (!source.apply(source_node) //
|| !source.apply(dest_node) //
|| !source.apply(fail_state)) {
CAF_LOG_WARNING("unable to deserialize payload of down message:"
<< source.get_error());
return serializing_basp_payload_failed;
......@@ -520,7 +531,7 @@ void instance::forward(execution_unit* ctx, const node_id& dest_node,
auto path = lookup(dest_node);
if (path) {
binary_serializer sink{ctx, callee_.get_buffer(path->hdl)};
if (!sink.apply_object(hdr)) {
if (!sink.apply(hdr)) {
CAF_LOG_ERROR("unable to serialize BASP header:" << sink.get_error());
return;
}
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -34,6 +35,51 @@ std::string to_string(message_type x) {
};
}
bool from_string(string_view in, message_type& out) {
if (in == "server_handshake") {
out = message_type::server_handshake;
return true;
} else if (in == "client_handshake") {
out = message_type::client_handshake;
return true;
} else if (in == "direct_message") {
out = message_type::direct_message;
return true;
} else if (in == "routed_message") {
out = message_type::routed_message;
return true;
} else if (in == "monitor_message") {
out = message_type::monitor_message;
return true;
} else if (in == "down_message") {
out = message_type::down_message;
return true;
} else if (in == "heartbeat") {
out = message_type::heartbeat;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<message_type> in,
message_type& out) {
auto result = static_cast<message_type>(in);
switch(result) {
default:
return false;
case message_type::server_handshake:
case message_type::client_handshake:
case message_type::direct_message:
case message_type::routed_message:
case message_type::monitor_message:
case message_type::down_message:
case message_type::heartbeat:
out = result;
return true;
};
}
} // namespace basp
} // namespace io
} // namespace caf
......
......@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING
......@@ -26,6 +27,35 @@ std::string to_string(operation x) {
};
}
bool from_string(string_view in, operation& out) {
if (in == "read") {
out = operation::read;
return true;
} else if (in == "write") {
out = operation::write;
return true;
} else if (in == "propagate_error") {
out = operation::propagate_error;
return true;
} else {
return false;
}
}
bool from_integer(std::underlying_type_t<operation> in,
operation& out) {
auto result = static_cast<operation>(in);
switch(result) {
default:
return false;
case operation::read:
case operation::write:
case operation::propagate_error:
out = result;
return true;
};
}
} // namespace network
} // namespace io
} // namespace caf
......
......@@ -163,7 +163,7 @@ public:
uint32_t serialized_size(const message& msg) {
byte_buffer buf;
binary_serializer sink{mpx_, buf};
if (!sink.apply_object(msg))
if (!sink.apply(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 (!sink.apply_objects(xs...))
if (!(sink.apply(xs) && ...))
CAF_FAIL("failed to serialize payload: " << sink.get_error());
}
......@@ -229,13 +229,13 @@ public:
instance().write(mpx_, buf, hdr, writer);
}
template <class T, class... Ts>
template <class... Ts>
void to_buf(byte_buffer& buf, basp::header& hdr, payload_writer* writer,
const T& x, const Ts&... xs) {
const Ts&... xs) {
auto pw = make_callback([&](binary_serializer& sink) {
if (writer != nullptr && !(*writer)(sink))
return false;
return sink.apply_objects(x, xs...);
return (sink.apply(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 (!source.apply_object(hdr))
if (!source.apply(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 (!source.apply_objects(stages, msg))
if (!source.apply(stages) || !source.apply(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 (!source.apply_objects(hdr))
if (!source.apply(hdr))
CAF_FAIL("failed to deserialize header: " << source.get_error());
}
byte_buffer payload;
......@@ -486,7 +486,10 @@ 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 (!sink.apply_objects(instance().this_node(), app_ids, id, ifs))
if (!sink.apply(instance().this_node()) //
|| !sink.apply(app_ids) //
|| !sink.apply(id) //
|| !sink.apply(ifs))
CAF_FAIL("serializing handshake failed: " << sink.get_error());
CAF_CHECK_EQUAL(hexstr(payload), hexstr(expected_payload));
}
......
......@@ -45,19 +45,19 @@ public:
};
struct fixture : test_coordinator_fixture<> {
template <class T, class... Ts>
auto serialize(T& x, Ts&... xs) {
template <class... Ts>
auto serialize(Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (!sink.apply_objects(x, xs...))
if (!(sink.apply(xs) && ...))
CAF_FAIL("serialization failed: " << sink.get_error());
return buf;
}
template <class Buffer, class T, class... Ts>
void deserialize(const Buffer& buf, T& x, Ts&... xs) {
template <class Buffer, class... Ts>
void deserialize(const Buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf};
if (!source.apply_objects(x, xs...))
if (!(source.apply(xs) && ...))
CAF_FAIL("serialization failed: " << source.get_error());
}
};
......
......@@ -120,7 +120,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 (!sink.apply_objects(stages, msg))
if (!sink.apply(stages) || !sink.apply(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 (!sink.apply_objects(xs...))
if (!sink.apply(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 (!source.apply_objects(xs...))
if (!source.apply(xs...))
CAF_FAIL("deserialization failed: " << source.get_error());
}
......
......@@ -6,20 +6,36 @@ Type Inspection
We designed CAF with distributed systems in mind. Hence, all message types must
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`).
:ref:`unsafe-message-type`). Any unsafe message type may used only for messages
that remain local, i.e., never cross the wire.
.. _type-inspection-data-model:
Data Model
----------
Type inspection in CAF uses a hierarchical, object-oriented data model. The main
abstraction is the *object*. An object has one or more *fields*. Fields have a
*name* and may be *optional*. Further, fields can take on several different (but
fixed) *types*.
Type inspection in CAF uses a hierarchical data model with the following
building blocks:
Depending on its type, a field contains: a single *value*, a variable-length
*sequence*, or a fixed-size *tuple*.
built-in types
- Signed and unsigned integer types for 8, 16, 32 and 64 bit
- The floating point types ``float``, ``double`` and ``long double``
- Bytes, booleans, and strings
lists
Dynamically-sized container types such as ``std::vector``.
tuples
Fixed-sized container types such as ``std::tuple`` or ``std::array`` as well
as builtin C array types.
maps
Dynamically-sized container types with key/value pairs such as ``std::map``.
objects
User-defined types. An object has one or more *fields*. Fields have a *name*
and may be *optional*. Further, fields may take on a fixed number of different
types.
To see how this maps to C++ types, consider the following type definition:
......@@ -32,25 +48,25 @@ To see how this maps to C++ types, consider the following type definition:
};
Here, field ``x1`` is either a ``string`` or a ``double`` at runtime. The field
``x2`` is optional and may contain a fixed-size tuple with two elements. Lastly,
field ``x3`` contains any number of string values at runtime.
Numbers and strings are primitive types in the inspector API.
``x2`` is optional and may contain a fixed-size tuple with two elements
(built-in types). Lastly, field ``x3`` contains any number of string values at
runtime.
Inspecting Objects
------------------
The inspection API allows CAF to deconstruct C++ objects into fields and values.
Users can either provide free functions named ``inspect`` that CAF picks up via
`ADL <https://en.wikipedia.org/wiki/Argument-dependent_name_lookup>`_ or
specialize ``caf::inspector_access``.
The inspection API allows CAF to deconstruct C++ objects. Users can either
provide free functions named ``inspect`` that CAF picks up via `ADL
<https://en.wikipedia.org/wiki/Argument-dependent_name_lookup>`_ or specialize
``caf::inspector_access``.
In both cases, users call members and member functions on an ``Inspector`` that
provides a domain-specific language (DSL) for describing the structure of a C++
object.
After listing a custom type ``T`` in a type ID block and either providing a free
``inspect`` overload or specializing ``inspector_access``, CAF is able to:
``inspect`` function overload or specializing ``inspector_access``, CAF is able
to:
- Serialize and deserialize objects of type ``T`` to/from Byte sequences.
- Render objects of type ``T`` as a human-readable string via
......@@ -65,11 +81,18 @@ the inspection API.
.. code-block:: C++
struct point_3d {
int x;
int y;
int z;
int32_t x;
int32_t y;
int32_t z;
};
.. note::
We strongly recommend using the fixed-width integer types in all user-defined
messaging types. Consistently using these types over ``short``, ``int``,
``long``, etc. avoids bugs in heterogeneous environemnts that are hard to
debug.
.. _writing-inspect-overloads:
Writing ``inspect`` Overloads
......@@ -92,15 +115,13 @@ 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.
As mentioned in the section on the :ref:`data model
<type-inspection-data-model>`, objects are containers for fields that in turn
contain values. When providing an ``inspect`` overload, CAF recursively
traverses all fields.
For example, consider the following ID type that simply wraps a string:
Not every type needs to expose itself as ``object``, though. For example,
consider the following ID type that simply wraps a string:
.. code-block:: C++
......@@ -111,14 +132,16 @@ For example, consider the following ID type that simply wraps a string:
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:
The type ``id`` is basically a *strong typedef* to improve type safety when
writing code. To a type inspector, ID objects look as follows:
.. code-block:: none
object(type: "id") {
field(name: "value") {
value(type: "string")
value(type: "string") {
...
}
}
}
......@@ -138,7 +161,7 @@ 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 such types.
With the current implementation, we could read the key ``manager.ceo`` from a
configuration file with this content:
......@@ -154,20 +177,23 @@ configuration file with this content:
}
}
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:
This clearly appears more verbose than it needs to be. Users generally need not
care about such internal types like ``id`` that only exist as a safeguard during
programming.
Hence, we generally recommend making such types transparent to CAF inspectors.
For our ``id`` type, the ``inspect`` overload may instead look as follows:
.. code-block:: C++
template <class Inspector>
bool inspect_value(Inspector& f, id& x) {
return f.apply_value(x.value);
bool inspect(Inspector& f, id& x) {
return f.apply(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:
With this implementation instead of the previous one, inspectors simply read or
write strings whenever they encounter an ``id`` as value. This simplifies our
config file from before and thus gives a much cleaner interface to users:
.. code-block:: none
......@@ -184,7 +210,7 @@ Specializing ``inspector_access``
Working with 3rd party libraries usually rules out adding free functions for
existing classes, because the namespace belongs to a another project. Hence, CAF
also allows specializing ``inspector_access`` instead. This requires writing
more boilerplate code and allows customizing every step of the inspection
more boilerplate code but allows customizing every step of the inspection
process.
The full interface of ``inspector_access`` looks as follows:
......@@ -194,10 +220,7 @@ The full interface of ``inspector_access`` looks as follows:
template <class T>
struct inspector_access {
template <class Inspector>
static bool apply_object(Inspector& f, T& x);
template <class Inspector>
static bool apply_value(Inspector& f, T& x);
static bool apply(Inspector& f, T& x);
template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, T& x);
......@@ -216,11 +239,8 @@ The full interface of ``inspector_access`` looks as follows:
SetFallback& set_fallback);
};
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
The static member function ``apply`` has the same role as the free ``inspect``
function. For most types, we can implement only ``apply`` and use a default
implementation for the other member functions. For example, specializing
``inspector_access`` for our ``point_3d`` would look as follows:
......@@ -231,16 +251,11 @@ implementation for the other member functions. For example, specializing
template <>
struct inspector_access<point_3d> : inspector_access_base<point_3d> {
template <class Inspector>
static bool apply_object(Inspector& f, point_3d& x) {
static bool apply(Inspector& f, point_3d& x) {
return f.object(x).fields(f.field("x", x.x),
f.field("y", x.y),
f.field("z", x.z));
}
template <class Inspector>
static bool apply_value(Inspector& f, point_3d& x) {
return apply_object(f, x);
}
};
} // namespace caf
......@@ -255,13 +270,6 @@ becomes necessary when integration custom types that have semantics similar to
Please refer to the Doxygen documentation for more details on ``save_field``
and ``load_field``.
In :ref:`our previous example <writing-inspect-overloads>`, we provided an
``inspect`` overload that was similar to our implementation of ``apply_object``.
Most of the time, we only need to implement ``apply_object`` and can call it
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.
Types with Getter and Setter Access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -433,13 +441,13 @@ machine-to-machine communication, using the underlying integer representation
gives the best performance. However, using the constant names results in a much
better user experience in all other cases.
The following code illustrates how to use a string representation for inspectors
that operate on human-readable data representation and the underlying type for
an ``enum class`` otherwise.
The following code illustrates how to provide a string representation for
inspectors that operate on human-readable data representation and the underlying
type for an ``enum class`` otherwise.
.. code-block:: C++
enum class weekday {
enum class weekday : uint8_t {
monday,
tuesday,
wednesday,
......@@ -458,34 +466,24 @@ an ``enum class`` otherwise.
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.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>
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);
return f.apply(get, set);
} else {
using default_impl = caf::default_inspector_access<weekday>;
return default_impl::apply_value(f, x);
auto get = [&x] { return static_cast<uint8_t>(x); };
auto set = [&x](uint8_t val) {
if (val < 7) {
x = static_cast<weekday>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
}
}
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
are going to inspect other objects that contain values of type ``weekday``. In
both cases, we provide getter and setter functions that convert between strings
and enumeration values.
For inspectors that operate on machine-to-machine data formats, we simply fall
back to the default implementation that is going to use the underlying integer
values.
When inspecting an object of type ``weekday``, we treat is as if it were a
string for inspectors with human-readable data formats. Otherwise, we treat the
weekday as if it were an integer between 0 and 6.
.. _unsafe-message-type:
......
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