2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 04:22:13 +00:00

Operators in pod_ops namespace are now disabled for POD type if that POD type could be implicitly converted to type that has required operator (fixes output of 'using namespace pod_ops; std::cout << std::true_type{};')

This commit is contained in:
Antony Polukhin
2016-04-12 21:53:03 +03:00
parent f0a92f01e7
commit 0127ec9f8e
3 changed files with 118 additions and 38 deletions

View File

@@ -142,17 +142,19 @@ template <class Char, class Traits, class T>
void flat_read(std::basic_istream<Char, Traits>& in, T& value);
/// Contains comparison operators and stream operators for any POD types
/// Contains comparison operators and stream operators for any POD types that does not have it's own operators.
/// If POD is comparable or streamable using it's own operator or it's conversion operator, then the original operator is be used.
///
/// Example usage:
/// struct comparable_struct {
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// }; // No operators defined for that structure
/// };
/// using namespace pod_ops;
///
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
namespace pod_ops;
```

View File

@@ -746,7 +746,7 @@ namespace detail {
};
}
/// Writes to `out` POD `value`
/// Writes to `out` the POD `value`
/// Example usage:
/// struct my_struct { int i, short s; };
/// my_struct s{12, 13};
@@ -811,12 +811,6 @@ void flat_read(std::basic_istream<Char, Traits>& in, T& value) {
namespace detail {
template <class T1, class T2>
using same_pods_enable = typename std::enable_if<
std::is_same<T1, T2>::value && std::is_pod<T1>::value && std::is_class<T1>::value,
bool
>::type;
template <std::size_t I, std::size_t N>
struct equal_impl {
template <class T>
@@ -825,7 +819,7 @@ namespace detail {
&& equal_impl<I + 1, N>::cmp(v1, v2);
}
};
template <std::size_t N>
struct equal_impl<N, N> {
template <class T>
@@ -833,7 +827,7 @@ namespace detail {
return true;
}
};
template <std::size_t I, std::size_t N>
struct less_impl {
template <class T>
@@ -842,7 +836,7 @@ namespace detail {
|| (flat_get<I>(v1) == flat_get<I>(v2) && less_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct less_impl<N, N> {
template <class T>
@@ -850,11 +844,63 @@ namespace detail {
return false;
}
};
///////////////////// `value` is true if Detector<Tleft, Tright> does not compile (SFINAE)
template <template <class, class> class Detector, class Tleft, class Tright>
struct not_appliable {
struct success{};
template <class Tl, class Tr> static Detector<Tl, Tr> detector_impl(long) noexcept;
template <class Tl, class Tr> static success detector_impl(int) noexcept;
static constexpr bool value = std::is_same<
decltype(detector_impl<Tleft, Tright>(1L)),
success
>::value;
};
///////////////////// Detectors for different operators
template <class T1, class T2> using comp_eq_detector = decltype(std::declval<T1>() == std::declval<T2>());
template <class T1, class T2> using comp_ne_detector = decltype(std::declval<T1>() != std::declval<T2>());
template <class T1, class T2> using comp_lt_detector = decltype(std::declval<T1>() < std::declval<T2>());
template <class T1, class T2> using comp_le_detector = decltype(std::declval<T1>() <= std::declval<T2>());
template <class T1, class T2> using comp_gt_detector = decltype(std::declval<T1>() > std::declval<T2>());
template <class T1, class T2> using comp_ge_detector = decltype(std::declval<T1>() >= std::declval<T2>());
template <class S, class T> using ostreamable_detector = decltype(std::declval<S>() << std::declval<T>());
template <class S, class T> using istreamable_detector = decltype(std::declval<S>() >> std::declval<T>());
///////////////////// Helper typedef that it used by all the enable_not_*_comp_t
template <template <class, class> class Detector, class T>
using enable_not_comp_base_t = typename std::enable_if<
not_appliable<Detector, T const&, T const&>::value && std::is_pod<T>::value,
bool
>::type;
///////////////////// std::enable_if_t like functions that enable only if types do not support operation and are PODs
template <class T> using enable_not_eq_comp_t = enable_not_comp_base_t<comp_eq_detector, T>;
template <class T> using enable_not_ne_comp_t = enable_not_comp_base_t<comp_ne_detector, T>;
template <class T> using enable_not_lt_comp_t = enable_not_comp_base_t<comp_lt_detector, T>;
template <class T> using enable_not_le_comp_t = enable_not_comp_base_t<comp_le_detector, T>;
template <class T> using enable_not_gt_comp_t = enable_not_comp_base_t<comp_gt_detector, T>;
template <class T> using enable_not_ge_comp_t = enable_not_comp_base_t<comp_ge_detector, T>;
template <class Stream, class Type>
using enable_not_ostreamable_t = typename std::enable_if<
not_appliable<ostreamable_detector, Stream&, Type const&>::value && std::is_pod<Type>::value,
Stream&
>::type;
template <class Stream, class Type>
using enable_not_istreamable_t = typename std::enable_if<
not_appliable<istreamable_detector, Stream&, Type&>::value && std::is_pod<Type>::value,
Stream&
>::type;
}
/// Contains comparison operators and stream operators for any POD types
/// Contains comparison operators and stream operators for any POD types that does not have it's own operators.
/// If POD is comparable or streamable using it's own operator or it's conversion operator, then the original operator is be used.
///
/// Example usage:
/// struct comparable_struct {
/// struct comparable_struct { // No operators defined for that structure
/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
/// };
/// using namespace pod_ops;
@@ -862,44 +908,42 @@ namespace detail {
/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
/// assert(s1 < s2);
/// std::cout << s1 << std::endl; // Outputs: { 0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11 }
/// std::cout << s1 << std::endl; // Outputs: {0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11}
namespace pod_ops {
///////////////////// Comparisons
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator==(const T1& lhs, const T2& rhs) noexcept {
return detail::equal_impl<0, flat_tuple_size_v<T1> >::cmp(lhs, rhs);
template <class T>
inline detail::enable_not_eq_comp_t<T> operator==(const T& lhs, const T& rhs) noexcept {
return detail::equal_impl<0, flat_tuple_size_v<T> >::cmp(lhs, rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator!=(const T1& lhs, const T2& rhs) noexcept {
template <class T>
inline detail::enable_not_ne_comp_t<T> operator!=(const T& lhs, const T& rhs) noexcept {
return !(lhs == rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator<(const T1& lhs, const T2& rhs) noexcept {
return detail::less_impl<0, flat_tuple_size_v<T1> >::cmp(lhs, rhs);
template <class T>
inline detail::enable_not_lt_comp_t<T> operator<(const T& lhs, const T& rhs) noexcept {
return detail::less_impl<0, flat_tuple_size_v<T> >::cmp(lhs, rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator>(const T1& lhs, const T2& rhs) noexcept {
template <class T>
inline detail::enable_not_gt_comp_t<T> operator>(const T& lhs, const T& rhs) noexcept {
return rhs < lhs;
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator<=(const T1& lhs, const T2& rhs) noexcept {
template <class T>
inline detail::enable_not_le_comp_t<T> operator<=(const T& lhs, const T& rhs) noexcept {
return !(lhs > rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator>=(const T1& lhs, const T2& rhs) noexcept {
template <class T>
inline detail::enable_not_ge_comp_t<T> operator>=(const T& lhs, const T& rhs) noexcept {
return !(lhs < rhs);
}
///////////////////// basic_ostream operator <<
template <class Char, class Traits, class T>
typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value, std::basic_ostream<Char, Traits>& >::type
operator<<(std::basic_ostream<Char, Traits>& out, const T& value)
detail::enable_not_ostreamable_t<std::basic_ostream<Char, Traits>, T> operator<<(std::basic_ostream<Char, Traits>& out, const T& value)
{
flat_write(out, value);
return out;
@@ -907,8 +951,7 @@ namespace pod_ops {
///////////////////// basic_istream operator >>
template <class Char, class Traits, class T>
typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value, std::basic_istream<Char, Traits>& >::type
operator>>(std::basic_istream<Char, Traits>& in, T& value)
detail::enable_not_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_istream<Char, Traits>& in, T& value)
{
flat_read(in, value);
return in;

View File

@@ -234,6 +234,40 @@ void test_empty_struct() {
assert(empty{} == empty{});
}
void test_pods_with_int_operators() {
using namespace pod_ops;
std::stringstream ss;
ss << std::is_pod<int>{};
int i = 0;
ss >> i;
assert(i == 1);
std::cout << i << std::endl;
}
void test_struct_with_single_field() {
struct f1 { int i; };
using namespace pod_ops;
std::stringstream ss;
ss << f1{ 777 };
f1 var{};
ss >> var;
assert(var.i == 777);
assert(var == f1{ 777 });
assert(var != f1{ 778 });
assert(var <= f1{ 777 });
assert(var <= f1{ 778 });
assert(var < f1{ 778 });
assert(var >= f1{ 777 });
assert(var >= f1{ 776 });
assert(var > f1{ 776 });
}
int main() {
test_compiletime<foo>();
test_compiletime_array<int>();
@@ -261,7 +295,8 @@ int main() {
test_with_enums();
test_comparable_struct();
test_empty_struct();
test_pods_with_int_operators();
test_struct_with_single_field();
test_print();
}