Commit 844ad3db authored by Dominik Charousset's avatar Dominik Charousset

Fix handling of implicitly convertible types

parent 3e5d8071
...@@ -127,13 +127,16 @@ public: ...@@ -127,13 +127,16 @@ public:
bool value(const std::vector<bool>& xs); bool value(const std::vector<bool>& xs);
// -- builtin inspection to pick up to_string or provide nicer formatting ----
template <class Rep, class Period> template <class Rep, class Period>
bool value(const std::chrono::duration<Rep, Period> x) { bool builtin_inspect(const std::chrono::duration<Rep, Period> x) {
return value(std::chrono::duration_cast<timespan>(x)); return value(std::chrono::duration_cast<timespan>(x));
} }
template <class T> template <class T>
std::enable_if_t<detail::is_map_like_v<T>, bool> value(const T& xs) { std::enable_if_t<detail::is_map_like_v<T>, bool>
builtin_inspect(const T& xs) {
sep(); sep();
auto i = xs.begin(); auto i = xs.begin();
auto last = xs.end(); auto last = xs.end();
...@@ -159,7 +162,7 @@ public: ...@@ -159,7 +162,7 @@ public:
std::enable_if_t<has_to_string<T>::value std::enable_if_t<has_to_string<T>::value
&& !std::is_convertible<T, string_view>::value, && !std::is_convertible<T, string_view>::value,
bool> bool>
value(const T& x) { builtin_inspect(const T& x) {
auto str = to_string(x); auto str = to_string(x);
if constexpr (std::is_convertible<decltype(str), const char*>::value) { if constexpr (std::is_convertible<decltype(str), const char*>::value) {
const char* cstr = str; const char* cstr = str;
...@@ -171,10 +174,10 @@ public: ...@@ -171,10 +174,10 @@ public:
return true; return true;
} }
bool value(const char* x); bool builtin_inspect(const char* x);
template <class T> template <class T>
bool value(const T* x) { bool builtin_inspect(const T* x) {
sep(); sep();
if (!x) { if (!x) {
result_ += "null"; result_ += "null";
...@@ -186,7 +189,7 @@ public: ...@@ -186,7 +189,7 @@ public:
} }
template <class T> template <class T>
bool value(const optional<T>& x) { bool builtin_inspect(const optional<T>& x) {
sep(); sep();
if (!x) { if (!x) {
result_ += "null"; result_ += "null";
...@@ -197,17 +200,7 @@ public: ...@@ -197,17 +200,7 @@ public:
return true; return true;
} }
template <class T> // -- fallbacks --------------------------------------------------------------
void append(T&& str) {
sep();
result_.insert(result_.end(), str.begin(), str.end());
}
template <class... Ts>
bool opaque_value(variant<Ts...>& val) {
auto f = [this](auto& x) { return save_value(*this, x); };
return visit(f, val);
}
template <class T> template <class T>
bool opaque_value(T& val) { bool opaque_value(T& val) {
...@@ -221,6 +214,12 @@ public: ...@@ -221,6 +214,12 @@ public:
} }
private: private:
template <class T>
void append(T&& str) {
sep();
result_.insert(result_.end(), str.begin(), str.end());
}
template <class Iterator, class Sentinel> template <class Iterator, class Sentinel>
void print_list(Iterator first, Sentinel sentinel) { void print_list(Iterator first, Sentinel sentinel) {
sep(); sep();
......
...@@ -831,15 +831,16 @@ public: ...@@ -831,15 +831,16 @@ public:
static constexpr bool value = result_type::value; static constexpr bool value = result_type::value;
}; };
/// Checks whether the inspector has a `value` 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 is_trivially_inspectable { class has_builtin_inspect {
private: private:
template <class U> template <class I, class U>
static auto sfinae(Inspector& f, U& x) static auto sfinae(I& f, U& x)
-> decltype(f.value(x), std::true_type{}); -> decltype(f.builtin_inspect(x), std::true_type{});
static std::false_type sfinae(Inspector&, ...); template <class I>
static std::false_type sfinae(I&, ...);
using sfinae_result using sfinae_result
= decltype(sfinae(std::declval<Inspector&>(), std::declval<T&>())); = decltype(sfinae(std::declval<Inspector&>(), std::declval<T&>()));
...@@ -848,6 +849,57 @@ public: ...@@ -848,6 +849,57 @@ public:
static constexpr bool value = sfinae_result::value; static constexpr bool value = sfinae_result::value;
}; };
/// Checks whether inspectors are required to provide a `value` overload for T.
template <bool IsLoading, class T>
struct is_trivial_inspector_value;
template <class T>
struct is_trivial_inspector_value<true, T> {
static constexpr bool value = false;
};
template <class T>
struct is_trivial_inspector_value<false, T> {
static constexpr bool value = std::is_convertible<T, string_view>::value;
};
#define CAF_ADD_TRIVIAL_LOAD_INSPECTOR_VALUE(type) \
template <> \
struct is_trivial_inspector_value<true, type> { \
static constexpr bool value = true; \
};
#define CAF_ADD_TRIVIAL_SAVE_INSPECTOR_VALUE(type) \
template <> \
struct is_trivial_inspector_value<false, type> { \
static constexpr bool value = true; \
};
#define CAF_ADD_TRIVIAL_INSPECTOR_VALUE(type) \
CAF_ADD_TRIVIAL_LOAD_INSPECTOR_VALUE(type) \
CAF_ADD_TRIVIAL_SAVE_INSPECTOR_VALUE(type)
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(bool);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(float);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(double);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(long double);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(std::u16string);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(std::u32string);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(std::vector<bool>);
CAF_ADD_TRIVIAL_INSPECTOR_VALUE(span<byte>);
CAF_ADD_TRIVIAL_SAVE_INSPECTOR_VALUE(span<const byte>);
CAF_ADD_TRIVIAL_LOAD_INSPECTOR_VALUE(std::string);
#undef CAF_ADD_TRIVIAL_INSPECTOR_VALUE
#undef CAF_ADD_TRIVIAL_SAVE_INSPECTOR_VALUE
#undef CAF_ADD_TRIVIAL_LOAD_INSPECTOR_VALUE
template <bool IsLoading, class T>
constexpr bool is_trivial_inspector_value_v
= is_trivial_inspector_value<IsLoading, T>::value;
/// Checks whether the inspector has an `opaque_value` overload for `T`. /// Checks whether the inspector has an `opaque_value` overload for `T`.
template <class Inspector, class T> template <class Inspector, class T>
class accepts_opaque_value { class accepts_opaque_value {
......
...@@ -102,6 +102,11 @@ bool load_value(Inspector& f, T& x, inspector_access_type::integral) { ...@@ -102,6 +102,11 @@ bool load_value(Inspector& f, T& x, inspector_access_type::integral) {
template <class Inspector, class T> template <class Inspector, class T>
bool load_value(Inspector& f, T& x, inspector_access_type::builtin) { 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);
} }
...@@ -280,6 +285,11 @@ bool save_value(Inspector& f, T& x, inspector_access_type::integral) { ...@@ -280,6 +285,11 @@ bool save_value(Inspector& f, T& x, inspector_access_type::integral) {
template <class Inspector, class T> template <class Inspector, class T>
bool save_value(Inspector& f, T& x, inspector_access_type::builtin) { 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);
} }
......
...@@ -18,11 +18,15 @@ ...@@ -18,11 +18,15 @@
#pragma once #pragma once
#include <string>
#include <type_traits> #include <type_traits>
#include <vector>
#include "caf/detail/is_complete.hpp" #include "caf/detail/is_complete.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
namespace caf { namespace caf {
...@@ -40,9 +44,12 @@ struct inspector_access_type { ...@@ -40,9 +44,12 @@ struct inspector_access_type {
/// Flags builtin integral types. /// Flags builtin integral types.
struct integral {}; struct integral {};
/// Flags types with builtin support via `.value()`. /// Flags types with builtin support via `Inspector::builtin_inspect`.
struct builtin {}; struct builtin {};
/// Flags types with builtin support via `Inspector::value`.
struct trivial {};
/// Flags `enum` types. /// Flags `enum` types.
struct enumeration {}; struct enumeration {};
...@@ -74,69 +81,73 @@ struct inspector_access_type { ...@@ -74,69 +81,73 @@ 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_object_access_type() {
// Order: inspector_access > inspect > defaults.
using namespace detail; using namespace detail;
if constexpr (is_allowed_unsafe_message_type_v<T>) { // Order: unsafe (error) > C Array > builtin_inspect > inspector_access >
// inspect > trivial > defaults.
if constexpr (is_allowed_unsafe_message_type_v<T>)
return inspector_access_type::unsafe{}; return inspector_access_type::unsafe{};
} else if constexpr (detail::is_complete<inspector_access<T>>) { 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{}; return inspector_access_type::specialization{};
} 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 (std::is_integral<T>::value else if constexpr (is_trivial_inspector_value_v<Inspector::is_loading, T>)
&& !std::is_same<T, bool>::value) { return inspector_access_type::trivial{};
else if constexpr (std::is_integral<T>::value)
return inspector_access_type::integral{}; return inspector_access_type::integral{};
} else if constexpr (std::is_array<T>::value) { else if constexpr (std::is_enum<T>::value)
return inspector_access_type::array{};
} else if constexpr (std::is_enum<T>::value) {
return inspector_access_type::enumeration{}; 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>)
return inspector_access_type::tuple{}; return inspector_access_type::tuple{};
} else if constexpr (is_map_like_v<T>) { else if constexpr (is_map_like_v<T>)
return inspector_access_type::map{}; return inspector_access_type::map{};
} else if constexpr (is_list_like_v<T>) { else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{}; return inspector_access_type::list{};
} else { else
return inspector_access_type::none{}; return inspector_access_type::none{};
}
} }
/// @relates inspector_access_type /// @relates inspector_access_type
template <class Inspector, class T> template <class Inspector, class T>
constexpr auto inspect_value_access_type() { constexpr auto inspect_value_access_type() {
// Order: .value > inspector_access > inspect_value > inspect > defaults. // Order: unsafe (error) > C Array > builtin_inspect > inspector_access >
// However, C-arrays convert easily into something that .value overloads might // inspect_value > inspect > defaults.
// pick up. Hence, we single them out above all else. // This is the same as in inspect_object_access_type, except that we pick up
// inspect_value overloads.
using namespace detail; using namespace detail;
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{}; return inspector_access_type::array{};
} else if constexpr (is_trivially_inspectable<Inspector, T>::value) { else if constexpr (has_builtin_inspect<Inspector, T>::value)
return inspector_access_type::builtin{}; return inspector_access_type::builtin{};
} 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) { else if constexpr (has_inspect_value_overload<Inspector, T>::value)
return inspector_access_type::inspect_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 (std::is_integral<T>::value else if constexpr (is_trivial_inspector_value_v<Inspector::is_loading, T>)
&& !std::is_same<T, bool>::value) { return inspector_access_type::trivial{};
else if constexpr (std::is_integral<T>::value)
return inspector_access_type::integral{}; return inspector_access_type::integral{};
} else if constexpr (std::is_enum<T>::value) { else if constexpr (std::is_enum<T>::value)
return inspector_access_type::enumeration{}; 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>)
return inspector_access_type::tuple{}; return inspector_access_type::tuple{};
} else if constexpr (is_map_like_v<T>) { else if constexpr (is_map_like_v<T>)
return inspector_access_type::map{}; return inspector_access_type::map{};
} else if constexpr (is_list_like_v<T>) { else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{}; return inspector_access_type::list{};
} else { else
return inspector_access_type::none{}; return inspector_access_type::none{};
}
} }
} // namespace caf } // namespace caf
...@@ -150,6 +150,11 @@ struct to_string_visitor { ...@@ -150,6 +150,11 @@ struct to_string_visitor {
f.value(x); f.value(x);
} }
void operator()(const uri& x) {
auto x_str = x.str();
str.insert(str.end(), x_str.begin(), x_str.end());
}
void operator()(const config_value::list& xs) { void operator()(const config_value::list& xs) {
if (xs.empty()) { if (xs.empty()) {
str += "[]"; str += "[]";
......
...@@ -228,7 +228,7 @@ bool stringification_inspector::value(const std::vector<bool>& xs) { ...@@ -228,7 +228,7 @@ bool stringification_inspector::value(const std::vector<bool>& xs) {
return end_sequence(); return end_sequence();
} }
bool stringification_inspector::value(const char* x) { bool stringification_inspector::builtin_inspect(const char* x) {
return value(string_view{x, strlen(x)}); return value(string_view{x, strlen(x)});
} }
......
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