Recomendações gerais
Formatação
clang-format.
2. A indentação é de 4 espaços. Configure seu ambiente de desenvolvimento para que uma tabulação insira quatro espaços.
3. As chaves de abertura e fechamento devem ficar em uma linha separada.
statement, ele pode ser colocado em uma única linha. Coloque espaços ao redor das chaves (além do espaço no final da linha).
if, for, while e outras, um espaço é inserido antes do parêntese de abertura (ao contrário das chamadas de função).
+, -, *, /, %, …) e do operador ternário ?:.
., ->.
Se necessário, o operador pode ser quebrado para a próxima linha. Nesse caso, o recuo antes dele é aumentado.
11. Não use espaço para separar operadores unários (--, ++, *, &, …) do argumento.
12. Coloque um espaço após uma vírgula, mas não antes dela. A mesma regra vale para um ponto e vírgula dentro de uma expressão for.
13. Não use espaços para separar o operador [].
14. Em uma expressão template <...>, use um espaço entre template e <; sem espaços após < ou antes de >.
public, private e protected no mesmo nível de class/struct, e indente o restante do código.
namespace for usado em todo o arquivo e não houver mais nada significativo, a indentação dentro do namespace não é necessária.
17. Se o bloco de um if, for, while ou outra expressão consistir em um único statement, as chaves são opcionais. Coloque o statement em uma linha separada. Essa regra também é válida para if, for, while aninhados, …
Mas se o statement interno contiver chaves ou else, o bloco externo deverá ser escrito entre chaves.
A const (relacionado a um valor) deve ser escrito antes do nome do tipo.
* e & devem ser separados por espaços dos dois lados.
using (exceto nos casos mais simples).
Em outras palavras, os parâmetros de Template são especificados apenas em using e não são repetidos no código.
using pode ser declarado localmente, por exemplo, dentro de uma função.
Comentários
///, e comentários de várias linhas começam com /**. Esses comentários são considerados “documentação”.
Observação: Você pode usar o Doxygen para gerar documentação a partir desses comentários. Mas, em geral, o Doxygen não é usado, porque é mais prático navegar pelo código na IDE.
9. Comentários de várias linhas não devem ter linhas em branco no início nem no fim (exceto a linha que fecha um comentário de várias linhas).
10. Para comentar trechos de código, use comentários básicos, não comentários de “documentação”.
11. Exclua as partes comentadas do código antes de fazer o commit.
12. Não use palavrões em comentários nem no código.
13. Não use letras maiúsculas. Não use pontuação em excesso.
Nomes
using recebem nomes da mesma forma que as classes.
5. Nomes de argumentos de tipo de template: em casos simples, use T; T, U; T1, T2.
Em casos mais complexos, siga as regras para nomes de classes ou adicione o prefixo T.
N.
I.
defines e constantes globais usam ALL_CAPS com sublinhados.
- Para nomes de variáveis, a abreviação deve usar letras minúsculas:
mysql_connection(nãomySQL_connection). - Para nomes de classes e funções, mantenha as letras maiúsculas na abreviação:
MySQLConnection(nãoMySqlConnection).
enum, use CamelCase com inicial maiúscula. ALL_CAPS também é aceitável. Se o enum não for local, use uma enum class.
AST, SQL.
Não NVDH (algumas letras aleatórias)
Palavras incompletas são aceitáveis se a forma abreviada for de uso comum.
Você também pode usar uma abreviação se o nome completo estiver incluído ao lado dela nos comentários.
17. Nomes de arquivos com código-fonte em C++ devem ter a extensão .cpp. Arquivos de cabeçalho devem ter a extensão .h.
18. O nome do produto é escrito como ClickHouse — uma palavra, com C e H maiúsculos. A verificação de estilo clickhouse_spelling também aceita as grafias convencionais dos tokens clickhouse e CLICKHOUSE; todas as outras variantes são erros de grafia. Ela verifica código, comentários, mensagens, documentação e nomes de arquivos. Use clickhouse para tokens formados apenas por letras minúsculas, como nomes de pacotes, binários e hosts; use ClickHouse em identificadores CamelCase; e use CLICKHOUSE para macros e variáveis de ambiente.
ClickHouse, clickhouse-client, CLICKHOUSE_DATABASE
Não Clickhouse, clickHouse, click_house, CLICK_HOUSE, click-house, Click House
Como escrever código
delete) só pode ser usada em código de biblioteca.
Em código de biblioteca, o operador delete só pode ser usado em destrutores.
No código da aplicação, a memória deve ser liberada pelo objeto que é seu dono.
Exemplos:
- A forma mais fácil é colocar um objeto na stack ou torná-lo membro de outra classe.
- Para um grande número de objetos pequenos, use contêineres.
- Para a desalocação automática de um pequeno número de objetos que residem no heap, use
shared_ptr/unique_ptr.
RAII e veja acima.
3. Tratamento de erros.
Use exceções. Na maioria dos casos, você só precisa lançar uma exceção e não precisa capturá-la (por causa de RAII).
Em aplicações de processamento de dados offline, muitas vezes é aceitável não capturar exceções.
Em servidores que tratam solicitações de usuários, geralmente basta capturar exceções no nível mais alto do handler de conexão.
Em funções de thread, você deve capturar e armazenar todas as exceções para relançá-las na thread principal após join.
errno, sempre verifique o resultado e lance uma exceção em caso de erro.
- Crie uma função (
done()oufinalize()) que execute antecipadamente todo o trabalho que possa levar a uma exceção. Se essa função tiver sido chamada, não deverá haver exceções no destrutor depois. - Tarefas muito complexas (como enviar mensagens pela rede) podem ser colocadas em um método separado que o usuário da classe terá de chamar antes da destruição.
- Se houver uma exceção no destrutor, é melhor registrá-la em log do que ocultá-la (se o logger estiver disponível).
- Em aplicações simples, é aceitável contar com
std::terminate(para casos denoexceptpor padrão no C++11) para lidar com exceções.
- Tente obter o melhor desempenho possível em um único núcleo de CPU. Depois, você pode paralelizar o código, se necessário.
- Use o pool de threads para processar solicitações. Até o momento, não tivemos nenhuma tarefa que exigisse troca de contexto em userspace.
joinAll).
Se a sincronização for necessária, na maioria dos casos, basta usar um mutex com lock_guard.
Em outros casos, use primitivas de sincronização do sistema. Não use espera ocupada.
Operações atômicas devem ser usadas apenas nos casos mais simples.
Não tente implementar estruturas de dados lock-free, a menos que essa seja sua principal área de especialização.
9. Ponteiros vs referências.
Na maioria dos casos, prefira referências.
10. const.
Use referências constantes, ponteiros para constantes, const_iterator e métodos const.
Considere const como o padrão e use não const apenas quando necessário.
Ao passar variáveis por valor, usar const geralmente não faz sentido.
11. unsigned.
Use unsigned se necessário.
12. Tipos numéricos.
Use os tipos UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32 e Int64, bem como size_t, ssize_t e ptrdiff_t.
Não use estes tipos para números: signed/unsigned long, long long, short, signed/unsigned char, char.
13. Passagem de argumentos.
Passe valores complexos por valor se eles forem movidos e use std::move; passe por referência se quiser atualizar o valor em um loop.
Se uma função assumir a posse de um objeto criado no heap, defina o tipo do argumento como shared_ptr ou unique_ptr.
14. Valores de retorno.
Na maioria dos casos, basta usar return. Não escreva return std::move(res).
Se a função alocar um objeto no heap e retorná-lo, use shared_ptr ou unique_ptr.
Em casos raros (ao atualizar um valor em um loop), talvez seja necessário retornar o valor por meio de um argumento. Nesse caso, o argumento deve ser uma referência.
namespace.
Não é necessário usar um namespace separado para o código da aplicação.
Bibliotecas pequenas também não precisam disso.
Para bibliotecas de médio a grande porte, coloque tudo em um namespace.
No arquivo .h da biblioteca, você pode usar namespace detail para ocultar detalhes de implementação desnecessários para o código da aplicação.
Em um arquivo .cpp, você pode usar static ou um namespace anônimo para ocultar símbolos.
Além disso, um namespace pode ser usado com um enum para evitar que os nomes correspondentes vazem para um namespace externo (mas é melhor usar enum class).
16. Inicialização adiada.
Se a inicialização exigir argumentos, normalmente você não deve escrever um construtor padrão.
Se mais tarde for preciso adiar a inicialização, você pode adicionar um construtor padrão que criará um objeto inválido. Ou, para um número pequeno de objetos, usar shared_ptr/unique_ptr.
std::string e char *. Não use std::wstring nem wchar_t.
19. Logging.
Veja os exemplos ao longo do código.
Antes de fazer commit, remova todo logging sem sentido e de depuração, bem como quaisquer outros tipos de saída de depuração.
O logging em loops deve ser evitado, mesmo no nível Trace.
Os logs devem ser legíveis em qualquer nível de log.
Em geral, o logging deve ser usado apenas no código da aplicação.
As mensagens de log devem ser escritas em inglês.
De preferência, o log deve ser compreensível para o administrador do sistema.
Não use palavrões no log.
Use codificação UTF-8 no log. Em casos raros, você pode usar caracteres não ASCII no log.
20. Entrada e saída.
Não use iostreams em loops internos críticos para o desempenho da aplicação (e nunca use stringstream).
Use a biblioteca DB/IO em vez disso.
21. Data e hora.
Veja a biblioteca DateLUT.
22. include.
Sempre use #pragma once em vez de guardas de inclusão.
23. using.
Não use using namespace. Você pode usar using para algo específico. Mas mantenha isso local dentro de uma classe ou função.
24. Não use trailing return type para funções, a menos que seja necessário.
virtual na classe base, mas use override em vez de virtual nas classes derivadas.
Funcionalidades não utilizadas do C++
Plataforma
clang. No momento em que este texto foi escrito (março de 2025), o código é compilado com clang versão >= 19.
A biblioteca padrão utilizada é a libc++.
4. SO: Ubuntu Linux, não anterior ao Precise.
5. O código é escrito para a arquitetura de CPU x86_64.
O conjunto de instruções da CPU é o conjunto mínimo compatível entre nossos servidores. Atualmente, é SSE 4.2.
6. Use as flags de compilação -Wall -Wextra -Werror -Weverything, com algumas exceções.
7. Use vinculação estática com todas as bibliotecas, exceto aquelas que são difíceis de vincular estaticamente (veja a saída do comando ldd).
8. O código é desenvolvido e depurado com configurações de release.
Ferramentas
gdb, valgrind (memcheck), strace, -fsanitize=... ou tcmalloc_minimal_debug.
3. Para análise de desempenho, use Linux Perf, valgrind (callgrind) ou strace -cf.
4. O código-fonte está no Git.
5. A compilação usa CMake.
6. Os programas são distribuídos em pacotes deb.
7. Commits na master não devem quebrar a compilação.
Embora apenas revisões selecionadas sejam consideradas funcionais.
8. Faça commits com a maior frequência possível, mesmo que o código esteja apenas parcialmente pronto.
Use branches para isso.
Se o seu código na branch master ainda não puder ser compilado, exclua-o da compilação antes do push. Você precisará finalizá-lo ou removê-lo em poucos dias.
9. Para alterações não triviais, use branches e publique-as no servidor.
10. Código não utilizado é removido do repositório.
Bibliotecas
boost e Poco.
2. Não é permitido usar bibliotecas de pacotes do sistema operacional. Também não é permitido usar bibliotecas pré-instaladas. Todas as bibliotecas devem ser incluídas como código-fonte no diretório contrib e compiladas com o ClickHouse. Consulte Diretrizes para adicionar novas bibliotecas de terceiros para mais detalhes.
3. A preferência é sempre por bibliotecas que já estão em uso.
Recomendações gerais
using em vez de classes ou structs.
5. Se possível, não implemente construtores de cópia, operadores de atribuição, destrutores (exceto um virtual, se a classe contiver pelo menos uma função virtual), construtores de movimento nem operadores de atribuição por movimento. Em outras palavras, as funções geradas pelo compilador devem funcionar corretamente. Você pode usar default.
6. A simplificação do código é incentivada. Reduza o tamanho do código sempre que possível.
Recomendações adicionais
std:: para tipos de stddef.h
não é recomendável. Em outras palavras, recomendamos escrever size_t em vez de std::size_t, porque é mais curto.
É aceitável adicionar std::.
2. Especificar explicitamente std:: para funções da biblioteca padrão de C
não é recomendável. Em outras palavras, escreva memcpy em vez de std::memcpy.
O motivo é que existem funções não padrão semelhantes, como memmem. Nós as usamos ocasionalmente. Essas funções não existem no namespace std.
Se você escrever std::memcpy em vez de memcpy em todos os lugares, memmem sem std:: vai parecer estranho.
Ainda assim, você pode usar std:: se preferir.
3. Usar funções de C quando as mesmas estiverem disponíveis na biblioteca padrão de C++.
Isso é aceitável se for mais eficiente.
Por exemplo, use memcpy em vez de std::copy para copiar grandes blocos de memória.
4. Argumentos de função em várias linhas.
Qualquer um dos estilos de quebra de linha a seguir é permitido: