Template Metaprogramming with Modern C++: Introduction

Template Metaprogramming with Modern C++: Introduction

Template Metaprogramming with Modern C++: Introduction

Any sufficiently complex C++ code is indistinguishable from trolling

Arthur C. Clarke

Preface

Template metaprogramming is one of the things that makes C++ that complex, poor known, and sometimes horrible language. However, its power and expressiveness are some of the best features of C++.

Extensible and fully generic C++ libraries aren’t possible without template metapogramming. Even the Standard Library implementations hide many template metaprogramming tricks to make standard containers and algorithms that generic, high level, and efficient tools we use everyday.

The fact that tmp is a powerful tool can be seen in the evolution of the language, which now has features designed to improve metaprogramming, see C++11 <type_traits>, C++11 variadic templates, C++14 variable templates, C++14std::integer_sequence, etc.

template metaprogramming

Credit: https://gitorious.org/metatrace

 

But C++ template metaprogramming power comes at a high cost: Its really hard to do and understand. The template system was not originally designed to do such things, and that’s reflected primarily in the cumbersome syntax involved and the cryptic error messages one get when something fails. Those are the reasons why people is usually scared by tmp, and most of us doesn’t even try to use it.

These posts try to introduce template metaprogramming to the average C++ programmer, showing how it works, what can do, and finally leading with its problems trying to make it easier than in the old days of C++98, thanks to C++11 and C++14 language improvements.



But, what is metaprogramming?

From Wikipedia:

Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data, or that do part of the work at compile time that would otherwise be done at run-time.

So instead of writing code that is compiled and does something at run-time (i.e. represents some actions to be done at run-time), we write code (meta-code?) that generates code. Let me show you a simple example:

C parameterized macros could be viewed as metaprogramming functions, metafunctions. That is, a function that takes some parameters and generates C code. If you use that macro:

Please ignore the UB, is just an example.

The C preprocessor parses that macro, interprets its arguments, and returns the code (((a) > (b)) ? (a) : (b)), so the resulting code becomes:

Reflection, the ability of some programming languages to inspect type and code information at runtime and modify it, could be another type of metaprogramming.



C++ Template Metaprogramming

Template metaprogramming, sometimes shorted to tmp, consists in using the C++ template system to generate C++ types, and C++ code in the process.

Consider what a C++ template is: As the name says, it’s only a template. A template function is not a function at all, it is a template to generate functions, and the same for class templates.

That wonderful thing we all love, std::vector, is not a class. Is a template designed to generate a correct vector class for each type. When we instance a template, like std::vector<int>, then the compiler generates the code for a vector of ints, following the template the Standard Library developer provided.

So if we write a template foo parameterized with a type parameter:

and then that template is instanced:

and the compiler generates different versions of the foo struct, one for each different combinations of template parameters:

Note that the generated classes foo_int and foo_char are not written in your source file at all, like what the C preprocessor does. The template instantiation is managed internally by the compiler. I wrote them in that way to make a clear example.

template metaprogramming tree diagram

Credit: http://plc.inf.elte.hu/templight/

As you can see, the C++ template system actually generates code. We, as C++ metaprogrammers, explode this to generate some code automatically.



Metafunctions

In the C preprocessor example, we introduced the concept of metafunction. In general a metafunction is a function working in the specific metaprogramming domain we are. In the case of C preprocessor, we manipulate C sourcecode explicitly, so its metafunctions (macros) take and manipulate C source.

In C++ template metaprogramming we work with types, so a metafunction is a function working with types. C++ templates could take non-type parameters too, but its hard to be generic using heterogeneous categories of template parameters. Instead, we will work with type parameters only whenever possible.

The template identity is a metafunction representing the identity function: Takes a value (Actually a type, since we work with types) and returns itself untouched.

We can “call” that metafunction referencing its member type type:

Of course nested metafunction calls are possible:

But that typename ::type syntax doesn’t scale well. Consider a more complex example:

There are a few possible solutions to this problem:

Use aliases to the result instead of the metafunction itself

Since C++11 we have template aliases, a kind of parametrized typedef. We can use them to write user-side metafunctions:

Where add is the metafunction for the user, and impl::add is the class template that actually implements the metafunction. This allows us to write nested expressions in a clear way:

 

Build an expression evaluation system

The above approach hides the machinery to the user. But hidding means that those user side metafunctions are not metafunctions but aliases to their result. That means we cannot use user-side aliases in contexts expecting metafunctions: User-side metafunctions are not first class functions.

Instead, we could build an expression evaluation system which takes an expresssion (A template with its parameters) and evaluate it saying “Is this a metafunction? Ok, so I should get its result via typename ::type. This approach has the advantage that one could customize the evaluation and design it for many complex cases. The simplest one, before evaluating a metafunction evaluate its parameters.

This is what I did for Turbo, and Boost.MPL.Lambda takes a similar approach:

 

C++14 variable templates: Stop doing ugly template metaprogramming and use a natural syntax

This last approach is available since C++14 thanks to variable templates. A variable template is a constant parametrized with a template. The canonical example is a pi constant aware of the precision of the type used:

Variable templates are values parametrized with templates, instead of types. So we can use constexprfunctions instead of template metafunctions to operate even with types (Imagine a variable template acting as a box for a type).

See Boost.Hanna for an example of this approach.



A Haskell-like language inside C++

template metaprogramming meme

Since we work with the C++ type system, using types as values for our computations, tmp works like a functional programming language; because metafunctions have no side effects: We can only create types, not to modify existing ones.
And like in a functional language, one of the pillars of tmp is recursion. In this case recursive template instantiations (Remember that name).

I think the classic factorial/Fibonacci metafunctions examples are so boring. Here is something more interesting: The template throw_stars is a metafunction that takes a type and throws away all the “stars”.

The template specialization acts as the recursive case, and the main template as the base case. Note how C++ template specialization behaves like pattern matching.

Another example could be traversing of C++11 variadic packs:

which is a great example of a head:tail approach for list traversing common in functional languages.



Summary

In this first approach to C++ template metaprogramming we have seen that:

  • Metaprogramming is the process of writing code to generate code, that is, automatize code generation.
  • C++ template metaprogramming uses the template system to generate types, and code in the process: We generate types using templates, and we actually use those types to do computations or to generate the desired code.
  • The basic unit of metaprogramming is the metafunction, as in common programming the basic unit is the function. Metafunctions manipulate entities of their specific metaprogramming domain. In C++ template metaprogramming, those entities are types, and metafunctions are represented through templates.
  • Template metaprogramming is like a functional language embedded into C++ itself. That “language” has no side effects (We cannot modify an existing type, only create new ones), so we use the same patterns as in a functional programming language such as Haskell or F#.

Now we have a good overview of what C++ template metaprogramming is, but we need some C++ knowledge before getting into it.
The next time we will learn C++ templates in depth: Template parameters, template specialization, SFINAE, etc; to make sure we all have and understand the necessary tools to do proper metaprogramming in Modern C++.

Written by Manu Sánchez.

转载于:https://www.cnblogs.com/FEMONT/p/4623003.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值