#include <x/number.H> class number_tag; typedef x::number<int, number_tag> number_t; number_t number{4}, number2=number*2; int value=number;
x::number
implements a typesafe wrapper for a numerical type. This is a wrapper
for an integral type that implements overloaded operators for all
mathematical expressions, which produce the same type.
The first template parameter is any integer type.
The second template parameter is not referenced. Its only use is to
produce a separate, discrete type.
An x::number
can be added to, subtracted to,
etc..., to either another untyped numerical value, or the same
x::number
, resulting in the same
x::number
.
This is the default configuration, and
a custom base class can specify
different type-safety rules.
Using multiple
x::number
s prevents accidentally using
one x::number
in an expression with a different
x::number
(unless allowed by the template
aliases specified in the custom base class).
typedef number_t::value_type value_type;
The value_type
member is an alias
for the wrapped numerical type, the first template parameter.
The optional third template parameter specifies a class that the
resulting
x::number
class inherits from, inheriting all of the base class's methods
and definitions:
class base_class { public: static constexpr int infinite() { return -1; } template<typename number_1, typename number_2> using resulting_add_op=x::number_default_same_op_only<number_1, number_2>; template<typename number_1, typename number_2> using resulting_sub_op=x::number_default_same_op_only<number_1, number_2>; template<typename number_1, typename number_2> using resulting_mul_op=x::number_default_same_op_only<number_1, number_2>; template<typename number_1, typename number_2> using resulting_div_op=x::number_default_same_op_only<number_1, number_2>; template<typename number_1, typename number_2> using resulting_mod_op=x::number_default_same_op_only<number_1, number_2>; template<typename number_1> using resulting_uneg_op=number_default_uneg_op<number_1> }; typedef x::number<int, number_tag, base_class> number_t; int n=number_t::infinite();
Anything specified in the base class can be accessed directly via the
resulting number class, like infinite
() in this
example. The base class must also define the following
five template aliases:
resulting_add_op
,
resulting_sub_op
,
resulting_mul_op
,
resulting_div_op
,
resulting_mod_op
, and
resulting_uneg_op
.
The above example uses a copy of the template aliases defined
in
x::number_default_base
.
This example is equivalent to:
class base_class : public x::number_default_base { public: static constexpr int infinite() { return -1; } }; typedef x::number<int, number_tag, base_class> number_t; int n=number_t::infinite();
The six template aliases specify the number type resulting from
addition, subtraction, multiplication, division, modulus,
and unary negation operations respectively,
and the template alias specifies the resulting type of the
operation. The
x::number_default_same_op_only
template alias is defined as follows:
template<typename number_1, typename number_2> using number_default_same_op_only= typename std::enable_if<std::is_same<number_1, number_2>::value, number_1>::type;
The
+
,
-
,
*
,
/
, and
%
operators use the left-hand side's number's
base class's template aliases, and the first template parameter to
resulting_add_op
,
resulting_sub_op
,
resulting_mul_op
,
resulting_div_op
, or
resulting_mod_op
template alias is the left-hand
number type. The second template parameter is the right-hand number
type.
The default template aliases allow only numbers of the same type
to be operated on, and the resulting template alias is the same
number type.
In this manner, it is possible to use hand-tailored template aliases to specify that the result of an addition, multiplication, or some other operation on custom number types produce a completely different custom number type.
The ++
and --
operators always
result in the same number types.
The
x::number_default_uneg_op
template alias is defined as follows:
template<typename number_1> using number_default_uneg_op= typename std::enable_if<std::numeric_limits<typename number_1 ::value_type>::is_signed, number_1>::type;
This enables the unary negation operation on signed typesafe numbers, with the result being the same typesafe number type.