├── .gitattributes ├── .gitignore └── templates_without_depth_(Part I).md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /templates_without_depth_(Part I).md: -------------------------------------------------------------------------------- 1 | # Templates without depth (Part I) 2 | 3 | Copyright Jan Herrmann, 2017 4 | 5 | ## Introduction 6 | 7 | ### Sum of the first n natural numbers 8 | 9 | Look at the following simple example on doing template metaprogramming: 10 | 11 | ```cpp 12 | template 13 | struct sum 14 | { 15 | static const size_t value = N + sum::value; 16 | }; 17 | template <> 18 | struct sum<0> 19 | { 20 | static const size_t value 0; 21 | }; 22 | ``` 23 | 24 | This toy example implements a compile time function which calculates the sum 25 | of all positive numbers from 0 to n. But when we try to do this calculation 26 | with n=1000 27 | 28 | ```cpp 29 | int main() { 30 | cout << sum<1000>::value << endl; 31 | } 32 | ``` 33 | 34 | the compiler will probably complain about reaching the 35 | [instantiation depth](http://coliru.stacked-crooked.com/a/56cebb10f60d1661). 36 | The solution is not to increase the maximum but a simple 37 | [static assert](http://coliru.stacked-crooked.com/a/96af026bddaf9315): 38 | 39 | ```cpp 40 | int main() { 41 | static_assert(sum<500>::value!=0, ""); 42 | cout << sum<1000>::value << endl; 43 | } 44 | ``` 45 | 46 | Now the question is why this assert solves our problem. The simple answer is 47 | memoization. The static_assert forces the compiler to instantiate sum<500>, 48 | compute its value and memorize it. Later during computing `sum<1000>::value` 49 | the recursion is stoped when `sum<500>::value` is needed and the memorized 50 | result is used. Of course static_assert is not the only solution and it is 51 | very unflexible, too. But there are other mechanisms to create a specific 52 | order of template instantiations with the aim of reducing instantiation depth: 53 | 54 | * template arguments are instantiated before the template: 55 | in `using result = second_t>` `first_t` is evaluated before 56 | `second_t` 57 | * template arguments are instantiated from left to right: 58 | `using result = third_t, second_t>` the order is 59 | `first_t`, `second_t` and `third_t` 60 | * with `std::make_index_sequence` there exists a facility for generating numbers 61 | for templates, as long as we know how many templates to instantiate we can 62 | order them and create a map from size_t to our desired result 63 | 64 | Now our example gets a little bit more lengthy. First we rename `sum` to 65 | `sum_impl`: 66 | 67 | ```cpp 68 | template 69 | struct sum_impl 70 | { 71 | static const size_t value = N + sum_impl::value; 72 | }; 73 | template <> 74 | struct sum_impl<0> 75 | { 76 | static const size_t value=0; 77 | }; 78 | ``` 79 | 80 | As a second step we create a maping from an index sequence to our `sum_impl`: 81 | 82 | ```cpp 83 | template 84 | struct sum_map; 85 | 86 | template 87 | struct sum_map> 88 | { 89 | using type = index_sequence::value...>; 90 | }; 91 | ``` 92 | 93 | With this map we can create all sums from 0 to n with 94 | `using result = sum_map>>`. 95 | 96 | As a third step we need to get the last element out of this 97 | `index_sequence`. This is a non trivial task, at least if we need 98 | a diffrent kind of sequence e.g. a type sequence. But an easy solution 99 | can be implemented for an 2 element argument pack: 100 | 101 | ```cpp 102 | template 103 | struct second 104 | { 105 | using type=Second; 106 | }; 107 | ``` 108 | 109 | The idea behind this is to pass the `sum_map` as the first argument and 110 | the `sum_impl` we are interested in as the second argument. Arguments are 111 | instantiated from left to right, first we create all `sum_impl` elements 112 | we need inside of `sum_map` and as a second step we lookup the `sum_impl` 113 | we are interested in: 114 | 115 | ```cpp 116 | template 117 | using sum = typename second>::type,sum_impl>::type; 118 | ``` 119 | 120 | As we can [see](http://coliru.stacked-crooked.com/a/a6bddf19506dbdaa) it is 121 | possible to compute `sum<10000>::value` with an instantiation depth of 15 . 122 | With a perfect `make_index_sequence` it [works with an instantiation depth of 4](http://coliru.stacked-crooked.com/a/0656d99b10153515). 123 | 124 | Now the question is wether there are other (more important) metafunctions 125 | which can take advantage from memoization, have a known recursion depth 126 | and can be used iteratively with an index sequence. And indeed we can 127 | implement indexed access and fold this way. 128 | 129 | ### Indexed access to type sequences 130 | 131 | Indexed access for type lists has been implemented by Andrei Alexandrescu in 132 | Modern C++ Design. He has implemented TypeAt the following way: 133 | 134 | ```cpp 135 | template struct TypeAt; 136 | 137 | template 138 | struct TypeAt, 0> 139 | { 140 | typedef Head Result; 141 | }; 142 | 143 | template 144 | struct TypeAt, i> 145 | { 146 | typedef typename TypeAt::Result Result; 147 | }; 148 | ``` 149 | 150 | With C++11 and variadic templates it [becomes](http://coliru.stacked-crooked.com/a/72f403953dd30231): 151 | 152 | ```cpp 153 | template 154 | struct type_sequence 155 | {}; 156 | 157 | template 158 | struct type_at; 159 | 160 | template 161 | struct type_at, Index> 162 | { 163 | using type = typename type_at, Index-1>::type; 164 | }; 165 | 166 | template 167 | struct type_at,0> 168 | { 169 | using type = Head; 170 | }; 171 | 172 | template 173 | using type_at_t = typename type_at::type; 174 | ``` 175 | 176 | You can find lots of implementations of this algorithm 177 | (for example [from Peter Dimov](http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html)) 178 | and all these implementations are inefficient. To see and remove 179 | this inefficiency we nedd to transform the code: we add the 180 | following metafunctions and implement type_at_t in terms of them ([full implementation](http://coliru.stacked-crooked.com/a/b692a9cf6bb23a3e)): 181 | 182 | * `head` (trivial therefor not shown here) 183 | * `tail` (trivial) 184 | * `remove_first_n` 185 | 186 | ```cpp 187 | 188 | template 189 | struct remove_first_n 190 | { 191 | using type = typename remove_first_n::type, N-1>::type; 192 | }; 193 | 194 | template 195 | struct remove_first_n 196 | { 197 | using type = Sequence; 198 | }; 199 | 200 | template 201 | using type_at_t = typename head::type>::type; 202 | ``` 203 | 204 | As we see the structure of the algorithm has been moved to `remove_first_n`. 205 | Now we access all elements of a sequence (highly simplified): 206 | 207 | ``` 208 | remove_first_n,0>::type := 209 | type_sequence 210 | 211 | remove_first_n,1>::type := 212 | remove_first_n< 213 | tail>::type,0>::type := 214 | remove_first_n,0>::type := 215 | type_sequence 216 | 217 | remove_first_n,2>::type := 218 | remove_first_n< 219 | tail>::type,1>::type := 220 | remove_first_n,1>::type := 221 | remove_first_n< 222 | tail>::type,0>::type := 223 | remove_first_n,0>::type := 224 | type_sequence 225 | ``` 226 | 227 | We have 6 instantiations of remove_first_n. With a [slightly 228 | modified source](http://coliru.stacked-crooked.com/a/dd9bfd7623350d79) 229 | and clang we can omit erros on every instantiation and count them. 230 | To access all types of a n element type sequence get (n*n+n)/2 231 | instantiations of `remove_first_n`. The heard of this 232 | algorithm is `typename remove_first_n::type, N-1>::type` 233 | and it is saying: *remove one element and then n-1 elements* but 234 | we can change it to `typename tail::type>::type` 235 | which means *remove n-1 elements and then one element*. This looks 236 | like its not important but [with the modified source](http://coliru.stacked-crooked.com/a/e0d58463b9e38229) 237 | we have have only 3 errors wich means 3 instantiation (of `remove_first_n`): 238 | 239 | ``` 240 | remove_first_n,0>::type := 241 | type_sequence 242 | 243 | remove_first_n,1>::type := 244 | tail,0>::type> := 245 | tail> := 246 | type_sequence 247 | 248 | remove_first_n,2>::type := 249 | tail,1>::type> := 250 | tail> := 251 | type_sequence 252 | ``` 253 | 254 | For every instantiation we lookup a previously computed one 255 | and reduce the amount of computation. Now implementing 256 | a safe `type_at` is trivial. 257 | 258 | ### Implementation of fold 259 | 260 | Besides map, fold is the workhorse in functional programming and 261 | consequently in template metaprogramming. As long as we know 262 | which size our type sequence has, we know how many fold operations 263 | we need. Furthermore we know in which order they have to be computed. 264 | The simple formula is: 265 | ``` 266 | fold(Sequence, State, Operation, Index) = 267 | Operation( 268 | fold(Sequence, State, Operation, Index-1), 269 | type_at(Sequence, Index) 270 | ) 271 | fold(Sequence, State, Operation, 0) = 272 | Operation(State,type_at(Sequence, 0)) 273 | ``` 274 | So we can implement fold the following way: 275 | ```cpp 276 | template< 277 | class Sequence, 278 | class State, 279 | template class Operation, 280 | size_t Index> 281 | struct fold_impl 282 | { 283 | using type = typename Operation< 284 | typename fold_impl::type, 285 | typename type_at::type 286 | >::type; 287 | }; 288 | ``` 289 | and for the recursion base case: 290 | ```cpp 291 | template< 292 | class Sequence, 293 | class State, 294 | template class Operation 295 | > 296 | struct fold_impl 297 | { 298 | using type = typename Operation< 299 | State, 300 | typename type_at::type 301 | >::type; 302 | }; 303 | ``` 304 | Like with `sum` we need a maping for fold: 305 | ```cpp 306 | template< 307 | class Sequence, 308 | class State, 309 | template class Operation, 310 | class Index_Sequence 311 | > 312 | struct fold_map; 313 | 314 | template< 315 | class Sequence, 316 | class State, 317 | template class Operation, 318 | size_t... Ints 319 | > 320 | struct fold_map> 321 | { 322 | using type = type_sequence::type ...>; 323 | }; 324 | ``` 325 | and with an additional `sequence_size` metafunction we can create our fold: 326 | 327 | ```cpp 328 | template < 329 | class Sequence, 330 | class State, 331 | template class Operation 332 | > 333 | using fold = typename second< 334 | typename fold_map< 335 | Sequence, 336 | State, 337 | Operation, 338 | make_index_sequence::type::value> 339 | >::type, 340 | typename fold_impl::type::value-1>::type 341 | >::type; 342 | ``` 343 | The [example (folding a 21 element sequence)](http://coliru.stacked-crooked.com/a/85800da881a6ba06) 344 | works with an instantiation depth of 6. With larger type_sequences `std::make_index_sequence` 345 | can use more space. Furthermore we don't need a safe version of `type_at` to 346 | implement fold as we are iterating from the start over the sequence and access 347 | all elements in the order they are computed. But of course there is room for 348 | improvement. 349 | --------------------------------------------------------------------------------