-
Notifications
You must be signed in to change notification settings - Fork 216
/
tutorial.hpp
4196 lines (3200 loc) · 192 KB
/
tutorial.hpp
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright Louis Dionne 2013-2022
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
/*!
@mainpage User Manual
@tableofcontents
@section tutorial-description Description
------------------------------------------------------------------------------
Hana is a header-only library for C++ metaprogramming suited for computations
on both types and values. The functionality it provides is a superset of what
is provided by the well established [Boost.MPL][] and [Boost.Fusion][]
libraries. By leveraging C++11/14 implementation techniques and idioms,
Hana boasts faster compilation times and runtime performance on par or better
than previous metaprogramming libraries, while noticeably increasing the level
of expressiveness in the process. Hana is easy to extend in a ad-hoc manner
and it provides out-of-the-box inter-operation with Boost.Fusion, Boost.MPL
and the standard library.
@section tutorial-installation Prerequisites and installation
------------------------------------------------------------------------------
Hana is a header-only library without external dependencies (not even the rest
of Boost). Hence, using Hana in your own project is very easy. Basically, just
download the project and add the `include/` directory to your compiler's header
search path and you are done. However, if you want to cleanly install Hana, you
have a couple of options:
1. __Install Boost__\n
Hana is included in the [Boost][] distribution starting from Boost 1.61.0, so
installing that will give you access to Hana.
2. __Install manually__\n
You can download the code from the official GitHub [repository][Hana.repository]
and install the library manually by issuing the following commands from the root
of the project (requires [CMake][]):
@code{.sh}
mkdir build && cd build
cmake ..
cmake --build . --target install
@endcode
This will install Hana to the default install-directory for your platform
(`/usr/local` for Unix, `C:/Program Files` for Windows). If you want to
install Hana in a custom location, you can use
@code{.sh}
cmake .. -DCMAKE_INSTALL_PREFIX=/custom/install/prefix
@endcode
If you just want to contribute to Hana, you can see how to best setup your
environment for development in the [README][Hana.hacking].
@note
Do not mix a standalone installation of Hana (i.e. Hana not installed through
Boost) with a full installation of Boost. The Hana provided within Boost and
the standalone one may clash, and you won't know which version is used where.
This is asking for trouble.
@subsection tutorial-installation-cmake Note for CMake users
If you use [CMake][], depending on Hana has never been so easy. When installed
manually, Hana creates a `HanaConfig.cmake` file that exports the `hana`
interface library target with all the required settings. All you need is to
install Hana manually with CMake, use `find_package(Hana)`, and then link your
own targets against the `hana` target. Here is a minimal example of doing this:
@snippet example/cmake_integration/CMakeLists.txt snip
If you have installed Hana in a non-standard place, you might need to play with
`CMAKE_PREFIX_PATH`. For example, this can happen if you "manually" install
Hana locally to another project. In this case, you'll need to tell CMake where
to find the `HanaConfig.cmake` file by using
@code{cmake}
list(APPEND CMAKE_PREFIX_PATH "${INSTALLATION_PREFIX_FOR_HANA}")
or
cmake ... -DCMAKE_PREFIX_PATH=${INSTALLATION_PREFIX_FOR_HANA}
@endcode
where `INSTALLATION_PREFIX_FOR_HANA` is the path to the place where Hana was
installed.
@subsection tutorial-installation-requirements Compiler requirements
The library relies on a C++14 compiler and standard library, but nothing else
is required. However, we only guarantee support for the compilers listed
below, which are tested on an ongoing basis:
Compiler/Toolchain | Status
------------------ | ------
Clang >= 7 | Fully working; tested on each push to GitHub
Xcode >= 11 | Fully working; tested on each push to GitHub
GCC >= 8 | Fully working; tested on each push to GitHub
VS2017 >= Update 7 | Fully working; tested on each push to GitHub
More specifically, Hana requires a compiler/standard library supporting the
following C++14 features (non-exhaustively):
- Generic lambdas
- Generalized `constexpr`
- Variable templates
- Automatically deduced return type
- All the C++14 type traits from the `<type_traits>` header
Using a compiler not listed above may work, but support for such compilers is
not guaranteed. More information for specific platforms is available on
[the wiki][Hana.wiki].
@section tutorial-support Support
------------------------------------------------------------------------------
If you have a problem, please review the [FAQ](@ref tutorial-rationales) and
[the wiki][Hana.wiki]. Searching [the issues][Hana.issues] for your problem is
also a good idea. If that doesn't help, feel free to chat with us in [Gitter][Hana.chat],
or open a new issue. [StackOverflow][] with the [boost-hana][Hana.StackOverflow]
tag is the preferred place to ask questions on usage. If you are encountering
what you think is a bug, please open an issue.
@section tutorial-introduction Introduction
------------------------------------------------------------------------------
When Boost.MPL first appeared, it provided C++ programmers with a huge relief
by abstracting tons of template hackery behind a workable interface. This
breakthrough greatly contributed to making C++ template metaprogramming more
mainstream, and today the discipline is deeply rooted in many serious projects.
Recently, C++11 and C++14 brought many major changes to the language, some of
which make metaprogramming much easier, while others drastically widen the
design space for libraries. A natural question then arises: is it still
desirable to have abstractions for metaprogramming, and if so, which ones?
After investigating different options like the [MPL11][], the answer eventually
came by itself in the form of a library; Hana. The key insight to Hana is that
the manipulation of types and values are nothing but two sides of the same
coin. By unifying both concepts, metaprogramming becomes easier and new
exciting possibilities open before us.
@subsection tutorial-introduction-quadrants C++ computational quadrants
But to really understand what is Hana all about, it is essential to understand
the different types of computations in C++. We will focus our attention on
four different kinds of computations, even though a finer grained separation
would be possible. First, we have runtime computations, which are the usual
computations we use in C++. In that world, we have runtime containers,
runtime functions and runtime algorithms:
@snippet example/tutorial/introduction.cpp runtime
The usual toolbox for programming within this quadrant is the C++ standard
library, which provides reusable algorithms and containers operating at
runtime. Since C++11, a second kind of computation is possible: `constexpr`
computations. There, we have `constexpr` containers, `constexpr` functions
and `constexpr` algorithms:
@code{cpp}
constexpr int factorial(int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
template <typename T, std::size_t N, typename F>
constexpr std::array<std::invoke_result_t<F, T>, N>
transform(std::array<T, N> array, F f) {
// ...
}
constexpr std::array<int, 4> ints{{1, 2, 3, 4}};
constexpr std::array<int, 4> facts = transform(ints, factorial);
static_assert(facts == std::array<int, 4>{{1, 2, 6, 24}}, "");
@endcode
@note
For the above code to actually work, `std::array`'s `operator==` would have to
be marked `constexpr`, which is not the case (even in C++14).
Basically, a `constexpr` computation is different from a runtime computation
in that it is simple enough to be evaluated (interpreted, really) by the
compiler. In general, any function that does not perform anything too
_unfriendly_ to the compiler's evaluator (like throwing or allocating memory)
can be marked `constexpr` without any further change. This makes `constexpr`
computations very similar to runtime computations, except `constexpr`
computations are more restricted and they gain the ability to be evaluated
at compile-time. Unfortunately, there is no commonly used toolbox for
`constexpr`-programming, i.e. there is no widely adopted "standard library"
for `constexpr` programming. However, the [Sprout][] library may be worth
checking out for those with some interest in `constexpr` computations.
The third kind of computations are heterogeneous computations. Heterogeneous
computations differ from normal computations in that instead of having
containers holding homogeneous objects (all objects having the same type),
the containers may hold objects with different types. Furthermore, functions
in this quadrant of computation are _heterogeneous_ functions, which is a
complicated way of talking about template functions. Similarly, we have
heterogeneous algorithms that manipulate heterogeneous containers and
functions:
@snippet example/tutorial/introduction.cpp heterogeneous
If manipulating heterogeneous containers seems overly weird to you, just think
of it as glorified `std::tuple` manipulation. In a C++03 world, the go-to
library for doing this kind of computation is [Boost.Fusion][], which provides
several data structures and algorithms to manipulate heterogeneous collections
of data. The fourth and last quadrant of computation that we'll be considering
here is the quadrant of type-level computations. In this quadrant, we have
type-level containers, type-level functions (usually called metafunctions)
and type-level algorithms. Here, everything operates on types: containers hold
types and metafunctions take types as arguments and return types as results.
@snippet example/tutorial/introduction.cpp type-level
The realm of type-level computations has been explored quite extensively, and
the de-facto solution for type-level computations in C++03 is a library named
[Boost.MPL][], which provides type-level containers and algorithms. For
low-level type transformations, the metafunctions provided by the
`<type_traits>` standard header can also be used since C++11.
@subsection tutorial-quadrants-about What is this library about?
So all is good, but what is this library actually about? Now that we have set
the table by clarifying the kinds of computations available to us in C++, the
answer might strike you as very simple. __The purpose of Hana is to merge the
3rd and the 4th quadrants of computation__. More specifically, Hana is a
(long-winded) constructive proof that heterogeneous computations are strictly
more powerful than type-level computations, and that we can therefore express
any type-level computation by an equivalent heterogeneous computation. This
construction is done in two steps. First, Hana is a fully featured library of
heterogeneous algorithms and containers, a bit like a modernized Boost.Fusion.
Secondly, Hana provides a way of translating any type-level computation into its
equivalent heterogeneous computation and back, which allows the full machinery
of heterogeneous computations to be reused for type-level computations without
any code duplication. Of course, the biggest advantage of this unification is
seen by the user, as you will witness by yourself.
@section tutorial-quickstart Quick start
------------------------------------------------------------------------------
The goal of this section is to introduce the main concepts of the library
from a very high level and at a fairly rapid pace; don't worry if you don't
understand everything that's about to be thrown at you. However, this tutorial
assumes the reader is already at least _familiar_ with basic metaprogramming
and the [C++14 standard][C++14]. First, let's include the library:
@snippet example/tutorial/quickstart.cpp includes
Unless specified otherwise, the documentation assumes the above lines to be
present before examples and code snippets. Also note that finer grained
headers are provided and will be explained in the [Header organization]
(@ref tutorial-header_organization) section. For the purpose of the
quickstart, let's now include some additional headers and define some
lovely animal types that we'll need below:
@snippet example/tutorial/quickstart.cpp additional_setup
If you are reading this documentation, chances are you already know
`std::tuple` and `std::make_tuple`. Hana provides its own tuple and
`make_tuple`:
@snippet example/tutorial/quickstart.cpp animals
This creates a tuple, which is like an array, except that it can hold elements
with different types. Containers that can hold elements with different types
such as this are called heterogeneous containers. While the standard library
provides very few operations to manipulate `std::tuple`s, Hana provides several
operations and algorithms to manipulate its own tuples:
@snippet example/tutorial/quickstart.cpp algorithms
@note
`1_c` is a [C++14 user-defined literal][C++14.udl] creating a
[compile-time number](@ref tutorial-integral). These user-defined
literals are contained in the `boost::hana::literals` namespace,
hence the `using` directive.
Notice how we pass a [C++14 generic lambda][C++14.glambda] to `transform`;
this is required because the lambda will first be called with a `Fish`, then
a `Cat`, and finally a `Dog`, which all have different types. Hana provides
most of the algorithms provided by the C++ standard library, except they work
on tuples and related heterogeneous containers instead of `std::vector` &
friends. In addition to working with heterogeneous values, Hana makes it
possible to perform type-level computations with a natural syntax, all at
compile-time and with no overhead whatsoever. This compiles and does just
what you would expect:
@snippet example/tutorial/quickstart.cpp type-level
@note
`type_c<...>` is not a type! It is a [C++14 variable template][C++14.vtemplate]
yielding an object representing a type for Hana. This is explained in the
section on [type computations](@ref tutorial-type).
In addition to heterogeneous and compile-time sequences, Hana provides several
features to make your metaprogramming nightmares a thing of the past. For
example, one can check for the existence of a struct member with one easy
line instead of relying on [clunky SFINAE hacks][SO.sfinae]:
@snippet example/tutorial/quickstart.cpp has_name
Writing a serialization library? Stop crying, we've got you covered.
Reflection can be added to user-defined types very easily. This allows
iterating over the members of a user-defined type, querying members with
a programmatic interface and much more, without any runtime overhead:
@snippet example/tutorial/quickstart.cpp serialization
That's cool, but I can already hear you complaining about incomprehensible
error messages. However, it turns out Hana was built for humans, not
professional template metaprogrammers, and this shows. Let's intentionally
screw up and see what kind of mess is thrown at us. First, the mistake:
@snippet example/tutorial/quickstart.cpp screw_up
Now, the punishment:
@code{cpp}
error: static_assert failed "hana::for_each(xs, f) requires 'xs' to be Foldable"
static_assert(Foldable<S>::value,
^ ~~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization
'boost::hana::for_each_t::operator()<
std::__1::basic_ostream<char> &, (lambda at [snip])>' requested here
hana::for_each(os, [&](auto member) {
^
note: in instantiation of function template specialization
'main()::(anonymous class)::operator()<Person>' requested here
serialize(std::cout, john);
^
@endcode
Not that bad, right? However, since small examples are very good to show off
without actually doing something useful, let's examine a real world example.
@subsection tutorial-quickstart-any A real world example
In this section our goal will be to implement a kind of `switch` statement
able to process `boost::any`s. Given a `boost::any`, the goal is to dispatch
to the function associated to the dynamic type of the `any`:
@snippet example/tutorial/quickstart.switchAny.cpp usage
@note
In the documentation, we will often use the `s` suffix on string literals to
create `std::string`s without syntactic overhead. This is a standard-defined
[C++14 user-defined literal][C++14.udl].
Since the any holds a `char`, the second function is called with the `char`
inside it. If the `any` had held an `int` instead, the first function would
have been called with the `int` inside it. When the dynamic type of the `any`
does not match any of the covered cases, the `%default_` function is called
instead. Finally, the result of the `switch` is the result of calling the
function associated to the `any`'s dynamic type. The type of that result is
inferred to be the common type of the result of all the provided functions:
@snippet example/tutorial/quickstart.switchAny.cpp result_inference
We'll now look at how this utility can be implemented using Hana. The first
step is to associate each type to a function. To do so, we represent each
`case_` as a `hana::pair` whose first element is a type and whose second
element is a function. Furthermore, we (arbitrarily) decide to represent the
`%default_` case as a `hana::pair` mapping a dummy type to a function:
@snippet example/tutorial/quickstart.switchAny.cpp cases
To provide the interface we showed above, `switch_` will have to return a
function taking the cases. In other words, `switch_(a)` must be a function
taking any number of cases (which are `hana::pair`s), and performing the logic
to dispatch `a` to the right function. This can easily be achieved by having
`switch_` return a C++14 generic lambda:
@code{cpp}
template <typename Any>
auto switch_(Any& a) {
return [&a](auto ...cases_) {
// ...
};
}
@endcode
However, since parameter packs are not very flexible, we'll put the cases
into a tuple so we can manipulate them:
@code{cpp}
template <typename Any>
auto switch_(Any& a) {
return [&a](auto ...cases_) {
auto cases = hana::make_tuple(cases_...);
// ...
};
}
@endcode
Notice how the `auto` keyword is used when defining `cases`; it is often
easier to let the compiler deduce the type of the tuple and use `make_tuple`
instead of working out the types manually. The next step is to separate the
default case from the rest of the cases. This is where things start to get
interesting. To do so, we use Hana's `find_if` algorithm, which works a bit
like `std::find_if`:
@code{cpp}
template <typename Any>
auto switch_(Any& a) {
return [&a](auto ...cases_) {
auto cases = hana::make_tuple(cases_...);
auto default_ = hana::find_if(cases, [](auto const& c) {
return hana::first(c) == hana::type_c<default_t>;
});
// ...
};
}
@endcode
`find_if` takes a `tuple` and a predicate, and returns the first element of
the tuple which satisfies the predicate. The result is returned as a
`hana::optional`, which is very similar to a `std::optional`, except
whether that optional value is empty or not is known at compile-time. If the
predicate is not satisfied for any element of the `tuple`, `find_if` returns
`nothing` (an empty value). Otherwise, it returns `just(x)` (a non-empty value),
where `x` is the first element satisfying the predicate. Unlike predicates
used in STL algorithms, the predicate used here must be generic because the
tuple's elements are heterogeneous. Furthermore, that predicate must return
what Hana calls an `IntegralConstant`, which means that the predicate's result
must be known at compile-time. These details are explained in the section on
[cross-phase algorithms](@ref tutorial-algorithms-cross_phase). Inside the
predicate, we simply compare the type of the case's first element to
`type_c<default_t>`. If you recall that we were using `hana::pair`s to encode
cases, this simply means that we're finding the default case among all of the
provided cases. But what if no default case was provided? We should fail at
compile-time, of course!
@code{cpp}
template <typename Any>
auto switch_(Any& a) {
return [&a](auto ...cases_) {
auto cases = hana::make_tuple(cases_...);
auto default_ = hana::find_if(cases, [](auto const& c) {
return hana::first(c) == hana::type_c<default_t>;
});
static_assert(default_ != hana::nothing,
"switch is missing a default_ case");
// ...
};
}
@endcode
Notice how we can use `static_assert` on the result of the comparison with
`nothing`, even though `%default_` is a non-`constexpr` object? Boldly, Hana
makes sure that no information that's known at compile-time is lost to the
runtime, which is clearly the case of the presence of a `%default_` case.
The next step is to gather the set of non-default cases. To achieve this, we
use the `filter` algorithm, which keeps only the elements of the sequence
satisfying the predicate:
@code{cpp}
template <typename Any>
auto switch_(Any& a) {
return [&a](auto ...cases_) {
auto cases = hana::make_tuple(cases_...);
auto default_ = hana::find_if(cases, [](auto const& c) {
return hana::first(c) == hana::type_c<default_t>;
});
static_assert(default_ != hana::nothing,
"switch is missing a default_ case");
auto rest = hana::filter(cases, [](auto const& c) {
return hana::first(c) != hana::type_c<default_t>;
});
// ...
};
}
@endcode
The next step is to find the first case matching the dynamic type of the `any`,
and then call the function associated to that case. The simplest way to do this
is to use classic recursion with variadic parameter packs. Of course, we could
probably intertwine Hana algorithms in a convoluted way to achieve this, but
sometimes the best way to do something is to write it from scratch using basic
techniques. To do so, we'll call an implementation function with the contents
of the `rest` tuple by using the `unpack` function:
@snippet example/tutorial/quickstart.switchAny.cpp switch_
`unpack` takes a `tuple` and a function, and calls the function with the content
of the `tuple` as arguments. The result of `unpack` is the result of calling that
function. In our case, the function is a generic lambda which in turn calls the
`process` function. Our reason for using `unpack` here was to turn the `rest`
tuple into a parameter pack of arguments, which are easier to process recursively
than tuples. Before we move on to the `process` function, it is worthwhile to
explain what `second(*%default_)` is all about. As we explained earlier,
`%default_` is an optional value. Like `std::optional`, this optional value
overloads the dereference operator (and the arrow operator) to allow accessing
the value inside the `optional`. If the optional is empty (`nothing`), a
compile-time error is triggered. Since we know `%default_` is not empty (we
checked that just above), what we're doing is simply pass the function
associated to the default case to the `process` function. We're now ready
for the final step, which is the implementation of the `process` function:
@snippet example/tutorial/quickstart.switchAny.cpp process
There are two overloads of this function: an overload for when there is at least
one case to process, and the base case overload for when there's only the default
case. As we would expect, the base case simply calls the default function and
returns that result. The other overload is slightly more interesting. First,
we retrieve the type associated to that case and store it in `T`. This
`decltype(...)::%type` dance might seem convoluted, but it is actually quite
simple. Roughly speaking, this takes a type represented as an object (a `type<T>`)
and pulls it back down to the type level (a `T`). The details are explained in
the section on [type-level computations](@ref tutorial-type). Then, we compare
whether the dynamic type of the `any` matches this case, and if so we call the
function associated to this case with the `any` casted to the proper type.
Otherwise, we simply call `process` recursively with the rest of the cases.
Pretty simple, wasn't it? Here's the final solution:
@snippet example/tutorial/quickstart.switchAny.cpp full
That's it for the quick start! This example only introduced a couple of useful
algorithms (`find_if`, `filter`, `unpack`) and heterogeneous containers
(`tuple`, `optional`), but rest assured that there is much more. The next
sections of the tutorial gradually introduce general concepts pertaining to
Hana in a friendly way, but you may use the following cheatsheet for quick
reference if you want to start coding right away. This cheatsheet contains
the most frequently used algorithms and containers, along with a short
description of what each of them does.
@section tutorial-cheatsheet Cheatsheet
### Remarks
- Most algorithms work on both types and values (see the section on
[type computations](@ref tutorial-type))
- Algorithms always return their result as a new container; no in-place
mutation is ever performed (see the section on [algorithms]
(@ref tutorial-algorithms))
- All algorithms are `constexpr` function objects
container | description
:----------------- | :----------
<code>[tuple](@ref boost::hana::tuple)</code> | General purpose index-based heterogeneous sequence with a fixed length. Use this as a `std::vector` for heterogeneous objects.
<code>[optional](@ref boost::hana::optional)</code> | Represents an optional value, i.e. a value that can be empty. This is a bit like `std::optional`, except that the emptiness is known at compile-time.
<code>[map](@ref boost::hana::map)</code> | Unordered associative array mapping (unique) compile-time entities to arbitrary objects. This is like `std::unordered_map` for heterogeneous objects.
<code>[set](@ref boost::hana::set)</code> | Unordered container holding unique keys that must be compile-time entities. This is like `std::unordered_set` for heterogeneous objects.
<code>[range](@ref boost::hana::range)</code> | Represents an interval of compile-time numbers. This is like `std::integer_sequence`, but better.
<code>[pair](@ref boost::hana::pair)</code> | Container holding two heterogeneous objects. Like `std::pair`, but compresses the storage of empty types.
<code>[string](@ref boost::hana::string)</code> | Compile-time string.
<code>[type](@ref boost::hana::type)</code> | Container representing a C++ type. This is the root of the unification between types and values, and is of interest for MPL-style computations (type-level computations).
<code>[integral_constant](@ref boost::hana::integral_constant)</code> | Represents a compile-time number. This is very similar to `std::integral_constant`, except that `hana::integral_constant` also defines operators and more syntactic sugar.
<code>[lazy](@ref boost::hana::lazy)</code> | Encapsulates a lazy value or computation.
<code>[basic_tuple](@ref boost::hana::basic_tuple)</code> | Stripped-down version of `hana::tuple`. Not standards conforming, but more compile-time efficient.
function | description
:------------------------------------------ | :----------
<code>[adjust](@ref ::boost::hana::adjust)(sequence, value, f)</code> | Apply a function to each element of a sequence that compares equal to some value and return the result.
<code>[adjust_if](@ref ::boost::hana::adjust_if)(sequence, predicate, f)</code> | Apply a function to each element of a sequence satisfying some predicate and return the result.
<code>{[all](@ref ::boost::hana::all),[any](@ref ::boost::hana::any),[none](@ref ::boost::hana::none)}(sequence)</code> | Returns whether all/any/none of the elements of a sequence are true-valued.
<code>{[all](@ref ::boost::hana::all_of),[any](@ref ::boost::hana::any_of),[none](@ref ::boost::hana::none_of)}_of(sequence, predicate)</code> | Returns whether all/any/none of the elements of the sequence satisfy some predicate.
<code>[append](@ref ::boost::hana::append)(sequence, value)</code> | Append an element to a sequence.
<code>[at](@ref ::boost::hana::at)(sequence, index)</code> | Returns the n-th element of a sequence. The index must be an `IntegralConstant`.
<code>[back](@ref ::boost::hana::back)(sequence)</code> | Returns the last element of a non-empty sequence.
<code>[concat](@ref ::boost::hana::concat)(sequence1, sequence2)</code> | Concatenate two sequences.
<code>[contains](@ref ::boost::hana::contains)(sequence, value)</code> | Returns whether a sequence contains the given object.
<code>[count](@ref ::boost::hana::count)(sequence, value)</code> | Returns the number of elements that compare equal to the given value.
<code>[count_if](@ref ::boost::hana::count_if)(sequence, predicate)</code> | Returns the number of elements that satisfy the predicate.
<code>[drop_front](@ref ::boost::hana::drop_front)(sequence[, n])</code> | Drop the first `n` elements from a sequence, or the whole sequence if `length(sequence) <= n`. `n` must be an `IntegralConstant`. When not provided, `n` defaults to 1.
<code>[drop_front_exactly](@ref ::boost::hana::drop_front_exactly)(sequence[, n])</code> | Drop the first `n` elements from a sequence. `n` must be an `IntegralConstant` and the sequence must have at least `n` elements. When not provided, `n` defaults to 1.
<code>[drop_back](@ref ::boost::hana::drop_back)(sequence[, n])</code> | Drop the last `n` elements from a sequence, or the whole sequence if `length(sequence) <= n`. `n` must be an `IntegralConstant`. When not provided, `n` defaults to 1.
<code>[drop_while](@ref ::boost::hana::drop_while)(sequence, predicate)</code> | Drops elements from a sequence while a predicate is satisfied. The predicate must return an `IntegralConstant`.
<code>[fill](@ref ::boost::hana::fill)(sequence, value)</code> | Replace all the elements of a sequence with some value.
<code>[filter](@ref ::boost::hana::filter)(sequence, predicate)</code> | Remove all the elements that do not satisfy a predicate. The predicate must return an `IntegralConstant`.
<code>[find](@ref ::boost::hana::find)(sequence, value)</code> | Find the first element of a sequence which compares equal to some value and return `just` it, or return `nothing`. See `hana::optional`.
<code>[find_if](@ref ::boost::hana::find_if)(sequence, predicate)</code> | Find the first element of a sequence satisfying the predicate and return `just` it, or return `nothing`. See `hana::optional`.
<code>[flatten](@ref ::boost::hana::flatten)(sequence)</code> | Flatten a sequence of sequences, a bit like `std::tuple_cat`.
<code>[fold_left](@ref ::boost::hana::fold_left)(sequence[, state], f)</code> | Accumulates the elements of a sequence from the left, optionally with a provided initial state.
<code>[fold_right](@ref ::boost::hana::fold_right)(sequence[, state], f)</code> | Accumulates the elements of a sequence from the right, optionally with a provided initial state.
<code>[fold](@ref ::boost::hana::fold)(sequence[, state], f)</code> | Equivalent to `fold_left`; provided for consistency with Boost.MPL and Boost.Fusion.
<code>[for_each](@ref ::boost::hana::for_each)(sequence, f)</code> | Call a function on each element of a sequence. Returns `void`.
<code>[front](@ref ::boost::hana::front)(sequence)</code> | Returns the first element of a non-empty sequence.
<code>[group](@ref ::boost::hana::group)(sequence[, predicate])</code> | %Group adjacent elements of a sequence which all satisfy (or all do not satisfy) some predicate. The predicate defaults to equality, in which case the elements must be `Comparable`.
<code>[index_if](@ref ::boost::hana::index_if)(sequence, predicate)</code> | Find the index of the first element in a sequence satisfying the predicate and return `just` it, or return `nothing`. See `hana::optional`.
<code>[insert](@ref ::boost::hana::insert)(sequence, index, element)</code> | Insert an element at a given index. The index must be an `IntegralConstant`.
<code>[insert_range](@ref ::boost::hana::insert_range)(sequence, index, elements)</code> | Insert a sequence of elements at a given index. The index must be an `IntegralConstant`.
<code>[is_empty](@ref ::boost::hana::is_empty)(sequence)</code> | Returns whether a sequence is empty as an `IntegralConstant`.
<code>[length](@ref ::boost::hana::length)(sequence)</code> | Returns the length of a sequence as an `IntegralConstant`.
<code>[lexicographical_compare](@ref ::boost::hana::lexicographical_compare)(sequence1, sequence2[, predicate])</code> | Performs a lexicographical comparison of two sequences, optionally with a custom predicate, by default with `hana::less`.
<code>[maximum](@ref ::boost::hana::maximum)(sequence[, predicate])</code> | Returns the greatest element of a sequence, optionally according to a predicate. The elements must be `Orderable` if no predicate is provided.
<code>[minimum](@ref ::boost::hana::minimum)(sequence[, predicate])</code> | Returns the smallest element of a sequence, optionally according to a predicate. The elements must be `Orderable` if no predicate is provided.
<code>[partition](@ref ::boost::hana::partition)(sequence, predicate)</code> | Partition a sequence into a pair of elements that satisfy some predicate, and elements that do not satisfy it.
<code>[prepend](@ref ::boost::hana::prepend)(sequence, value)</code> | Prepend an element to a sequence.
<code>[remove](@ref ::boost::hana::remove)(sequence, value)</code> | Remove all the elements that are equal to a given value.
<code>[remove_at](@ref ::boost::hana::remove_at)(sequence, index)</code> | Remove the element at the given index. The index must be an `IntegralConstant`.
<code>[remove_if](@ref ::boost::hana::remove_if)(sequence, predicate)</code> | Remove all the elements that satisfy a predicate. The predicate must return an `IntegralConstant`.
<code>[remove_range](@ref ::boost::hana::remove_range)(sequence, from, to)</code> | Remove the elements at indices in the given `[from, to)` half-open interval. The indices must be `IntegralConstant`s.
<code>[replace](@ref ::boost::hana::replace)(sequence, oldval, newval)</code> | Replace the elements of a sequence that compare equal to some value by some other value.
<code>[replace_if](@ref ::boost::hana::replace_if)(sequence, predicate, newval)</code> | Replace the elements of a sequence that satisfy some predicate by some value.
<code>[reverse](@ref ::boost::hana::reverse)(sequence)</code> | Reverse the order of the elements in a sequence.
<code>[reverse_fold](@ref ::boost::hana::reverse_fold)(sequence[, state], f)</code> | Equivalent to `fold_right`; provided for consistency with Boost.MPL and Boost.Fusion.
<code>[size](@ref ::boost::hana::size)(sequence)</code> | Equivalent to `length`; provided for consistency with the C++ standard library.
<code>[slice](@ref ::boost::hana::slice)(sequence, indices)</code> | Returns a new sequence containing the elements at the given indices of the original sequence.
<code>[slice_c](@ref ::boost::hana::slice_c)<from, to>(sequence)</code> | Returns a new sequence containing the elements at indices contained in `[from, to)` of the original sequence.
<code>[sort](@ref ::boost::hana::sort)(sequence[, predicate])</code> | Sort (stably) the elements of a sequence, optionally according to a predicate. The elements must be `Orderable` if no predicate is provided.
<code>[take_back](@ref ::boost::hana::take_back)(sequence, number)</code> | Take the last n elements of a sequence, or the whole sequence if `length(sequence) <= n`. n must be an `IntegralConstant`.
<code>[take_front](@ref ::boost::hana::take_front)(sequence, number)</code> | Take the first n elements of a sequence, or the whole sequence if `length(sequence) <= n`. n must be an `IntegralConstant`.
<code>[take_while](@ref ::boost::hana::take_while)(sequence, predicate)</code> | Take elements of a sequence while some predicate is satisfied, and return that.
<code>[transform](@ref ::boost::hana::transform)(sequence, f)</code> | Apply a function to each element of a sequence and return the result.
<code>[unique](@ref ::boost::hana::unique)(sequence[, predicate])</code> | Removes all consecutive duplicates from a sequence. The predicate defaults to equality, in which case the elements must be `Comparable`.
<code>[unpack](@ref ::boost::hana::unpack)(sequence, f)</code> | Calls a function with the contents of a sequence. Equivalent to `f(x1, ..., xN)`.
<code>[zip](@ref ::boost::hana::zip)(s1, ..., sN)</code> | Zip `N` sequences into a sequence of tuples. All the sequences must have the same length.
<code>[zip_shortest](@ref ::boost::hana::zip_shortest)(s1, ..., sN)</code> | Zip `N` sequences into a sequence of tuples. The resulting sequence has the length of the shortest input sequence.
<code>[zip_with](@ref ::boost::hana::zip_with)(f, s1, ..., sN)</code> | Zip `N` sequences with a `N`-ary function. All the sequences must have the same length.
<code>[zip_shortest_with](@ref ::boost::hana::zip_shortest_with)(f, s1, ..., sN)</code> | Zip `N` sequences with a `N`-ary function. The resulting sequence has the length of the shortest input sequence.
@section tutorial-assert Assertions
------------------------------------------------------------------------------
In the rest of this tutorial, you will come across code snippets where different
kinds of assertions like `BOOST_HANA_RUNTIME_CHECK` and `BOOST_HANA_CONSTANT_CHECK`
are used. Like any sensible `assert` macro, they basically check that the
condition they are given is satisfied. However, in the context of heterogeneous
programming, some informations are known at compile-time, while others are known
only at runtime. The exact type of assertion that's used in a context tells you
whether the condition that's asserted upon can be known at compile-time or if it
must be computed at runtime, which is a very precious piece of information. Here
are the different kinds of assertions used in the tutorial, with a small
description of their particularities. For more details, you should check
the [reference on assertions](@ref group-assertions).
assertion | description
:--------------------------- | :----------
`BOOST_HANA_RUNTIME_CHECK` | Assertion on a condition that is not known until runtime. This assertion provides the weakest form of guarantee.
`BOOST_HANA_CONSTEXPR_CHECK` | Assertion on a condition that would be `constexpr` if lambdas were allowed inside constant expressions. In other words, the only reason for it not being a `static_assert` is the language limitation that lambdas can't appear in constant expressions, which [might be lifted][N4487] in C++17.
`static_assert` | Assertion on a `constexpr` condition. This is stronger than `BOOST_HANA_CONSTEXPR_CHECK` in that it requires the condition to be a constant expression, and it hence assures that the algorithms used in the expression are `constexpr`-friendly.
`BOOST_HANA_CONSTANT_CHECK` | Assertion on a boolean `IntegralConstant`. This assertion provides the strongest form of guarantee, because an `IntegralConstant` can be converted to a `constexpr` value even if it is not `constexpr` itself.
@section tutorial-integral Compile-time numbers
------------------------------------------------------------------------------
This section introduces the important notion of `IntegralConstant` and the
philosophy behind Hana's metaprogramming paradigm. Let's start with a rather
odd question. What is an `integral_constant`?
@code{cpp}
template<class T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
@endcode
@note
If this is totally new to you, you might want to take a look at the
[documentation][C++14.ice] for `std::integral_constant`.
One valid answer is that `integral_constant` represents a type-level
encoding of a number, or more generally any object of an integral type.
For illustration, we could define a successor function on numbers in that
representation very easily by using a template alias:
@code{cpp}
template <typename N>
using succ = integral_constant<int, N::value + 1>;
using one = integral_constant<int, 1>;
using two = succ<one>;
using three = succ<two>;
// ...
@endcode
This is the way `integral_constant`s are usually thought of; as _type-level_
entities that can be used for template metaprogramming. Another way to see
an `integral_constant` is as a runtime object representing a `constexpr` value
of an integral type:
@code{cpp}
auto one = integral_constant<int, 1>{};
@endcode
Here, while `one` is not marked as `constexpr`, the abstract value it holds
(a `constexpr 1`) is still available at compile-time, because that value is
encoded in the type of `one`. Indeed, even if `one` is not `constexpr`, we
can use `decltype` to retrieve the compile-time value it represents:
@code{cpp}
auto one = integral_constant<int, 1>{};
constexpr int one_constexpr = decltype(one)::value;
@endcode
But why on earth would we want to consider `integral_constant`s as objects
instead of type-level entities? To see why, consider how we could now
implement the same successor function as before:
@code{cpp}
template <typename N>
auto succ(N) {
return integral_constant<int, N::value + 1>{};
}
auto one = integral_constant<int, 1>{};
auto two = succ(one);
auto three = succ(two);
// ...
@endcode
Did you notice anything new? The difference is that instead of implementing
`succ` at the type-level with a template alias, we're now implementing it at
the value-level with a template function. Furthermore, we can now perform
compile-time arithmetic using the same syntax as that of normal C++. This
way of seeing compile-time entities as objects instead of types is the key
to Hana's expressive power.
@subsection tutorial-integral-arithmetic Compile-time arithmetic
The MPL defines [arithmetic operators][MPL.arithmetic] that can be used to do
compile-time computations with `integral_constant`s. A typical example of such
an operation is `plus`, which is implemented roughly as:
@code{cpp}
template <typename X, typename Y>
struct plus {
using type = integral_constant<
decltype(X::value + Y::value),
X::value + Y::value
>;
};
using three = plus<integral_constant<int, 1>,
integral_constant<int, 2>>::type;
@endcode
By viewing `integral_constant`s as objects instead of types, the translation
from a metafunction to a function is very straightforward:
@code{cpp}
template <typename V, V v, typename U, U u>
constexpr auto
operator+(integral_constant<V, v>, integral_constant<U, u>)
{ return integral_constant<decltype(v + u), v + u>{}; }
auto three = integral_constant<int, 1>{} + integral_constant<int, 2>{};
@endcode
It is very important to emphasize the fact that this operator does not return
a normal integer. Instead, it returns a value-initialized object whose type
contains the result of the addition. The only useful information contained in
that object is actually in its type, and we're creating an object because it
allows us to use this nice value-level syntax. It turns out that we can make
this syntax even better by using a [C++14 variable template][C++14.vtemplate]
to simplify the creation of an `integral_constant`:
@code{cpp}
template <int i>
constexpr integral_constant<int, i> int_c{};
auto three = int_c<1> + int_c<2>;
@endcode
Now we're talking about a visible gain in expressiveness over the initial
type-level approach, aren't we? But there's more; we can also use
[C++14 user defined literals][C++14.udl] to make this process even simpler:
@code{cpp}
template <char ...digits>
constexpr auto operator"" _c() {
// parse the digits and return an integral_constant
}
auto three = 1_c + 2_c;
@endcode
Hana provides its own `integral_constant`s, which define arithmetic operators
just like we showed above. Hana also provides variable templates to easily
create different kinds of `integral_constant`s: `int_c`, `long_c`, `bool_c`,
etc... This allows you to omit the trailing `{}` braces otherwise required to
value-initialize these objects. Of course, the `_c` suffix is also provided;
it is part of the `hana::literals` namespace, and you must import it into
your namespace before using it:
@code{cpp}
using namespace hana::literals;
auto three = 1_c + 2_c;
@endcode
This way, you may do compile-time arithmetic without having to struggle with
awkward type-level idiosyncrasies, and your coworkers will now be able to
understand what's going on.
@subsection tutorial-integral-distance Example: Euclidean distance
To illustrate how good it gets, let's implement a function computing a 2-D
euclidean distance at compile-time. As a reminder, the euclidean distance of
two points in the 2-D plane is given by
@f[
\mathrm{distance}\left((x_1, y_1), (x_2, y_2)\right)
:= \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}
@f]
First, here's how it looks like with a type-level approach (using the MPL):
@snippet example/tutorial/integral.cpp distance-mpl
Yeah... Now, let's implement it with the value-level approach presented above:
@snippet example/tutorial/integral.cpp distance-hana
This version looks arguably cleaner. However, this is not all. Notice how the
`distance` function looks exactly as the one you would have written for
computing the euclidean distance on dynamic values? Indeed, because we're
using the same syntax for dynamic and compile-time arithmetic, generic
functions written for one will work for both!
@snippet example/tutorial/integral.cpp distance-dynamic
__Without changing any code__, we can use our `distance` function on runtime
values and everything just works. Now that's DRY.
@subsection tutorial-integral-branching Compile-time branching
Once we have compile-time arithmetic, the next thing that might come to mind
is compile-time branching. When metaprogramming, it is often useful to have
one piece of code be compiled if some condition is true, and a different one
otherwise. If you have heard of [static_if][N4461], this should sound very
familiar, and indeed it is exactly what we are talking about. Otherwise, if
you don't know why we might want to branch at compile-time, consider the
following code (adapted from [N4461][]):
@code{cpp}
template <typename T, typename ...Args>
std::enable_if_t<std::is_constructible<T, Args...>::value,
std::unique_ptr<T>> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T, typename ...Args>
std::enable_if_t<!std::is_constructible<T, Args...>::value,
std::unique_ptr<T>> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
@endcode
This code creates a `std::unique_ptr` using the correct form of syntax for the
constructor. To achieve this, it uses [SFINAE][] and requires two different
overloads. Now, anyone sane seeing this for the first time would ask why it
is not possible to simply write:
@code{cpp}
template <typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args&&... args) {
if (std::is_constructible<T, Args...>::value)
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
else
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
@endcode
The reason is that the compiler is required to compile both branches of the
`if` statement, regardless of the condition (even though it is known at
compile-time). But when `T` is _not_ constructible from `Args...`, the second
branch will fail to compile, which will cause a hard compilation error. What
we need really is a way to tell the compiler __not to compile__ the second
branch when the condition is true, and the first branch when the condition is
false.
To emulate this, Hana provides an `if_` function that works a bit like a
normal `if` statement, except except it takes a condition that can be an
`IntegralConstant` and returns the one of two values (which may have
different types) chosen by the condition. If the condition is true, the
first value is returned, and otherwise the second value is returned. A
somewhat vain example is the following:
@code{cpp}
auto one_two_three = hana::if_(hana::true_c, 123, "hello");
auto hello = hana::if_(hana::false_c, 123, "hello");
@endcode
@note
`hana::true_c` and `hana::false_c` are just boolean `IntegralConstant`s
representing a compile-time true value and a compile-time false value,
respectively.
Here, `one_two_three` is equal to `123`, and `hello` is equal to `"hello"`.
In other words, `if_` is a little bit like the ternary conditional operator
`? :`, except that both sides of the `:` can have different types:
@code{cpp}
// fails in both cases because both branches have incompatible types
auto one_two_three = hana::true_c ? 123 : "hello";
auto hello = hana::false_c ? 123 : "hello";
@endcode
Ok, so this is neat, but how can it actually help us write complete branches
that are lazily instantiated by the compiler? The answer is to represent both
branches of the `if` statement we'd like to write as generic lambdas, and to
use `hana::if_` to return the branch that we'd like to execute. Here's how we
could rewrite `make_unique`:
@snippet example/tutorial/integral-branching.cpp make_unique.if_
Here, the first value given to `hana::if_` is a generic lambda representing
the branch we want to execute if the condition is true, and the second value
is the branch we want to execute otherwise. `hana::if_` simply returns the
branch chosen by the condition, and we call that branch (which is a generic
lambda) immediately with `std::forward<Args>(args)...`. Hence, the proper
generic lambda ends up being called, with `x...` being `args...`, and we
return the result of that call.
The reason why this approach works is because the body of each branch can only
be instantiated when the types of all `x...` are known. Indeed, since the
branch is a generic lambda, the type of the argument is not known until the
lambda is called, and the compiler must wait for the types of `x...` to be
known before type-checking the lambda's body. Since the erroneous lambda is
never called when the condition is not satisfied (`hana::if_` takes care of
that), the body of the lambda that would fail is never type-checked and no
compilation error happens.
@note
The branches inside the `if_` are lambdas. As such, they really are different
functions from the `make_unique` function. The variables appearing inside
those branches have to be either captured by the lambdas or passed to them as
arguments, and so they are affected by the way they are captured or passed
(by value, by reference, etc..).
Since this pattern of expressing branches as lambdas and then calling them