Hanais 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.
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:
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:
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
If you have a problem, please review the FAQ and the wiki. Searching the issues for your problem is also a good idea. If that doesn't help, feel free to chat with us in Gitter, or open a new issue. StackOverflow with the boost-hana tag is the preferred place to ask questions on usage. If you are encountering what you think is a bug, please open an issue.
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.
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:
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:
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:
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.
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 standard header can also be used since C++11.
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.
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. First, let's include the library:
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 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:
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::tuples, Hana provides several operations and algorithms to manipulate its own tuples:
Notice how we pass a C++14 generic lambda 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:
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:
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:
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:
In this section our goal will be to implement a kind of switch statement able to process boost::anys. Given a boost::any, the goal is to dispatch to the function associated to the dynamic type of the any:
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:
3a8082e126