Skip to content

Commit

Permalink
simplify Numbers by fixing their underlying integral type
Browse files Browse the repository at this point in the history
metal::int_ is implementation defined, but may be safely used to
partialy specialize templates expecting Numbers if so desired
  • Loading branch information
brunocodutra committed Aug 18, 2016
1 parent a8fdc48 commit 4f56bca
Show file tree
Hide file tree
Showing 83 changed files with 787 additions and 1,203 deletions.
6 changes: 3 additions & 3 deletions doc/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,9 @@ ALIASES +=maps="\ref map \"Maps\""

ALIASES +=semantics="\par Semantics"

ALIASES +=tip{1}="<div class=\"panel panel-info\"><div class="panel-heading"><span class=\"octicon octicon-light-bulb\"></span></div><div class="panel-body">\1</div></div>"
ALIASES +=note{1}="<div class=\"panel panel-warning\"><div class="panel-heading"><span class=\"octicon octicon-info\"></span></div><div class="panel-body">\1</div></div>"
ALIASES +=danger{1}="<div class=\"panel panel-danger\"><div class="panel-heading"><span class=\"octicon octicon-stop\"></span></div><div class="panel-body">\1</div></div>"
ALIASES +=tip{1}="<div class=\"panel panel-info\"><div class="panel-heading"><span class=\"octicon octicon-light-bulb\"> Tip</span></div><div class="panel-body">\1</div></div>"
ALIASES +=note{1}="<div class=\"panel panel-warning\"><div class="panel-heading"><span class=\"octicon octicon-info\"> Note</span></div><div class="panel-body">\1</div></div>"
ALIASES +=danger{1}="<div class=\"panel panel-danger\"><div class="panel-heading"><span class=\"octicon octicon-stop\"> Warning</span></div><div class="panel-body">\1</div></div>"

# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
Expand Down
166 changes: 96 additions & 70 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ A [Number] is a compile-time representation of a numerical value.
`metal::number`.

\note{
`metal::number` is guaranteed to be an alias template to
[`std::integral_constant`][integral].
`metal::number<n>` is guaranteed to be an alias template to
`std::integral_constant<metal::int_, n>`.
}

### Examples
Expand All @@ -279,7 +279,7 @@ A [Number] is a compile-time representation of a numerical value.

### See Also

metal::number, metal::bool_, metal::int_, metal::char_
metal::number, metal::int_

Expression {#expression}
--------------------------------------------------------------------------------
Expand Down Expand Up @@ -394,112 +394,133 @@ Parsing Raw Literals {#parsing_raw_literals}
--------------------------------------------------------------------------------

If you ever considered augmenting [`std::tuple`][tuple],
so that instead of the rather odd [`std::get<N>()`][get]
so that instead of the rather clunky [`std::get<N>()`][get]

\snippet literal.cpp teaser_1

one could just use the more intuitive subscript operator `[N]`,
one could just use the more intuitive subscript operator `[N]`

\strike{
\snippet literal.cpp teaser_2
}

chances are you realized the hard way that there is simply no way of
overloading such an operator.
you might have come up with something that looks pretty much like this

All is not lost however if you can live with the subscript operator taking an
object of type `std::integral_constant`, or in Metal's parlance a [Number],
instead of an usual integral value.
\strike{
\snippet literal.cpp naive_1
}

only to realize the hard way that this is simply not valid C++14.

> error: non-type template argument is not a constant expression
While the keyword `constexpr` tells the compiler the value returned by
`operator ""_c` *might* be a compile time constant, it tells no such thing
about its arguments, which may very well be unknown at compile-time.

So how can we convince the compiler that indices are always known at
compile-time? We refactor the subscript operator to take an instance of
`metal::number`.

\snippet literal.cpp augmented_tuple

Since `i` in this context is already a template argument, the compiler has no
reason to complain about passing it on as an argument to other template
instantiations.

\snippet literal.cpp teaser_3

Neat, now we need a [literal operator][literal] `_c` that encodes an integral
value as a [Number]. Easy, right?
That looks promising, but then again isn't `metal::number<1>{}` just as clunky
as `std::get<1>()`? Yes. Absolutely.

That's where [literal operators][literal] come into play.

\snippet literal.cpp teaser_4

We're are getting there, but how is `_c` implemented again?

At a first glance it might be tempting to try something like this

\strike{
\snippet literal.cpp naive
\snippet literal.cpp naive_2
}

Not really. While `constexpr` tells the compiler the value returned by
`operator ""_c` might be a compile time constant,
it tells no such thing about its argument.
We are thus left no other option but to parse raw literals ourselves,
in other words, we are in for some fun!
but do not forget why we're here to begin with, recall we can't instantiate
a template using a non-constexpr variable as argument!

At this point, a watchful reader might argue that in theory there is no real
reason for this to be rejected, since the literal value must always be known at
compile-time and that makes a lot of sense indeed, but unfortunately that's just
not how it works. All is not lost however, because we can still parse raw
literals, that is, we are in for some fun!

### The Raw Literal Operator Template

Raw literal operator templates are defined as a nullary function templated over
`char...`, such as
Raw literal operator templates in C++ are defined as a nullary constexpr
function templated over `char...`

\snippet literal.cpp raw
\snippet literal.cpp _raw

where `tokens...` are mapped to the exact characters that make up the literal,
where `cs...` are mapped to the exact characters that make up the literal,
including the prefixes `0x` and `0b`

\snippet literal.cpp raw_examples_1
\snippet literal.cpp _raw_ex1

as well as the digit separator `'` introduced in [C++14]
as well as digit separators

\snippet literal.cpp raw_examples_2
\snippet literal.cpp _raw_ex2

### The `operator ""_c`

We start by defining our very own literal operator `_c`.
It simply wraps each token into a `metal::character` and forwards them to an
[Expression] that effectively parses the [Number], we'll call it,
suggestively, `make_number`.
We start by defining the literal operator `_c` as a function that forwards the
raw literal characters as a [List] of [Numbers] to `parse_number` and returns a
default constructed object of whatever type aliases to.
In this case it is guaranteed to be a [Number].

\snippet literal.cpp _c

### Resolving the Radix

In its turn `make_number` strips the prefix, if any, thus resolving the radix,
then forwards the remaining tokens to `parse_digits`,
which is in charge of translating the raw characters to a [List] of
integral [Numbers].
The radix and digits are then forwarded to `compute`, which adds up the digits
according to the radix.
In its turn `parse_number` strips the prefix, if any, thus resolving the radix,
then forwards the remaining characters to `parse_digits`, which is in charge of
translating the raw characters to the numerical values they represent.
The radix and digits are then forwarded to `assemble_number`, which adds up the
individual digits according to the radix.

\snippet literal.cpp make_number
\snippet literal.cpp parse_number

### Parsing Digits

To parse the characters into the corresponding integral, we need first to remove
all digit separators.
To parse characters into their corresponding numerical value, we must first
remove all digit separators.
That can be easily accomplished using `metal::remove`, which takes a [List] `l`
and a [Value] `v` and returns another [List] that contains every element from
`l` and in the same order, except for those that are equal to `v`.
`l` and in the same order, except for those that are identical to `v`.

\snippet literal.cpp remove

The remaining digits can then be transformed into the corresponding
[Numbers] using `metal::transform`, which takes a [Lambda] `lbd` and a
[List] `l` and returns another [List] containing the results of *invoking* `lbd`
for each element in `l`.
The remaining characters can then be individually parsed with the help of
`metal::transform`, which takes a [Lambda] `lbd` and a [List] `l` and returns
another [List] containing the results of invoking `lbd` for each element in `l`.

[lbd(l[0]), lbd(l[1]), ..., lbd(l[n-2]), lbd(l[n-1])]

First we need an [Expression] that maps characters to
[Numbers] from which we can construct our `lbd`.
We'll call it `to_number` and it is rather trivial.

\snippet literal.cpp to_number
\snippet literal.cpp parse_digit

We may now transform characters to [Numbers].
\snippet literal.cpp transform

\snippet literal.cpp transform_1
Notice how integral values of type `char` are translated into their actual
numerical representation.

Putting it all together we have
So there it is

\snippet literal.cpp parse_digits

### Computing Numbers
### Assembling Numbers

Now we turn our attention to `compute`.
It takes the radix and a list of [Numbers] representing the digits and is in
charge of adding up the digits according to the radix, that is
We now turn our attention to `assemble_number`.
It takes the radix and a [List] of digits and is in charge of adding them up
according to the radix, that is

D0*radix^(n-1) + D1*radix^(n-2) + ... + D{n-2}*radix + D{n-1}

Expand All @@ -509,11 +530,11 @@ To make things simpler, we really want to `metal::reverse` the order of digits.

\snippet literal.cpp reverse

Then we have
so that it becomes

D0 + D1*radix + ... + D{n-2}*radix^(n-2) + D{n-1}*radix^(n-1)

Now we need to `metal::enumerate` the exponents that correspond to each digit.
Next we need to `metal::enumerate` the exponents that correspond to each digit.

\snippet literal.cpp enumerate

Expand All @@ -523,43 +544,49 @@ and ending in `size - 1`.

[start, start + 1, ..., size - 2, size - 1]

The next step is to compute each term of the sum.
We'll be using `metal::transform` again, but this time it takes a *binary*
[Lambda] `lbd` and two [Lists] `l1` and `l2` and returns a new [List] formed by
*invoking* `lbd` for the elements of `l1` and `l2` pairwise.
To compute each term of the sum, we'll be using `metal::transform` again,
but this time it takes a binary [Lambda] `lbd` and two [Lists] `l1` and `l2`
and returns a new [List] formed by invoking `lbd` for the elements of `l1` and
`l2` pairwise.

[lbd(l1[0], l2[0]), lbd(l1[1], l2[1]), ..., lbd(l1[n-2], l2[n-2]), lbd(l1[n-1], l2[n-1])]

\snippet literal.cpp transform_2
Here we need an [Lambda] that takes a digit and the corresponding radix exponent
and computes a term of the sum. Again we could define an [Expression] for that
specific purpose and construct a [Lambda] from it, much like we did for
`parse_digit`, but instead we'll be using *bind expressions* here.

\snippet literal.cpp zip

If *bind expressions* look scary to you, don't panic, we will cover [Expression]
composition in detail in our [next practical example](#church_booleans).
Here it suffices to know that *bind expressions* are themselves [Lambdas] and
that `metal::_1` and `metal::_2` are [Placeholders] that get substituted by the
first and second arguments with which the *bind expression* is invoked prior to
the recursive evaluation of the [Expressions] that form the *bind expression*.
the recursive evaluation of the [Expressions] that compose the
*bind expression*.

The final step is to sum up the terms, that is we need to invoke
`metal::add` for the elements contained in a [List].
That's exactly what `metal::apply` is for.
That's where `metal::apply` comes into play.

\snippet literal.cpp sum

We now have all the pieces needed to define `compute`.
And there you go

\snippet literal.cpp compute
\snippet literal.cpp assemble_number

### Fun With `operator ""_c`

\snippet literal.cpp test_1
\snippet literal.cpp _c_ex1

It also works for very long binary literals.

\snippet literal.cpp test_2
\snippet literal.cpp _c_ex2

And ignores digit separators too.

\snippet literal.cpp test_3
\snippet literal.cpp _c_ex3

Church Booleans {#church_booleans}
--------------------------------------------------------------------------------
Expand Down Expand Up @@ -605,7 +632,6 @@ TODO
[alias templates]: http://en.cppreference.com/w/cpp/language/type_alias
[decltype]: http://en.cppreference.com/w/cpp/language/decltype
[constexpr]: http://en.cppreference.com/w/cpp/language/constexpr
[integral]: http://en.cppreference.com/w/cpp/types/integral_constant
[tuple]: http://en.cppreference.com/w/cpp/utility/tuple
[get]: http://en.cppreference.com/w/cpp/utility/tuple/get
[literal]: http://en.cppreference.com/w/cpp/language/user_literal
Expand Down
3 changes: 2 additions & 1 deletion example/include/example.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

#define HIDE(...) struct CAT(anonymous, __LINE__) {__VA_ARGS__};

#define ASSERT(...) static_assert(__VA_ARGS__::value, "")
#define IS_SAME_IMPL(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
#define IS_SAME(...) IS_SAME_IMPL(std::is_same<__VA_ARGS__>::value)

int main() {
return 0;
Expand Down
36 changes: 18 additions & 18 deletions example/src/church.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,33 @@
using true_ = metal::_1;
using false_ = metal::_2;

ASSERT(std::is_same<metal::invoke<true_, true_, false_>, true_>);
ASSERT(std::is_same<metal::invoke<true_, false_, true_>, false_>);
ASSERT(std::is_same<metal::invoke<false_, true_, false_>, false_>);
ASSERT(std::is_same<metal::invoke<false_, false_, true_>, true_>);
IS_SAME(metal::invoke<true_, true_, false_>, true_);
IS_SAME(metal::invoke<true_, false_, true_>, false_);
IS_SAME(metal::invoke<false_, true_, false_>, false_);
IS_SAME(metal::invoke<false_, false_, true_>, true_);

using not_ = metal::bind<metal::lambda<metal::invoke>, metal::_1, metal::quote<false_>, metal::quote<true_>>;

ASSERT(std::is_same<metal::invoke<not_, true_>, false_>);
ASSERT(std::is_same<metal::invoke<not_, false_>, true_>);
IS_SAME(metal::invoke<not_, true_>, false_);
IS_SAME(metal::invoke<not_, false_>, true_);

using and_ = metal::bind<metal::lambda<metal::invoke>, metal::_1, metal::_2, metal::_1>;

ASSERT(std::is_same<metal::invoke<and_, true_, true_>, true_>);
ASSERT(std::is_same<metal::invoke<and_, true_, false_>, false_>);
ASSERT(std::is_same<metal::invoke<and_, false_, true_>, false_>);
ASSERT(std::is_same<metal::invoke<and_, false_, false_>, false_>);
IS_SAME(metal::invoke<and_, true_, true_>, true_);
IS_SAME(metal::invoke<and_, true_, false_>, false_);
IS_SAME(metal::invoke<and_, false_, true_>, false_);
IS_SAME(metal::invoke<and_, false_, false_>, false_);

using or_ = metal::bind<metal::lambda<metal::invoke>, metal::_1, metal::_1, metal::_2>;

ASSERT(std::is_same<metal::invoke<or_, true_, true_>, true_>);
ASSERT(std::is_same<metal::invoke<or_, true_, false_>, true_>);
ASSERT(std::is_same<metal::invoke<or_, false_, true_>, true_>);
ASSERT(std::is_same<metal::invoke<or_, false_, false_>, false_>);
IS_SAME(metal::invoke<or_, true_, true_>, true_);
IS_SAME(metal::invoke<or_, true_, false_>, true_);
IS_SAME(metal::invoke<or_, false_, true_>, true_);
IS_SAME(metal::invoke<or_, false_, false_>, false_);

using xor_ = metal::bind<metal::lambda<metal::invoke>, metal::_1, metal::bind<not_, metal::_2>, metal::_2>;

ASSERT(std::is_same<metal::invoke<xor_, true_, true_>, false_>);
ASSERT(std::is_same<metal::invoke<xor_, true_, false_>, true_>);
ASSERT(std::is_same<metal::invoke<xor_, false_, true_>, true_>);
ASSERT(std::is_same<metal::invoke<xor_, false_, false_>, false_>);
IS_SAME(metal::invoke<xor_, true_, true_>, false_);
IS_SAME(metal::invoke<xor_, true_, false_>, true_);
IS_SAME(metal::invoke<xor_, false_, true_>, true_);
IS_SAME(metal::invoke<xor_, false_, false_>, false_);
6 changes: 3 additions & 3 deletions example/src/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
HIDE(
/// [expr1]
template<typename... vals>
using expr = metal::size_t<sizeof...(vals)>;
using expr = metal::number<sizeof...(vals)>;
/// [expr1]
)

Expand All @@ -29,7 +29,7 @@ struct not_an_expr;

HIDE(
/// [not_an_expr2]
template<typename t, t v> // non-type parameter
using not_an_expr = metal::number<t, v>;
template<int v> // non-type parameter
using not_an_expr = metal::number<v>;
/// [not_an_expr2]
)
2 changes: 1 addition & 1 deletion example/src/lambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ HIDE(
using lbd = metal::lambda<std::add_pointer_t>;
/// [lbd1]

ASSERT(metal::is_lambda<lbd>);
IS_SAME(metal::is_lambda<lbd>, metal::true_);
)
Loading

0 comments on commit 4f56bca

Please sign in to comment.