lesson/1.1-values.cpp (228 lines of code) (raw):

/* * Copyright (c) 2016, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #include <fatal/lesson/driver.h> namespace lesson { /** * @author: Marcelo Juchem <marcelo@fb.com> */ LESSON( "representing values, part 1/4", "This lesson gives an overview on how values are represented. Subsequent " "tutorials will elaborate on proper ways of achieving such representation." "\n\n" "The goal, for now, is to come up with the intuition behind it without " "drowing in syntax and correctness.", template <int Value> struct int_constant { static int value; }; template <int Value> int int_constant<Value>::value = Value; ) { COMMENT( "A previous lesson mentioned that values can be emulated using types to " "represent them. Here's an overview on the intuition of how this can be " "achieved." ); CODE( using x = int_constant<15>; ); TYPE(x); VALUE(x::value); COMMENT( "Note, however, that `int_constant::value` is a regular runtime variable " "as opposed to a compile time constant. It is possible, for instance, to " "change the value associated with it:" ); CODE( x::value = 30; ); VALUE(x::value); COMMENT( "This makes it illegal to use such variable as an argument to a template. " "Template parameters must be immutable and available at compile time. This " "includes, for instance, type and integer constants." ); ILLEGAL( "`int_constant::value` is not a constant", using y = int_constant<x::value>; ); } /** * @author: Marcelo Juchem <marcelo@fb.com> */ LESSON( "representing values, part 2/4", "This lesson demonstrates proper ways to represent values that can be used " "at compile time." "\n\n" "Let's modify the `int_constant` template to properly represent compile time " "constants.", template <int Value> struct int_constant_proper { static constexpr int const value = Value; }; template <int Value> constexpr int const int_constant_proper<Value>::value; ) { COMMENT( "The `constexpr` keyword roughly allows us to tell the compiler that a " "given variable holds the result of a constant expression." "\n\n" "Once we have such guarantee, the compiler can evaluate the contents of " "such variable at compile time, effectivelly making it a compile time " "constant." ); CODE( using x = int_constant_proper<15>; ); TYPE(x); VALUE(x::value); COMMENT( "As noted before, constants can be used as template parameters." ); CODE( using y = int_constant_proper<x::value>; ); TYPE(y); VALUE(y::value); COMMENT( "In fact, any expression that can be evaluated at compile time can be used " "as a compile time constant:" ); CODE( using z = int_constant_proper<x::value * 2>; ); TYPE(z); VALUE(z::value); CODE( using w = int_constant_proper<x::value + z::value - 3>; ); TYPE(w); VALUE(w::value); } /** * @author: Marcelo Juchem <marcelo@fb.com> */ LESSON( "representing values, part 3/4", "This lesson gives an overview on the implementation of " "`std::integral_constant`." "\n\n" "So far we've been limited to `int` constants. One could be interested in " "employing other types for a constant, like `char` or `unsigned long`." "\n\n" "Let's modify the `int_constant_proper` template to represent arbitrary " "integral types.", template <typename T, T Value> struct constant { static constexpr T const value = Value; }; template <typename T, T Value> constexpr T const constant<T, Value>::value; ) { COMMENT( "Now we can specify the type of the constant, as well as its value." ); CODE( using x = constant<int, -15>; ); TYPE(x); VALUE(x::value); CODE( using y = constant<bool, true>; ); TYPE(y); VALUE(y::value); COMMENT( "Again, any expression that can be evaluated at compile time will do:" ); CODE( using z = constant<unsigned, (x::value > 0) ? x::value : -x::value>; ); TYPE(z); VALUE(z::value); } /** * @author: Marcelo Juchem <marcelo@fb.com> */ LESSON( "representing values, part 4/4", "This lesson gives an overview of some basic features that " "`std::integral_constant` offers." "\n\n" "The implementation and library features built around " "`std::integral_constant` are a bit more involved than what we've seen so " "far, but for the purposes of a lesson, we don't need to dig too deep." "\n\n" "For now, let's look at a few more things that `std::integral_constant` " "offers." ) { COMMENT( "We already covered how to represent a compile time constant with a type, " "and how to access the constant's value." ); CODE( using x = std::integral_constant<int, -15>; ); TYPE(x); VALUE(x::value); COMMENT( "For convenience purposes, `std::integral_constant` also provides an " "identity alias in the form of a member called `type`:" ); TYPE(x::type); COMMENT( "It also exposes the type of the constant it represents:" ); TYPE(x::value_type); COMMENT( "Shortcuts to boolean constants are also provided:" ); CODE( using t = std::true_type; ); TYPE(t); VALUE(t::value); TYPE(t::value_type); CODE( using f = std::false_type; ); TYPE(f); VALUE(f::value); TYPE(f::value_type); } /** * @author: Marcelo Juchem <marcelo@fb.com> */ LESSON( "convenience aliases", "This lesson gives an overview on how to reduce verbosity through the use of " "convenience aliases." "\n\n" "Some types will be extensively used throughout the examples in this lesson. " "For instance, `std::integral_constant` for `int` values." "\n\n" "For this reason, let's see how we can shorten the code we write when " "declaring an integral constant through the use of aliases.", template <int Value> using int_value = std::integral_constant<int, Value>; ) { COMMENT( "Let's start by going the verbose route and fully specifying `x` as an " "`std::integral_constant`." ); CODE( using x = std::integral_constant<int, 10>; ); TYPE(x); VALUE(x::value); COMMENT( "Now let's use the convenient alias `int_value` to declare the same thing." ); CODE( using y = int_value<10>; ); TYPE(y); VALUE(y::value); COMMENT( "The beauty of aliases is that they don't create new types. Instead, " "they're just shortcuts to existing types. For instance, by checking the " "output of this lesson, it's easy to see that both `x` and `y` reference " "exactly the same type: `std::integral_constant<int, 10>`." "\n\n" "The code below will be further explained in a later lesson. For now, it " "suffices to know that it will prevent the program from compiling " "if both `x` and `y` do not represent the same type." "\n\n" "This means that, if the line below doesn't result in a compilation error, " "then both `x` and `y` are guaranteed to reference the same type." ); CODE( static_assert(std::is_same<x, y>::value, "type mismatch"); ); } } // namespace lesson {