El Preprocesador de C (CPP) a menudo se malinterpreta como un simple traductor de texto a texto. En realidad, el estándar de C lo define como una serie de fases que transforman el código fuente en un flujo de tokens de C. Aunque muchas implementaciones proporcionan una salida de texto para mayor visibilidad, el mecanismo subyacente está diseñado para preparar una unidad de traducción para el analizador de C. David Priver, mientras desarrollaba su propio compilador e intérprete C2Y, implementó un CPP personalizado e introdujo varias extensiones para hacer que el preprocesador fuera más expresivo y fácil de depurar.
Uno de los objetivos principales de estas extensiones es reducir la necesidad de 'abuso del preprocesador', es decir, definiciones de macros complejas y anidadas utilizadas para simular funcionalidades que el CPP estándar no posee. Por ejemplo, Priver introdujo `__print(...)` y `__where()` como macros de depuración integradas. `__print()` expande sus argumentos y los escribe directamente en stderr, lo que permite a los desarrolladores ver exactamente en qué se expande una macro sin volcar toda la unidad de traducción. `__where()` identifica el archivo y la línea específicos donde se definió una macro, resolviendo el problema común de rastrear expansiones oscuras en bases de código grandes.
Para manejar las transformaciones de listas, Priver añadió un builtin `__map(macro, ...)`. En el C estándar, aplicar una transformación a cada argumento en una secuencia normalmente requiere macros de tipo función recursiva, las cuales el estándar de C restringe suprimiendo la expansión. Si bien los desarrolladores pueden 'fingir' la recursión bombeando iteraciones manualmente, esto suele ser lento y visualmente desordenado. El builtin `__map()` simplifica esto realizando un escaneo lineal, aplicando la macro proporcionada a cada argumento una sola vez. Además, incluyó un builtin `__VA_COUNT__` para simplificar el manejo de macros variádicas, permitiendo a los desarrolladores obtener directamente el número de argumentos en lugar de depender de técnicas complejas de conteo manual. Estas adiciones tienen como objetivo hacer que el preprocesador sea una herramienta más potente y legible al proporcionar soporte nativo para patrones comunes de metaprogramación.
