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) { ...@@ -94,6 +94,27 @@ int main(int argc, char** argv) {
cerr << "empty enum name found\n"; cerr << "empty enum name found\n";
return EXIT_FAILURE; 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]}; std::ofstream out{argv[2]};
if (!out) { if (!out) {
cerr << "unable to open output file: " << argv[2] << '\n'; cerr << "unable to open output file: " << argv[2] << '\n';
...@@ -104,7 +125,8 @@ int main(int argc, char** argv) { ...@@ -104,7 +125,8 @@ int main(int argc, char** argv) {
<< "// DO NOT EDIT: " << "// DO NOT EDIT: "
"this file is auto-generated by caf-generate-enum-strings.\n" "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" "// 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" "CAF_PUSH_DEPRECATED_WARNING\n\n"
<< "#include \"" << namespaces[0]; << "#include \"" << namespaces[0];
for (size_t i = 1; i < namespaces.size(); ++i) for (size_t i = 1; i < namespaces.size(); ++i)
...@@ -114,35 +136,44 @@ int main(int argc, char** argv) { ...@@ -114,35 +136,44 @@ int main(int argc, char** argv) {
<< "namespace " << namespaces[0] << " {\n"; << "namespace " << namespaces[0] << " {\n";
for (size_t i = 1; i < namespaces.size(); ++i) for (size_t i = 1; i < namespaces.size(); ++i)
out << "namespace " << namespaces[i] << " {\n"; 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" << " switch(x) {\n"
<< " default:\n" << " default:\n"
<< " return \"???\";\n"; << " return \"???\";\n";
for (auto& val : enum_values)
// Read until hitting the closing '}'. out << " case " << case_label_prefix << val << ":\n"
std::string case_label_prefix; << " return \"" << val << "\";\n";
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.
out << " };\n" out << " };\n"
<< "}\n\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) for (auto i = namespaces.rbegin(); i != namespaces.rend(); ++i)
out << "} // namespace " << *i << '\n'; out << "} // namespace " << *i << '\n';
out << "\nCAF_POP_WARNINGS\n"; out << "\nCAF_POP_WARNINGS\n";
return EXIT_SUCCESS;
} }
...@@ -94,14 +94,14 @@ void caf_main(actor_system& sys) { ...@@ -94,14 +94,14 @@ void caf_main(actor_system& sys) {
binary_serializer::container_type buf; binary_serializer::container_type buf;
// write f1 to buffer // write f1 to buffer
binary_serializer sink{sys, buf}; 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()) std::cerr << "*** failed to serialize foo2: " << to_string(sink.get_error())
<< '\n'; << '\n';
return; return;
} }
// read f2 back from buffer // read f2 back from buffer
binary_deserializer source{sys, buf}; binary_deserializer source{sys, buf};
if (!source.apply_object(f2)) { if (!source.apply(f2)) {
std::cerr << "*** failed to deserialize foo2: " std::cerr << "*** failed to deserialize foo2: "
<< to_string(source.get_error()) << '\n'; << to_string(source.get_error()) << '\n';
return; return;
......
...@@ -21,9 +21,11 @@ ...@@ -21,9 +21,11 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits>
#include <utility> #include <utility>
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/error_code.hpp" #include "caf/error_code.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/load_inspector_base.hpp" #include "caf/load_inspector_base.hpp"
...@@ -37,6 +39,10 @@ namespace caf { ...@@ -37,6 +39,10 @@ namespace caf {
class CAF_CORE_EXPORT binary_deserializer class CAF_CORE_EXPORT binary_deserializer
: public load_inspector_base<binary_deserializer> { : public load_inspector_base<binary_deserializer> {
public: public:
// -- member types -----------------------------------------------------------
using super = load_inspector_base<binary_deserializer>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
template <class Container> template <class Container>
...@@ -107,11 +113,11 @@ public: ...@@ -107,11 +113,11 @@ public:
bool fetch_next_object_type(type_id_t& type) noexcept; bool fetch_next_object_type(type_id_t& type) noexcept;
constexpr bool begin_object(string_view) noexcept { constexpr bool begin_object(string_view) noexcept {
return ok; return true;
} }
constexpr bool end_object() noexcept { constexpr bool end_object() noexcept {
return ok; return true;
} }
constexpr bool begin_field(string_view) noexcept { constexpr bool begin_field(string_view) noexcept {
...@@ -127,29 +133,29 @@ public: ...@@ -127,29 +133,29 @@ public:
span<const type_id_t> types, size_t& index) noexcept; span<const type_id_t> types, size_t& index) noexcept;
constexpr bool end_field() { constexpr bool end_field() {
return ok; return true;
} }
constexpr bool begin_tuple(size_t) noexcept { constexpr bool begin_tuple(size_t) noexcept {
return ok; return true;
} }
constexpr bool end_tuple() noexcept { constexpr bool end_tuple() noexcept {
return ok; return true;
} }
constexpr bool begin_key_value_pair() noexcept { constexpr bool begin_key_value_pair() noexcept {
return ok; return true;
} }
constexpr bool end_key_value_pair() noexcept { constexpr bool end_key_value_pair() noexcept {
return ok; return true;
} }
bool begin_sequence(size_t& list_size) noexcept; bool begin_sequence(size_t& list_size) noexcept;
constexpr bool end_sequence() noexcept { constexpr bool end_sequence() noexcept {
return ok; return true;
} }
bool begin_associative_array(size_t& size) noexcept { bool begin_associative_array(size_t& size) noexcept {
...@@ -180,6 +186,17 @@ public: ...@@ -180,6 +186,17 @@ public:
bool value(uint64_t& x) noexcept; 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(float& x) noexcept;
bool value(double& x) noexcept; bool value(double& x) noexcept;
......
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <type_traits>
#include <vector> #include <vector>
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/byte_buffer.hpp" #include "caf/byte_buffer.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/save_inspector_base.hpp" #include "caf/save_inspector_base.hpp"
#include "caf/span.hpp" #include "caf/span.hpp"
...@@ -37,6 +39,8 @@ class CAF_CORE_EXPORT binary_serializer ...@@ -37,6 +39,8 @@ class CAF_CORE_EXPORT binary_serializer
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using super = save_inspector_base<binary_serializer>;
using container_type = byte_buffer; using container_type = byte_buffer;
using value_type = byte; using value_type = byte;
...@@ -94,15 +98,15 @@ public: ...@@ -94,15 +98,15 @@ public:
bool inject_next_object_type(type_id_t type); bool inject_next_object_type(type_id_t type);
constexpr bool begin_object(string_view) { constexpr bool begin_object(string_view) {
return ok; return true;
} }
constexpr bool end_object() { constexpr bool end_object() {
return ok; return true;
} }
constexpr bool begin_field(string_view) noexcept { constexpr bool begin_field(string_view) noexcept {
return ok; return true;
} }
bool begin_field(string_view, bool is_present); bool begin_field(string_view, bool is_present);
...@@ -113,29 +117,29 @@ public: ...@@ -113,29 +117,29 @@ public:
size_t index); size_t index);
constexpr bool end_field() { constexpr bool end_field() {
return ok; return true;
} }
constexpr bool begin_tuple(size_t) { constexpr bool begin_tuple(size_t) {
return ok; return true;
} }
constexpr bool end_tuple() { constexpr bool end_tuple() {
return ok; return true;
} }
constexpr bool begin_key_value_pair() { constexpr bool begin_key_value_pair() {
return ok; return true;
} }
constexpr bool end_key_value_pair() { constexpr bool end_key_value_pair() {
return ok; return true;
} }
bool begin_sequence(size_t list_size); bool begin_sequence(size_t list_size);
constexpr bool end_sequence() { constexpr bool end_sequence() {
return ok; return true;
} }
bool begin_associative_array(size_t size) { bool begin_associative_array(size_t size) {
...@@ -166,6 +170,11 @@ public: ...@@ -166,6 +170,11 @@ public:
bool value(uint64_t x); 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(float x);
bool value(double x); bool value(double x);
......
...@@ -942,7 +942,7 @@ struct inspect_config_value_access { ...@@ -942,7 +942,7 @@ struct inspect_config_value_access {
static optional<T> get_if(const config_value* x) { static optional<T> get_if(const config_value* x) {
config_value_reader reader{x}; config_value_reader reader{x};
auto tmp = T{}; auto tmp = T{};
if (detail::load_value(reader, tmp)) if (detail::load(reader, tmp))
return optional<T>{std::move(tmp)}; return optional<T>{std::move(tmp)};
return none; return none;
} }
...@@ -985,7 +985,7 @@ struct inspect_config_value_access { ...@@ -985,7 +985,7 @@ struct inspect_config_value_access {
static config_value convert(const T& x) { static config_value convert(const T& x) {
config_value result; config_value result;
config_value_writer writer{&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"); CAF_RAISE_ERROR("unable to convert type to a config_value");
return result; return result;
} }
......
...@@ -131,6 +131,8 @@ public: ...@@ -131,6 +131,8 @@ public:
bool end_associative_array() override; bool end_associative_array() override;
bool value(byte& x) override;
bool value(bool& x) override; bool value(bool& x) override;
bool value(int8_t& x) override; bool value(int8_t& x) override;
......
...@@ -102,6 +102,8 @@ public: ...@@ -102,6 +102,8 @@ public:
bool end_associative_array() override; bool end_associative_array() override;
bool value(byte x) override;
bool value(bool x) override; bool value(bool x) override;
bool value(int8_t x) override; bool value(int8_t x) override;
......
...@@ -136,19 +136,11 @@ struct inspector_access<cow_tuple<Ts...>> { ...@@ -136,19 +136,11 @@ struct inspector_access<cow_tuple<Ts...>> {
using value_type = cow_tuple<Ts...>; using value_type = cow_tuple<Ts...>;
template <class Inspector> 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) if constexpr (Inspector::is_loading)
return detail::load_object(f, x.unshared()); return f.tuple(x.unshared());
else else
return detail::save_object(f, detail::as_mutable_ref(x.data())); return f.tuple(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()));
} }
template <class Inspector> template <class Inspector>
......
...@@ -35,7 +35,7 @@ std::string deep_to_string(const T& x) { ...@@ -35,7 +35,7 @@ std::string deep_to_string(const T& x) {
using inspector_type = detail::stringification_inspector; using inspector_type = detail::stringification_inspector;
std::string result; std::string result;
inspector_type f{result}; inspector_type f{result};
detail::save_value(f, detail::as_mutable_ref(x)); detail::save(f, detail::as_mutable_ref(x));
return result; 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 @@ ...@@ -26,6 +26,7 @@
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/load_inspector_base.hpp" #include "caf/load_inspector_base.hpp"
#include "caf/span.hpp" #include "caf/span.hpp"
...@@ -37,6 +38,10 @@ namespace caf { ...@@ -37,6 +38,10 @@ namespace caf {
/// Technology-independent deserialization interface. /// Technology-independent deserialization interface.
class CAF_CORE_EXPORT deserializer : public load_inspector_base<deserializer> { class CAF_CORE_EXPORT deserializer : public load_inspector_base<deserializer> {
public: public:
// -- member types -----------------------------------------------------------
using super = load_inspector_base<deserializer>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
explicit deserializer(actor_system& sys) noexcept; explicit deserializer(actor_system& sys) noexcept;
...@@ -111,9 +116,12 @@ public: ...@@ -111,9 +116,12 @@ public:
/// @note the default implementation calls `end_sequence()`. /// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array(); virtual bool end_associative_array();
/// Reads primitive value from the input. /// Reads `x` from the input.
/// @param x The primitive value. /// @param x A reference to a builtin type.
/// @returns A non-zero error code on failure, `sec::success` otherwise. /// @returns `true` on success, `false` otherwise.
virtual bool value(byte& x) = 0;
/// @copydoc value
virtual bool value(bool& x) = 0; virtual bool value(bool& x) = 0;
/// @copydoc value /// @copydoc value
...@@ -140,6 +148,17 @@ public: ...@@ -140,6 +148,17 @@ public:
/// @copydoc value /// @copydoc value
virtual bool value(uint64_t&) = 0; 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 /// @copydoc value
virtual bool value(float&) = 0; virtual bool value(float&) = 0;
...@@ -163,10 +182,12 @@ public: ...@@ -163,10 +182,12 @@ public:
/// @returns A non-zero error code on failure, `sec::success` otherwise. /// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual bool value(span<byte> x) = 0; virtual bool value(span<byte> x) = 0;
using super::list;
/// Adds each boolean in `xs` to the output. Derived classes can override this /// 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 /// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format. /// for each value in a binary output format.
virtual bool value(std::vector<bool>& xs); virtual bool list(std::vector<bool>& xs);
protected: protected:
/// Provides access to the ::proxy_registry and to the ::actor_system. /// Provides access to the ::proxy_registry and to the ::actor_system.
......
...@@ -50,28 +50,28 @@ void copy_construct(void* ptr, const void* src) { ...@@ -50,28 +50,28 @@ void copy_construct(void* ptr, const void* src) {
template <class T> template <class T>
bool save_binary(binary_serializer& sink, const void* ptr) { 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> template <class T>
bool load_binary(binary_deserializer& source, void* ptr) { 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> template <class T>
bool save(serializer& sink, const void* ptr) { 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> template <class T>
bool load(deserializer& source, void* ptr) { bool load(deserializer& source, void* ptr) {
return source.apply_object(*static_cast<T*>(ptr)); return source.apply(*static_cast<T*>(ptr));
} }
template <class T> template <class T>
void stringify(std::string& buf, const void* ptr) { void stringify(std::string& buf, const void* ptr) {
stringification_inspector f{buf}; 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); static_cast<void>(unused);
} }
......
...@@ -58,6 +58,8 @@ public: ...@@ -58,6 +58,8 @@ public:
bool end_sequence() override; bool end_sequence() override;
bool value(byte x) override;
bool value(bool x) override; bool value(bool x) override;
bool value(int8_t x) override; bool value(int8_t x) override;
...@@ -90,13 +92,15 @@ public: ...@@ -90,13 +92,15 @@ public:
bool value(span<const byte> x) override; 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> template <class T>
size_t serialized_size(const T& x) { size_t serialized_size(const T& x) {
serialized_size_inspector f; serialized_size_inspector f;
auto unused = f.apply_object(x); auto unused = f.apply(x);
static_cast<void>(unused); // Always true. static_cast<void>(unused); // Always true.
return f.result; return f.result;
} }
...@@ -104,7 +108,7 @@ size_t serialized_size(const T& x) { ...@@ -104,7 +108,7 @@ size_t serialized_size(const T& x) {
template <class T> template <class T>
size_t serialized_size(actor_system& sys, const T& x) { size_t serialized_size(actor_system& sys, const T& x) {
serialized_size_inspector f{sys}; serialized_size_inspector f{sys};
auto unused = f.apply_object(x); auto unused = f.apply(x);
static_cast<void>(unused); // Always true. static_cast<void>(unused); // Always true.
return f.result; return f.result;
} }
......
...@@ -66,7 +66,7 @@ public: ...@@ -66,7 +66,7 @@ public:
this->inspector_.result = 0; this->inspector_.result = 0;
this->sampled_elements_ += x.xs_size; this->sampled_elements_ += x.xs_size;
for (auto& element : x.xs.get_as<std::vector<T>>(0)) 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_ this->sampled_total_size_
+= static_cast<int64_t>(this->inspector_.result); += static_cast<int64_t>(this->inspector_.result);
} }
......
...@@ -40,7 +40,7 @@ class CAF_CORE_EXPORT stringification_inspector ...@@ -40,7 +40,7 @@ class CAF_CORE_EXPORT stringification_inspector
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using super = save_inspector; using super = save_inspector_base<stringification_inspector>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
...@@ -100,6 +100,8 @@ public: ...@@ -100,6 +100,8 @@ public:
return end_sequence(); return end_sequence();
} }
bool value(byte x);
bool value(bool x); bool value(bool x);
template <class Integral> template <class Integral>
...@@ -126,7 +128,11 @@ public: ...@@ -126,7 +128,11 @@ public:
bool value(const std::u32string& x); 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 ---- // -- builtin inspection to pick up to_string or provide nicer formatting ----
...@@ -146,14 +152,14 @@ public: ...@@ -146,14 +152,14 @@ public:
return true; return true;
} }
result_ += '{'; result_ += '{';
save_value(*this, i->first); save(*this, i->first);
result_ += " = "; result_ += " = ";
save_value(*this, i->second); save(*this, i->second);
while (++i != last) { while (++i != last) {
sep(); sep();
save_value(*this, i->first); save(*this, i->first);
result_ += " = "; result_ += " = ";
save_value(*this, i->second); save(*this, i->second);
} }
result_ += '}'; result_ += '}';
return true; return true;
...@@ -186,7 +192,7 @@ public: ...@@ -186,7 +192,7 @@ public:
} else { } else {
sep(); sep();
result_ += '*'; result_ += '*';
save_value(*this, detail::as_mutable_ref(*x)); save(*this, detail::as_mutable_ref(*x));
return true; return true;
} }
} }
...@@ -198,7 +204,7 @@ public: ...@@ -198,7 +204,7 @@ public:
result_ += "null"; result_ += "null";
} else { } else {
result_ += '*'; result_ += '*';
save_value(*this, detail::as_mutable_ref(*x)); save(*this, detail::as_mutable_ref(*x));
} }
return true; return true;
} }
...@@ -228,7 +234,7 @@ private: ...@@ -228,7 +234,7 @@ private:
sep(); sep();
result_ += '['; result_ += '[';
while (first != sentinel) while (first != sentinel)
save_value(*this, *first++); save(*this, *first++);
result_ += ']'; result_ += ']';
} }
......
...@@ -878,23 +878,6 @@ public: ...@@ -878,23 +878,6 @@ public:
static constexpr bool value = result_type::value; 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`. /// Checks whether the inspector has a `builtin_inspect` overload for `T`.
template <class Inspector, class T> template <class Inspector, class T>
class has_builtin_inspect { class has_builtin_inspect {
...@@ -980,6 +963,48 @@ public: ...@@ -980,6 +963,48 @@ public:
static constexpr bool value = sfinae_result::value; 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 } // namespace caf::detail
#undef CAF_HAS_MEMBER_TRAIT #undef CAF_HAS_MEMBER_TRAIT
......
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp" #include "caf/is_error_code_enum.hpp"
namespace caf { namespace caf {
...@@ -55,6 +58,18 @@ enum class exit_reason : uint8_t { ...@@ -55,6 +58,18 @@ enum class exit_reason : uint8_t {
/// @relates exit_reason /// @relates exit_reason
CAF_CORE_EXPORT std::string to_string(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 } // namespace caf
CAF_ERROR_CODE_ENUM(exit_reason) CAF_ERROR_CODE_ENUM(exit_reason)
...@@ -196,7 +196,7 @@ enum class exit_reason : uint8_t; ...@@ -196,7 +196,7 @@ enum class exit_reason : uint8_t;
enum class invoke_message_result; enum class invoke_message_result;
enum class pec : uint8_t; enum class pec : uint8_t;
enum class sec : uint8_t; enum class sec : uint8_t;
enum class stream_priority; enum class stream_priority : uint8_t;
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
......
...@@ -43,6 +43,8 @@ class fnv : public save_inspector_base<fnv<T>> { ...@@ -43,6 +43,8 @@ class fnv : public save_inspector_base<fnv<T>> {
public: public:
static_assert(sizeof(T) == 4 || sizeof(T) == 8); static_assert(sizeof(T) == 4 || sizeof(T) == 8);
using super = save_inspector_base<fnv<T>>;
constexpr fnv() noexcept : result(init()) { constexpr fnv() noexcept : result(init()) {
// nop // nop
} }
...@@ -154,7 +156,7 @@ public: ...@@ -154,7 +156,7 @@ public:
static T compute(Ts&&... xs) noexcept { static T compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref; using detail::as_mutable_ref;
fnv f; fnv f;
auto unused = f.apply_objects(xs...); auto unused = (f.apply(xs) && ...);
static_cast<void>(unused); // Always true. static_cast<void>(unused); // Always true.
return f.result; return f.result;
} }
......
...@@ -37,6 +37,9 @@ public: ...@@ -37,6 +37,9 @@ public:
/// Hash size in bytes. /// Hash size in bytes.
static constexpr size_t hash_size = 20; 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. /// Array type for storing a 160-bit hash.
using result_type = std::array<byte, hash_size>; using result_type = std::array<byte, hash_size>;
...@@ -152,7 +155,7 @@ public: ...@@ -152,7 +155,7 @@ public:
static result_type compute(Ts&&... xs) noexcept { static result_type compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref; using detail::as_mutable_ref;
sha1 f; sha1 f;
auto unused = f.apply_objects(xs...); auto unused = f.apply(xs...);
static_cast<void>(unused); // Always true. static_cast<void>(unused); // Always true.
return f.result(); return f.result();
} }
......
...@@ -73,137 +73,65 @@ constexpr bool assertion_failed_v = false; ...@@ -73,137 +73,65 @@ constexpr bool assertion_failed_v = false;
// -- loading ------------------------------------------------------------------ // -- loading ------------------------------------------------------------------
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& 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::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);
} }
template <class Inspector, class T> 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); return inspect(f, x);
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::integral) { bool load(Inspector& f, T& x, inspector_access_type::builtin) {
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) {
return f.value(x); return f.value(x);
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::enumeration) { bool load(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
auto tmp = detail::squashed_int_t<std::underlying_type_t<T>>{0}; return f.builtin_inspect(x);
if (f.value(tmp)) {
x = static_cast<T>(tmp);
return true;
}
return false;
} }
template <class Inspector, class T> 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(); return f.object(x).fields();
} }
template <class Inspector, class T> 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); f.emplace_error(sec::unsafe_type);
return false; return false;
} }
template <class Inspector, class T, size_t N> template <class Inspector, class T, size_t N>
bool load_value(Inspector& f, T (&xs)[N], inspector_access_type::array) { bool load(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
if (!f.begin_tuple(N)) return f.tuple(xs);
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();
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::tuple) { bool load(Inspector& f, T& xs, inspector_access_type::tuple) {
return load_tuple(f, x, return f.tuple(xs);
std::make_index_sequence<std::tuple_size<T>::value>{});
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::map) { bool load(Inspector& f, T& x, inspector_access_type::map) {
x.clear(); return f.map(x);
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();
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::list) { bool load(Inspector& f, T& x, inspector_access_type::list) {
x.clear(); return f.list(x);
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();
} }
template <class Inspector, class T> template <class Inspector, class T>
std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool> 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); return f.opaque_value(x);
} }
template <class Inspector, class T> template <class Inspector, class T>
std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool> 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( static_assert(
detail::assertion_failed_v<T>, detail::assertion_failed_v<T>,
"please provide an inspect overload for T or specialize inspector_access"); "please provide an inspect overload for T or specialize inspector_access");
...@@ -211,28 +139,8 @@ load_value(Inspector&, T&, inspector_access_type::none) { ...@@ -211,28 +139,8 @@ load_value(Inspector&, T&, inspector_access_type::none) {
} }
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x) { bool load(Inspector& f, T& x) {
return load_value(f, x, inspect_value_access_type<Inspector, T>()); return load(f, x, inspect_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>());
} }
template <class Inspector, class T, class IsValid, class SyncValue> 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, ...@@ -257,116 +165,65 @@ bool load_field(Inspector& f, string_view field_name, T& x, IsValid& is_valid,
// -- saving ------------------------------------------------------------------- // -- saving -------------------------------------------------------------------
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& 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, 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);
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::inspect_value) { bool save(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect_value(f, x);
}
template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::inspect) {
return inspect(f, x); return inspect(f, x);
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::integral) { bool save(Inspector& f, T& x, inspector_access_type::builtin) {
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) {
return f.value(x); return f.value(x);
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::enumeration) { bool save(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
auto tmp = static_cast<detail::squashed_int_t<std::underlying_type_t<T>>>(x); return f.builtin_inspect(x);
return f.value(tmp);
} }
template <class Inspector, class T> 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(); return f.object(x).fields();
} }
template <class Inspector, class T> 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); f.emplace_error(sec::unsafe_type);
return false; return false;
} }
template <class Inspector, class T, size_t N> template <class Inspector, class T, size_t N>
bool save_value(Inspector& f, T (&xs)[N], inspector_access_type::array) { bool save(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
if (!f.begin_tuple(N)) return f.tuple(xs);
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();
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::tuple) { bool save(Inspector& f, const T& xs, inspector_access_type::tuple) {
return save_tuple(f, x, return f.tuple(xs);
std::make_index_sequence<std::tuple_size<T>::value>{});
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::map) { bool save(Inspector& f, T& x, inspector_access_type::map) {
if (!f.begin_associative_array(x.size())) return f.map(x);
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();
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::list) { bool save(Inspector& f, T& x, inspector_access_type::list) {
auto size = x.size(); return f.list(x);
if (!f.begin_sequence(size))
return false;
for (auto&& val : x)
if (!save_value(f, val))
return false;
return f.end_sequence();
} }
template <class Inspector, class T> template <class Inspector, class T>
std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool> 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); return f.opaque_value(x);
} }
template <class Inspector, class T> template <class Inspector, class T>
std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool> 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( static_assert(
detail::assertion_failed_v<T>, detail::assertion_failed_v<T>,
"please provide an inspect overload for T or specialize inspector_access"); "please provide an inspect overload for T or specialize inspector_access");
...@@ -374,34 +231,13 @@ save_value(Inspector&, T&, inspector_access_type::none) { ...@@ -374,34 +231,13 @@ save_value(Inspector&, T&, inspector_access_type::none) {
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x) { bool save(Inspector& f, T& x) {
return save_value(f, x, inspect_value_access_type<Inspector, T>()); return save(f, x, inspect_access_type<Inspector, T>());
} }
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, const T& x) { bool save(Inspector& f, const T& x) {
return save_value(f, as_mutable_ref(x), return save(f, as_mutable_ref(x), inspect_access_type<Inspector, T>());
inspect_value_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>());
} }
template <class Inspector, class T> template <class Inspector, class T>
...@@ -422,77 +258,13 @@ bool save_field(Inspector& f, string_view field_name, IsPresent& is_present, ...@@ -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); 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::detail
namespace caf { namespace caf {
/// Default implementation for @ref inspector_access. // -- customization points -----------------------------------------------------
template <class T>
struct default_inspector_access : inspector_access_base<T> {
// -- interface functions ----------------------------------------------------
/// Applies `x` as an object to `f`. /// Customization point for adding support for a custom type.
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 point for adding support for a custom type. The default
/// implementation requires an `inspect` overload for `T` that is available via
/// ADL.
template <class T> template <class T>
struct inspector_access; struct inspector_access;
...@@ -559,15 +331,10 @@ struct optional_inspector_access { ...@@ -559,15 +331,10 @@ struct optional_inspector_access {
using value_type = typename traits::value_type; using value_type = typename traits::value_type;
template <class Inspector> 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)); 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> template <class Inspector>
static bool static bool
save_field(Inspector& f, string_view field_name, container_type& x) { save_field(Inspector& f, string_view field_name, container_type& x) {
...@@ -696,18 +463,13 @@ struct variant_inspector_access { ...@@ -696,18 +463,13 @@ struct variant_inspector_access {
using traits = variant_inspector_traits<T>; using traits = variant_inspector_traits<T>;
template <class Inspector> 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)); 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> template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, value_type& x) { 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), return f.begin_field(field_name, make_span(traits::allowed_types),
traits::type_index(x)) // traits::type_index(x)) //
&& traits::visit(g, x) // && traits::visit(g, x) //
...@@ -720,7 +482,7 @@ struct variant_inspector_access { ...@@ -720,7 +482,7 @@ struct variant_inspector_access {
auto allowed_types = make_span(traits::allowed_types); auto allowed_types = make_span(traits::allowed_types);
if (is_present()) { if (is_present()) {
auto&& x = get(); 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, return f.begin_field(field_name, true, allowed_types,
traits::type_index(x)) // traits::type_index(x)) //
&& traits::visit(g, x) // && traits::visit(g, x) //
...@@ -735,7 +497,7 @@ struct variant_inspector_access { ...@@ -735,7 +497,7 @@ struct variant_inspector_access {
value_type& x, type_id_t runtime_type) { value_type& x, type_id_t runtime_type) {
auto res = false; auto res = false;
auto type_found = traits::load(runtime_type, [&](auto& tmp) { auto type_found = traits::load(runtime_type, [&](auto& tmp) {
if (!detail::load_value(f, tmp)) if (!detail::load(f, tmp))
return; return;
traits::assign(x, std::move(tmp)); traits::assign(x, std::move(tmp));
res = true; res = true;
...@@ -876,12 +638,7 @@ struct inspector_access<std::chrono::duration<Rep, Period>> ...@@ -876,12 +638,7 @@ struct inspector_access<std::chrono::duration<Rep, Period>>
using value_type = std::chrono::duration<Rep, Period>; using value_type = std::chrono::duration<Rep, Period>;
template <class Inspector> template <class Inspector>
static bool apply_object(Inspector& f, value_type& x) { static bool apply(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) {
if (f.has_human_readable_format()) { if (f.has_human_readable_format()) {
auto get = [&x] { auto get = [&x] {
std::string str; std::string str;
...@@ -892,14 +649,15 @@ struct inspector_access<std::chrono::duration<Rep, Period>> ...@@ -892,14 +649,15 @@ struct inspector_access<std::chrono::duration<Rep, Period>>
auto err = detail::parse(str, x); auto err = detail::parse(str, x);
return !err; return !err;
}; };
return detail::split_save_load(f, get, set); return f.apply(get, set);
} } else {
auto get = [&x] { return x.count(); }; auto get = [&x] { return x.count(); };
auto set = [&x](Rep value) { auto set = [&x](Rep value) {
x = std::chrono::duration<Rep, Period>{value}; x = std::chrono::duration<Rep, Period>{value};
return true; return true;
}; };
return detail::split_save_load(f, get, set); return f.apply(get, set);
}
} }
}; };
...@@ -912,12 +670,7 @@ struct inspector_access< ...@@ -912,12 +670,7 @@ struct inspector_access<
= std::chrono::time_point<std::chrono::system_clock, Duration>; = std::chrono::time_point<std::chrono::system_clock, Duration>;
template <class Inspector> template <class Inspector>
static bool apply_object(Inspector& f, value_type& x) { static bool apply(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) {
if (f.has_human_readable_format()) { if (f.has_human_readable_format()) {
auto get = [&x] { auto get = [&x] {
std::string str; std::string str;
...@@ -928,15 +681,16 @@ struct inspector_access< ...@@ -928,15 +681,16 @@ struct inspector_access<
auto err = detail::parse(str, x); auto err = detail::parse(str, x);
return !err; return !err;
}; };
return detail::split_save_load(f, get, set); return f.apply(get, set);
} } else {
using rep_type = typename Duration::rep; using rep_type = typename Duration::rep;
auto get = [&x] { return x.time_since_epoch().count(); }; auto get = [&x] { return x.time_since_epoch().count(); };
auto set = [&x](rep_type value) { auto set = [&x](rep_type value) {
x = value_type{Duration{value}}; x = value_type{Duration{value}};
return true; return true;
}; };
return detail::split_save_load(f, get, set); return f.apply(get, set);
}
} }
}; };
......
...@@ -32,7 +32,7 @@ struct inspector_access_base { ...@@ -32,7 +32,7 @@ struct inspector_access_base {
template <class Inspector, class IsValid, class SyncValue> template <class Inspector, class IsValid, class SyncValue>
static bool load_field(Inspector& f, string_view field_name, T& x, static bool load_field(Inspector& f, string_view field_name, T& x,
IsValid& is_valid, SyncValue& sync_value) { 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)) { if (!is_valid(x)) {
f.emplace_error(sec::field_invariant_check_failed, f.emplace_error(sec::field_invariant_check_failed,
to_string(field_name)); to_string(field_name));
...@@ -58,7 +58,7 @@ struct inspector_access_base { ...@@ -58,7 +58,7 @@ struct inspector_access_base {
if (!f.begin_field(field_name, is_present)) if (!f.begin_field(field_name, is_present))
return false; return false;
if (is_present) { if (is_present) {
if (!f.apply_value(x)) if (!f.apply(x))
return false; return false;
if (!is_valid(x)) { if (!is_valid(x)) {
f.emplace_error(sec::field_invariant_check_failed, f.emplace_error(sec::field_invariant_check_failed,
...@@ -80,7 +80,7 @@ struct inspector_access_base { ...@@ -80,7 +80,7 @@ struct inspector_access_base {
template <class Inspector> template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, T& x) { static bool save_field(Inspector& f, string_view field_name, T& x) {
return f.begin_field(field_name) // return f.begin_field(field_name) //
&& f.apply_value(x) // && f.apply(x) //
&& f.end_field(); && f.end_field();
} }
...@@ -91,7 +91,7 @@ struct inspector_access_base { ...@@ -91,7 +91,7 @@ struct inspector_access_base {
if (is_present()) { if (is_present()) {
auto&& x = get(); auto&& x = get();
return f.begin_field(field_name, true) // return f.begin_field(field_name, true) //
&& f.apply_value(x) // && f.apply(x) //
&& f.end_field(); && f.end_field();
} }
return f.begin_field(field_name, false) && f.end_field(); return f.begin_field(field_name, false) && f.end_field();
......
...@@ -35,23 +35,14 @@ struct inspector_access_type { ...@@ -35,23 +35,14 @@ struct inspector_access_type {
/// Flags types that provide an `inspector_access` specialization. /// Flags types that provide an `inspector_access` specialization.
struct specialization {}; struct specialization {};
/// Flags types that provide an `inspect_value()` overload.
struct inspect_value {};
/// Flags types that provide an `inspect()` overload. /// Flags types that provide an `inspect()` overload.
struct inspect {}; struct inspect {};
/// Flags builtin integral types. /// Flags types with builtin support of the inspection API.
struct integral {};
/// Flags types with builtin support via `Inspector::builtin_inspect`.
struct builtin {}; struct builtin {};
/// Flags types with builtin support via `Inspector::value`. /// Flags types with builtin support via `Inspector::builtin_inspect`.
struct trivial {}; struct builtin_inspect {};
/// Flags `enum` types.
struct enumeration {};
/// Flags stateless message types. /// Flags stateless message types.
struct empty {}; struct empty {};
...@@ -59,15 +50,9 @@ struct inspector_access_type { ...@@ -59,15 +50,9 @@ struct inspector_access_type {
/// Flags allowed unsafe message types. /// Flags allowed unsafe message types.
struct unsafe {}; struct unsafe {};
/// Flags native C array types.
struct array {};
/// Flags types with `std::tuple`-like API. /// Flags types with `std::tuple`-like API.
struct tuple {}; struct tuple {};
/// Flags type that specialize `inspector_access_boxed_value_traits`.
struct boxed_value {};
/// Flags types with `std::map`-like API. /// Flags types with `std::map`-like API.
struct map {}; struct map {};
...@@ -80,98 +65,23 @@ struct inspector_access_type { ...@@ -80,98 +65,23 @@ struct inspector_access_type {
/// @relates inspector_access_type /// @relates inspector_access_type
template <class Inspector, class T> template <class Inspector, class T>
constexpr auto inspect_object_access_type() { constexpr auto inspect_access_type() {
using namespace detail; using namespace detail;
// Order: unsafe (error) > C Array > builtin_inspect > inspector_access > // Order: unsafe (error) > C Array > builtin > builtin_inspect
// inspect > trivial > defaults. // > inspector_access > inspect > trivial > defaults.
if constexpr (is_allowed_unsafe_message_type_v<T>) if constexpr (is_allowed_unsafe_message_type_v<T>)
return inspector_access_type::unsafe{}; return inspector_access_type::unsafe{};
else if constexpr (std::is_array<T>::value) 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{}; return inspector_access_type::tuple{};
else if constexpr (is_map_like_v<T>) else if constexpr (detail::is_builtin_inspector_type<
return inspector_access_type::map{}; T, Inspector::is_loading>::value)
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)
return inspector_access_type::builtin{}; 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>>) else if constexpr (detail::is_complete<inspector_access<T>>)
return inspector_access_type::specialization{}; 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) else if constexpr (has_inspect_overload<Inspector, T>::value)
return inspector_access_type::inspect{}; 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) else if constexpr (std::is_empty<T>::value)
return inspector_access_type::empty{}; return inspector_access_type::empty{};
else if constexpr (is_stl_tuple_type_v<T>) else if constexpr (is_stl_tuple_type_v<T>)
......
...@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT load_inspector { ...@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT load_inspector {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using result_type = bool; using result_type [[deprecated("inspectors always return bool")]] = bool;
// -- constants -------------------------------------------------------------- // -- 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. /// Enables dispatching on the inspector type.
static constexpr bool is_loading = true; static constexpr bool is_loading = true;
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
#pragma once #pragma once
#include <array>
#include <tuple>
#include <utility>
#include "caf/inspector_access.hpp" #include "caf/inspector_access.hpp"
#include "caf/load_inspector.hpp" #include "caf/load_inspector.hpp"
...@@ -37,36 +41,132 @@ public: ...@@ -37,36 +41,132 @@ public:
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()}; 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> template <class T>
[[nodiscard]] bool apply_object(T& x) { bool map(T& xs) {
static_assert(!std::is_const<T>::value); xs.clear();
constexpr auto token = inspect_object_access_type<Subtype, T>(); auto size = size_t{0};
return detail::load_object(dref(), x, token); 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> template <class T, size_t... Is>
[[nodiscard]] bool apply_objects(Ts&... xs) { bool tuple(T& xs, std::index_sequence<Is...>) {
return (apply_object(xs) && ...); return dref().begin_tuple(sizeof...(Is))
&& (detail::load(dref(), get<Is>(xs)) && ...) //
&& dref().end_tuple();
} }
template <class T> template <class T>
bool apply_value(T& x) { bool tuple(T& xs) {
return detail::load_value(dref(), x); 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> template <class Get, class Set>
bool apply_value(Get&& get, Set&& set) { [[nodiscard]] bool apply(Get&& get, Set&& set) {
using field_type = std::decay_t<decltype(get())>; using value_type = std::decay_t<decltype(get())>;
auto tmp = field_type{}; auto tmp = value_type{};
if (apply_value(tmp)) { using setter_result = decltype(set(std::move(tmp)));
if (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; return true;
this->set_error(sec::load_callback_failed); } 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: private:
Subtype* dptr() { Subtype* dptr() {
......
...@@ -229,28 +229,15 @@ constexpr message_id make_message_id(message_priority p) { ...@@ -229,28 +229,15 @@ constexpr message_id make_message_id(message_priority p) {
// -- inspection support ------------------------------------------------------- // -- inspection support -------------------------------------------------------
template <> template <class Inspector>
struct inspector_access<message_id> : inspector_access_base<message_id> { bool inspect(Inspector& f, message_id& x) {
template <class Inspector>
static bool apply_object(Inspector& f, message_id& x) {
auto get = [&x] { return x.integer_value(); }; auto get = [&x] { return x.integer_value(); };
auto set = [&x](uint64_t val) { auto set = [&x](uint64_t val) {
x = message_id{val}; x = message_id{val};
return true; return true;
}; };
return f.object(x).fields(f.field("value", get, set)); return f.apply(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);
}
};
} // namespace caf } // namespace caf
......
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp" #include "caf/is_error_code_enum.hpp"
...@@ -80,8 +82,21 @@ enum class pec : uint8_t { ...@@ -80,8 +82,21 @@ enum class pec : uint8_t {
invalid_state, invalid_state,
}; };
/// @relates pec
CAF_CORE_EXPORT std::string to_string(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 } // namespace caf
CAF_ERROR_CODE_ENUM(pec) CAF_ERROR_CODE_ENUM(pec)
...@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT save_inspector { ...@@ -37,17 +37,10 @@ class CAF_CORE_EXPORT save_inspector {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using result_type = bool; using result_type [[deprecated("inspectors always return bool")]] = bool;
// -- constants -------------------------------------------------------------- // -- 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. /// Enables dispatching on the inspector type.
static constexpr bool is_loading = false; static constexpr bool is_loading = false;
......
...@@ -30,41 +30,104 @@ public: ...@@ -30,41 +30,104 @@ public:
using super = save_inspector; using super = save_inspector;
// -- DSL entry point -------------------------------------------------------- // -- DSL entry points -------------------------------------------------------
template <class T> template <class T>
constexpr auto object(T&) noexcept { constexpr auto object(T&) noexcept {
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()}; 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> template <class T>
[[nodiscard]] bool apply_object(T& x) { bool map(const T& xs) {
constexpr auto token = inspect_object_access_type<Subtype, T>(); if (!dref().begin_associative_array(xs.size()))
return detail::save_object(dref(), x, token); 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> template <class T>
[[nodiscard]] bool apply_object(const T& x) { bool tuple(const T& xs) {
constexpr auto token = inspect_object_access_type<Subtype, T>(); return tuple(xs, std::make_index_sequence<std::tuple_size<T>::value>{});
return detail::save_object(dref(), detail::as_mutable_ref(x), token);
} }
template <class... Ts> template <class T, size_t N>
[[nodiscard]] bool apply_objects(Ts&&... xs) { bool tuple(T (&xs)[N]) {
return (apply_object(xs) && ...); 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> template <class T>
[[nodiscard]] bool apply_value(T& x) { [[nodiscard]] bool apply(const T& x) {
return detail::save_value(dref(), x); return detail::save(dref(), x);
} }
template <class Get, class Set> template <class Get, class Set>
[[nodiscard]] bool apply_value(Get&& get, Set&&) { [[nodiscard]] bool apply(Get&& get, Set&&) {
auto&& x = get(); return detail::save(dref(), get());
return detail::save_value(dref(), x); }
// -- 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: private:
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/is_error_code_enum.hpp" #include "caf/is_error_code_enum.hpp"
...@@ -167,6 +169,18 @@ enum class sec : uint8_t { ...@@ -167,6 +169,18 @@ enum class sec : uint8_t {
/// @relates sec /// @relates sec
CAF_CORE_EXPORT std::string to_string(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 } // namespace caf
CAF_ERROR_CODE_ENUM(sec) CAF_ERROR_CODE_ENUM(sec)
...@@ -22,10 +22,12 @@ ...@@ -22,10 +22,12 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits>
#include <utility> #include <utility>
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/meta/annotation.hpp" #include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp" #include "caf/meta/save_callback.hpp"
...@@ -40,6 +42,10 @@ namespace caf { ...@@ -40,6 +42,10 @@ namespace caf {
/// Technology-independent serialization interface. /// Technology-independent serialization interface.
class CAF_CORE_EXPORT serializer : public save_inspector_base<serializer> { class CAF_CORE_EXPORT serializer : public save_inspector_base<serializer> {
public: public:
// -- member types -----------------------------------------------------------
using super = save_inspector_base<serializer>;
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
explicit serializer(actor_system& sys) noexcept; explicit serializer(actor_system& sys) noexcept;
...@@ -116,9 +122,12 @@ public: ...@@ -116,9 +122,12 @@ public:
/// @note the default implementation calls `end_sequence()`. /// @note the default implementation calls `end_sequence()`.
virtual bool end_associative_array(); virtual bool end_associative_array();
/// Adds the primitive type `x` to the output. /// Adds `x` to the output.
/// @param x The primitive value. /// @param x A value for a builtin type.
/// @returns A non-zero error code on failure, `sec::success` otherwise. /// @returns `true` on success, `false` otherwise.
virtual bool value(byte x) = 0;
/// @copydoc value
virtual bool value(bool x) = 0; virtual bool value(bool x) = 0;
/// @copydoc value /// @copydoc value
...@@ -145,6 +154,12 @@ public: ...@@ -145,6 +154,12 @@ public:
/// @copydoc value /// @copydoc value
virtual bool value(uint64_t x) = 0; 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 /// @copydoc value
virtual bool value(float x) = 0; virtual bool value(float x) = 0;
...@@ -168,10 +183,12 @@ public: ...@@ -168,10 +183,12 @@ public:
/// @returns A non-zero error code on failure, `sec::success` otherwise. /// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual bool value(span<const byte> x) = 0; virtual bool value(span<const byte> x) = 0;
using super::list;
/// Adds each boolean in `xs` to the output. Derived classes can override this /// 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 /// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format. /// 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: protected:
/// Provides access to the ::proxy_registry and to the ::actor_system. /// Provides access to the ::proxy_registry and to the ::actor_system.
......
...@@ -18,14 +18,18 @@ ...@@ -18,14 +18,18 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
namespace caf { namespace caf {
/// Categorizes individual streams. /// Categorizes individual streams.
enum class stream_priority { enum class stream_priority : uint8_t {
/// Denotes soft-realtime traffic. /// Denotes soft-realtime traffic.
very_high, very_high,
/// Denotes time-sensitive traffic. /// Denotes time-sensitive traffic.
...@@ -38,9 +42,20 @@ enum class stream_priority { ...@@ -38,9 +42,20 @@ enum class stream_priority {
very_low very_low
}; };
/// Stores the number of `stream_priority` classes. /// @relates stream_priority
static constexpr size_t stream_priorities = 5;
CAF_CORE_EXPORT std::string to_string(stream_priority x); 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 } // namespace caf
...@@ -260,35 +260,14 @@ CAF_CORE_EXPORT expected<uri> make_uri(string_view str); ...@@ -260,35 +260,14 @@ CAF_CORE_EXPORT expected<uri> make_uri(string_view str);
template <> template <>
struct inspector_access<uri> : inspector_access_base<uri> { struct inspector_access<uri> : inspector_access_base<uri> {
template <class Inspector> template <class Inspector>
static bool apply_object(Inspector& f, uri& x) { static bool apply(Inspector& f, uri& x) {
if (f.has_human_readable_format()) { 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); }; 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 set = [&x](std::string str) {
auto err = parse(str, x); auto err = parse(str, x);
return err.empty(); return !err;
}; };
return f.object(x).fields(f.field("value", get, set)); return f.apply(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);
} else { } else {
if constexpr (Inspector::is_loading) if constexpr (Inspector::is_loading)
if (!x.impl_->unique()) if (!x.impl_->unique())
......
...@@ -139,7 +139,7 @@ bool binary_serializer::value(span<const byte> x) { ...@@ -139,7 +139,7 @@ bool binary_serializer::value(span<const byte> x) {
} }
write_pos_ += x.size(); write_pos_ += x.size();
CAF_ASSERT(write_pos_ <= buf_.size()); CAF_ASSERT(write_pos_ <= buf_.size());
return ok; return true;
} }
bool binary_serializer::value(byte x) { bool binary_serializer::value(byte x) {
...@@ -148,7 +148,7 @@ bool binary_serializer::value(byte x) { ...@@ -148,7 +148,7 @@ bool binary_serializer::value(byte x) {
else else
buf_[write_pos_] = x; buf_[write_pos_] = x;
++write_pos_; ++write_pos_;
return ok; return true;
} }
bool binary_serializer::value(bool x) { bool binary_serializer::value(bool x) {
......
...@@ -448,6 +448,17 @@ bool pull(config_value_reader& reader, T& x) { ...@@ -448,6 +448,17 @@ bool pull(config_value_reader& reader, T& x) {
} // namespace } // 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) { bool config_value_reader::value(bool& x) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
return pull(*this, x); return pull(*this, x);
......
...@@ -339,6 +339,10 @@ bool config_value_writer::end_associative_array() { ...@@ -339,6 +339,10 @@ bool config_value_writer::end_associative_array() {
return true; 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) { bool config_value_writer::value(bool x) {
return push(config_value{x}); return push(config_value{x});
} }
......
...@@ -51,7 +51,7 @@ bool deserializer::end_associative_array() { ...@@ -51,7 +51,7 @@ bool deserializer::end_associative_array() {
return end_sequence(); return end_sequence();
} }
bool deserializer::value(std::vector<bool>& x) { bool deserializer::list(std::vector<bool>& x) {
x.clear(); x.clear();
size_t size = 0; size_t size = 0;
if (!begin_sequence(size)) if (!begin_sequence(size))
......
...@@ -107,6 +107,11 @@ bool serialized_size_inspector::end_sequence() { ...@@ -107,6 +107,11 @@ bool serialized_size_inspector::end_sequence() {
return true; return true;
} }
bool serialized_size_inspector::value(byte x) {
result += sizeof(x);
return true;
}
bool serialized_size_inspector::value(bool) { bool serialized_size_inspector::value(bool) {
result += sizeof(uint8_t); result += sizeof(uint8_t);
return true; return true;
...@@ -194,7 +199,7 @@ bool serialized_size_inspector::value(span<const byte> x) { ...@@ -194,7 +199,7 @@ bool serialized_size_inspector::value(span<const byte> x) {
return true; 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())); CAF_IGNORE_UNUSED(begin_sequence(xs.size()));
result += (xs.size() + static_cast<size_t>(xs.size() % 8 != 0)) / 8; result += (xs.size() + static_cast<size_t>(xs.size() % 8 != 0)) / 8;
return end_sequence(); return end_sequence();
......
...@@ -57,7 +57,7 @@ bool stringification_inspector::begin_object(string_view name) { ...@@ -57,7 +57,7 @@ bool stringification_inspector::begin_object(string_view name) {
} else { } else {
in_string_object_ = true; in_string_object_ = true;
} }
return ok; return true;
} }
bool stringification_inspector::end_object() { bool stringification_inspector::end_object() {
...@@ -65,11 +65,11 @@ bool stringification_inspector::end_object() { ...@@ -65,11 +65,11 @@ bool stringification_inspector::end_object() {
result_ += ')'; result_ += ')';
else else
in_string_object_ = false; in_string_object_ = false;
return ok; return true;
} }
bool stringification_inspector::begin_field(string_view) { bool stringification_inspector::begin_field(string_view) {
return ok; return true;
} }
bool stringification_inspector::begin_field(string_view, bool is_present) { bool stringification_inspector::begin_field(string_view, bool is_present) {
...@@ -78,12 +78,12 @@ 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"; result_ += "null";
else else
result_ += '*'; result_ += '*';
return ok; return true;
} }
bool stringification_inspector::begin_field(string_view, span<const type_id_t>, bool stringification_inspector::begin_field(string_view, span<const type_id_t>,
size_t) { size_t) {
return ok; return true;
} }
bool stringification_inspector::begin_field(string_view, bool is_present, bool stringification_inspector::begin_field(string_view, bool is_present,
...@@ -93,22 +93,26 @@ 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"; result_ += "null";
else else
result_ += '*'; result_ += '*';
return ok; return true;
} }
bool stringification_inspector::end_field() { bool stringification_inspector::end_field() {
return ok; return true;
} }
bool stringification_inspector::begin_sequence(size_t) { bool stringification_inspector::begin_sequence(size_t) {
sep(); sep();
result_ += '['; result_ += '[';
return ok; return true;
} }
bool stringification_inspector::end_sequence() { bool stringification_inspector::end_sequence() {
result_ += ']'; result_ += ']';
return ok; return true;
}
bool stringification_inspector::value(byte x) {
return value(span<const byte>(&x, 1));
} }
bool stringification_inspector::value(bool x) { bool stringification_inspector::value(bool x) {
...@@ -228,7 +232,13 @@ bool stringification_inspector::int_value(uint64_t x) { ...@@ -228,7 +232,13 @@ bool stringification_inspector::int_value(uint64_t x) {
return true; 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()); begin_sequence(xs.size());
for (bool x : xs) for (bool x : xs)
value(x); value(x);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -34,6 +35,55 @@ std::string to_string(exit_reason x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -25,6 +26,35 @@ std::string to_string(inbox_result x) { ...@@ -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 intrusive
} // namespace caf } // namespace caf
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -27,6 +28,39 @@ std::string to_string(task_result x) { ...@@ -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 intrusive
} // namespace caf } // namespace caf
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -24,6 +25,35 @@ std::string to_string(invoke_message_result x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -602,7 +602,7 @@ void logger::log_first_line() { ...@@ -602,7 +602,7 @@ void logger::log_first_line() {
msg += to_string(system_.node()); msg += to_string(system_.node());
msg += ", excluded-components = "; msg += ", excluded-components = ";
detail::stringification_inspector f{msg}; detail::stringification_inspector f{msg};
detail::save_value(f, filter); detail::save(f, filter);
return msg; return msg;
}; };
namespace lg = defaults::logger; namespace lg = defaults::logger;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -22,6 +23,31 @@ std::string to_string(message_priority x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -64,6 +65,115 @@ std::string to_string(pec x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -144,6 +145,275 @@ std::string to_string(sec x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -51,7 +51,7 @@ bool serializer::end_associative_array() { ...@@ -51,7 +51,7 @@ bool serializer::end_associative_array() {
return end_sequence(); return end_sequence();
} }
bool serializer::value(const std::vector<bool>& xs) { bool serializer::list(const std::vector<bool>& xs) {
if (!begin_sequence(xs.size())) if (!begin_sequence(xs.size()))
return false; return false;
for (bool x : xs) for (bool x : xs)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -28,6 +29,43 @@ std::string to_string(stream_priority x) { ...@@ -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 } // namespace caf
CAF_POP_WARNINGS CAF_POP_WARNINGS
...@@ -210,15 +210,15 @@ CAF_TEST(profilers record request / response messaging) { ...@@ -210,15 +210,15 @@ CAF_TEST(profilers record request / response messaging) {
"new: worker", "new: worker",
"new: server", "new: server",
"new: client", "new: client",
"client sends: message(int32_t(19), int32_t(23))", "client sends: message(19, 23)",
"server got: message(int32_t(19), int32_t(23))", "server got: message(19, 23)",
"server sends: message(int32_t(19), int32_t(23))", "server sends: message(19, 23)",
"server consumed the message", "server consumed the message",
"delete: server", "delete: server",
"worker got: message(int32_t(19), int32_t(23))", "worker got: message(19, 23)",
"worker sends: message(int32_t(42))", "worker sends: message(42)",
"worker consumed the message", "worker consumed the message",
"client got: message(int32_t(42))", "client got: message(42)",
"client consumed the message", "client consumed the message",
"delete: worker", "delete: worker",
"delete: client", "delete: client",
......
...@@ -54,12 +54,12 @@ CAF_TEST(serialization roundtrips go through the registry) { ...@@ -54,12 +54,12 @@ CAF_TEST(serialization roundtrips go through the registry) {
CAF_MESSAGE("hdl.id: " << hdl->id()); CAF_MESSAGE("hdl.id: " << hdl->id());
byte_buffer buf; byte_buffer buf;
binary_serializer sink{sys, buf}; binary_serializer sink{sys, buf};
if (!sink.apply_object(hdl)) if (!sink.apply(hdl))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
CAF_MESSAGE("buf: " << buf); CAF_MESSAGE("buf: " << buf);
actor hdl2; actor hdl2;
binary_deserializer source{sys, buf}; binary_deserializer source{sys, buf};
if (!source.apply_object(hdl2)) if (!source.apply(hdl2))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
CAF_CHECK_EQUAL(hdl, hdl2); CAF_CHECK_EQUAL(hdl, hdl2);
anon_send_exit(hdl, exit_reason::user_shutdown); anon_send_exit(hdl, exit_reason::user_shutdown);
......
...@@ -60,7 +60,7 @@ struct fixture { ...@@ -60,7 +60,7 @@ struct fixture {
template <class... Ts> template <class... Ts>
void load(const std::vector<byte>& buf, Ts&... xs) { void load(const std::vector<byte>& buf, Ts&... xs) {
binary_deserializer source{nullptr, buf}; binary_deserializer source{nullptr, buf};
if (!source.apply_objects(xs...)) if (!(source.apply(xs) && ...))
CAF_FAIL("binary_deserializer failed to load: " << source.get_error()); CAF_FAIL("binary_deserializer failed to load: " << source.get_error());
} }
......
...@@ -58,7 +58,7 @@ struct fixture { ...@@ -58,7 +58,7 @@ struct fixture {
auto save(const Ts&... xs) { auto save(const Ts&... xs) {
byte_buffer result; byte_buffer result;
binary_serializer sink{nullptr, 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()); CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
return result; return result;
} }
...@@ -66,7 +66,7 @@ struct fixture { ...@@ -66,7 +66,7 @@ struct fixture {
template <class... Ts> template <class... Ts>
void save_to_buf(byte_buffer& data, const Ts&... xs) { void save_to_buf(byte_buffer& data, const Ts&... xs) {
binary_serializer sink{nullptr, data}; binary_serializer sink{nullptr, data};
if (!sink.apply_objects(xs...)) if (!(sink.apply(xs) && ...))
CAF_FAIL("binary_serializer failed to save: " << sink.get_error()); CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
} }
}; };
......
...@@ -43,13 +43,13 @@ constexpr i64 operator""_i64(unsigned long long int x) { ...@@ -43,13 +43,13 @@ constexpr i64 operator""_i64(unsigned long long int x) {
using i64_list = std::vector<i64>; using i64_list = std::vector<i64>;
struct fixture { struct fixture {
settings xs; config_value x;
template <class T> template <class T>
void deserialize(const config_value& src, T& value) { void deserialize(const config_value& src, T& value) {
config_value_reader reader{&src}; config_value_reader reader{&src};
if (!detail::load_value(reader, value)) if (!detail::load(reader, value))
CAF_FAIL("failed to deserialize from settings: " << reader.get_error()); CAF_FAIL("deserialization failed: " << reader.get_error());
} }
template <class T> template <class T>
...@@ -59,7 +59,7 @@ struct fixture { ...@@ -59,7 +59,7 @@ struct fixture {
template <class T> template <class T>
void deserialize(T& value) { void deserialize(T& value) {
return deserialize(xs, value); return deserialize(x, value);
} }
template <class T> template <class T>
...@@ -71,7 +71,10 @@ struct fixture { ...@@ -71,7 +71,10 @@ struct fixture {
template <class T> template <class T>
optional<T> get(string_view key) { 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) ...@@ -81,12 +84,14 @@ CAF_TEST_FIXTURE_SCOPE(config_value_reader_tests, fixture)
CAF_TEST(readers deserialize builtin types from config values) { CAF_TEST(readers deserialize builtin types from config values) {
std::string value; std::string value;
auto& xs = x.as_dictionary();
put(xs, "foo", "bar"); put(xs, "foo", "bar");
deserialize(xs["foo"], value); deserialize(xs["foo"], value);
CAF_CHECK_EQUAL(value, "bar"); CAF_CHECK_EQUAL(value, "bar");
} }
CAF_TEST(readers deserialize simple objects from configs) { CAF_TEST(readers deserialize simple objects from configs) {
auto& xs = x.as_dictionary();
put(xs, "foo", "hello"); put(xs, "foo", "hello");
put(xs, "bar", "world"); put(xs, "bar", "world");
foobar fb; foobar fb;
...@@ -97,6 +102,7 @@ CAF_TEST(readers deserialize simple objects from configs) { ...@@ -97,6 +102,7 @@ CAF_TEST(readers deserialize simple objects from configs) {
CAF_TEST(readers deserialize complex objects from configs) { CAF_TEST(readers deserialize complex objects from configs) {
CAF_MESSAGE("fill a dictionary with data for a 'basics' object"); CAF_MESSAGE("fill a dictionary with data for a 'basics' object");
auto& xs = x.as_dictionary();
put(xs, "v1", settings{}); put(xs, "v1", settings{});
put(xs, "v2", 42_i64); put(xs, "v2", 42_i64);
put(xs, "v3", i64_list({1, 2, 3, 4})); put(xs, "v3", i64_list({1, 2, 3, 4}));
...@@ -140,11 +146,11 @@ CAF_TEST(readers deserialize objects from the output of writers) { ...@@ -140,11 +146,11 @@ CAF_TEST(readers deserialize objects from the output of writers) {
line l{{10, 20, 30}, {70, 60, 50}}; line l{{10, 20, 30}, {70, 60, 50}};
config_value tmp; config_value tmp;
config_value_writer writer{&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()); CAF_FAIL("failed two write to settings: " << writer.get_error());
if (!holds_alternative<settings>(tmp)) if (!holds_alternative<settings>(tmp))
CAF_FAIL("writer failed to produce a dictionary"); 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"); CAF_MESSAGE("serialize and verify the 'line' object");
{ {
......
...@@ -41,17 +41,13 @@ constexpr i64 operator""_i64(unsigned long long int x) { ...@@ -41,17 +41,13 @@ constexpr i64 operator""_i64(unsigned long long int x) {
using i64_list = std::vector<i64>; using i64_list = std::vector<i64>;
struct fixture { struct fixture {
settings xs; config_value x;
template <class T> template <class T>
void set(const T& value) { void set(const T& value) {
config_value val; config_value_writer writer{&x};
config_value_writer writer{&val}; if (!detail::save(writer, value))
if (!detail::save_value(writer, value))
CAF_FAIL("failed two write to settings: " << writer.get_error()); 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> template <class T>
...@@ -63,7 +59,10 @@ struct fixture { ...@@ -63,7 +59,10 @@ struct fixture {
template <class T> template <class T>
optional<T> get(string_view key) { 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) { ...@@ -101,7 +100,6 @@ CAF_TEST(empty types and maps become dictionaries) {
tst.v7["two"] = 2; tst.v7["two"] = 2;
tst.v7["three"] = 3; tst.v7["three"] = 3;
set(tst); set(tst);
CAF_MESSAGE(xs);
CAF_CHECK_EQUAL(get<settings>("v1"), settings{}); CAF_CHECK_EQUAL(get<settings>("v1"), settings{});
CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64); CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64);
CAF_CHECK_EQUAL(get<i64_list>("v3"), i64_list({-1, -2, -3, -4})); 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) { ...@@ -128,4 +126,10 @@ CAF_TEST(empty types and maps become dictionaries) {
CAF_CHECK_EQUAL(get<config_value::list>("v8"), config_value::list()); 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() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -200,6 +200,20 @@ enum class test_enum : int32_t { ...@@ -200,6 +200,20 @@ enum class test_enum : int32_t {
// Implemented in serialization.cpp // Implemented in serialization.cpp
std::string to_string(test_enum x); 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 // Used in serializer.cpp and deserializer.cpp
struct test_data { struct test_data {
int32_t i32; int32_t i32;
...@@ -231,10 +245,54 @@ enum class dummy_enum_class : short { foo, bar }; ...@@ -231,10 +245,54 @@ enum class dummy_enum_class : short { foo, bar };
return x == dummy_enum_class::foo ? "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 }; 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 }; 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 ------------------------------------ // -- type IDs for for all unit test suites ------------------------------------
#define ADD_TYPE_ID(type) CAF_ADD_TYPE_ID(core_test, type) #define ADD_TYPE_ID(type) CAF_ADD_TYPE_ID(core_test, type)
......
...@@ -39,7 +39,7 @@ struct fixture : test_coordinator_fixture<> { ...@@ -39,7 +39,7 @@ struct fixture : test_coordinator_fixture<> {
size_t actual_size(const Ts&... xs) { size_t actual_size(const Ts&... xs) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{sys, buf}; binary_serializer sink{sys, buf};
if (!sink.apply_objects(xs...)) if (!(sink.apply(xs) && ...))
CAF_FAIL("failed to serialize data: " << sink.get_error()); CAF_FAIL("failed to serialize data: " << sink.get_error());
return buf.size(); return buf.size();
} }
......
...@@ -54,11 +54,11 @@ struct fixture { ...@@ -54,11 +54,11 @@ struct fixture {
T roundtrip(T x) { T roundtrip(T x) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink(sys, buf); binary_serializer sink(sys, buf);
if (!sink.apply_object(x)) if (!sink.apply(x))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf)); binary_deserializer source(sys, make_span(buf));
T y; T y;
if (!source.apply_object(y)) if (!source.apply(y))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
return y; return y;
} }
......
...@@ -54,11 +54,11 @@ struct fixture { ...@@ -54,11 +54,11 @@ struct fixture {
T roundtrip(T x) { T roundtrip(T x) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink(sys, buf); binary_serializer sink(sys, buf);
if (!sink.apply_object(x)) if (!sink.apply(x))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
binary_deserializer source(sys, make_span(buf)); binary_deserializer source(sys, make_span(buf));
T y; T y;
if (!source.apply_object(y)) if (!source.apply(y))
CAF_FAIL("serialization failed: " << source.get_error()); CAF_FAIL("serialization failed: " << source.get_error());
return y; return y;
} }
......
...@@ -44,7 +44,7 @@ struct testee : deserializer { ...@@ -44,7 +44,7 @@ struct testee : deserializer {
bool load_field_failed(string_view, sec code) { bool load_field_failed(string_view, sec code) {
set_error(make_error(code)); set_error(make_error(code));
return stop; return false;
} }
size_t indent = 0; size_t indent = 0;
...@@ -63,14 +63,14 @@ struct testee : deserializer { ...@@ -63,14 +63,14 @@ struct testee : deserializer {
indent += 2; indent += 2;
log += "begin object "; log += "begin object ";
log.insert(log.end(), object_name.begin(), object_name.end()); log.insert(log.end(), object_name.begin(), object_name.end());
return ok; return true;
} }
bool end_object() override { bool end_object() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end object"; log += "end object";
return ok; return true;
} }
bool begin_field(string_view name) override { bool begin_field(string_view name) override {
...@@ -78,7 +78,7 @@ struct testee : deserializer { ...@@ -78,7 +78,7 @@ struct testee : deserializer {
indent += 2; indent += 2;
log += "begin field "; log += "begin field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
return ok; return true;
} }
bool begin_field(string_view name, bool& is_present) override { bool begin_field(string_view name, bool& is_present) override {
...@@ -87,7 +87,7 @@ struct testee : deserializer { ...@@ -87,7 +87,7 @@ struct testee : deserializer {
log += "begin optional field "; log += "begin optional field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
is_present = false; is_present = false;
return ok; return true;
} }
bool begin_field(string_view name, span<const type_id_t>, bool begin_field(string_view name, span<const type_id_t>,
...@@ -97,7 +97,7 @@ struct testee : deserializer { ...@@ -97,7 +97,7 @@ struct testee : deserializer {
log += "begin variant field "; log += "begin variant field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
type_index = 0; type_index = 0;
return ok; return true;
} }
bool begin_field(string_view name, bool& is_present, span<const type_id_t>, bool begin_field(string_view name, bool& is_present, span<const type_id_t>,
...@@ -107,14 +107,14 @@ struct testee : deserializer { ...@@ -107,14 +107,14 @@ struct testee : deserializer {
log += "begin optional variant field "; log += "begin optional variant field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
is_present = false; is_present = false;
return ok; return true;
} }
bool end_field() override { bool end_field() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end field"; log += "end field";
return ok; return true;
} }
bool begin_tuple(size_t size) override { bool begin_tuple(size_t size) override {
...@@ -122,28 +122,28 @@ struct testee : deserializer { ...@@ -122,28 +122,28 @@ struct testee : deserializer {
indent += 2; indent += 2;
log += "begin tuple of size "; log += "begin tuple of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_tuple() override { bool end_tuple() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end tuple"; log += "end tuple";
return ok; return true;
} }
bool begin_key_value_pair() override { bool begin_key_value_pair() override {
new_line(); new_line();
indent += 2; indent += 2;
log += "begin key-value pair"; log += "begin key-value pair";
return ok; return true;
} }
bool end_key_value_pair() override { bool end_key_value_pair() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end key-value pair"; log += "end key-value pair";
return ok; return true;
} }
bool begin_sequence(size_t& size) override { bool begin_sequence(size_t& size) override {
...@@ -152,14 +152,14 @@ struct testee : deserializer { ...@@ -152,14 +152,14 @@ struct testee : deserializer {
indent += 2; indent += 2;
log += "begin sequence of size "; log += "begin sequence of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_sequence() override { bool end_sequence() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end sequence"; log += "end sequence";
return ok; return true;
} }
bool begin_associative_array(size_t& size) override { bool begin_associative_array(size_t& size) override {
...@@ -168,21 +168,21 @@ struct testee : deserializer { ...@@ -168,21 +168,21 @@ struct testee : deserializer {
indent += 2; indent += 2;
log += "begin associative array of size "; log += "begin associative array of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_associative_array() override { bool end_associative_array() override {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end associative array"; log += "end associative array";
return ok; return true;
} }
bool value(bool& x) override { bool value(bool& x) override {
new_line(); new_line();
log += "bool value"; log += "bool value";
x = false; x = false;
return ok; return true;
} }
template <class T> template <class T>
...@@ -192,7 +192,14 @@ struct testee : deserializer { ...@@ -192,7 +192,14 @@ struct testee : deserializer {
log.insert(log.end(), tn.begin(), tn.end()); log.insert(log.end(), tn.begin(), tn.end());
log += " value"; log += " value";
x = T{}; 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 { bool value(int8_t& x) override {
...@@ -256,7 +263,7 @@ struct testee : deserializer { ...@@ -256,7 +263,7 @@ struct testee : deserializer {
log += "caf::span<caf::byte> value"; log += "caf::span<caf::byte> value";
for (auto& x : xs) for (auto& x : xs)
x = byte{0}; x = byte{0};
return ok; return true;
} }
}; };
...@@ -330,7 +337,7 @@ end object)_"); ...@@ -330,7 +337,7 @@ end object)_");
CAF_TEST(load inspectors support optional) { CAF_TEST(load inspectors support optional) {
optional<int32_t> x; 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"_( CAF_CHECK_EQUAL(f.log, R"_(
begin object anonymous begin object anonymous
begin optional field value begin optional field value
......
...@@ -110,19 +110,16 @@ CAF_TEST(to_string converts messages to strings) { ...@@ -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(), "message()");
CAF_CHECK_EQUAL(msg_as_string("hello", "world"), CAF_CHECK_EQUAL(msg_as_string("hello", "world"),
R"__(message("hello", "world"))__"); R"__(message("hello", "world"))__");
CAF_CHECK_EQUAL( CAF_CHECK_EQUAL(msg_as_string(svec{"one", "two", "three"}),
msg_as_string(svec{"one", "two", "three"}), R"__(message(["one", "two", "three"]))__");
R"__(message(std::vector<std::string>(["one", "two", "three"])))__"); CAF_CHECK_EQUAL(msg_as_string(svec{"one", "two"}, "three", "four",
CAF_CHECK_EQUAL(
msg_as_string(svec{"one", "two"}, "three", "four",
svec{"five", "six", "seven"}), svec{"five", "six", "seven"}),
R"__(message(std::vector<std::string>(["one", "two"]), "three", "four", )__" R"__(message(["one", "two"], "three", "four", )__"
R"__(std::vector<std::string>(["five", "six", "seven"])))__"); R"__(["five", "six", "seven"]))__");
auto teststr = R"__(message("this is a \"test\""))__"; // fails inline on MSVC 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(R"__(this is a "test")__"), teststr);
CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5), 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]), " "message([1, 2, 3], 4, 5)");
"int32_t(4), int32_t(5))");
CAF_CHECK_EQUAL(msg_as_string(s1{}), "message(s1([10, 20, 30]))"); CAF_CHECK_EQUAL(msg_as_string(s1{}), "message(s1([10, 20, 30]))");
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
......
...@@ -47,7 +47,7 @@ CAF_TEST(message builder can build messages incrermenetally) { ...@@ -47,7 +47,7 @@ CAF_TEST(message builder can build messages incrermenetally) {
CAF_CHECK_EQUAL(builder.size(), 1u); CAF_CHECK_EQUAL(builder.size(), 1u);
CAF_CHECK_EQUAL(msg.types(), make_type_id_list<int32_t>()); 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.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)") { STEP("after adding [2, 3], the message is (1, 2, 3)") {
std::vector<int32_t> xs{2, 3}; std::vector<int32_t> xs{2, 3};
...@@ -57,15 +57,13 @@ CAF_TEST(message builder can build messages incrermenetally) { ...@@ -57,15 +57,13 @@ CAF_TEST(message builder can build messages incrermenetally) {
CAF_CHECK_EQUAL(msg.types(), CAF_CHECK_EQUAL(msg.types(),
(make_type_id_list<int32_t, int32_t, int32_t>())); (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.types()), "[int32_t, int32_t, int32_t]");
CAF_CHECK_EQUAL(to_string(msg), CAF_CHECK_EQUAL(to_string(msg), "message(1, 2, 3)");
"message(int32_t(1), int32_t(2), int32_t(3))");
} }
STEP("moving the content to a message produces the same message again") { STEP("moving the content to a message produces the same message again") {
auto msg = builder.move_to_message(); auto msg = builder.move_to_message();
CAF_CHECK_EQUAL(msg.types(), CAF_CHECK_EQUAL(msg.types(),
(make_type_id_list<int32_t, int32_t, int32_t>())); (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.types()), "[int32_t, int32_t, int32_t]");
CAF_CHECK_EQUAL(to_string(msg), CAF_CHECK_EQUAL(to_string(msg), "message(1, 2, 3)");
"message(int32_t(1), int32_t(2), int32_t(3))");
} }
} }
...@@ -28,22 +28,18 @@ bool inspect(Inspector& f, weekday& x) { ...@@ -28,22 +28,18 @@ bool inspect(Inspector& f, weekday& x) {
if (f.has_human_readable_format()) { if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); }; auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, 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 { } else {
using default_impl = caf::default_inspector_access<weekday>; auto get = [&x] { return static_cast<uint8_t>(x); };
return default_impl::apply_object(f, x); auto set = [&x](uint8_t val) {
} if (val < 7) {
} x = static_cast<weekday>(val);
return true;
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 { } else {
using default_impl = caf::default_inspector_access<weekday>; return false;
return default_impl::apply_value(f, x); }
};
return f.apply(get, set);
} }
} }
......
...@@ -33,7 +33,7 @@ node_id roundtrip(node_id nid) { ...@@ -33,7 +33,7 @@ node_id roundtrip(node_id nid) {
byte_buffer buf; byte_buffer buf;
{ {
binary_serializer sink{nullptr, buf}; binary_serializer sink{nullptr, buf};
if (!sink.apply_object(nid)) if (!sink.apply(nid))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
} }
if (buf.empty()) if (buf.empty())
...@@ -41,7 +41,7 @@ node_id roundtrip(node_id nid) { ...@@ -41,7 +41,7 @@ node_id roundtrip(node_id nid) {
node_id result; node_id result;
{ {
binary_deserializer source{nullptr, buf}; binary_deserializer source{nullptr, buf};
if (!source.apply_object(result)) if (!source.apply(result))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
if (source.remaining() > 0) if (source.remaining() > 0)
CAF_FAIL("binary_serializer ignored part of its input"); CAF_FAIL("binary_serializer ignored part of its input");
......
...@@ -54,7 +54,7 @@ struct testee : serializer { ...@@ -54,7 +54,7 @@ struct testee : serializer {
log += "next object type: "; log += "next object type: ";
auto tn = detail::global_meta_object(type)->type_name; auto tn = detail::global_meta_object(type)->type_name;
log.insert(log.end(), tn.begin(), tn.end()); log.insert(log.end(), tn.begin(), tn.end());
return ok; return true;
} }
bool begin_object(string_view object_name) override { bool begin_object(string_view object_name) override {
...@@ -62,7 +62,7 @@ struct testee : serializer { ...@@ -62,7 +62,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin object "; log += "begin object ";
log.insert(log.end(), object_name.begin(), object_name.end()); log.insert(log.end(), object_name.begin(), object_name.end());
return ok; return true;
} }
bool end_object() override { bool end_object() override {
...@@ -71,7 +71,7 @@ struct testee : serializer { ...@@ -71,7 +71,7 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end object"; log += "end object";
return ok; return true;
} }
bool begin_field(string_view name) override { bool begin_field(string_view name) override {
...@@ -79,7 +79,7 @@ struct testee : serializer { ...@@ -79,7 +79,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin field "; log += "begin field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
return ok; return true;
} }
bool begin_field(string_view name, bool) override { bool begin_field(string_view name, bool) override {
...@@ -87,7 +87,7 @@ struct testee : serializer { ...@@ -87,7 +87,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin optional field "; log += "begin optional field ";
log.insert(log.end(), name.begin(), name.end()); 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 { bool begin_field(string_view name, span<const type_id_t>, size_t) override {
...@@ -95,7 +95,7 @@ struct testee : serializer { ...@@ -95,7 +95,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin variant field "; log += "begin variant field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
return ok; return true;
} }
bool begin_field(string_view name, bool, span<const type_id_t>, bool begin_field(string_view name, bool, span<const type_id_t>,
...@@ -104,7 +104,7 @@ struct testee : serializer { ...@@ -104,7 +104,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin optional variant field "; log += "begin optional variant field ";
log.insert(log.end(), name.begin(), name.end()); log.insert(log.end(), name.begin(), name.end());
return ok; return true;
} }
bool end_field() override { bool end_field() override {
...@@ -113,7 +113,7 @@ struct testee : serializer { ...@@ -113,7 +113,7 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end field"; log += "end field";
return ok; return true;
} }
bool begin_tuple(size_t size) override { bool begin_tuple(size_t size) override {
...@@ -121,7 +121,7 @@ struct testee : serializer { ...@@ -121,7 +121,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin tuple of size "; log += "begin tuple of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_tuple() override { bool end_tuple() override {
...@@ -130,14 +130,14 @@ struct testee : serializer { ...@@ -130,14 +130,14 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end tuple"; log += "end tuple";
return ok; return true;
} }
bool begin_key_value_pair() override { bool begin_key_value_pair() override {
new_line(); new_line();
indent += 2; indent += 2;
log += "begin key-value pair"; log += "begin key-value pair";
return ok; return true;
} }
bool end_key_value_pair() override { bool end_key_value_pair() override {
...@@ -146,7 +146,7 @@ struct testee : serializer { ...@@ -146,7 +146,7 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end key-value pair"; log += "end key-value pair";
return ok; return true;
} }
bool begin_sequence(size_t size) override { bool begin_sequence(size_t size) override {
...@@ -154,7 +154,7 @@ struct testee : serializer { ...@@ -154,7 +154,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin sequence of size "; log += "begin sequence of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_sequence() override { bool end_sequence() override {
...@@ -163,7 +163,7 @@ struct testee : serializer { ...@@ -163,7 +163,7 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end sequence"; log += "end sequence";
return ok; return true;
} }
bool begin_associative_array(size_t size) override { bool begin_associative_array(size_t size) override {
...@@ -171,7 +171,7 @@ struct testee : serializer { ...@@ -171,7 +171,7 @@ struct testee : serializer {
indent += 2; indent += 2;
log += "begin associative array of size "; log += "begin associative array of size ";
log += std::to_string(size); log += std::to_string(size);
return ok; return true;
} }
bool end_associative_array() override { bool end_associative_array() override {
...@@ -180,103 +180,109 @@ struct testee : serializer { ...@@ -180,103 +180,109 @@ struct testee : serializer {
indent -= 2; indent -= 2;
new_line(); new_line();
log += "end associative array"; log += "end associative array";
return ok; return true;
}
bool value(byte) override {
new_line();
log += "byte value";
return true;
} }
bool value(bool) override { bool value(bool) override {
new_line(); new_line();
log += "bool value"; log += "bool value";
return ok; return true;
} }
bool value(int8_t) override { bool value(int8_t) override {
new_line(); new_line();
log += "int8_t value"; log += "int8_t value";
return ok; return true;
} }
bool value(uint8_t) override { bool value(uint8_t) override {
new_line(); new_line();
log += "uint8_t value"; log += "uint8_t value";
return ok; return true;
} }
bool value(int16_t) override { bool value(int16_t) override {
new_line(); new_line();
log += "int16_t value"; log += "int16_t value";
return ok; return true;
} }
bool value(uint16_t) override { bool value(uint16_t) override {
new_line(); new_line();
log += "uint16_t value"; log += "uint16_t value";
return ok; return true;
} }
bool value(int32_t) override { bool value(int32_t) override {
new_line(); new_line();
log += "int32_t value"; log += "int32_t value";
return ok; return true;
} }
bool value(uint32_t) override { bool value(uint32_t) override {
new_line(); new_line();
log += "uint32_t value"; log += "uint32_t value";
return ok; return true;
} }
bool value(int64_t) override { bool value(int64_t) override {
new_line(); new_line();
log += "int64_t value"; log += "int64_t value";
return ok; return true;
} }
bool value(uint64_t) override { bool value(uint64_t) override {
new_line(); new_line();
log += "uint64_t value"; log += "uint64_t value";
return ok; return true;
} }
bool value(float) override { bool value(float) override {
new_line(); new_line();
log += "float value"; log += "float value";
return ok; return true;
} }
bool value(double) override { bool value(double) override {
new_line(); new_line();
log += "double value"; log += "double value";
return ok; return true;
} }
bool value(long double) override { bool value(long double) override {
new_line(); new_line();
log += "long double value"; log += "long double value";
return ok; return true;
} }
bool value(string_view) override { bool value(string_view) override {
new_line(); new_line();
log += "std::string value"; log += "std::string value";
return ok; return true;
} }
bool value(const std::u16string&) override { bool value(const std::u16string&) override {
new_line(); new_line();
log += "std::u16string value"; log += "std::u16string value";
return ok; return true;
} }
bool value(const std::u32string&) override { bool value(const std::u32string&) override {
new_line(); new_line();
log += "std::u32string value"; log += "std::u32string value";
return ok; return true;
} }
bool value(span<const byte>) override { bool value(span<const byte>) override {
new_line(); new_line();
log += "byte_span value"; log += "byte_span value";
return ok; return true;
} }
}; };
...@@ -290,7 +296,7 @@ CAF_TEST_FIXTURE_SCOPE(load_inspector_tests, fixture) ...@@ -290,7 +296,7 @@ CAF_TEST_FIXTURE_SCOPE(load_inspector_tests, fixture)
CAF_TEST(save inspectors can visit C arrays) { CAF_TEST(save inspectors can visit C arrays) {
int32_t xs[] = {1, 2, 3}; 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"_( CAF_CHECK_EQUAL(f.log, R"_(
begin tuple of size 3 begin tuple of size 3
int32_t value int32_t value
...@@ -436,7 +442,7 @@ end object)_"); ...@@ -436,7 +442,7 @@ end object)_");
CAF_TEST(save inspectors support optional) { CAF_TEST(save inspectors support optional) {
optional<int32_t> x; 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"_( CAF_CHECK_EQUAL(f.log, R"_(
begin object anonymous begin object anonymous
begin optional field value begin optional field value
...@@ -717,21 +723,9 @@ begin object message ...@@ -717,21 +723,9 @@ begin object message
end field end field
begin field values begin field values
begin tuple of size 3 begin tuple of size 3
begin object int32_t
begin field value
int32_t value int32_t value
end field
end object
begin object std::string
begin field value
std::string value std::string value
end field
end object
begin object double
begin field value
double value double value
end field
end object
end tuple end tuple
end field end field
end object)_"); end object)_");
...@@ -744,23 +738,11 @@ begin object message ...@@ -744,23 +738,11 @@ begin object message
begin field values begin field values
begin sequence of size 3 begin sequence of size 3
next object type: int32_t next object type: int32_t
begin object int32_t
begin field value
int32_t value int32_t value
end field
end object
next object type: std::string next object type: std::string
begin object std::string
begin field value
std::string value std::string value
end field
end object
next object type: double next object type: double
begin object double
begin field value
double value double value
end field
end object
end sequence end sequence
end field end field
end object)_"); end object)_");
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <limits> #include <limits>
#include <list> #include <list>
#include <locale> #include <locale>
#include <map>
#include <memory> #include <memory>
#include <new> #include <new>
#include <set> #include <set>
...@@ -52,17 +53,66 @@ ...@@ -52,17 +53,66 @@
#include "caf/detail/ieee_754.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/stringification_inspector.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/message_handler.hpp" #include "caf/message_handler.hpp"
#include "caf/proxy_registry.hpp" #include "caf/proxy_registry.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/variant.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 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[] = { const char* test_enum_strings[] = {
"a", "a",
"b", "b",
...@@ -111,7 +161,7 @@ struct fixture : test_coordinator_fixture<> { ...@@ -111,7 +161,7 @@ struct fixture : test_coordinator_fixture<> {
byte_buffer serialize(const Ts&... xs) { byte_buffer serialize(const Ts&... xs) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{sys, buf}; binary_serializer sink{sys, buf};
if (!sink.apply_objects(xs...)) if (!(sink.apply(xs) && ...))
CAF_FAIL("serialization failed: " CAF_FAIL("serialization failed: "
<< sink.get_error() << sink.get_error()
<< ", data: " << deep_to_string(std::forward_as_tuple(xs...))); << ", data: " << deep_to_string(std::forward_as_tuple(xs...)));
...@@ -121,7 +171,7 @@ struct fixture : test_coordinator_fixture<> { ...@@ -121,7 +171,7 @@ struct fixture : test_coordinator_fixture<> {
template <class... Ts> template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) { void deserialize(const byte_buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf}; binary_deserializer source{sys, buf};
if (!source.apply_objects(xs...)) if (!(source.apply(xs) && ...))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
} }
......
...@@ -47,11 +47,11 @@ public: ...@@ -47,11 +47,11 @@ public:
} }
bool serialize(serializer& sink) const override { bool serialize(serializer& sink) const override {
return sink.apply_object(value); return sink.apply(value);
} }
bool serialize(binary_serializer& sink) const override { bool serialize(binary_serializer& sink) const override {
return sink.apply_object(value); return sink.apply(value);
} }
}; };
...@@ -72,7 +72,7 @@ private: ...@@ -72,7 +72,7 @@ private:
bool deserialize_impl(Deserializer& source, bool deserialize_impl(Deserializer& source,
std::unique_ptr<tracing_data>& dst) const { std::unique_ptr<tracing_data>& dst) const {
string value; string value;
if (!source.apply_object(value)) if (!source.apply(value))
return false; return false;
dst.reset(new dummy_tracing_data(std::move(value))); dst.reset(new dummy_tracing_data(std::move(value)));
return true; return true;
...@@ -193,10 +193,10 @@ CAF_TEST(tracing data is serializable) { ...@@ -193,10 +193,10 @@ CAF_TEST(tracing data is serializable) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{sys, buf}; binary_serializer sink{sys, buf};
tracing_data_ptr data{new dummy_tracing_data("iTrace")}; 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}; binary_deserializer source{sys, buf};
tracing_data_ptr copy; tracing_data_ptr copy;
CAF_CHECK(source.apply_object(copy)); CAF_CHECK(source.apply(copy));
CAF_REQUIRE_NOT_EQUAL(copy.get(), nullptr); CAF_REQUIRE_NOT_EQUAL(copy.get(), nullptr);
CAF_CHECK_EQUAL(dynamic_cast<dummy_tracing_data&>(*copy).value, "iTrace"); CAF_CHECK_EQUAL(dynamic_cast<dummy_tracing_data&>(*copy).value, "iTrace");
} }
......
...@@ -133,7 +133,7 @@ struct fixture { ...@@ -133,7 +133,7 @@ struct fixture {
byte_buffer serialize(uri x) { byte_buffer serialize(uri x) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{nullptr, buf}; binary_serializer sink{nullptr, buf};
if (!sink.apply_objects(x)) if (!sink.apply(x))
CAF_FAIL("unable to serialize " << x << ": " << sink.get_error()); CAF_FAIL("unable to serialize " << x << ": " << sink.get_error());
return buf; return buf;
} }
...@@ -141,7 +141,7 @@ struct fixture { ...@@ -141,7 +141,7 @@ struct fixture {
uri deserialize(byte_buffer buf) { uri deserialize(byte_buffer buf) {
uri result; uri result;
binary_deserializer source{nullptr, buf}; binary_deserializer source{nullptr, buf};
if (!source.apply_objects(result)) if (!source.apply(result))
CAF_FAIL("unable to deserialize from buffer: " << source.get_error()); CAF_FAIL("unable to deserialize from buffer: " << source.get_error());
return result; return result;
} }
......
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/io_export.hpp" #include "caf/detail/io_export.hpp"
namespace caf::io::basp { namespace caf::io::basp {
...@@ -76,6 +78,16 @@ enum class message_type : uint8_t { ...@@ -76,6 +78,16 @@ enum class message_type : uint8_t {
CAF_IO_EXPORT std::string to_string(message_type); 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 } // namespace caf::io::basp
...@@ -79,12 +79,12 @@ public: ...@@ -79,12 +79,12 @@ public:
if (dref.hdr_.operation == basp::message_type::routed_message) { if (dref.hdr_.operation == basp::message_type::routed_message) {
node_id src_node; node_id src_node;
node_id dst_node; node_id dst_node;
if (!source.apply_object(src_node)) { if (!source.apply(src_node)) {
CAF_LOG_ERROR( CAF_LOG_ERROR(
"failed to read source of routed message:" << source.get_error()); "failed to read source of routed message:" << source.get_error());
return; return;
} }
if (!source.apply_object(dst_node)) { if (!source.apply(dst_node)) {
CAF_LOG_ERROR("failed to read destination of routed message:" CAF_LOG_ERROR("failed to read destination of routed message:"
<< source.get_error()); << source.get_error());
return; return;
...@@ -108,13 +108,13 @@ public: ...@@ -108,13 +108,13 @@ public:
return; return;
} }
// Get the remainder of the message. // 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()); CAF_LOG_ERROR("failed to read stages:" << source.get_error());
return; return;
} }
auto& mm_metrics = ctx->system().middleman().metric_singletons; auto& mm_metrics = ctx->system().middleman().metric_singletons;
auto t0 = telemetry::timer::clock_type::now(); 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()); CAF_LOG_ERROR("failed to read message content:" << source.get_error());
return; return;
} }
......
...@@ -40,10 +40,32 @@ inline std::string to_string(protocol::transport x) { ...@@ -40,10 +40,32 @@ inline std::string to_string(protocol::transport x) {
return x == protocol::tcp ? "TCP" : "UDP"; 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) { inline std::string to_string(protocol::network x) {
return x == protocol::ipv4 ? "IPv4" : "IPv6"; 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> template <class Inspector>
bool inspect(Inspector& f, protocol& x) { bool inspect(Inspector& f, protocol& x) {
return f.object(x).fields(f.field("trans", x.trans), f.field("net", x.net)); 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, ...@@ -73,7 +73,7 @@ connection_state instance::handle(execution_unit* ctx, new_data_msg& dm,
} }
} else { } else {
binary_deserializer source{ctx, dm.buf}; 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()); CAF_LOG_WARNING("failed to receive header:" << source.get_error());
return err(malformed_basp_message); return err(malformed_basp_message);
} }
...@@ -185,7 +185,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender, ...@@ -185,7 +185,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
sender ? sender->id() : invalid_actor_id, sender ? sender->id() : invalid_actor_id,
dest_actor}; dest_actor};
auto writer = make_callback([&](binary_serializer& sink) { // 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); write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
} else { } else {
...@@ -199,7 +199,10 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender, ...@@ -199,7 +199,10 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
CAF_LOG_DEBUG("send routed message: " CAF_LOG_DEBUG("send routed message: "
<< CAF_ARG(source_node) << CAF_ARG(dest_node) << CAF_ARG(source_node) << CAF_ARG(dest_node)
<< CAF_ARG(forwarding_stack) << CAF_ARG(msg)); << 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); write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
} }
...@@ -229,7 +232,7 @@ void instance::write(execution_unit* ctx, byte_buffer& buf, header& hdr, ...@@ -229,7 +232,7 @@ void instance::write(execution_unit* ctx, byte_buffer& buf, header& hdr,
mm_metrics.outbound_messages_size->observe(signed_payload_len); mm_metrics.outbound_messages_size->observe(signed_payload_len);
hdr.payload_len = static_cast<uint32_t>(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()); CAF_LOG_ERROR(sink.get_error());
} }
...@@ -258,7 +261,10 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf, ...@@ -258,7 +261,10 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
aid = pa->first->id(); aid = pa->first->id();
iface = pa->second; 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, header hdr{message_type::server_handshake,
0, 0,
...@@ -271,7 +277,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf, ...@@ -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) { void instance::write_client_handshake(execution_unit* ctx, byte_buffer& buf) {
auto writer = make_callback([&](binary_serializer& sink) { // auto writer = make_callback([&](binary_serializer& sink) { //
return sink.apply_objects(this_node_); return sink.apply(this_node_);
}); });
header hdr{message_type::client_handshake, header hdr{message_type::client_handshake,
0, 0,
...@@ -286,7 +292,7 @@ void instance::write_monitor_message(execution_unit* ctx, byte_buffer& buf, ...@@ -286,7 +292,7 @@ void instance::write_monitor_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid) { const node_id& dest_node, actor_id aid) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid)); CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid));
auto writer = make_callback([&](binary_serializer& sink) { // 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}; header hdr{message_type::monitor_message, 0, 0, 0, invalid_actor_id, aid};
write(ctx, buf, hdr, &writer); write(ctx, buf, hdr, &writer);
...@@ -296,8 +302,8 @@ void instance::write_down_message(execution_unit* ctx, byte_buffer& buf, ...@@ -296,8 +302,8 @@ void instance::write_down_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid, const node_id& dest_node, actor_id aid,
const error& rsn) { const error& rsn) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn)); CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn));
auto writer = make_callback([&](binary_serializer& sink) { // auto writer = make_callback([&](binary_serializer& sink) {
return sink.apply_objects(this_node_, dest_node, rsn); 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}; header hdr{message_type::down_message, 0, 0, 0, aid, invalid_actor_id};
write(ctx, buf, hdr, &writer); write(ctx, buf, hdr, &writer);
...@@ -333,7 +339,10 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -333,7 +339,10 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
string_list app_ids; string_list app_ids;
actor_id aid = invalid_actor_id; actor_id aid = invalid_actor_id;
std::set<std::string> sigs; 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:" CAF_LOG_WARNING("unable to deserialize payload of server handshake:"
<< source.get_error()); << source.get_error());
return serializing_basp_payload_failed; return serializing_basp_payload_failed;
...@@ -383,7 +392,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -383,7 +392,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
// Deserialize payload. // Deserialize payload.
binary_deserializer source{ctx, *payload}; binary_deserializer source{ctx, *payload};
node_id source_node; 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:" CAF_LOG_WARNING("unable to deserialize payload of client handshake:"
<< source.get_error()); << source.get_error());
return serializing_basp_payload_failed; return serializing_basp_payload_failed;
...@@ -406,7 +415,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -406,7 +415,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
binary_deserializer source{ctx, *payload}; binary_deserializer source{ctx, *payload};
node_id source_node; node_id source_node;
node_id dest_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( CAF_LOG_WARNING(
"unable to deserialize source and destination for routed message:" "unable to deserialize source and destination for routed message:"
<< source.get_error()); << source.get_error());
...@@ -465,7 +474,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -465,7 +474,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
binary_deserializer source{ctx, *payload}; binary_deserializer source{ctx, *payload};
node_id source_node; node_id source_node;
node_id dest_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:" CAF_LOG_WARNING("unable to deserialize payload of monitor message:"
<< source.get_error()); << source.get_error());
return serializing_basp_payload_failed; return serializing_basp_payload_failed;
...@@ -482,7 +491,9 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -482,7 +491,9 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
node_id source_node; node_id source_node;
node_id dest_node; node_id dest_node;
error fail_state; 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:" CAF_LOG_WARNING("unable to deserialize payload of down message:"
<< source.get_error()); << source.get_error());
return serializing_basp_payload_failed; return serializing_basp_payload_failed;
...@@ -520,7 +531,7 @@ void instance::forward(execution_unit* ctx, const node_id& dest_node, ...@@ -520,7 +531,7 @@ void instance::forward(execution_unit* ctx, const node_id& dest_node,
auto path = lookup(dest_node); auto path = lookup(dest_node);
if (path) { if (path) {
binary_serializer sink{ctx, callee_.get_buffer(path->hdl)}; 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()); CAF_LOG_ERROR("unable to serialize BASP header:" << sink.get_error());
return; return;
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -34,6 +35,51 @@ std::string to_string(message_type x) { ...@@ -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 basp
} // namespace io } // namespace io
} // namespace caf } // namespace caf
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings. // 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. // Run the target update-enum-strings if this file is out of sync.
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/string_view.hpp"
CAF_PUSH_DEPRECATED_WARNING CAF_PUSH_DEPRECATED_WARNING
...@@ -26,6 +27,35 @@ std::string to_string(operation x) { ...@@ -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 network
} // namespace io } // namespace io
} // namespace caf } // namespace caf
......
...@@ -163,7 +163,7 @@ public: ...@@ -163,7 +163,7 @@ public:
uint32_t serialized_size(const message& msg) { uint32_t serialized_size(const message& msg) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{mpx_, buf}; binary_serializer sink{mpx_, buf};
if (!sink.apply_object(msg)) if (!sink.apply(msg))
CAF_FAIL("failed to serialize message: " << sink.get_error()); CAF_FAIL("failed to serialize message: " << sink.get_error());
return static_cast<uint32_t>(buf.size()); return static_cast<uint32_t>(buf.size());
} }
...@@ -221,7 +221,7 @@ public: ...@@ -221,7 +221,7 @@ public:
template <class... Ts> template <class... Ts>
void to_payload(byte_buffer& buf, const Ts&... xs) { void to_payload(byte_buffer& buf, const Ts&... xs) {
binary_serializer sink{mpx_, buf}; binary_serializer sink{mpx_, buf};
if (!sink.apply_objects(xs...)) if (!(sink.apply(xs) && ...))
CAF_FAIL("failed to serialize payload: " << sink.get_error()); CAF_FAIL("failed to serialize payload: " << sink.get_error());
} }
...@@ -229,13 +229,13 @@ public: ...@@ -229,13 +229,13 @@ public:
instance().write(mpx_, buf, hdr, writer); 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, 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) { auto pw = make_callback([&](binary_serializer& sink) {
if (writer != nullptr && !(*writer)(sink)) if (writer != nullptr && !(*writer)(sink))
return false; return false;
return sink.apply_objects(x, xs...); return (sink.apply(xs) && ...);
}); });
to_buf(buf, hdr, &pw); to_buf(buf, hdr, &pw);
} }
...@@ -243,7 +243,7 @@ public: ...@@ -243,7 +243,7 @@ public:
std::pair<basp::header, byte_buffer> from_buf(const byte_buffer& buf) { std::pair<basp::header, byte_buffer> from_buf(const byte_buffer& buf) {
basp::header hdr; basp::header hdr;
binary_deserializer source{mpx_, buf}; binary_deserializer source{mpx_, buf};
if (!source.apply_object(hdr)) if (!source.apply(hdr))
CAF_FAIL("failed to deserialize header: " << source.get_error()); CAF_FAIL("failed to deserialize header: " << source.get_error());
byte_buffer payload; byte_buffer payload;
if (hdr.payload_len > 0) { if (hdr.payload_len > 0) {
...@@ -307,7 +307,7 @@ public: ...@@ -307,7 +307,7 @@ public:
binary_deserializer source{mpx_, buf}; binary_deserializer source{mpx_, buf};
std::vector<strong_actor_ptr> stages; std::vector<strong_actor_ptr> stages;
message msg; message msg;
if (!source.apply_objects(stages, msg)) if (!source.apply(stages) || !source.apply(msg))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
auto src = actor_cast<strong_actor_ptr>(registry_->get(hdr.source_actor)); auto src = actor_cast<strong_actor_ptr>(registry_->get(hdr.source_actor));
auto dest = registry_->get(hdr.dest_actor); auto dest = registry_->get(hdr.dest_actor);
...@@ -348,7 +348,7 @@ public: ...@@ -348,7 +348,7 @@ public:
basp::header hdr; basp::header hdr;
{ // lifetime scope of source { // lifetime scope of source
binary_deserializer source{this_->mpx(), ob}; binary_deserializer source{this_->mpx(), ob};
if (!source.apply_objects(hdr)) if (!source.apply(hdr))
CAF_FAIL("failed to deserialize header: " << source.get_error()); CAF_FAIL("failed to deserialize header: " << source.get_error());
} }
byte_buffer payload; byte_buffer payload;
...@@ -486,7 +486,10 @@ CAF_TEST(non_empty_server_handshake) { ...@@ -486,7 +486,10 @@ CAF_TEST(non_empty_server_handshake) {
std::set<std::string> ifs{"caf::replies_to<@u16>::with<@u16>"}; std::set<std::string> ifs{"caf::replies_to<@u16>::with<@u16>"};
binary_serializer sink{nullptr, expected_payload}; binary_serializer sink{nullptr, expected_payload};
auto id = self()->id(); 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_FAIL("serializing handshake failed: " << sink.get_error());
CAF_CHECK_EQUAL(hexstr(payload), hexstr(expected_payload)); CAF_CHECK_EQUAL(hexstr(payload), hexstr(expected_payload));
} }
......
...@@ -45,19 +45,19 @@ public: ...@@ -45,19 +45,19 @@ public:
}; };
struct fixture : test_coordinator_fixture<> { struct fixture : test_coordinator_fixture<> {
template <class T, class... Ts> template <class... Ts>
auto serialize(T& x, Ts&... xs) { auto serialize(Ts&... xs) {
byte_buffer buf; byte_buffer buf;
binary_serializer sink{sys, buf}; binary_serializer sink{sys, buf};
if (!sink.apply_objects(x, xs...)) if (!(sink.apply(xs) && ...))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
return buf; return buf;
} }
template <class Buffer, class T, class... Ts> template <class Buffer, class... Ts>
void deserialize(const Buffer& buf, T& x, Ts&... xs) { void deserialize(const Buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf}; binary_deserializer source{sys, buf};
if (!source.apply_objects(x, xs...)) if (!(source.apply(xs) && ...))
CAF_FAIL("serialization failed: " << source.get_error()); CAF_FAIL("serialization failed: " << source.get_error());
} }
}; };
......
...@@ -120,7 +120,7 @@ CAF_TEST(deliver serialized message) { ...@@ -120,7 +120,7 @@ CAF_TEST(deliver serialized message) {
std::vector<strong_actor_ptr> stages; std::vector<strong_actor_ptr> stages;
binary_serializer sink{sys, payload}; binary_serializer sink{sys, payload};
auto msg = make_message(ok_atom_v); 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()); CAF_FAIL("unable to serialize message: " << sink.get_error());
io::basp::header hdr{io::basp::message_type::direct_message, io::basp::header hdr{io::basp::message_type::direct_message,
0, 0,
......
...@@ -843,7 +843,7 @@ public: ...@@ -843,7 +843,7 @@ public:
caf::byte_buffer serialize(const Ts&... xs) { caf::byte_buffer serialize(const Ts&... xs) {
caf::byte_buffer buf; caf::byte_buffer buf;
caf::binary_serializer sink{sys, buf}; caf::binary_serializer sink{sys, buf};
if (!sink.apply_objects(xs...)) if (!sink.apply(xs...))
CAF_FAIL("serialization failed: " << sink.get_error()); CAF_FAIL("serialization failed: " << sink.get_error());
return buf; return buf;
} }
...@@ -851,7 +851,7 @@ public: ...@@ -851,7 +851,7 @@ public:
template <class... Ts> template <class... Ts>
void deserialize(const caf::byte_buffer& buf, Ts&... xs) { void deserialize(const caf::byte_buffer& buf, Ts&... xs) {
caf::binary_deserializer source{sys, buf}; caf::binary_deserializer source{sys, buf};
if (!source.apply_objects(xs...)) if (!source.apply(xs...))
CAF_FAIL("deserialization failed: " << source.get_error()); CAF_FAIL("deserialization failed: " << source.get_error());
} }
......
...@@ -6,20 +6,36 @@ Type Inspection ...@@ -6,20 +6,36 @@ Type Inspection
We designed CAF with distributed systems in mind. Hence, all message types must 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 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 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: .. _type-inspection-data-model:
Data Model Data Model
---------- ----------
Type inspection in CAF uses a hierarchical, object-oriented data model. The main Type inspection in CAF uses a hierarchical data model with the following
abstraction is the *object*. An object has one or more *fields*. Fields have a building blocks:
*name* and may be *optional*. Further, fields can take on several different (but
fixed) *types*.
Depending on its type, a field contains: a single *value*, a variable-length built-in types
*sequence*, or a fixed-size *tuple*. - 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: 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: ...@@ -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 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, ``x2`` is optional and may contain a fixed-size tuple with two elements
field ``x3`` contains any number of string values at runtime. (built-in types). Lastly, field ``x3`` contains any number of string values at
runtime.
Numbers and strings are primitive types in the inspector API.
Inspecting Objects Inspecting Objects
------------------ ------------------
The inspection API allows CAF to deconstruct C++ objects into fields and values. The inspection API allows CAF to deconstruct C++ objects. Users can either
Users can either provide free functions named ``inspect`` that CAF picks up via provide free functions named ``inspect`` that CAF picks up via `ADL
`ADL <https://en.wikipedia.org/wiki/Argument-dependent_name_lookup>`_ or <https://en.wikipedia.org/wiki/Argument-dependent_name_lookup>`_ or specialize
specialize ``caf::inspector_access``. ``caf::inspector_access``.
In both cases, users call members and member functions on an ``Inspector`` that 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++ provides a domain-specific language (DSL) for describing the structure of a C++
object. object.
After listing a custom type ``T`` in a type ID block and either providing a free 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. - Serialize and deserialize objects of type ``T`` to/from Byte sequences.
- Render objects of type ``T`` as a human-readable string via - Render objects of type ``T`` as a human-readable string via
...@@ -65,11 +81,18 @@ the inspection API. ...@@ -65,11 +81,18 @@ the inspection API.
.. code-block:: C++ .. code-block:: C++
struct point_3d { struct point_3d {
int x; int32_t x;
int y; int32_t y;
int z; 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:
Writing ``inspect`` Overloads Writing ``inspect`` Overloads
...@@ -92,15 +115,13 @@ the inspector: ...@@ -92,15 +115,13 @@ the inspector:
f.field("z", x.z)); f.field("z", x.z));
} }
Writing ``inspect_value`` Overloads 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
The free function ``inspect`` models the object-level type inspection. As traverses all fields.
mentioned in the section on the :ref:`data model <type-inspection-data-model>`,
objects are containers for fields that in turn contain a value. When providing
an ``inspect`` overload, CAF recursively visits a value as an object.
For example, consider the following ID type that simply wraps a string: 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++ .. code-block:: C++
...@@ -111,14 +132,16 @@ For example, consider the following ID type that simply wraps a string: ...@@ -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)); return f.object(x).fields(f.field("value", x.value));
} }
The type ``id`` is basically a *strong typedef* to improve type safety. To a The type ``id`` is basically a *strong typedef* to improve type safety when
type inspector, ID objects look as follows: writing code. To a type inspector, ID objects look as follows:
.. code-block:: none .. code-block:: none
object(type: "id") { object(type: "id") {
field(name: "value") { field(name: "value") {
value(type: "string") value(type: "string") {
...
}
} }
} }
...@@ -138,7 +161,7 @@ By providing the ``inspect`` overload for ID, inspectors can recursively visit ...@@ -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 an ``id`` as an object. Hence, the above implementations work as expected. When
using ``person`` in human-readable data formats such as CAF configurations, using ``person`` in human-readable data formats such as CAF configurations,
however, allowing CAF to look "inside" a strong typedef can simplify working 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 With the current implementation, we could read the key ``manager.ceo`` from a
configuration file with this content: configuration file with this content:
...@@ -154,20 +177,23 @@ 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 This clearly appears more verbose than it needs to be. Users generally need not
for ``inspect_value``, we can teach CAF how to inspect an ID directly as a care about such internal types like ``id`` that only exist as a safeguard during
*value* without having to recursively visit it as an object: 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++ .. code-block:: C++
template <class Inspector> template <class Inspector>
bool inspect_value(Inspector& f, id& x) { bool inspect(Inspector& f, id& x) {
return f.apply_value(x.value); return f.apply(x.value);
} }
With this overload in place, inspectors can now remove one level of indirection With this implementation instead of the previous one, inspectors simply read or
and read or write strings whenever they encounter an ``id`` as value. This write strings whenever they encounter an ``id`` as value. This simplifies our
allows us to simply our config file from before: config file from before and thus gives a much cleaner interface to users:
.. code-block:: none .. code-block:: none
...@@ -184,7 +210,7 @@ Specializing ``inspector_access`` ...@@ -184,7 +210,7 @@ Specializing ``inspector_access``
Working with 3rd party libraries usually rules out adding free functions for Working with 3rd party libraries usually rules out adding free functions for
existing classes, because the namespace belongs to a another project. Hence, CAF existing classes, because the namespace belongs to a another project. Hence, CAF
also allows specializing ``inspector_access`` instead. This requires writing 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. process.
The full interface of ``inspector_access`` looks as follows: The full interface of ``inspector_access`` looks as follows:
...@@ -194,10 +220,7 @@ 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> template <class T>
struct inspector_access { struct inspector_access {
template <class Inspector> template <class Inspector>
static bool apply_object(Inspector& f, T& x); static bool apply(Inspector& f, T& x);
template <class Inspector>
static bool apply_value(Inspector& f, T& x);
template <class Inspector> template <class Inspector>
static bool save_field(Inspector& f, string_view field_name, T& x); 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: ...@@ -216,11 +239,8 @@ The full interface of ``inspector_access`` looks as follows:
SetFallback& set_fallback); SetFallback& set_fallback);
}; };
The static member function ``apply_object`` has the same role as the free The static member function ``apply`` has the same role as the free ``inspect``
``inspect`` function. Likewise, ``apply_value`` corresponds to the free function. For most types, we can implement only ``apply`` and use a default
``inspect_value`` function.
For most types, we can implement only ``apply_object`` and use default
implementation for the other member functions. For example, specializing implementation for the other member functions. For example, specializing
``inspector_access`` for our ``point_3d`` would look as follows: ``inspector_access`` for our ``point_3d`` would look as follows:
...@@ -231,16 +251,11 @@ implementation for the other member functions. For example, specializing ...@@ -231,16 +251,11 @@ implementation for the other member functions. For example, specializing
template <> template <>
struct inspector_access<point_3d> : inspector_access_base<point_3d> { struct inspector_access<point_3d> : inspector_access_base<point_3d> {
template <class Inspector> 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), return f.object(x).fields(f.field("x", x.x),
f.field("y", x.y), f.field("y", x.y),
f.field("z", x.z)); f.field("z", x.z));
} }
template <class Inspector>
static bool apply_value(Inspector& f, point_3d& x) {
return apply_object(f, x);
}
}; };
} // namespace caf } // namespace caf
...@@ -255,13 +270,6 @@ becomes necessary when integration custom types that have semantics similar to ...@@ -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`` Please refer to the Doxygen documentation for more details on ``save_field``
and ``load_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 Types with Getter and Setter Access
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -433,13 +441,13 @@ machine-to-machine communication, using the underlying integer representation ...@@ -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 gives the best performance. However, using the constant names results in a much
better user experience in all other cases. better user experience in all other cases.
The following code illustrates how to use a string representation for inspectors The following code illustrates how to provide a string representation for
that operate on human-readable data representation and the underlying type for inspectors that operate on human-readable data representation and the underlying
an ``enum class`` otherwise. type for an ``enum class`` otherwise.
.. code-block:: C++ .. code-block:: C++
enum class weekday { enum class weekday : uint8_t {
monday, monday,
tuesday, tuesday,
wednesday, wednesday,
...@@ -458,34 +466,24 @@ an ``enum class`` otherwise. ...@@ -458,34 +466,24 @@ an ``enum class`` otherwise.
if (f.has_human_readable_format()) { if (f.has_human_readable_format()) {
auto get = [&x] { return to_string(x); }; auto get = [&x] { return to_string(x); };
auto set = [&x](std::string str) { return parse(str, 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 { } else {
using default_impl = caf::default_inspector_access<weekday>; auto get = [&x] { return static_cast<uint8_t>(x); };
return default_impl::apply_object(f, x); auto set = [&x](uint8_t val) {
} if (val < 7) {
} x = static_cast<weekday>(val);
return true;
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 { } else {
using default_impl = caf::default_inspector_access<weekday>; return false;
return default_impl::apply_value(f, x); }
};
return f.apply(get, set);
} }
} }
When inspecting an object of type ``weekday``, we treat it as if we were When inspecting an object of type ``weekday``, we treat is as if it were a
inspecting an object with a single field named ``value``. However, usually we string for inspectors with human-readable data formats. Otherwise, we treat the
are going to inspect other objects that contain values of type ``weekday``. In weekday as if it were an integer between 0 and 6.
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.
.. _unsafe-message-type: .. _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