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

Fix handling of implicitly convertible types

parent 3e5d8071
......@@ -127,13 +127,16 @@ public:
bool value(const std::vector<bool>& xs);
// -- builtin inspection to pick up to_string or provide nicer formatting ----
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));
}
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();
auto i = xs.begin();
auto last = xs.end();
......@@ -159,7 +162,7 @@ public:
std::enable_if_t<has_to_string<T>::value
&& !std::is_convertible<T, string_view>::value,
bool>
value(const T& x) {
builtin_inspect(const T& x) {
auto str = to_string(x);
if constexpr (std::is_convertible<decltype(str), const char*>::value) {
const char* cstr = str;
......@@ -171,10 +174,10 @@ public:
return true;
}
bool value(const char* x);
bool builtin_inspect(const char* x);
template <class T>
bool value(const T* x) {
bool builtin_inspect(const T* x) {
sep();
if (!x) {
result_ += "null";
......@@ -186,7 +189,7 @@ public:
}
template <class T>
bool value(const optional<T>& x) {
bool builtin_inspect(const optional<T>& x) {
sep();
if (!x) {
result_ += "null";
......@@ -197,17 +200,7 @@ public:
return true;
}
template <class T>
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);
}
// -- fallbacks --------------------------------------------------------------
template <class T>
bool opaque_value(T& val) {
......@@ -221,6 +214,12 @@ public:
}
private:
template <class T>
void append(T&& str) {
sep();
result_.insert(result_.end(), str.begin(), str.end());
}
template <class Iterator, class Sentinel>
void print_list(Iterator first, Sentinel sentinel) {
sep();
......
......@@ -831,15 +831,16 @@ public:
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>
class is_trivially_inspectable {
class has_builtin_inspect {
private:
template <class U>
static auto sfinae(Inspector& f, U& x)
-> decltype(f.value(x), std::true_type{});
template <class I, class U>
static auto sfinae(I& f, U& x)
-> 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
= decltype(sfinae(std::declval<Inspector&>(), std::declval<T&>()));
......@@ -848,6 +849,57 @@ public:
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`.
template <class Inspector, class T>
class accepts_opaque_value {
......
......@@ -102,6 +102,11 @@ bool load_value(Inspector& f, T& x, inspector_access_type::integral) {
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);
}
......@@ -280,6 +285,11 @@ bool save_value(Inspector& f, T& x, inspector_access_type::integral) {
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);
}
......
......@@ -18,11 +18,15 @@
#pragma once
#include <string>
#include <type_traits>
#include <vector>
#include "caf/detail/is_complete.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
namespace caf {
......@@ -40,9 +44,12 @@ struct inspector_access_type {
/// Flags builtin integral types.
struct integral {};
/// Flags types with builtin support via `.value()`.
/// Flags types with builtin support via `Inspector::builtin_inspect`.
struct builtin {};
/// Flags types with builtin support via `Inspector::value`.
struct trivial {};
/// Flags `enum` types.
struct enumeration {};
......@@ -74,69 +81,73 @@ struct inspector_access_type {
/// @relates inspector_access_type
template <class Inspector, class T>
constexpr auto inspect_object_access_type() {
// Order: inspector_access > inspect > defaults.
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{};
} 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{};
} else if constexpr (has_inspect_overload<Inspector, T>::value) {
else if constexpr (has_inspect_overload<Inspector, T>::value)
return inspector_access_type::inspect{};
} else if constexpr (std::is_integral<T>::value
&& !std::is_same<T, bool>::value) {
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_array<T>::value) {
return inspector_access_type::array{};
} else if constexpr (std::is_enum<T>::value) {
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{};
} else if constexpr (is_stl_tuple_type_v<T>) {
else if constexpr (is_stl_tuple_type_v<T>)
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{};
} else if constexpr (is_list_like_v<T>) {
else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{};
} else {
else
return inspector_access_type::none{};
}
}
/// @relates inspector_access_type
template <class Inspector, class T>
constexpr auto inspect_value_access_type() {
// Order: .value > inspector_access > inspect_value > inspect > defaults.
// However, C-arrays convert easily into something that .value overloads might
// pick up. Hence, we single them out above all else.
// 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>) {
if constexpr (is_allowed_unsafe_message_type_v<T>)
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 (is_trivially_inspectable<Inspector, T>::value) {
else if constexpr (has_builtin_inspect<Inspector, T>::value)
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{};
} 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{};
} else if constexpr (has_inspect_overload<Inspector, T>::value) {
else if constexpr (has_inspect_overload<Inspector, T>::value)
return inspector_access_type::inspect{};
} else if constexpr (std::is_integral<T>::value
&& !std::is_same<T, bool>::value) {
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) {
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{};
} else if constexpr (is_stl_tuple_type_v<T>) {
else if constexpr (is_stl_tuple_type_v<T>)
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{};
} else if constexpr (is_list_like_v<T>) {
else if constexpr (is_list_like_v<T>)
return inspector_access_type::list{};
} else {
else
return inspector_access_type::none{};
}
}
} // namespace caf
......@@ -150,6 +150,11 @@ struct to_string_visitor {
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) {
if (xs.empty()) {
str += "[]";
......
......@@ -228,7 +228,7 @@ bool stringification_inspector::value(const std::vector<bool>& xs) {
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)});
}
......
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