Pular para o conteúdo principal

Templates e geração de código

O que é um template ?

A programação genérica no C++ se dá em grande parte através de templates. Eles parecem mágicos, pois diferente de um objeto, ele não verifica assinaturas.

https://gist.github.com/thiagomg/679eefc59beb8cf03d3b

Como saber se "C" tem o método sum, sem ter uma interface ?
Isso é validado em tempo de compilação, pois diferente de linguagens como Java, a função declarada com template existe somente quando é instanciada.

Vamos ver isso com exemplos e código rodando !



Tendo o arquivo abaixo

https://gist.github.com/thiagomg/53e521337e2bc2c32710

Vamos copilar e listar os símbolos
g++ -std=c++14 -c temp_gen.cpp && nm -C temp_gen.o

E temos o seguinte resultado:
0000000000000000 T sumNonTemplated(int, int)
0000000000000014 T run()

O sumTemplated não aparece pois ele não existe! Apenas quando o template for instanciado ele vai aparecer, pois um template é compile-time programming.

Vamos verificar se isso está correto fazendo uma pequena modificação no nosso exemplo

https://gist.github.com/thiagomg/f640a1d878547b31655f

E o resultado agora é bem diferente
W double sumTemplated<double>(double, double)
W float sumTemplated<float>(float, float)
W int sumTemplated<int>(int, int)
T sumNonTemplated(int, int)
T run()

Ao fazer as chamadas usando int, double e float, o compilador criou uma versão de cada função para cada tipo, e assim, tendo versões concretas, elas aparecem na listagem de símbolos. Além disso, os tipos das funções template foram inferidos pelo compilador.

O funcionamento é o mesmo para classes com e sem templates, exceto pelo fato de que as classes que nunca são usadas também não aparecem na listagem de símbolos.

https://gist.github.com/thiagomg/c444563a5efd275df823
W NonTemplated::sum(int)
W NonTemplated::NonTemplated(int)
W NonTemplated::NonTemplated(int)
n NonTemplated::NonTemplated(int)
W Templated<double>::sum(double)
W Templated<double>::Templated(double)
W Templated<double>::Templated(double)
n Templated<double>::Templated(double)
W Templated<int>::sum(int)
W Templated<int>::Templated(int)
W Templated<int>::Templated(int)
n Templated<int>::Templated(int)

Temos aqui também uma versão para cada tipo gerado.

Ao final, mas não menos importante, como as funções e classes que usam templates somente existem quando são instanciadas em tempo de compilação, elas tendem a favorecer a geração de código inline, coisa muito importante para otimização!
Vamos ver um exemplo, pegando todos os códigos anteriores e compilando com otimização -O2
g++ -O2 -std=c++14 -c temp_gen.cpp && nm -C temp_gen.o

E o resultado
T sumNonTemplated(int, int)
T run()

O único código gerado foi o sumNonTemplated, que é exportado por padrão. As funções com templates todas ficaram inline.

Eu pensei em falar em template instantiation neste post, mas isso é assunto para um post inteiro.

Até breve !

Código: https://github.com/SimplyCpp/examples/blob/master/temp_gen.cpp

Comentários

Postagens mais visitadas deste blog

Mestre Iota

Iota é a nona letra do alfabeto grego, ela é equivalente à letra i do nosso alfabeto. Por convenção ou hábito, utilizamos a letra i na programação para indicar algum tipo de incrementador, como por exemplo, em um for-loop . https://gist.github.com/fabiogaluppo/a23894ae743f7dd29274 Curiosamente, iota , como identificador, também é utilizado na programação para indicar uma sequência finita e consecutiva de números inteiros, como por exemplo, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Inclusive, originalmente na STL existia a função iota , inspirada pela linguagem de programação APL , você pode conferir neste link: http://www.sgi.com/tech/stl/iota.html

Valores Aleatórios Simplificados

A partir do C++ 11, foi introduzido o header <random>  com diversos facilitadores para suporte de geração de números aleatórios. A produção destes números é feita através da combinação de duas categorias de objetos: os geradores e os distribuidores. Os geradores, são responsáveis pela geração dos números, e os distribuidores são responsáveis pela transformação dos números gerados em algum tipo de distribuição de probabilidade.  Como por exemplo, uma distribuição normal (aquela da Gaussiana) ou uma distribuição de Pareto (aquela do 80-20). As opções não faltam, como você pode ver nas referências, por exemplo:   http://www.cplusplus.com/reference/random/ ou  http://en.cppreference.com/w/cpp/header/random .

Policy-based design: log writer

Policy-based design Vamos neste artigo dar mais uma pincelada no Policy-based design . Vamos fazer como exemplo uma classe de log. Como este é só um exemplo, não vamos considerar múltiplos parâmetros no log, mas somente uma string, assim não fugiremos do assunto. Uma das coisas mais importantes neste tipo de design é o desacoplamento. Ele é uma excelente alternativa ao uso de interfaces por duas razões: Não gera chamadas virtuais (ou um nível de indireção em tempo de execução) Duck typing ( https://pt.wikipedia.org/wiki/Duck_typing ) Eu gosto bastante desse tipo de design, já usado aqui: http://simplycpp.com/2016/02/05/leitura-de-configuracao-em-c/