Arvorezinha 2.0 – C++11 Templates
Ora viva!
Desde a minha última submissão, tem havido vasto progresso no estado da arte da Arvorezinha. Foi lançado um novo standard, e tem havido um renovado interesse em criar arvorezinhas cada vez mais obscuras e intricadas.
Isto traz-nos a este post. Um dos problemas fundamentais com as arvorezinhas anteriores em templates de C++ era que cada caracter era impresso de cada vez. Era muito mais interessante se pudéssemos gerar a string completa da arvorezinha durante a compilação, após o qual imprimir seria apenas uma questão de enviar a string para a função adequada (printf, cout, etc). Uma das novidades no novo standard de C++, oficializado o ano passado, são os variadic templates. Estes são uma versão em templates das funções com número de argumentos variável, como já existiam em C(++) e no pré-processador de C, que nos permite "construir" uma string caracter a caracter, e despejá-la numa initializer list quando acabamos.
Para simplificar a apresentação, desacoplei a lógica da arvorezinha da lógica que constrói a string, para ser mais fácil compreender a implementação. Também incluí uma versão não variádica opcional, caso não estejam satisfeitos.
#include <cstdio> struct NullType {}; template<size_t N> struct Arvorezinha2 { static const size_t NC = 2*N * (N + N/2); template<bool B, size_t L, size_t C> struct Pedaco; template<size_t L, size_t C> struct Pedaco<true, L, C> // Arvore { template<bool B, typename D> struct Ramo { static const char value = ' '; }; template<typename D> struct Ramo<true, D> { static const char value = '*'; }; static const size_t len = N*2 - 1; static const size_t beg = N-(L+1); static const size_t end = 2*(L+1)-1; static const char value = Ramo<C >= beg && C < beg + end, NullType>::value; }; template<size_t L, size_t C> struct Pedaco<false, L, C> // Tronco { template<bool B, typename D> struct Lenha { static const char value = ' '; }; template<typename D> struct Lenha<true,D> { static const char value = '#'; }; static const size_t len = N*2 - 1; static const size_t beg = len/4 - N%2 + 1; static const size_t end = len/2 - !(N%2); static const char value = Lenha<C >= beg && C <= beg + end, NullType>::value; }; template<size_t L> struct Pedaco<true, L, 2*N-1> { static const char value = '\n'; }; template<size_t L> struct Pedaco<false, L, 2*N-1> { static const char value = '\n'; }; template<size_t X> struct Arvorezinha { static const size_t L = X / (2*N); static const size_t C = X % (2*N); static const char value = Pedaco<L < N, L, C>::value; }; template<size_t I> struct AT { static const char value = Arvorezinha<I>::value; }; }; template<template<size_t> class T, size_t N, bool V/*ariadic*/ = true> struct Desenhar { template<char... Str> static inline const char (&str())[sizeof...(Str)+1] { static const char value[sizeof...(Str)+1] = {Str..., 0}; return value; } template<size_t I, typename D, char...Str> struct StringBuilder { static inline const char *toStr() { return StringBuilder<I+1, D, Str..., T<N>::template AT<I>::value>::toStr(); } }; template<typename D, char...Str> struct StringBuilder<T<N>::NC, D, Str...> { static inline const char *toStr() { return str<Str...>(); } }; static inline const char *toString() { return StringBuilder<0,NullType>::toStr(); } static inline void desenhar() { puts(toString()); } }; template<template<size_t> class T, size_t N> struct Desenhar<T, N, false> { template<size_t i, typename D/*ummy*/> struct Rec { static inline void desenhar() { Rec<i-1,D>::desenhar(); putchar(T<N>::template AT<i>::value); } }; template<typename D> struct Rec<0,D> { static inline void desenhar() { putchar(T<N>::template AT<0>::value); } }; static inline void desenhar() { Rec<T<N>::NC-1, NullType>::desenhar(); } }; int main(int argc, char **argv) { Desenhar<Arvorezinha2, 5/*, true*/>::desenhar(); return 0; }
O resultado da compilação é essencialmente óptimo: o programa resume-me a carregar o endereço de uma string, e chamar a função puts:
0000000000400430 <main>: 400430: 48 83 ec 08 sub $0x8,%rsp 400434: bf 40 06 40 00 mov $0x400640,%edi 400439: e8 d2 ff ff ff callq 400410 <puts@plt> 40043e: 31 c0 xor %eax,%eax 400440: 48 83 c4 08 add $0x8,%rsp 400444: c3 retq 400445: 90 nop 400446: 90 nop 400447: 90 nop
Bem haja!
IMHO a arvorezinha mais obfuscada e campeã que gracejou a internet.
Estas coisas deixam-me completamente maravilhado! Que bela arvorezinha!
Very nice .