Última actualización:2026-10-12
Cómo leer código C++ de código abierto¶
¿Cómo entender al instante el código de proyectos de código abierto?¶
En proyectos reales o en código abierto, sobre todo en código C, gustan mucho las macros, lo que provoca un gran problema: leer el código resulta muy difícil, y la depuración es otro problema aparte. Si te dan un proyecto lleno de macros, ¿cómo entenderlo rápidamente?
En las entrevistas solemos encontrarnos con un punto de conocimiento muy importante: el proceso de compilación de gcc. Puede que lo recites de memoria; pues bien, para diferenciar el nivel de los candidatos se deriva hacia: ¿qué parámetros de compilación de gcc conoces y para qué sirve cada uno? ¿Qué ocurre en la etapa de preprocesamiento? ¿Qué haces normalmente en esa etapa y en qué te ayuda?
En esta sección tomamos un proyecto de código abierto como ejemplo para entender el código al instante.
A continuación hay un fragmento de código de libev con varias macros, por ejemplo:
- EV_FREQUENT_CHECK
- assert
EV_P_- ANHE_w
- ANHE_at_cache
void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
if (expect_false (ev_is_active (w)))
return;
ev_at (w) += mn_now;
assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.));
EV_FREQUENT_CHECK;
++timercnt;
ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);
array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);
ANHE_w (timers [ev_active (w)]) = (WT)w;
ANHE_at_cache (timers [ev_active (w)]);
upheap (timers, ev_active (w));
EV_FREQUENT_CHECK;
/*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
}
¿No parece un galimatías a primera vista? Este código plantea varias preguntas:
- ¿Cuántos parámetros tiene la función?
- ¿Cuál es el tipo de la función?
- ¿Qué demonios es ev_at en el código?
Para entender el código al instante, necesitamos usar -E para obtener el código tras la expansión de las macros en la etapa de preprocesamiento, lo que facilita su comprensión y lectura.
Tras la expansión, obtenemos:
void __attribute__ ((__noinline__))
ev_timer_start (struct ev_loop *loop, ev_timer *w)
{
if (__builtin_expect ((!!((0 + ((ev_watcher *)(void *)(w))->active))),(0)))
return;
((WT)(w))->at += ((loop)->mn_now);
# 3881 "ev.c" 3 4
((
# 3881 "ev.c"
("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.)
# 3881 "ev.c" 3 4
) ? (void) (0) : __assert_fail (
# 388libev: ev_timer_start called with negative timer repeat value\", w->repeat >= 0.)"
# 3881 "ev.c" 3 4
, "ev.c", 3881, __PRETTY_FUNCTION__))
# 3881 "ev.c"
;
do { } while (0);
++((loop)->timercnt);
ev_start (loop, (W)w, ((loop)->timercnt) + (4 - 1) - 1);
if (__builtin_expect ((!!((((W)(w))->active + 1) > (((loop)->timermax)))),(0))) { int __attribute__ ((__unused__)) ocur_ = (((loop)->timermax)); (((loop)->timers)) = (ANHE *)array_realloc (sizeof (ANHE), (((loop)->timers)), &(((loop)->timermax)), (((W)(w))->active + 1)); ; };
(((loop)->timers) [((W)(w))->active]).w = (WT)w;
(((loop)->timers) [((W)(w))->active]).at = (((loop)->timers) [((W)(w))->active]).w->at;
upheap (((loop)->timers), ((W)(w))->active);
do { } while (0);
}
A estas alturas, entender el código ya no presenta apenas obstáculos, pero todavía no es lo bastante claro y nítido. Para marcar la diferencia con otros candidatos, planteo algunas preguntas:
- Arriba se muestran muchos números de línea, ¿se pueden quitar? ¿Cómo se resuelve?
- Las macros de sistema tipo assert, como la de arriba, son muy sencillas de entender y no hace falta expandirlas; ¿sabes cómo hacerlo?
- En esos dos pasos de expansión pueden surgir problemas; ¿sabes qué ocurre y cómo resolverlo?
Bien, esto es todo por ahora; en la próxima sección sigo compartiendo. ¡Te invito a discutir las respuestas anteriores en la zona de comentarios!
Temas relacionados¶
- Seis métodos para dividir cadenas en C++ | C++那些事
- Notas sobre el desarrollo de extensiones de CPython | C++那些事
- Chuleta de desarrollo con libcurl en C++ | C++那些事
- Un fragmento de código C++ interesante | C++那些事
- Imprimir 1-100 en C++ sin bucles | C++那些事