simplify mapnik::value conversion rules
- fixes #3570 - avoids recursive exception-specification on value constructor by only constructing a temporary for arithmetic types (everything else passes a reference to the base variant constructor) - also removes `is_same<decay_t<T>, value>` SFINAE check -- because we're only passing a reference down, explicitly forcing the compiler to use the implicitly-defined copy/move instead is pointless
This commit is contained in:
parent
81fafe8198
commit
7c14964de3
3 changed files with 29 additions and 111 deletions
|
@ -100,18 +100,13 @@ struct strict_value : value_base_type
|
||||||
{
|
{
|
||||||
strict_value() = default;
|
strict_value() = default;
|
||||||
|
|
||||||
strict_value(const char* val)
|
strict_value(const char* val) noexcept(false)
|
||||||
: value_base_type(std::string(val)) {}
|
: value_base_type(std::string(val)) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename U = detail::mapnik_value_type_t<T>>
|
||||||
strict_value(T const& obj)
|
|
||||||
: value_base_type(typename detail::mapnik_value_type<T>::type(obj))
|
|
||||||
{}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
strict_value(T && obj)
|
strict_value(T && obj)
|
||||||
noexcept(std::is_nothrow_constructible<value_base_type, T && >::value)
|
noexcept(std::is_nothrow_constructible<value_base_type, U>::value)
|
||||||
: value_base_type(std::forward<T>(obj))
|
: value_base_type(U(std::forward<T>(obj)))
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,30 +47,20 @@ class MAPNIK_DECL value : public value_base
|
||||||
public:
|
public:
|
||||||
value() = default;
|
value() = default;
|
||||||
|
|
||||||
// conversion from type T is done via a temporary of type U, which
|
// Conversion from type T is done via a temporary value or reference
|
||||||
// is determined by mapnik_value_type;
|
// of type U, which is determined by mapnik_value_type_t.
|
||||||
// enable_if< decay<T> != value > is necessary to avoid ill-formed
|
//
|
||||||
// recursion in noexcept specifier; and it also prevents using this
|
// CAVEAT: We don't check `noexcept(conversion from T to U)`.
|
||||||
// constructor where implicitly-declared copy/move should be used
|
// But since the type U is either value_bool, value_integer,
|
||||||
// (e.g. value(value&))
|
// value_double or T &&, this conversion SHOULD NEVER throw.
|
||||||
template <typename T,
|
template <typename T, typename U = detail::mapnik_value_type_t<T>>
|
||||||
typename U = typename std::enable_if<
|
|
||||||
!detail::is_same_decay<T, value>::value,
|
|
||||||
detail::mapnik_value_type_decay<T>
|
|
||||||
>::type::type>
|
|
||||||
value(T && val)
|
value(T && val)
|
||||||
noexcept(noexcept(U(std::forward<T>(val))) &&
|
noexcept(std::is_nothrow_constructible<value_base, U>::value)
|
||||||
std::is_nothrow_constructible<value_base, U && >::value)
|
|
||||||
: value_base(U(std::forward<T>(val))) {}
|
: value_base(U(std::forward<T>(val))) {}
|
||||||
|
|
||||||
template <typename T,
|
template <typename T, typename U = detail::mapnik_value_type_t<T>>
|
||||||
typename U = typename std::enable_if<
|
|
||||||
!detail::is_same_decay<T, value>::value,
|
|
||||||
detail::mapnik_value_type_decay<T>
|
|
||||||
>::type::type>
|
|
||||||
value& operator=(T && val)
|
value& operator=(T && val)
|
||||||
noexcept(noexcept(U(std::forward<T>(val))) &&
|
noexcept(std::is_nothrow_assignable<value_base, U>::value)
|
||||||
std::is_nothrow_assignable<value_base, U && >::value)
|
|
||||||
{
|
{
|
||||||
value_base::operator=(U(std::forward<T>(val)));
|
value_base::operator=(U(std::forward<T>(val)));
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -152,90 +152,23 @@ inline std::istream& operator>> ( std::istream & s, value_null & )
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// to mapnik::value_type conversions traits
|
|
||||||
template <typename T>
|
|
||||||
struct is_value_bool
|
|
||||||
{
|
|
||||||
constexpr static bool value = std::is_same<T, bool>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
// Helper metafunction for mapnik::value construction and assignment.
|
||||||
struct is_value_integer
|
// Returns:
|
||||||
{
|
// value_bool if T is bool
|
||||||
constexpr static bool value = std::is_integral<T>::value && !std::is_same<T, bool>::value;
|
// value_integer if T is an integral type (except bool)
|
||||||
};
|
// value_double if T is a floating-point type
|
||||||
|
// T && otherwise
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename dT = std::decay_t<T>>
|
||||||
struct is_value_double
|
using mapnik_value_type_t =
|
||||||
{
|
std::conditional_t<
|
||||||
constexpr static bool value = std::is_floating_point<T>::value;
|
std::is_same<dT, bool>::value, value_bool,
|
||||||
};
|
std::conditional_t<
|
||||||
|
std::is_integral<dT>::value, value_integer,
|
||||||
template <typename T>
|
std::conditional_t<
|
||||||
struct is_value_unicode_string
|
std::is_floating_point<dT>::value, value_double,
|
||||||
{
|
T && >>>;
|
||||||
constexpr static bool value = std::is_same<T, typename mapnik::value_unicode_string>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_value_string
|
|
||||||
{
|
|
||||||
constexpr static bool value = std::is_same<T, typename std::string>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_value_null
|
|
||||||
{
|
|
||||||
constexpr static bool value = std::is_same<T, typename mapnik::value_null>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, class Enable = void>
|
|
||||||
struct mapnik_value_type
|
|
||||||
{
|
|
||||||
using type = T;
|
|
||||||
};
|
|
||||||
|
|
||||||
// value_null
|
|
||||||
template <typename T>
|
|
||||||
struct mapnik_value_type<T, typename std::enable_if<detail::is_value_null<T>::value>::type>
|
|
||||||
{
|
|
||||||
using type = mapnik::value_null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// value_bool
|
|
||||||
template <typename T>
|
|
||||||
struct mapnik_value_type<T, typename std::enable_if<detail::is_value_bool<T>::value>::type>
|
|
||||||
{
|
|
||||||
using type = mapnik::value_bool;
|
|
||||||
};
|
|
||||||
|
|
||||||
// value_integer
|
|
||||||
template <typename T>
|
|
||||||
struct mapnik_value_type<T, typename std::enable_if<detail::is_value_integer<T>::value>::type>
|
|
||||||
{
|
|
||||||
using type = mapnik::value_integer;
|
|
||||||
};
|
|
||||||
|
|
||||||
// value_double
|
|
||||||
template <typename T>
|
|
||||||
struct mapnik_value_type<T, typename std::enable_if<detail::is_value_double<T>::value>::type>
|
|
||||||
{
|
|
||||||
using type = mapnik::value_double;
|
|
||||||
};
|
|
||||||
|
|
||||||
// value_unicode_string
|
|
||||||
template <typename T>
|
|
||||||
struct mapnik_value_type<T, typename std::enable_if<detail::is_value_unicode_string<T>::value>::type>
|
|
||||||
{
|
|
||||||
using type = mapnik::value_unicode_string const&;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using mapnik_value_type_decay = mapnik_value_type<typename std::decay<T>::type>;
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
using is_same_decay = std::is_same<typename std::decay<T>::type,
|
|
||||||
typename std::decay<U>::type>;
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue