1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4 
5 #ifndef TYPE_SAFE_BOUNDED_TYPE_HPP_INCLUDED
6 #define TYPE_SAFE_BOUNDED_TYPE_HPP_INCLUDED
7 
8 #include <limits>
9 #include <type_traits>
10 
11 #include <type_safe/constrained_type.hpp>
12 #include <type_safe/detail/constant_parser.hpp>
13 
14 namespace type_safe
15 {
16 namespace constraints
17 {
18     /// Tag type to enable a dynamic bound.
19     struct dynamic_bound
20     {};
21 
22     /// \exclude
23     namespace detail
24     {
25         // Base to enable empty base optimization when Bound is not dynamic_bound.
26         // Necessary when T is not a class.
27         template <typename T>
28         struct wrapper
29         {
30             using value_type = T;
31             T value;
32         };
33 
34         template <typename Bound>
35         struct is_dynamic : std::is_same<Bound, dynamic_bound>
36         {};
37 
38         template <bool Cond, typename T, typename Bound>
39         struct select_bound;
40 
41         template <typename T, typename Bound>
42         struct select_bound<true, T, Bound>
43         {
44             using type = wrapper<T>;
45         };
46 
47         template <typename T, typename Bound>
48         struct select_bound<false, T, Bound>
49         {
50             static_assert(
51                 std::is_convertible<decltype(std::declval<const T&>() < Bound::value), bool>::value,
52                 "static bound has wrong type");
53             using type = Bound;
54         };
55 
56         template <typename T, typename Bound>
57         using base = typename select_bound<is_dynamic<Bound>::value, T, Bound>::type;
58     } // namespace detail
59 
60 // clang-format off
61 /// \exclude
62 #define TYPE_SAFE_DETAIL_MAKE(Name, Op)                                                            \
63     template <typename T, typename Bound = dynamic_bound>                                          \
64     class Name : detail::base<T, Bound>                                                            \
65     {                                                                                              \
66         static constexpr bool is_dynamic = detail::is_dynamic<Bound>::value;                       \
67                                                                                                    \
68         using base     = detail::base<T, Bound>;                                                   \
69         using arg_type = typename std::conditional<is_dynamic, T, Bound>::type;                    \
70                                                                                                    \
71     public:                                                                                        \
72         using value_type = decltype(base::value);                                                                      \
73         using bound_type = Bound;                                                                  \
74                                                                                                    \
75         /** Initializes it with a static bound.
76           * \effects Does nothing, a static bound is not stored.
77           * It will use `Bound::value` as the bound.
78           * \notes This constructor only participates in overload resolution,
79           * if a static bound is used, i.e. `Bound` is not [ts::constraints::dynamic_bound]().
80           * \param Condition
81           * \exclude
82           * \param 1
83           * \exclude */     \
84         template <bool Condition = !is_dynamic,                                                    \
85                   typename       = typename std::enable_if<Condition>::type>                       \
86         constexpr Name(Bound = {})                                                                 \
87         {                                                                                          \
88         }                                                                                          \
89                                                                                                    \
90         /** Initializes it with a dynamic bound.
91           * \effects Copies (1)/moves (2) the object and uses that as bound.
92           * \notes These constructors only participate in overload resolution,
93           * if a dynamic bound is used, i.e. `Bound` is [ts::constraints::dynamic_bound]().
94           * \group dynamic_ctor
95           * \param Condition
96           * \exclude
97           * \param 1
98           * \exclude */     \
99         template <bool Condition = is_dynamic,                                                     \
100                   typename       = typename std::enable_if<Condition>::type>                       \
101         explicit constexpr Name(const T& bound) : base{bound}                                      \
102         {                                                                                          \
103         }                                                                                          \
104                                                                                                    \
105         /** \group dynamic_ctor
106           * \param Condition
107           * \exclude
108           * \param 1
109           * \exclude */     \
110         template <bool Condition = is_dynamic,                                                     \
111                   typename       = typename std::enable_if<Condition>::type>                       \
112         explicit constexpr Name(T&& bound) noexcept(std::is_nothrow_move_constructible<T>::value)  \
113         : base{std::move(bound)}                                                                   \
114         {                                                                                          \
115         }                                                                                          \
116                                                                                                    \
117         /** Does the actual bounds check.*/                                                        \
118         template <typename U>                                                                      \
119         constexpr bool operator()(const U& u) const                                                \
120         {                                                                                          \
121             return u Op get_bound();                                                               \
122         }                                                                                          \
123                                                                                                    \
124         /** \returns The bound.*/                                                                  \
125         constexpr const value_type& get_bound() const noexcept                                                  \
126         {                                                                                          \
127             return base::value;                                                                    \
128         }                                                                                          \
129     };
130     // clang-format on
131 
132     /// A `Constraint` for the [ts::constrained_type]().
133     ///
134     /// A value is valid if it is less than some given value.
135     TYPE_SAFE_DETAIL_MAKE(less, <)
136 
137     /// A `Constraint` for the [ts::constrained_type]().
138     ///
139     /// A value is valid if it is less than or equal to some given value.
140     TYPE_SAFE_DETAIL_MAKE(less_equal, <=)
141 
142     /// A `Constraint` for the [ts::constrained_type]().
143     ///
144     /// A value is valid if it is greater than some given value.
145     TYPE_SAFE_DETAIL_MAKE(greater, >)
146 
147     /// A `Constraint` for the [ts::constrained_type]().
148     ///
149     /// A value is valid if it is greater than or equal to some given value.
150     TYPE_SAFE_DETAIL_MAKE(greater_equal, >=)
151 
152 #undef TYPE_SAFE_DETAIL_MAKE
153 
154     /// \exclude
155     namespace detail
156     {
157         // checks that that the value is less than the upper bound
158         template <bool Inclusive, typename T, typename Bound>
159         using upper_bound_t =
160             typename std::conditional<Inclusive, less_equal<T, Bound>, less<T, Bound>>::type;
161 
162         // checks that the value is greater than the lower bound
163         template <bool Inclusive, typename T, typename Bound>
164         using lower_bound_t =
165             typename std::conditional<Inclusive, greater_equal<T, Bound>, greater<T, Bound>>::type;
166     } // namespace detail
167 
168     /// Tag objects to specify bounds for [ts::constraints::bounded]().
169     /// \group open_closed Open/Closed Tags
170     constexpr bool open = false;
171     /// \group open_closed
172     constexpr bool closed = true;
173 
174     /// A `Constraint` for the [ts::constrained_type]().
175     ///
176     /// A value is valid if it is between two given bounds,
177     /// `LowerInclusive`/`UpperInclusive` control whether the lower/upper bound itself is valid too.
178     template <typename T, bool LowerInclusive, bool UpperInclusive,
179               typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
180     class bounded : detail::lower_bound_t<LowerInclusive, T, LowerBound>,
181                     detail::upper_bound_t<UpperInclusive, T, UpperBound>
182     {
183         static constexpr bool lower_is_dynamic = detail::is_dynamic<LowerBound>::value;
184         static constexpr bool upper_is_dynamic = detail::is_dynamic<UpperBound>::value;
185 
186         using lower_type = detail::lower_bound_t<LowerInclusive, T, LowerBound>;
187         using upper_type = detail::upper_bound_t<UpperInclusive, T, UpperBound>;
188 
lower() const189         constexpr const lower_type& lower() const noexcept
190         {
191             return static_cast<const lower_type&>(*this);
192         }
upper() const193         constexpr const upper_type& upper() const noexcept
194         {
195             return static_cast<const upper_type&>(*this);
196         }
197 
198         template <typename U>
199         using decay_same = std::is_same<typename std::decay<U>::type, T>;
200 
201     public:
202         using value_type  = T;
203         using lower_bound = LowerBound;
204         using upper_bound = UpperBound;
205 
206         static constexpr auto lower_inclusive = LowerInclusive;
207         static constexpr auto upper_inclusive = UpperInclusive;
208 
209         /// Initializes it with static bounds.
210         /// \effects Does nothing, a static bound is not stored.
211         /// It will use `LowerBound::value` as lower bound and `UpperBound::value` as upper bound.
212         /// \notes This constructor does not participate in overload resolution,
213         /// unless both bounds are static,
214         /// i.e. not [ts::constraints::dynamic_bound]().
215         /// \param Condition
216         /// \exclude
217         /// \param 1
218         /// \exclude
219         template <bool Condition = !lower_is_dynamic && !upper_is_dynamic,
220                   typename       = typename std::enable_if<Condition>::type>
bounded()221         constexpr bounded()
222         {}
223 
224         /// Initializes it with (mixed) dynamic bounds.
225         /// \effects Perfectly forwards the arguments to the bounds.
226         /// If a bound is static, the static member `value` will be used as bound,
227         /// if it is dynamic, a copy created by perfectly forwarding will be stored and used as
228         /// bound. \notes This constructor does not participate in overload resolution, unless the
229         /// arguments are convertible to the bounds. \param 2 \exclude \param 3 \exclude
230         template <typename U1, typename U2>
231         explicit constexpr bounded(U1&& lower, U2&& upper,
232                                    decltype(lower_type(std::forward<U1>(lower)), 0) = 0,
233                                    decltype(upper_type(std::forward<U2>(upper)), 0) = 0)
234         : lower_type(std::forward<U1>(lower)), upper_type(std::forward<U2>(upper))
235         {}
236 
237         /// Does the bounds check.
238         template <typename U>
operator ()(const U & u) const239         constexpr bool operator()(const U& u) const
240         {
241             return lower()(u) && upper()(u);
242         }
243 
244         /// \returns The value of the lower bound.
get_lower_bound() const245         constexpr const typename lower_type::value_type& get_lower_bound() const noexcept
246         {
247             return lower().get_bound();
248         }
249 
250         /// \returns The value of the upper bound.
get_upper_bound() const251         constexpr const typename upper_type::value_type& get_upper_bound() const noexcept
252         {
253             return upper().get_bound();
254         }
255     };
256 
257     /// A `Constraint` for the [ts::constrained_type]().
258     ///
259     /// A value is valid if it is between two given bounds but not the bounds themselves.
260     template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
261     using open_interval = bounded<T, open, open, LowerBound, UpperBound>;
262 
263     /// A `Constraint` for the [ts::constrained_type]().
264     ///
265     /// A value is valid if it is between two given bounds or the bounds themselves.
266     template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
267     using closed_interval = bounded<T, closed, closed, LowerBound, UpperBound>;
268 } // namespace constraints
269 
270 /// \exclude
271 namespace detail
272 {
273     template <typename T>
274     struct valid_bound : std::integral_constant<bool, true || T::value>
275     {};
276 
277     template <class Verifier, bool LowerInclusive, bool UpperInclusive, typename T, typename U1,
278               typename U2>
279     struct bounded_type_impl
280     {
281         static_assert(valid_bound<U1>::value && valid_bound<U2>::value,
282                       "make_bounded() called with mismatched types");
283 
284         using value_type     = T;
285         using lower_constant = U1;
286         using upper_constant = U2;
287 
288         using type
289             = constrained_type<value_type,
290                                constraints::bounded<value_type, LowerInclusive, UpperInclusive,
291                                                     lower_constant, upper_constant>,
292                                Verifier>;
293     };
294 
295     template <class Verifier, bool LowerInclusive, bool UpperInclusive, typename T>
296     struct bounded_type_impl<Verifier, LowerInclusive, UpperInclusive, T, T, T>
297     {
298         using value_type     = T;
299         using lower_constant = constraints::dynamic_bound;
300         using upper_constant = constraints::dynamic_bound;
301 
302         using type
303             = constrained_type<value_type,
304                                constraints::bounded<value_type, LowerInclusive, UpperInclusive,
305                                                     lower_constant, upper_constant>,
306                                Verifier>;
307     };
308 
309     template <class Verifier, bool LowerInclusive, bool UpperInclusive, typename T,
310               typename UpperBound>
311     struct bounded_type_impl<Verifier, LowerInclusive, UpperInclusive, T, T, UpperBound>
312     {
313         static_assert(valid_bound<UpperBound>::value,
314                       "make_bounded() called with mismatched types");
315 
316         using value_type     = T;
317         using lower_constant = constraints::dynamic_bound;
318         using upper_constant = UpperBound;
319 
320         using type
321             = constrained_type<value_type,
322                                constraints::bounded<value_type, LowerInclusive, UpperInclusive,
323                                                     lower_constant, upper_constant>,
324                                Verifier>;
325     };
326 
327     template <class Verifier, bool LowerInclusive, bool UpperInclusive, typename T,
328               typename LowerBound>
329     struct bounded_type_impl<Verifier, LowerInclusive, UpperInclusive, T, LowerBound, T>
330     {
331         static_assert(valid_bound<LowerBound>::value,
332                       "make_bounded() called with mismatched types");
333 
334         using value_type     = T;
335         using lower_constant = LowerBound;
336         using upper_constant = constraints::dynamic_bound;
337 
338         using type
339             = constrained_type<value_type,
340                                constraints::bounded<value_type, LowerInclusive, UpperInclusive,
341                                                     lower_constant, upper_constant>,
342                                Verifier>;
343     };
344 
345     template <class Verifier, bool LowerInclusive, bool UpperInclusive, typename T, typename U1,
346               typename U2>
347     using make_bounded_type =
348         typename bounded_type_impl<Verifier, LowerInclusive, UpperInclusive,
349                                    typename std::decay<T>::type, typename std::decay<U1>::type,
350                                    typename std::decay<U2>::type>::type;
351 } // namespace detail
352 
353 /// An alias for [ts::constrained_type]() that uses [ts::constraints::bounded]() as its
354 /// `Constraint`. \notes This is some type where the values must be in a certain interval.
355 template <typename T, bool LowerInclusive, bool UpperInclusive,
356           typename LowerBound = constraints::dynamic_bound,
357           typename UpperBound = constraints::dynamic_bound, typename Verifier = assertion_verifier>
358 using bounded_type = constrained_type<
359     T, constraints::bounded<T, LowerInclusive, UpperInclusive, LowerBound, UpperBound>, Verifier>;
360 
361 inline namespace literals
362 {
363     /// \exclude
364     namespace lit_detail
365     {
366         template <typename T, T Value>
367         struct integer_bound
368         {
369             static constexpr T value = Value;
370         };
371 
372         template <typename T, T Value>
373         constexpr T integer_bound<T, Value>::value;
374 
375         template <typename T, T Value>
operator -(integer_bound<T,Value>)376         constexpr auto operator-(integer_bound<T, Value>) noexcept
377             -> integer_bound<T, Value * T(-1)>
378         {
379             static_assert(std::is_signed<T>::value, "must be a signed integer type");
380             return {};
381         }
382     } // namespace lit_detail
383 
384     /// Creates a static bound for [ts::bounded_type]().
385     ///
386     /// This is a bound encapsulated in the type, so there is no overhead.
387     /// You can use it for example like this `ts::make_bounded(50, 0_bound, 100_bound)`,
388     /// to bound an integer between `0` and `100`.
389     /// \returns A type representing the given value,
390     /// the value has type `long long` (1)/`unsigned long long`(2).
391     /// \group bound_lit
392     template <char... Digits>
operator ""_bound()393     constexpr auto operator"" _bound()
394         -> lit_detail::integer_bound<long long, detail::parse<long long, Digits...>()>
395     {
396         return {};
397     }
398 
399     /// \group bound_lit
400     template <char... Digits>
operator ""_boundu()401     constexpr auto operator"" _boundu()
402         -> lit_detail::integer_bound<unsigned long long,
403                                      detail::parse<unsigned long long, Digits...>()>
404     {
405         return {};
406     }
407 } // namespace literals
408 
409 /// Creates a [ts::bounded_type]() to a specified [ts::constraints::closed_interval]().
410 /// \returns A [ts::bounded_type]() with the given `value` and lower and upper bounds,
411 /// where the bounds are valid values as well.
412 /// \requires As it uses [ts::assertion_verifier](),
413 /// the value must be valid.
414 /// \notes If this function is passed in dynamic values of the same type as `value`,
415 /// it will create a dynamic bound.
416 /// Otherwise it must be passed static bounds.
417 template <typename T, typename U1, typename U2>
make_bounded(T && value,U1 && lower,U2 && upper)418 constexpr auto make_bounded(T&& value, U1&& lower, U2&& upper)
419     -> detail::make_bounded_type<assertion_verifier, true, true, T, U1, U2>
420 {
421     using result_type = detail::make_bounded_type<assertion_verifier, true, true, T, U1, U2>;
422     return result_type(std::forward<T>(value),
423                        typename result_type::constraint_predicate(std::forward<U1>(lower),
424                                                                   std::forward<U2>(upper)));
425 }
426 
427 /// Creates a [ts::bounded_type]() to a specified [ts::constraints::closed_interval]().
428 /// \returns A [ts::bounded_type]() with the given `value` and lower and upper bounds,
429 /// where the bounds are valid values as well.
430 /// \throws A [ts::constrain_error]() if the `value` isn't valid,
431 /// or anything else thrown by the constructor.
432 /// \notes This is meant for sanitizing user input,
433 /// using a recoverable error handling strategy.
434 /// \notes If this function is passed in dynamic values of the same type as `value`,
435 /// it will create a dynamic bound.
436 /// Otherwise it must be passed static bounds.
437 template <typename T, typename U1, typename U2>
sanitize_bounded(T && value,U1 && lower,U2 && upper)438 constexpr auto sanitize_bounded(T&& value, U1&& lower, U2&& upper)
439     -> detail::make_bounded_type<throwing_verifier, true, true, T, U1, U2>
440 {
441     using result_type = detail::make_bounded_type<throwing_verifier, true, true, T, U1, U2>;
442     return result_type(std::forward<T>(value),
443                        typename result_type::constraint_predicate(std::forward<U1>(lower),
444                                                                   std::forward<U2>(upper)));
445 }
446 
447 /// Creates a [ts::bounded_type]() to a specified [ts::constraints::open_interval]().
448 /// \returns A [ts::bounded_type]() with the given `value` and lower and upper bounds,
449 /// where the bounds are not valid values.
450 /// \requires As it uses [ts::assertion_verifier](),
451 /// the value must be valid.
452 /// \notes If this function is passed in dynamic values of the same type as `value`,
453 /// it will create a dynamic bound.
454 /// Otherwise it must be passed static bounds.
455 template <typename T, typename U1, typename U2>
make_bounded_exclusive(T && value,U1 && lower,U2 && upper)456 constexpr auto make_bounded_exclusive(T&& value, U1&& lower, U2&& upper)
457     -> detail::make_bounded_type<assertion_verifier, false, false, T, U1, U2>
458 {
459     using result_type = detail::make_bounded_type<assertion_verifier, false, false, T, U1, U2>;
460     return result_type(std::forward<T>(value),
461                        typename result_type::constraint_predicate(std::forward<U1>(lower),
462                                                                   std::forward<U2>(upper)));
463 }
464 
465 /// Creates a [ts::bounded_type]() to a specified [ts::constraints::open_interval](),
466 /// using [ts::throwing_verifier]().
467 /// \returns A [ts::bounded_type]() with the given `value` and lower and upper bounds,
468 /// where the bounds are not valid values.
469 /// \throws A [ts::constrain_error]() if the `value` isn't valid,
470 /// or anything else thrown by the constructor.
471 /// \notes This is meant for sanitizing user input,
472 /// using a recoverable error handling strategy.
473 /// \notes If this function is passed in dynamic values of the same type as `value`,
474 /// it will create a dynamic bound.
475 /// Otherwise it must be passed static bounds.
476 template <typename T, typename U1, typename U2>
sanitize_bounded_exclusive(T && value,U1 && lower,U2 && upper)477 constexpr auto sanitize_bounded_exclusive(T&& value, U1&& lower, U2&& upper)
478     -> detail::make_bounded_type<throwing_verifier, false, false, T, U1, U2>
479 {
480     using result_type = detail::make_bounded_type<throwing_verifier, false, false, T, U1, U2>;
481     return result_type(std::forward<T>(value),
482                        typename result_type::constraint_predicate(std::forward<U1>(lower),
483                                                                   std::forward<U2>(upper)));
484 }
485 
486 /// Returns a copy of `val` so that it is in the given [ts::constraints::closed_interval]().
487 /// \effects If it is not in the interval, returns the bound that is closer to the value.
488 /// \output_section clamped_type
489 template <typename T, typename LowerBound, typename UpperBound, typename U>
clamp(const constraints::closed_interval<T,LowerBound,UpperBound> & interval,U && val)490 constexpr auto clamp(const constraints::closed_interval<T, LowerBound, UpperBound>& interval,
491                      U&& val) -> typename std::decay<U>::type
492 {
493     return val < interval.get_lower_bound()
494                ? static_cast<typename std::decay<U>::type>(interval.get_lower_bound())
495                : (val > interval.get_upper_bound()
496                       ? static_cast<typename std::decay<U>::type>(interval.get_upper_bound())
497                       : std::forward<U>(val));
498 }
499 
500 /// A `Verifier` for [ts::constrained_type]() that clamps the value to make it valid.
501 ///
502 /// It must be used together with [ts::constraints::less_equal](),
503 /// [ts::constraints::greater_equal]() or [ts::constraints::closed_interval]().
504 struct clamping_verifier
505 {
506     /// \returns If `val` is greater than the bound of `p`,
507     /// returns the bound.
508     /// Otherwise returns the value unchanged
509     template <typename Value, typename T, typename Bound>
verifytype_safe::clamping_verifier510     static constexpr auto verify(Value&& val, const constraints::less_equal<T, Bound>& p) ->
511         typename std::decay<Value>::type
512     {
513         return p(val) ? std::forward<Value>(val)
514                       : static_cast<typename std::decay<Value>::type>(p.get_bound());
515     }
516 
517     /// \returns If `val` is less than the bound of `p`,
518     /// returns the bound.
519     /// Otherwise returns the value unchanged
520     template <typename Value, typename T, typename Bound>
verifytype_safe::clamping_verifier521     static constexpr auto verify(Value&& val, const constraints::greater_equal<T, Bound>& p) ->
522         typename std::decay<Value>::type
523     {
524         return p(val) ? std::forward<Value>(val)
525                       : static_cast<typename std::decay<Value>::type>(p.get_bound());
526     }
527 
528     /// \returns Same as `clamp(interval, val)`.
529     template <typename Value, typename T, typename LowerBound, typename UpperBound>
verifytype_safe::clamping_verifier530     static constexpr auto verify(
531         Value&& val, const constraints::closed_interval<T, LowerBound, UpperBound>& interval) ->
532         typename std::decay<Value>::type
533     {
534         return clamp(interval, std::forward<Value>(val));
535     }
536 };
537 
538 /// An alias for [ts::constrained_type]() that uses [ts::constraints::closed_interval]() as its
539 /// `Constraint` and [ts::clamping_verifier]() as its `Verifier`. \notes This is some type where the
540 /// values are always clamped so that they are in a certain interval.
541 template <typename T, typename LowerBound = constraints::dynamic_bound,
542           typename UpperBound = constraints::dynamic_bound>
543 using clamped_type = constrained_type<T, constraints::closed_interval<T, LowerBound, UpperBound>,
544                                       clamping_verifier>;
545 
546 /// Creates a [ts::clamped_type]() from the specified [ts::constraints::closed_interval]().
547 /// \returns A [ts::clamped_type]() with the given `value` and lower and upper bounds,
548 /// where the bounds are valid values.
549 /// \notes If this function is passed in dynamic values of the same type as `value`,
550 /// it will create a dynamic bound.
551 /// Otherwise it must be passed static bounds.
552 template <typename T, typename U1, typename U2>
make_clamped(T && value,U1 && lower,U2 && upper)553 constexpr auto make_clamped(T&& value, U1&& lower, U2&& upper)
554     -> detail::make_bounded_type<clamping_verifier, true, true, T, U1, U2>
555 {
556     using result_type = detail::make_bounded_type<clamping_verifier, true, true, T, U1, U2>;
557     return result_type(std::forward<T>(value),
558                        typename result_type::constraint_predicate(std::forward<U1>(lower),
559                                                                   std::forward<U2>(upper)));
560 }
561 } // namespace type_safe
562 
563 #endif // TYPE_SAFE_BOUNDED_TYPE_HPP_INCLUDED
564