enum class storage_type { fast, least };
template<intmax_t minimum, intmax_t maximum, typename overflow_policy, storage_type storage> class integer;
An overflow policy has the following requirements:
Additionally, if the overflow policy is DefaultConstructible, so will the bounded::integer that uses this policy.
The null_policy is the most basic policy of all. Its solution to out-of-bounds values is to declare them to be undefined behavior. If the values are used in a constexpr context, this policy does perform out-of-bounds checking, but it does not perform a run-time check.
If an out-of-bounds value is passed to assignment, it throws an exception of type std::range_error.
If an a value less than minimum is passed to assignment, it returns minimum. If a value greater than maximum is passed to assignment, it returns maximum.
There are three policy adapters defined as well: dynamic_policy, dynamic_min_policy, and dynamic_max_policy. dynamic_min_policy has a value that can be set at run-time that can further restrict the minimum bound beyond that of the static bound. dynamic_max_policy has a value that can be set at run-time that can further restrict the maximum bound beyond that of the static bound. dynamic_policy has both. All dynamic policies have member functions min and max that allow the user to retrieve the value (or assign to it, where appropriate). All of them accept the following template parameters:
template<intmax_t static_minimum, intmax_t static_maximum, typename overflow_policy, storage_type storage = storage_type::fast>
These parameters are passed directly to the bounded::integer type that will hold the run-time bound. If the overflow_policy template parameter is stateful, each dynamic policy has its own copy of that state that is separate from the state of the dynamic bounds.
There is a template<typename policy> basic_policy; defined to reduce boilerplate for most simple policies. If the policy passed in as a template parameter defines the member variables is_modulo, overflow_is_error and assignment, basic_policy provides all other requirements, including the ability to constrct from any other policy. basic_policy can only be used with stateless policies.
Similar to std::common_type, the bounded library has a metafunction common_policy. This can be specialized as long as least one of the types in the specialization is a user-defined policy. It accepts any number of types greater than zero. There is also an alias template, common_policy_t, to mirror std::common_type_t.
For three or more policies passed in, the metafunction is recursively applied, two policies at a time.
common_policy
In other words, the null_policy 'loses' to any other policy, otherwise, it is a compile-time error to attempt to get the common_policy of mixed policies.
namespace bounded { template<intmax_t minimum, intmax_t maximum> using checked_integer = integer<minimum, maximum, throw_policy>; template<intmax_t minimum, intmax_t maximum> using clamped_integer = integer<minimum, maximum, clamp_policy>; template<intmax_t minimum, intmax_t maximum, typename base_overflow_policy = throw_policy> using dynamic_integer = integer<minimum, maximum, dynamic_policy<minimum, maximum, base_overflow_policy>>; template<intmax_t minimum, intmax_t maximum, typename base_overflow_policy = throw_policy> using dynamic_min_integer = integer<minimum, maximum, dynamic_min_policy<minimum, maximum, base_overflow_policy>>; template<intmax_t minimum, intmax_t maximum, typename base_overflow_policy = throw_policy> using dynamic_max_integer = integer<minimum, maximum, dynamic_max_policy<minimum, maximum, base_overflow_policy>>; } // namespace bounded
All basic arithmetic operators return by value. bounded::integer supports the following arithmetic operators:
All of the above binary operators have an equivalent compound assignment operator (+=, -=, *=, /=, %=, <<=, >>=). These behave the same as first calling the above operator, then calling the assignment operator on the left-hand-side argument.
bounded::integer supports all of the comparison / relational operators (==, !=, <, >, <=, >=). Any bounded::integer can be compared with any other bounded::integer or any built-in type. Doing so never invokes undefined behavior, nor are there any surprising changes in value. A bounded::integer with a value of -1 will always compare less than any value of std::uintmax_t, for instance.
The bounded namespace contains all overloads of to_string that exist for std::to_string (by way of using std::to_string). It also provides overloads for bounded::integer types. The result of calling this function is the same as calling std::to_string on the underlying_type, except that if the underlying_type is a character type, it is first promoted to int. The underlying type can be a character type on many systems where std::uint8_t is just a typedef for unsigned char, for example.
bounded::integer overloads are provided for the insertion (<<) and extraction (>>) operators for a left-hand-side argument of std::basic_ostream. This inputs / outputs the value formatted as an integer. Very large integer values input can invoke undefined behavior, as operator>> first pulls in the value as std::intmax_t and then constructs a bounded::integer from that. This is a temporary limitation of the current implementation.
bounded::integer provides a specialization of std::numeric_limits. It provides all of the required values / functions.
bounded::min accepts an arbitrary number of arguments and returns the least value as determined by operator<. When comparing integer types, if std::numeric_limits reports that the range of one is entirely less than the range of the other (for instance, bounded::integer<0, 5> is definitely less than bounded::integer<6, 10>), then the comparison function is not invoked. For bounded::integer arguments, the range of the result is the minimum of all the minimums and the minimum of all the maximums.
bounded::max is the same as bounded::min, except that it uses operator>.
There is a more general function, bounded::extreme. The first argument to this function is a comparison function that returns true if the first value is more extreme than the second. The result of the function is the most extreme value as determined by this comparison function. The type of the result by default uses std::common_type, but that can be changed by providing a specialization of template<typename Compare, typename T1, typename T2> class extreme_type in the bounded namespace that provides a member type named type. T1 and T2 will always be decayed (cv-qualifiers and references stripped).
bounded::abs accepts any bounded::integer and returns a new bounded::integer that is guaranteed to be non-negative. The result type has the same overflow_policy type and storage as the argument. noexcept.
increase_min accepts an integer as a template argument and a bounded::integer as a function argument. The return type is a bounded::integer that has a new minimum that is the greater of its original minimum and the specified minimum. The function also accepts more arguments, which will be passed to the constructor of the new bounded::integer type.
decrease_max is the same as the above, except the template argument can lower the maximum rather than raise the minimum.
template< typename T, typename overflow_policy = see below, storage_type storage = storage_type::fast > using equivalent_type = integer< detail::basic_numeric_limits<T>::min(), detail::basic_numeric_limits<T>::max(), overflow_policy, storage >;
This alias template allows the user to specify a type and get its equivalent bounded version. For instance, bounded::equivalent_type<std::int8_t> is the same as bounded::integer<-128, 127, bounded::null_policy> on most systems. The defaulted overflow_policy parameter is bounded::null_policy for signed built-in integer types and the same as T::overflow_policy_type for bounded::integer types. Currently unsigned built-in types default to bounded::null_policy, but this is a temporary defect caused by not having a bounded::modulo_policy.
template<typename overflow_policy = see below, storage_type storage = storage_type::fast, typename T = unspecified> constexpr auto make(T const value) noexcept -> equivalent_type<T, overflow_policy, storage>;
It is intended the the user specifies 0, 1, or 2 template arguments. A call like make(value) will return an instance of the type specified by bounded::equivalent_type<decltype(value)>. However, the user can explicitly specify the overflow_policy and the storage to be used by passing them as the first and second template arguments, respectively.
template<intmax_t value, typename overflow_policy = null_policy, storage_type storage = storage_type::fast> constexpr auto make() noexcept -> integer<value, value, overflow_policy, storage> { return {value, non_check}; }
This overload is used to create a bounded::integer that has a known compile-time value. The purpose is to reduce the duplication of the value as in a call to bounded::integer<5, 5>(5) . Instead, you would simply use bounded::make<5>() .
The namespace bounded::literal contains a user defined integer literal _bi. This creates a bounded::integer with a min and max equal to the value. For example, 10_bi is the same as bounded::integer<10, 10, bounded::null_policy>(10) .
bounded::next is the same as std::next, except the default value is 1_bi instead of 1 (type of bounded::integer<1, 1> instead of int). bounded::prev is the same as std::prev, except its default value is also 1_bi instead of 1.
For a given type T, bounded::is_bounded_integer derives from std::true_type if the type is a bounded::integer, otherwise it derives from std::false_type.
`BOUNDED_CONDITIONAL` is a macro that accepts three arguments. The first argument is evaluated in `operator?:` as the first (boolean) argument. The result of the macro is the second argument if the condition is true, otherwise it is the third argument. The type of the result is the common type (as determined by std::common_type) and value category of the second and third arguments.
This exists as a workaround for a limitation of `operator?:`. The built-in operator attempts to convert each argument to the type of the other. This means that a statement like `true ? 1_bi : 2_bi` does not compile, as neither argument can be converted to the other. `BOUNDED_CONDITIONAL(true, 1_bi, 2_bi)`, however, returns the value 1, and the type is bounded::integer<1, 2>.
This library provides specializations for the class template std::common_type. The common_type of two bounded::integer types is another bounded::integer type. The minimum is the lesser of the two minimums, the maximum is the greater of the two maximums, and the policy is the result of common_policy. This specialization only exists when both arguments have the same storage_type.
The bounded library provides functions that are nearly identical to std::count and std::count_if. The difference is the type returned. bounded::count and bounded::count_if return the type bounded::integer<0, std::numeric_limits<iterator_difference_type>::max()>.
The header bounded_integer/integer_range.hpp defines two function overloads of bounded::integer_range: bounded::integer_range(begin, end) and bounded::integer_range(size), where begin, end, and size are all integers. bounded::integer_range(size) is equivalent to bounded::integer_range(0, size). The return type of this function is such that it can be used in a range-based for loop.
The given range contains all the values from begin to end, including begin but excluding end. For example, the code for (auto x : bounded::integer_range(5_bi) will result in x having the values 0, 1, 2, 3, and 4. The type of x is bounded::integer<0, 4>. The numbers are generated on demand, so it has O(1) space complexity in the size of the range.