Chapter 35. Typesafe numerical aliases

Index

Base classes
Testing for overflow
Using typesafe numerical aliases in unordered containers
#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::numbers 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.

Base classes

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.