Recomendaciones generales
Formato
clang-format.
2. La sangría es de 4 espacios. Configure su entorno de desarrollo para que una tabulación agregue cuatro espacios.
3. Las llaves de apertura y cierre deben estar en una línea aparte.
statement, puede escribirse en una sola línea. Añada espacios alrededor de las llaves (además del espacio al final de la línea).
if, for, while y otras similares, se inserta un espacio antes del paréntesis de apertura (a diferencia de las llamadas a funciones).
+, -, *, /, %, …) y el operador ternario ?:.
., ->.
Si es necesario, el operador puede pasarse a la línea siguiente. En ese caso, la sangría delante de él aumenta.
11. No uses un espacio para separar los operadores unarios (--, ++, *, &, …) del argumento.
12. Coloca un espacio después de una coma, pero no antes. La misma regla se aplica para un punto y coma dentro de una expresión for.
13. No utilice espacios para separar el operador [].
14. En una expresión template <...>, utilice un espacio entre template y <; sin espacios después de < ni antes de >.
public, private y protected al mismo nivel que class/struct, y aplique sangría al resto del código.
namespace se utiliza en todo el archivo y no hay nada más significativo, no es necesaria una sangría dentro de namespace.
17. Si el bloque de un if, for, while u otra expresión consta de una sola statement, las llaves son opcionales. En su lugar, coloque la statement en una línea aparte. Esta regla también aplica para if, for, while anidados, …
Pero si la sentencia interna contiene llaves o else, el bloque externo debe escribirse entre llaves.
A const (relacionado con un valor) debe escribirse antes del nombre del tipo.
* y & deben ir separados por espacios a ambos lados.
using (excepto en los casos más simples).
En otras palabras, los parámetros de la plantilla se especifican solo en using y no se repiten en el código.
using puede declararse localmente, por ejemplo, dentro de una función.
Comentarios
///, y los comentarios multilínea empiezan con /**. Estos comentarios se consideran “documentación”.
Nota: Puede usar Doxygen para generar documentación a partir de estos comentarios. Pero, por lo general, no se usa Doxygen porque resulta más cómodo navegar por el código en el IDE.
9. Los comentarios multilínea no deben tener líneas en blanco al principio ni al final (excepto la línea que cierra un comentario multilínea).
10. Para comentar código, use comentarios normales, no comentarios de “documentación”.
11. Elimine las partes comentadas del código antes de hacer commit.
12. No use lenguaje ofensivo en los comentarios ni en el código.
13. No use mayúsculas. No use puntuación excesiva.
Nombres
using se nombran de la misma manera que las clases.
5. Nombres de los argumentos de tipo de las plantillas: en los casos simples, use T; T, U; T1, T2.
En los casos más complejos, siga las reglas para los nombres de clase o añada el prefijo T.
N.
I.
defines y de las constantes globales se escriben en ALL_CAPS con guiones bajos.
- En los nombres de variables, la abreviatura debe escribirse en minúsculas:
mysql_connection(nomySQL_connection). - En los nombres de clases y funciones, conserva las letras mayúsculas de la abreviatura:
MySQLConnection(noMySqlConnection).
enum, usa CamelCase con inicial mayúscula. ALL_CAPS también es aceptable. Si el enum no es local, usa una enum class.
AST, SQL.
No NVDH (unas letras aleatorias cualquiera)
Se aceptan palabras incompletas si la versión abreviada es de uso común.
También puedes usar una abreviatura si el nombre completo aparece junto a ella en los comentarios.
17. Los nombres de archivo con código fuente de C++ deben tener la extensión .cpp. Los archivos de cabecera deben tener la extensión .h.
18. El nombre del producto se escribe ClickHouse: una sola palabra, con C y H mayúsculas. La comprobación de estilo clickhouse_spelling también acepta las grafías convencionales de los tokens clickhouse y CLICKHOUSE; todas las demás variantes son errores ortográficos. Comprueba el código, los comentarios, los mensajes, la documentación y los nombres de archivo. Usa clickhouse para tokens que solo usan minúsculas, como nombres de paquetes, binarios y hosts; usa ClickHouse en identificadores CamelCase; y usa CLICKHOUSE para macros y variables de entorno.
ClickHouse, clickhouse-client, CLICKHOUSE_DATABASE
No Clickhouse, clickHouse, click_house, CLICK_HOUSE, click-house, Click House
Cómo escribir código
delete) solo puede usarse en código de librería.
En código de librería, el operador delete solo puede usarse en destructores.
En el código de aplicación, la memoria debe liberarla el objeto que es su propietario.
Ejemplos:
- La forma más sencilla es colocar un objeto en la pila o hacerlo miembro de otra clase.
- Para una gran cantidad de objetos pequeños, use contenedores.
- Para liberar automáticamente una pequeña cantidad de objetos alojados en el heap, use
shared_ptr/unique_ptr.
RAII y consulte lo anterior.
3. Manejo de errores.
Use excepciones. En la mayoría de los casos, solo necesita lanzar una excepción y no necesita capturarla (gracias a RAII).
En las aplicaciones offline de procesamiento de datos, a menudo es aceptable no capturar excepciones.
En los servidores que atienden solicitudes de usuarios, normalmente basta con capturar las excepciones en el nivel superior del controlador de conexión.
En las funciones de hilo, debe capturar y conservar todas las excepciones para volver a lanzarlas en el hilo principal después de join.
errno, compruebe siempre el resultado y lance una excepción en caso de error.
- Cree una función (
done()ofinalize()) que haga de antemano todo el trabajo que pueda dar lugar a una excepción. Si se llamó a esa función, después no debería haber excepciones en el destructor. - Las tareas demasiado complejas (como enviar mensajes a través de la red) pueden colocarse en un método independiente que el usuario de la clase tendrá que llamar antes de la destrucción.
- Si hay una excepción en el destructor, es mejor registrarla que ocultarla (si el registrador está disponible).
- En aplicaciones simples, es aceptable confiar en
std::terminate(para los casos denoexceptde forma predeterminada en C++11) para gestionar las excepciones.
- Intente obtener el mejor rendimiento posible en un solo núcleo de CPU. Después, si es necesario, podrá paralelizar el código.
- Use el pool de hilos para procesar peticiones. Hasta ahora, no hemos tenido ninguna tarea que requiera cambio de contexto en espacio de usuario.
joinAll).
Si se requiere sincronización, en la mayoría de los casos basta con usar mutex con lock_guard.
En otros casos, use primitivas de sincronización del sistema. No use espera activa.
Las operaciones atómicas deben usarse solo en los casos más simples.
No intente implementar estructuras de datos lock-free a menos que sea su principal área de especialización.
9. Punteros frente a referencias.
En la mayoría de los casos, prefiera las referencias.
10. const.
Use referencias constantes, punteros a constantes, const_iterator y métodos const.
Considere const como la opción predeterminada y use variantes sin const solo cuando sea necesario.
Al pasar variables por valor, usar const normalmente no tiene sentido.
11. unsigned.
Use unsigned si es necesario.
12. Tipos numéricos.
Usa los tipos UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32 e Int64, así como size_t, ssize_t y ptrdiff_t.
No uses estos tipos para números: signed/unsigned long, long long, short, signed/unsigned char, char.
13. Paso de argumentos.
Pasa los valores complejos por valor si se van a mover y usa std::move; pásalos por referencia si quieres actualizar el valor en un bucle.
Si una función asume la propiedad de un objeto creado en el heap, haz que el tipo del argumento sea shared_ptr o unique_ptr.
14. Valores de retorno.
En la mayoría de los casos, simplemente usa return. No escribas return std::move(res).
Si la función reserva un objeto en el heap y lo devuelve, usa shared_ptr o unique_ptr.
En casos poco frecuentes (actualizar un valor en un bucle), puede que necesites devolver el valor mediante un argumento. En ese caso, el argumento debe ser una referencia.
namespace.
No es necesario usar un namespace separado para el código de la aplicación.
Las bibliotecas pequeñas tampoco lo necesitan.
En bibliotecas medianas o grandes, ponga todo en un namespace.
En el archivo .h de la biblioteca, puede usar namespace detail para ocultar detalles de implementación que el código de la aplicación no necesita.
En un archivo .cpp, puede usar un namespace static o anónimo para ocultar símbolos.
Además, se puede usar un namespace para un enum a fin de evitar que los nombres correspondientes terminen en un namespace externo (aunque es mejor usar un enum class).
16. Inicialización diferida.
Si se requieren argumentos para la inicialización, normalmente no debería escribir un constructor predeterminado.
Si más adelante necesita posponer la inicialización, puede agregar un constructor predeterminado que cree un objeto no válido. O, para una cantidad pequeña de objetos, puede usar shared_ptr/unique_ptr.
std::string y char *. No use std::wstring ni wchar_t.
19. Logging.
Vea los ejemplos en todo el código.
Antes de hacer commit, elimine todo logging irrelevante y de depuración, así como cualquier otro tipo de salida de depuración.
Debe evitarse el logging en bucles, incluso en el nivel Trace.
Los logs deben ser legibles en cualquier nivel de logging.
Por lo general, el logging solo debe usarse en el código de aplicación.
Los mensajes de log deben escribirse en inglés.
Preferiblemente, el log debe ser comprensible para el administrador del sistema.
No use lenguaje vulgar en el log.
Use codificación UTF-8 en el log. En casos excepcionales, puede usar caracteres no ASCII en el log.
20. Entrada/salida.
No use iostreams en bucles internos críticos para el rendimiento de la aplicación (y nunca use stringstream).
Use en su lugar la biblioteca DB/IO.
21. Fecha y hora.
Vea la biblioteca DateLUT.
22. include.
Use siempre #pragma once en lugar de guardas de inclusión.
23. using.
No se usa using namespace. Puede usar using para algo específico. Pero hágalo de forma local dentro de una clase o función.
24. No use trailing return type para las funciones, salvo que sea necesario.
virtual en la clase base, pero override en lugar de virtual en las clases derivadas.
Características no utilizadas de C++
Plataforma
clang. En el momento de redactar esto (marzo de 2025), el código se compila con clang versión >= 19.
Se utiliza la biblioteca estándar (libc++).
4. SO: Ubuntu Linux, no más antiguo que Precise.
5. El código está escrito para la arquitectura de CPU x86_64.
El conjunto de instrucciones de la CPU es el conjunto mínimo compatible entre nuestros servidores. Actualmente, es SSE 4.2.
6. Use las opciones de compilación -Wall -Wextra -Werror -Weverything con algunas excepciones.
7. Use enlazado estático con todas las bibliotecas, excepto aquellas que sean difíciles de enlazar estáticamente (vea la salida del comando ldd).
8. El código se desarrolla y depura con la configuración de release.
Herramientas
gdb, valgrind (memcheck), strace, -fsanitize=... o tcmalloc_minimal_debug.
3. Para el análisis de rendimiento, usa Linux Perf, valgrind (callgrind) o strace -cf.
4. El código fuente está en Git.
5. La compilación usa CMake.
6. Los programas se distribuyen en paquetes deb.
7. Los commits a master no deben romper la compilación.
Aunque solo determinadas revisiones se consideran utilizables.
8. Haz commits tan a menudo como sea posible, incluso si el código solo está parcialmente listo.
Usa ramas para ello.
Si tu código en la rama master aún no compila, exclúyelo de la compilación antes de hacer push. Tendrás que terminarlo o eliminarlo en unos pocos días.
9. Para cambios no triviales, usa ramas y publícalas en el servidor.
10. El código no utilizado se elimina del repositorio.
Bibliotecas
boost y Poco.
2. No se permite usar bibliotecas de paquetes del sistema operativo. Tampoco se permite usar bibliotecas preinstaladas. Todas las bibliotecas deben incluirse como código fuente en el directorio contrib y compilarse con ClickHouse. Consulta Directrices para añadir nuevas bibliotecas de terceros para más detalles.
3. Siempre se da preferencia a las bibliotecas que ya se están utilizando.
Recomendaciones generales
using en lugar de clases o structs.
5. Si es posible, no escriba constructores de copia, operadores de asignación, destructores (salvo uno virtual, si la clase contiene al menos una función virtual), constructores de movimiento ni operadores de asignación por movimiento. En otras palabras, las funciones generadas por el compilador deben funcionar correctamente. Puede usar default.
6. Se recomienda simplificar el código. Reduzca su tamaño siempre que sea posible.
Recomendaciones adicionales
std:: para los tipos de stddef.h
no se recomienda. En otras palabras, recomendamos escribir size_t en lugar de std::size_t, porque es más corto.
Es aceptable añadir std::.
2. Especificar explícitamente std:: para las funciones de la biblioteca estándar de C
no se recomienda. En otras palabras, escriba memcpy en lugar de std::memcpy.
La razón es que existen funciones similares no estándar, como memmem. Sí usamos estas funciones de vez en cuando. Estas funciones no existen en namespace std.
Si escribe std::memcpy en lugar de memcpy en todas partes, memmem sin std:: quedará raro.
No obstante, puede seguir usando std:: si lo prefiere.
3. Usar funciones de C cuando las mismas estén disponibles en la biblioteca estándar de C++.
Esto es aceptable si resulta más eficiente.
Por ejemplo, use memcpy en lugar de std::copy para copiar grandes fragmentos de memoria.
4. Argumentos de función en varias líneas.
Se permite cualquiera de los siguientes estilos de ajuste de línea: