Avoiding compile-time recursion

栏目: IT技术 · 发布时间: 5年前

内容简介:One of my favorite blogs, is Raymond Chen’s “The Old New Thing”. In a recentThe value is calculated at compile time and all is good in the world, no?You can follow the link to check his implementation, Raymond offers a great walk-through as always. The thi

One of my favorite blogs, is Raymond Chen’s “The Old New Thing”. In a recent post , he described a way to find a type in a tuple , with a compile time predicate that given a type and a tuple, will return the index of that type in the tuple, or fail to compile if the type does not exist (or exists more than once):

The value is calculated at compile time and all is good in the world, no?

You can follow the link to check his implementation, Raymond offers a great walk-through as always. The thing that bothers me is the use of recursive template instantiations. This happens when a template instantiates itself until it hits a termination case. The reason I dislike the technique is because it tends to:

  • Increase the binary size. Symbols are generated for a chain of instantiations which end up bloating the executable.
  • Slow down compilation. It’s a recursion and the compiler has to pay the price up front.
  • Complicate the error messages when something goes wrong. The compiler will spit out the whole recursion tree when something goes wrong, and the error messages get obfuscated.

There’s a chapter called “The Cost of Recursive Instantiation” in the template book , but what I’ve found really dangerous is that when trying to scale code designed this way the burden on the compiler may get out of hand. E.g. we may start adding nested types in such templates or try to piggy back on the recursion to design extra machinery.

Of course in the problem at hand, Raymond’s implementation is simple and efficient (even checked binary outputs and resulting generated symbol – as always you can trust what he’s saying). I’m writing this post to show there’s usually an alternative and to point out that not considering the cost of these things may haunt you in the future .

Let’s see how a non recursive implementation would look like:

I’m doing all the work at (3) with this guy:

Is * match_v<T, Is, Tuple<Args...>> + ... + 0

This fold is binary to handle empty parameter packs.  The “match_v” predicate (1) checks if a type “T” exist at index “I” in a tuple “Tuple”. We can see what an example instantiation would do:

type_index<int, tuple<char, bool, int>>

// T = int
// Tuple = tuple<char, bool, int>
// Args... = char, bool, int
// Is = 0, 1, 2 -> index sequence of cardinality = sizeof...(Args)

// If we denote as "m" the "match_v" predicate, we have
(0 * m<int,0,Tuple> + (1 * m<int,1,Tuple> + (2 * m<int,2,Tuple>)))
(0 * 0 + (1 * 0 + (2 * 1))) = 2

2 -> the index of "int" inside Tuple

Essentially we multiply each index with the predicate’s result, and sum the results. The predicate (1) could be written inline, but since there’s a second use for it, it’s good to abstract it out.

Our specialization has a static assertion. By using a variation of the fold expression described above, it makes sure that only one occurrence of the type “T” exists in “Tuple”. This way, when our meta-function returns zero, we can be positive it means “index zero” instead of “does not exist”; additionally we make sure that if the type exists more than once in the tuple, a static assertion is thrown much like in the original implementation.

Check a demo here .

A peace offering

I understand that flattening template instantiations might not appeal to some people, so let me extend an olive branch here: when done right, compile time recursion can be OK. Take for example the “index_sequence” we use in our code, which is implemented recursively:

  • Implementations use an amazing trick (I believe it’s attributed to Dave Abrahams but don’t quote me on that – it certainly appeared in his work a long time ago) to lower recursion depth to Log2(N). It is based on the realization that you can use the first half of an index sequence to build the second half, by adding an initial offset:
    index_sequence<20> = index_sequence<10, 10 + index_sequence<10>>
    
  • Ubiquitous library facilities are reusable. What this means, is that if someone instantiated an index sequence of twenty elements, most probably this will be shareable in your implementation.

Finally, if we end up using recursion (hope not but…), simplicity should compensate for potential hazards. Here’s how I’d solve the same problem recursively, with a single function:

Check a demo here .

Not much explanation is needed apart from the fact that I’ve opted to return the 1-based index and use 0 when the search fails , instead of a compilation error. We also avoid compilation error when more that one instances occur, by returning the index of the first match, thus short-circuiting template instantiations.

Disclaimer

If you’re in a situation where such things matter, always always measure: measure compilation time, executable size, check generated symbols, check compilation in different platforms (time and again I’ve seen VS struggle with sizes & symbol generation that gcc would simply optimize out and discard). I only write this post because it’s good to know your options.


以上所述就是小编给大家介绍的《Avoiding compile-time recursion》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机程序设计艺术(第1卷)

计算机程序设计艺术(第1卷)

[美] 唐纳德·E. 克努特 / 苏运霖 / 国防工业出版社 / 2002-9 / 98.00元

7卷本《计算机程序设计艺术》的第1卷以基本的程序设计概念和技术开始,然后专注于信息结构——计算机内部信息的表示、数据元素之间的结构关系以及如何有效地处理它们,给出了对于模拟、数值方法、符号计算、软件和系统设计的初等应用。书中附有大量习题和答案,标明了难易程序及数学概念的使用。 此新版本增加了几十项简单且重要的算法和技术,并对有关数学预备知识作了大量修改以适应现时研究的趋势。一起来看看 《计算机程序设计艺术(第1卷)》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具