1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
In addition to the literal operators discussed in section ref(UDLITERALS)
bf(C++) also offers a function template literal operator, matching the
prototype
verb( template <char ...Chars>
Type operator "" _identifier())
This variadic non-type parameter function template defines no parameters,
but merely a variadic non-type parameter list.
Its argument must be an int constant, as is also expected by the literal
operator defining an tt(unsigned long long int) parameter. All the characters
of the int constant are passed as individual tt(char) non-type template
arguments to the literal operator.
For example, if tt(_NM2km) is a literal operator function template, it can
be called as tt(80_NM2km). The function template is then actually called as
tt(_NM2km<'8', '0'>()). If this function template merely uses template meta
programming techniques and only processes integral data then its actions can
be performed completely at compile-time. To illustrate this, let's assume
tt(NM2km) only processes and returns unsigned values.
The function template tt(_NM2km) can forward its argument to a class template,
defining an enum constant tt(value), and that performs the required
computations. Here is the implementation of the variadic literal operator
function template tt(_NM2km):
verb( template <char ... Chars>
size_t constexpr operator "" _NM2km()
{
return static_cast<size_t>( // forward Chars to NM2km
NM2km<0, Chars ...>::value * 1.852);
})
The class template tt(NM2km) defines three non-type parameters: tt(acc)
accumulates the value, tt(c) is the first character of the variadic non-type
parameters, while tt(...Chars) represents the remaining non-type parameters,
contained in a non-type parameter pack. Since tt(c) is, at each recursive
call, the next character from the original non-type parameter pack, the value
so far multiplied by 10 plus the value of the next character is passed as the
next accumulated value to its recursive call, together with the remaining
elements of the parameter pack, represented by tt(Chars ...):
verb( template <size_t acc, char c, char ...Chars>
struct NM2km
{
enum
{
value = NM2km<10 * acc + c - '0', Chars ...>::value
};
};)
Eventually, the parameter pack is empty. For this case a partial
specialization of tt(NM2km) is available:
verb( template <size_t acc, char c> // empty parameter pack
struct NM2km<acc, c>
{
enum
{
value = 10 * acc + c - '0'
};
};)
This works fine, but of course not in cases where binary, octal, or
hexadecimal values must also be interpreted. In that case we must first
determine whether the first character(s) indicate a special number
system. This can be determined by the class template tt(NM2kmBase), that is
now called from the tt(_NM2km) literal operator:
verb( template <char ... Chars>
size_t constexpr operator "" _NM2km()
{
return static_cast<size_t>( // forward Chars to NM2kmBase
NM2kmBase<Chars ...>::value * 1.852);
})
The tt(NM2kmBase) class template normally assumes the decimal number
system, passing base value 10 and initial sum 0 to tt(NM2km). The tt(NM2km)
class template is provided with an additional (first) non-type parameter
representing the base value of the number system to use. Here is
tt(NM2kmBase):
verb( template <char ...Chars>
struct NM2kmBase
{
enum
{
value = NM2km<10, 0, Chars ...>::value
};
};)
Partial specializations handle the different number systems, by inspecting
the first (one or two) characters:
verb( template <char ...Chars>
struct NM2kmBase<'0', Chars ...> // "0..."
{
enum
{ // octal value: base 8
value = NM2km<8, 0, Chars ...>::value
};
};
template <char ...Chars>
struct NM2kmBase<'0', 'b', Chars ...> // "0b..."
{
enum
{ // binary value: base 2
value = NM2km<2, 0, Chars ...>::value
};
};
template <char ...Chars>
struct NM2kmBase<'0', 'x', Chars ...> // "0x..."
{
enum
{ // hex value: base 16
value = NM2km<16, 0, Chars ...>::value
};
};)
tt(NM2km) is implemented as before, albeit that it can now handle
various number systems. The conversion from character to numeric value is left
to a small support function template, tt(cVal):
verb( template <char c>
int constexpr cVal()
{
return '0' <= c <= '9' ? c - '0' : 10 + c - 'a';
}
template <size_t base, size_t acc, char c, char ...Chars>
struct NM2km
{
enum
{
value = NM2km<base, base * acc + cVal<c>(),
Chars ...>::value
};
};
template <size_t base, size_t acc, char c>
struct NM2km<base, acc, c>
{
enum { value = base * acc + cVal<c>() };
};)
|