1 // Copyright (c) 2016-2020 Antony Polukhin
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef BOOST_PFR_OPS_HPP
7 #define BOOST_PFR_OPS_HPP
8 #pragma once
9 
10 #include <boost/pfr/detail/config.hpp>
11 
12 #include <boost/pfr/detail/detectors.hpp>
13 #include <boost/pfr/ops_fields.hpp>
14 
15 /// \file boost/pfr/ops.hpp
16 /// Contains comparison and hashing functions.
17 /// If type is comparable using its own operator or its conversion operator, then the types operator is used. Otherwise
18 /// the operation is done via corresponding function from boost/pfr/ops.hpp header.
19 ///
20 /// \b Example:
21 /// \code
22 ///     #include <boost/pfr/ops.hpp>
23 ///     struct comparable_struct {      // No operators defined for that structure
24 ///         int i; short s; char data[7]; bool bl; int a,b,c,d,e,f;
25 ///     };
26 ///     // ...
27 ///
28 ///     comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11};
29 ///     comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111};
30 ///     assert(boost::pfr::lt(s1, s2));
31 /// \endcode
32 ///
33 /// \podops for other ways to define operators and more details.
34 ///
35 /// \b Synopsis:
36 namespace boost { namespace pfr {
37 
38 namespace detail {
39 
40 ///////////////////// Helper typedefs that are used by all the ops
41     template <template <class, class> class Detector, class T, class U>
42     using enable_not_comp_base_t = std::enable_if_t<
43         not_appliable<Detector, T const&, U const&>::value,
44         bool
45     >;
46 
47     template <template <class, class> class Detector, class T, class U>
48     using enable_comp_base_t = std::enable_if_t<
49         !not_appliable<Detector, T const&, U const&>::value,
50         bool
51     >;
52 ///////////////////// std::enable_if_t like functions that enable only if types do not support operation
53 
54     template <class T, class U> using enable_not_eq_comp_t = enable_not_comp_base_t<comp_eq_detector, T, U>;
55     template <class T, class U> using enable_not_ne_comp_t = enable_not_comp_base_t<comp_ne_detector, T, U>;
56     template <class T, class U> using enable_not_lt_comp_t = enable_not_comp_base_t<comp_lt_detector, T, U>;
57     template <class T, class U> using enable_not_le_comp_t = enable_not_comp_base_t<comp_le_detector, T, U>;
58     template <class T, class U> using enable_not_gt_comp_t = enable_not_comp_base_t<comp_gt_detector, T, U>;
59     template <class T, class U> using enable_not_ge_comp_t = enable_not_comp_base_t<comp_ge_detector, T, U>;
60 
61     template <class T> using enable_not_hashable_t = std::enable_if_t<
62         not_appliable<hash_detector, const T&, const T&>::value,
63         std::size_t
64     >;
65 ///////////////////// std::enable_if_t like functions that enable only if types do support operation
66 
67     template <class T, class U> using enable_eq_comp_t = enable_comp_base_t<comp_eq_detector, T, U>;
68     template <class T, class U> using enable_ne_comp_t = enable_comp_base_t<comp_ne_detector, T, U>;
69     template <class T, class U> using enable_lt_comp_t = enable_comp_base_t<comp_lt_detector, T, U>;
70     template <class T, class U> using enable_le_comp_t = enable_comp_base_t<comp_le_detector, T, U>;
71     template <class T, class U> using enable_gt_comp_t = enable_comp_base_t<comp_gt_detector, T, U>;
72     template <class T, class U> using enable_ge_comp_t = enable_comp_base_t<comp_ge_detector, T, U>;
73 
74     template <class T> using enable_hashable_t = std::enable_if_t<
75         !not_appliable<hash_detector, const T&, const T&>::value,
76         std::size_t
77     >;
78 } // namespace detail
79 
80 
81 /// \brief Compares lhs and rhs for equality using their own comparison and conversion operators; if no operators avalable returns \forcedlink{eq_fields}(lhs, rhs).
82 ///
83 /// \returns true if lhs is equal to rhs; false otherwise
84 template <class T, class U>
85 detail::enable_not_eq_comp_t<T, U> eq(const T& lhs, const U& rhs) noexcept {
86     return boost::pfr::eq_fields(lhs, rhs);
87 }
88 
89 /// \overload eq
90 template <class T, class U>
91 detail::enable_eq_comp_t<T, U> eq(const T& lhs, const U& rhs) {
92     return lhs == rhs;
93 }
94 
95 
96 /// \brief Compares lhs and rhs for inequality using their own comparison and conversion operators; if no operators avalable returns \forcedlink{ne_fields}(lhs, rhs).
97 ///
98 /// \returns true if lhs is not equal to rhs; false otherwise
99 template <class T, class U>
100 detail::enable_not_ne_comp_t<T, U> ne(const T& lhs, const U& rhs) noexcept {
101     return boost::pfr::ne_fields(lhs, rhs);
102 }
103 
104 /// \overload ne
105 template <class T, class U>
106 detail::enable_ne_comp_t<T, U> ne(const T& lhs, const U& rhs) {
107     return lhs != rhs;
108 }
109 
110 
111 /// \brief Compares lhs and rhs for less-than using their own comparison and conversion operators; if no operators avalable returns \forcedlink{lt_fields}(lhs, rhs).
112 ///
113 /// \returns true if lhs is less than rhs; false otherwise
114 template <class T, class U>
115 detail::enable_not_lt_comp_t<T, U> lt(const T& lhs, const U& rhs) noexcept {
116     return boost::pfr::lt_fields(lhs, rhs);
117 }
118 
119 /// \overload lt
120 template <class T, class U>
121 detail::enable_lt_comp_t<T, U> lt(const T& lhs, const U& rhs) {
122     return lhs < rhs;
123 }
124 
125 
126 /// \brief Compares lhs and rhs for greater-than using their own comparison and conversion operators; if no operators avalable returns \forcedlink{lt_fields}(lhs, rhs).
127 ///
128 /// \returns true if lhs is greater than rhs; false otherwise
129 template <class T, class U>
130 detail::enable_not_gt_comp_t<T, U> gt(const T& lhs, const U& rhs) noexcept {
131     return boost::pfr::gt_fields(lhs, rhs);
132 }
133 
134 /// \overload gt
135 template <class T, class U>
136 detail::enable_gt_comp_t<T, U> gt(const T& lhs, const U& rhs) {
137     return lhs > rhs;
138 }
139 
140 
141 /// \brief Compares lhs and rhs for less-equal using their own comparison and conversion operators; if no operators avalable returns \forcedlink{le_fields}(lhs, rhs).
142 ///
143 /// \returns true if lhs is less or equal to rhs; false otherwise
144 template <class T, class U>
145 detail::enable_not_le_comp_t<T, U> le(const T& lhs, const U& rhs) noexcept {
146     return boost::pfr::le_fields(lhs, rhs);
147 }
148 
149 /// \overload le
150 template <class T, class U>
151 detail::enable_le_comp_t<T, U> le(const T& lhs, const U& rhs) {
152     return lhs <= rhs;
153 }
154 
155 
156 /// \brief Compares lhs and rhs for greater-equal using their own comparison and conversion operators; if no operators avalable returns \forcedlink{ge_fields}(lhs, rhs).
157 ///
158 /// \returns true if lhs is greater or equal to rhs; false otherwise
159 template <class T, class U>
160 detail::enable_not_ge_comp_t<T, U> ge(const T& lhs, const U& rhs) noexcept {
161     return boost::pfr::ge_fields(lhs, rhs);
162 }
163 
164 /// \overload ge
165 template <class T, class U>
166 detail::enable_ge_comp_t<T, U> ge(const T& lhs, const U& rhs) {
167     return lhs >= rhs;
168 }
169 
170 
171 /// \brief Hashes value using its own std::hash specialization; if no std::hash specialization avalable returns \forcedlink{hash_fields}(value).
172 ///
173 /// \returns std::size_t with hash of the value
174 template <class T>
175 detail::enable_not_hashable_t<T> hash_value(const T& value) noexcept {
176     return boost::pfr::hash_fields(value);
177 }
178 
179 /// \overload hash_value
180 template <class T>
181 detail::enable_hashable_t<T> hash_value(const T& value) {
182     return std::hash<T>{}(value);
183 }
184 
185 }} // namespace boost::pfr
186 
187 #endif // BOOST_PFR_OPS_HPP
188