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

Operators and functors for precise mode added

This commit is contained in:
Antony Polukhin
2016-12-28 22:15:07 +03:00
parent 32b682261d
commit bc849f583e
6 changed files with 448 additions and 44 deletions

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_DETAIL_DETECTORS_HPP
#define BOOST_PFR_DETAIL_DETECTORS_HPP
namespace boost { namespace pfr { namespace detail {
///////////////////// `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>());
}}} // namespace boost::pfr::detail
#endif // BOOST_PFR_DETAIL_DETECTORS_HPP

View File

@@ -3,12 +3,14 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#ifndef BOOST_PFR_FLAT_OPS_HPP
#define BOOST_PFR_FLAT_OPS_HPP
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/detail/detectors.hpp>
#include <boost/pfr/flat/functors.hpp>
/// \file boost/pfr/flat/ops.hpp
@@ -39,53 +41,31 @@
namespace boost { namespace pfr {
namespace detail {
///////////////////// `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
///////////////////// Helper typedef that it used by all the enable_flat_not_*_comp_t
template <template <class, class> class Detector, class T>
using enable_not_comp_base_t = typename std::enable_if<
using enable_flat_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 T> using enable_flat_not_eq_comp_t = enable_flat_not_comp_base_t<comp_eq_detector, T>;
template <class T> using enable_flat_not_ne_comp_t = enable_flat_not_comp_base_t<comp_ne_detector, T>;
template <class T> using enable_flat_not_lt_comp_t = enable_flat_not_comp_base_t<comp_lt_detector, T>;
template <class T> using enable_flat_not_le_comp_t = enable_flat_not_comp_base_t<comp_le_detector, T>;
template <class T> using enable_flat_not_gt_comp_t = enable_flat_not_comp_base_t<comp_gt_detector, T>;
template <class T> using enable_flat_not_ge_comp_t = enable_flat_not_comp_base_t<comp_ge_detector, T>;
template <class Stream, class Type>
using enable_not_ostreamable_t = typename std::enable_if<
using enable_flat_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<
using enable_flat_not_istreamable_t = typename std::enable_if<
not_appliable<istreamable_detector, Stream&, Type&>::value && std::is_pod<Type>::value,
Stream&
>::type;
@@ -110,44 +90,44 @@ namespace flat_ops {
template <class T> std::size_t hash_value(const T& value) noexcept;
#else
template <class T>
static detail::enable_not_eq_comp_t<T> operator==(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_eq_comp_t<T> operator==(const T& lhs, const T& rhs) noexcept {
return flat_equal_to<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ne_comp_t<T> operator!=(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_ne_comp_t<T> operator!=(const T& lhs, const T& rhs) noexcept {
return flat_not_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_lt_comp_t<T> operator<(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_lt_comp_t<T> operator<(const T& lhs, const T& rhs) noexcept {
return flat_less<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_gt_comp_t<T> operator>(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_gt_comp_t<T> operator>(const T& lhs, const T& rhs) noexcept {
return flat_greater<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_le_comp_t<T> operator<=(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_le_comp_t<T> operator<=(const T& lhs, const T& rhs) noexcept {
return flat_less_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ge_comp_t<T> operator>=(const T& lhs, const T& rhs) noexcept {
static detail::enable_flat_not_ge_comp_t<T> operator>=(const T& lhs, const T& rhs) noexcept {
return flat_greater_equal<T>{}(lhs, rhs);
}
template <class Char, class Traits, class T>
static 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);
static detail::enable_flat_not_ostreamable_t<std::basic_ostream<Char, Traits>, T> operator<<(std::basic_ostream<Char, Traits>& out, const T& value) {
boost::pfr::flat_write(out, value);
return out;
}
template <class Char, class Traits, class T>
static detail::enable_not_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_istream<Char, Traits>& in, T& value) {
flat_read(in, value);
static detail::enable_flat_not_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_istream<Char, Traits>& in, T& value) {
boost::pfr::flat_read(in, value);
return in;
}
@@ -160,3 +140,5 @@ namespace flat_ops {
} // namespace flat_ops
}} // namespace boost::pfr
#endif // BOOST_PFR_FLAT_OPS_HPP

View File

@@ -10,5 +10,7 @@
/// Includes all the Boost.PFR headers that do not define `flat_*` functions, except \xmlonly<link linkend='header.boost.pfr.precise.global_ops_hpp'>boost/pfr/precise/global_ops.hpp</link>\endxmlonly
#include <boost/pfr/precise/core.hpp>
#include <boost/pfr/precise/functors.hpp>
#include <boost/pfr/precise/ops.hpp>
#endif // BOOST_PFR_PRECISE_HPP

View File

@@ -0,0 +1,239 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_PRECISE_FUNCTORS_HPP
#define BOOST_PFR_PRECISE_FUNCTORS_HPP
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/detail/functional.hpp>
#include <boost/pfr/flat/core.hpp>
/// \file boost/pfr/functors.hpp
/// Contains functors that are close to the Standard Library ones.
/// Each functor iterates over fields of the type.
namespace boost { namespace pfr {
namespace detail {
template <template <std::size_t, std::size_t> class Visitor, class T, class U>
bool binary_visit(const T& x, const U& y) {
constexpr std::size_t fields_count_lhs = detail::fields_count<std::remove_reference_t<T>>();
constexpr std::size_t fields_count_rhs = detail::fields_count<std::remove_reference_t<U>>();
constexpr std::size_t fields_count_min = detail::min_size(fields_count_lhs, fields_count_rhs);
typedef Visitor<0, fields_count_min> visitor_t;
#if __cplusplus >= 201606L /* Oulu meeting, not the exact value */
return visitor_t::cmp(detail::as_tuple(x), detail::as_tuple(y));
#else
bool result = true;
::boost::pfr::detail::for_each_field_dispatcher(
x,
[&result, &y](const auto& lhs) {
::boost::pfr::detail::for_each_field_dispatcher(
y,
[&result, &lhs](const auto& rhs) {
result = visitor_t::cmp(lhs, rhs);
},
std::make_index_sequence<fields_count_rhs>{}
);
},
std::make_index_sequence<fields_count_lhs>{}
);
return result;
#endif
}
} // namespace detail
///////////////////// Comparisons
/// \brief std::equal_to like comparator
template <class T = void> struct equal_to {
/// \return \b true if each field of \b x equals the field with same index of \b y
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::equal_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct equal_to<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::equal_impl>(x, y);
}
};
/// @endcond
/// \brief std::not_equal like comparator
template <class T = void> struct not_equal {
/// \return \b true if at least one field \b x not equals the field with same index of \b y
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::not_equal_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct not_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::not_equal_impl>(x, y);
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::greater like comparator
template <class T = void> struct greater {
/// \return \b true if field of \b x greater than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::greater_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct greater<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::greater_impl>(x, y);
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::less like comparator
template <class T = void> struct less {
/// \return \b true if field of \b x less than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::less_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct less<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::less_impl>(x, y);
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::greater_equal like comparator
template <class T = void> struct greater_equal {
/// \return \b true if field of \b x greater than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y;
/// or if each field of \b x equals the field with same index of \b y .
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::greater_equal_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct greater_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::greater_equal_impl>(x, y);
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::less_equal like comparator
template <class T = void> struct less_equal {
/// \return \b true if field of \b x less than the field with same index of \b y and all previous fields of \b x eqeal to the same fields of \b y;
/// or if each field of \b x equals the field with same index of \b y .
bool operator()(const T& x, const T& y) const {
return detail::binary_visit<detail::less_equal_impl>(x, y);
}
#ifdef BOOST_PFR_DOXYGEN_INVOKED
/// This typedef exists only if T \b is void
typedef std::true_type is_transparent;
/// This operator allows comparison of \b x and \b y that have different type.
/// \pre Exists only if T \b is void.
template <class V, class U> bool operator()(const V& x, const U& y) const;
#endif
};
/// @cond
template <> struct less_equal<void> {
template <class T, class U>
bool operator()(const T& x, const U& y) const {
return detail::binary_visit<detail::less_equal_impl>(x, y);
}
typedef std::true_type is_transparent;
};
/// @endcond
/// \brief std::hash like functor
template <class T> struct hash {
/// \return hash value of \b x
std::size_t operator()(const T& x) const noexcept {
return detail::hash_impl<0, detail::fields_count<std::remove_reference_t<T>>() >::compute(detail::as_tuple(x));
}
};
}} // namespace boost::pfr
#endif // BOOST_PFR_PRECISE_FUNCTORS_HPP

View File

@@ -0,0 +1,145 @@
// Copyright (c) 2016 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PFR_PRECISE_OPS_HPP
#define BOOST_PFR_PRECISE_OPS_HPP
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <boost/pfr/detail/detectors.hpp>
#include <boost/pfr/precise/functors.hpp>
#include <boost/pfr/precise/core.hpp>
/// \file boost/pfr/precise/ops.hpp
/// Contains comparison operators and stream operators for types that do not have their own operators.
/// If type is comparable or streamable using it's own operator or it's conversion operator, then the original operator is used.
///
/// Just write \b using \b namespace \b ops; and operators will be available in scope.
///
/// \b Example:
/// \code
/// #include <boost/pfr/precise/ops.hpp>
/// 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 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}
/// \endcode
///
/// \podops for other ways to define operators and more details.
///
/// \b This \b header \b contains:
namespace boost { namespace pfr {
namespace detail {
///////////////////// 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,
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,
Stream&
>::type;
template <class Stream, class Type>
using enable_not_istreamable_t = typename std::enable_if<
not_appliable<istreamable_detector, Stream&, Type&>::value,
Stream&
>::type;
} // namespace detail
namespace ops {
#ifdef BOOST_PFR_DOXYGEN_INVOKED
template <class T> bool operator==(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator!=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator< (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator> (const T& lhs, const T& rhs) noexcept;
template <class T> bool operator<=(const T& lhs, const T& rhs) noexcept;
template <class T> bool operator>=(const T& lhs, const T& rhs) noexcept;
template <class Char, class Traits, class T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, const T& value);
template <class Char, class Traits, class T>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, T& value);
/// \brief helper function for Boost
template <class T> std::size_t hash_value(const T& value) noexcept;
#else
template <class T>
static detail::enable_not_eq_comp_t<T> operator==(const T& lhs, const T& rhs) noexcept {
return equal_to<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ne_comp_t<T> operator!=(const T& lhs, const T& rhs) noexcept {
return not_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_lt_comp_t<T> operator<(const T& lhs, const T& rhs) noexcept {
return less<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_gt_comp_t<T> operator>(const T& lhs, const T& rhs) noexcept {
return greater<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_le_comp_t<T> operator<=(const T& lhs, const T& rhs) noexcept {
return less_equal<T>{}(lhs, rhs);
}
template <class T>
static detail::enable_not_ge_comp_t<T> operator>=(const T& lhs, const T& rhs) noexcept {
return greater_equal<T>{}(lhs, rhs);
}
template <class Char, class Traits, class T>
static detail::enable_not_ostreamable_t<std::basic_ostream<Char, Traits>, T> operator<<(std::basic_ostream<Char, Traits>& out, const T& value) {
boost::pfr::write(out, value);
return out;
}
template <class Char, class Traits, class T>
static detail::enable_not_istreamable_t<std::basic_istream<Char, Traits>, T> operator>>(std::basic_istream<Char, Traits>& in, T& value) {
boost::pfr::read(in, value);
return in;
}
template <class T>
static std::enable_if_t<std::is_pod<T>::value, std::size_t> hash_value(const T& value) noexcept {
return hash<T>{}(value);
}
#endif
} // namespace ops
}} // namespace boost::pfr
#endif // BOOST_PFR_PRECISE_OPS_HPP

View File

@@ -8,7 +8,7 @@
test-suite pfr
:
[ run common/ops.cpp : : : <define>BOOST_PFR_TEST_FLAT : flat_ops ]
#[ run common/ops.cpp : : : <define>BOOST_PFR_TEST_PRECISE : precise_ops ]
[ run common/ops.cpp : : : <define>BOOST_PFR_TEST_PRECISE : precise_ops ]
[ run common/read_write.cpp : : : <define>BOOST_PFR_TEST_FLAT : flat_read_write ]
[ run common/read_write.cpp : : : <define>BOOST_PFR_TEST_PRECISE : precise_read_write ]