Compilação em detalhes
1
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
O processo de compilação
Programas C++ são divididos entre fontes e headers. Vamos entender o papel de cada um deles?
Qual a sequência de eventos disparada sob o seguinte comando?
2
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
O processo de compilação
Programas C++ são divididos entre fontes e headers. Vamos entender o papel de cada um deles.
Qual a sequência de eventos disparada sob o seguinte comando?
3
Onde está o gargalo dessa sequência?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
O processo de compilação
Programas C++ são divididos entre fontes e headers. Vamos entender o papel de cada um deles.
Qual a sequência de eventos disparada sob o seguinte comando?
4
Onde está o gargalo dessa sequência?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
O processo de compilação
Programas C++ são divididos entre fontes e headers. Vamos entender o papel de cada um deles.
Qual a sequência de eventos disparada sob o seguinte comando?
5
Onde está o gargalo dessa sequência?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
O processo de compilação
Programas C++ são divididos entre fontes e headers. Vamos entender o papel de cada um deles.
Qual a sequência de eventos disparada sob o seguinte comando?
6
Onde está o gargalo dessa sequência?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Famigerado #include
O pré-processador é uma tecnologia arcaica. Ao encontrar um #include, ele realiza uma cópia textual do arquivo indicado. Originalmente era visto como uma ferramenta de reutilização de código. Hoje se tornou um pesadelo para os compiladores.
7
gcc -E -P program.cpp
?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Famigerado #include
O pré-processador é uma tecnologia arcaica. Ao encontrar um #include, ele realiza uma cópia textual do arquivo indicado. Originalmente era visto como uma ferramenta de reutilização de código. Hoje se tornou um pesadelo para os compiladores.
Uma forward declaration teria resolvido o problema nesse caso?
8
gcc -E -P program.cpp
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Consequências...
As mesmas definições, de classes ou funções, são compiladas inúmeras vezes.
Por quê o compilador não pode salvar o resultado da compilação e reutilizá-lo?
9
code_generator.cpp
optimizer.cpp
register.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
A One-Definition Rule (ODR) determina que uma única definição de determinada classe, função, template, etc. deve existir no programa. Mas, antes de tentar entendê-la em sua plenitude, damos um passo atrás para esclarecer uma confusão comum relacionada com diretivas de compilação.
10
p.cpp
a.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
11
p.cpp
a.h
b.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
12
p.cpp
a.h
b.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
13
p.cpp
a.h
b.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
O que é __Z10violateODRv?
14
p.cpp
a.h
b.h
q.cpp
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
Recapitulando: A One-Definition Rule (ODR) determina que uma única definição de determinada classe, função, template, etc. deve existir no programa.
Conseguimos resolvê-lo através de diretivas #ifdef, as quais protegem contra o risco de múltiplas definições dentro de uma única unidade de tradução (translation unit).
15
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Consequências...
As mesmas definições, de classes ou funções, são compiladas inúmeras vezes.
Apesar do linker utilizar apenas uma delas!
16
code_generator.cpp
optimizer.cpp
register.h
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
A regra da definição única
Recapitulando: A One-Definition Rule (ODR) determina que uma única definição de determinada classe, função, template, etc. deve existir no programa.
Conseguimos resolvê-lo através de diretivas #ifdef, as quais protegem contra o risco de múltiplas definições dentro de uma única unidade de tradução (translation unit).
Ele acontece pois a ODR protege contra o risco de múltiplas definições em um programa completo.
17
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
ODR: cuidado para não se confundir
18
Vários fontes pode fazer #include de um mesmo header.
Arquivos headers podem conter definições de classes.
Logo, podem existir classes duplicadas em fontes diferentes.
Por quê não acontece um erro de ODR nesse caso?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Os "segredos" da ODR
Enquanto que a essência da ODR é clara, os detalhes de sua especificação não são triviais. Em particular, o significado de unicidade relativo a uma entidade é o seguinte.
19
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Os "segredos" da ODR
Enquanto que a essência da ODR é clara, os detalhes de sua especificação não são triviais. Em particular, o significado de unicidade relativo a uma entidade é o seguinte.
Como reproduzir uma classe token-a-token, mas com outra semântica?
20
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Os "segredos" da ODR
Enquanto que a essência da ODR é clara, os detalhes de sua especificação não são triviais. Em particular, o significado de unicidade relativo a uma entidade é o seguinte.
Classes
O compilador precisa saber o tamanho (em bytes) da classe. Só assim é possível calcular o espaço ocupado por suas instâncias em memória.
21
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Os "segredos" da ODR
Enquanto que a essência da ODR é clara, os detalhes de sua especificação não são triviais. Em particular, o significado de unicidade relativo a uma entidade é o seguinte.
Classes
O compilador precisa saber o tamanho (em bytes) da classe. Só assim é possível calcular o espaço ocupado por suas instâncias em memória.
Templates
A definição da classe ou função deve estar disponível para que o compilador possa instanciar o tipo concreto correspondente à entidade parametrizada.
22
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Os "segredos" da ODR
Enquanto que a essência da ODR é clara, os detalhes de sua especificação não são triviais. Em particular, o significado de unicidade relativo a uma entidade é o seguinte.
Classes
O compilador precisa saber o tamanho (em bytes) da classe. Só assim é possível calcular o espaço ocupado por suas instâncias em memória.
Templates
A definição da classe ou função deve estar disponível para que o compilador possa instanciar o tipo concreto correspondente à entidade parametrizada.
Funções inline
Para que a corpo de uma função seja embutido no código objeto, ao invés de apenas uma chamada, sua implementação deve estar acessível.
23
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Não conte com o compilador...
A verificação de consistência da ODR não é trivial, e, tipicamente, não é realizada por completo. Se violada, pode levar a erros em tempo de execução que são de difícil diagnóstico.
24
client.cpp
server.cpp
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Ligação interna e externa
O nome só pode ser referenciado em sua própria unidade.
O nome pode ser referenciado por todo programa (de outras unidades).
O mecanismo de #include é essencialmente uma conveniência para evitar a reescrita manual de declarações e definições nos arquivos fontes. Ao final, o linker "unifica" os nomes de todas as translation units do programa.
25
Qual a diferença entre uma declaração e uma definição?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Ligação interna e externa
O nome só pode ser referenciado em sua própria unidade.
O nome pode ser referenciado por todo programa (de outras unidades).
O mecanismo de #include é essencialmente uma conveniência para evitar a reescrita manual de declarações e definições nos arquivos fontes. Ao final, o linker "unifica" os nomes de todas as translation units do programa.
26
Qual a diferença entre uma declaração e uma definição?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Ligação interna e externa
O mecanismo de #include é essencialmente uma conveniência para evitar a reescrita manual de declarações e definições nos arquivos fontes. Ao final, o linker "unifica" os nomes de todas as translation units do programa.
27
Qual a diferença entre uma declaração e uma definição?
Qual o linkage de static?
unit1.ii
unit2.ii
Qual o linkage padrão de const?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
28
entireworld.h
entireworld.gch
q.cpp
p.cpp
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
O problema é a invalidação do header pré-compilado:
29
entireworld.h
entireworld.gch
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
O problema é a invalidação do header pré-compilado:
30
entireworld.h
entireworld.gch
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
O problema é a invalidação do header pré-compilado:
31
entireworld.h
entireworld.gch
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
O problema é a invalidação do header pré-compilado:
32
entireworld.h
entireworld.gch
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Headers pré-compilados ao resgate
Com intuito de amenizar o problema de desempenho da inclusão textual, vários compiladores oferecem headers pré-compilados. Na teoria, a idéia é legal, mas, na prática, não escala tão bem.
O problema é a invalidação do header pré-compilado:
33
entireworld.h
entireworld.gch
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Impacto em ferramentas de análise
A complexidade resultante do modelo de inclusão textual não se reflete apenas no processo de compilação. Ela é também um fator impactante para a construção de ferramentas de análise estática.
Note, inclusive, que a não-expansão de check torna o código sintaticamente inválido!
34
Por que refatoração automática de código em linguagens como Java funciona melhor do que em C++?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Impacto em ferramentas de análise
A complexidade resultante do modelo de inclusão textual não se reflete apenas no processo de compilação. Ela é também um fator impactante para a construção de ferramentas de análise estática.
Note que renomear uma macro não é tão simples quanto renomear uma função.
35
Por que refatoração automática de código em linguagens como Java funciona melhor do que em C++?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Impacto em ferramentas de análise
A complexidade resultante do modelo de inclusão textual não se reflete apenas no processo de compilação. Ela é também um fator impactante para a construção de ferramentas de análise estática.
36
Por que refatoração automática de código em linguagens como Java funciona melhor do que em C++?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Impacto em ferramentas de análise
A complexidade resultante do modelo de inclusão textual não se reflete apenas no processo de compilação. Ela é também um fator impactante para a construção de ferramentas de análise estática.
37
Por que refatoração automática de código em linguagens como Java funciona melhor do que em C++?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Moral da história
38
Pense duas vezes antes de fazer um #include ou de criar macros.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Moral da história
39
Na dúvida, pense mais uma vez.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Algumas referências
40
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
41
Curso de C++ moderno
Perguntas?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com