saber que programa peso para necesito micronutrientes macronutrientes los como carbohidratos calcular calculadora bajar alimentos c++ c-preprocessor

c++ - que - Imprime valores de macro sin saber la cantidad de macros



micronutrientes (3)

Tengo un código que incluye un archivo generado (no sé de antemano su contenido), solo hay una convención en la que mis usuarios y yo acordamos cómo crear este archivo para poder usarlo. Este archivo parece

#define MACRO0 "A" #define MACRO1 "B" #define MACRO2 "C" ...

Quiero imprimir todos los valores de macros. Mi código actual parece

#ifdef MACRO0 std::cout << "MACRO0 " << MACRO0 << std::endl; #endif #ifdef MACRO1 std::cout << "MACRO1 " << MACRO1 << std::endl; #endif #ifdef MACRO2 std::cout << "MACRO2 " << MACRO2 << std::endl; #endif

Mi pregunta es, cómo iterar sobre las macros en el archivo generado para no tener que duplicar tanto mi código


En primer lugar, sabemos que podemos contar con Boost.Preprocessor para nuestras necesidades de bucle. Sin embargo, el código generado debe funcionar por sí solo. Desafortunadamente, #ifdef no puede funcionar como resultado de la expansión de macros, por lo que no hay forma de generar el código en su pregunta. ¿Estamos tostados?

¡Aún no! Podemos aprovechar el hecho de que todas sus macros son inexistentes o una cadena literal. Considera lo siguiente:

using StrPtr = char const *; StrPtr probe(StrPtr(MACRO1));

Estamos aprovechando de nuestro viejo amigo el análisis más molesto aquí. La segunda línea se puede interpretar de dos maneras dependiendo de si MACRO1 está definido. Sin ella, es equivalente a:

char const *probe(char const *MACRO1);

... que es una declaración de función donde MACRO1 es el nombre del parámetro. Pero, cuando MACRO1 se define como "B" , se convierte en equivalente a:

char const *probe = (char const *) "B";

... que es una variable inicializada para apuntar a "B" . Luego podemos activar el tipo de lo que acabamos de producir para ver si se produjo una sustitución:

if(!std::is_function<decltype(probe)>::value) std::cout << "MACRO1 " << probe << ''/n'';

Podríamos usar if constexpr aquí, pero std::cout puede generar un puntero de función (lo convierte en bool ), por lo que la rama muerta es válida y el compilador es lo suficientemente inteligente como para optimizarla por completo.

Finalmente, volvemos a Boost.Preprocessor para generar todas esas cosas para nosotros:

#define PRINT_IF_DEFINED(z, n, data) / { / StrPtr probe(StrPtr(BOOST_PP_CAT(MACRO, n))); / if(!std::is_function<decltype(probe)>::value) / std::cout << "MACRO" BOOST_PP_STRINGIZE(n) " " << probe << ''/n''; / } #define PRINT_MACROS(num) / do { / using StrPtr = char const *; / BOOST_PP_REPEAT(num, PRINT_IF_DEFINED, ~) / } while(false)

... voilà!

Véalo en vivo en Coliru

Nota: el fragmento de código de Coliru incluye desactivadores de advertencia para GCC y Clang, que advierten contra nuestro pobre amigo el análisis más molesto :(


Esta respuesta está escrita teniendo en cuenta una pregunta de seguimiento .

C ++ tiene soporte para la programación genérica que a menudo elimina la necesidad de preprocesador. En este caso, sería mejor hacer un conjunto de rasgos de tipo que declaren las propiedades de los parámetros que deben manejarse, reduciendo la función del preprocesador a la compilación condicional (o eliminándola por completo si se supone que este código se genera cada vez):

enum class t_Param { begin, a = begin, b, c, d, e, z, end }; template<t_Param param, typename TEnabled = void> class t_ParamIsEnabled final: public ::std::true_type {}; template<t_Param param> class t_ParamIsEnabled < param , typename ::std::enable_if < (t_Param::end == param) #ifndef A1 || (t_Param::a == param) #endif #ifndef B2 || (t_Param::b == param) #endif #ifndef C3 || (t_Param::c == param) #endif #ifndef D4 || (t_Param::d == param) #endif #ifndef E5 || (t_Param::e == param) #endif >::type > final: public ::std::false_type {}; template<t_Param param> class t_ParamTrait; template<> class t_ParamTrait<t_Param::a> final { public: static constexpr auto const & num{"1"}; public: static constexpr auto const & val{"A"}; }; template<> class t_ParamTrait<t_Param::b> final { public: static constexpr auto const & num{"2"}; public: static constexpr auto const & val{"B"}; }; template<> class t_ParamTrait<t_Param::c> final { public: static constexpr auto const & num{"3"}; public: static constexpr auto const & val{"C"}; }; template<> class t_ParamTrait<t_Param::d> final { public: static constexpr auto const & num{"4"}; public: static constexpr auto const & val{"D"}; }; template<> class t_ParamTrait<t_Param::e> final { public: static constexpr auto const & num{"5"}; public: static constexpr auto const & val{"E"}; }; template<> class t_ParamTrait<t_Param::z> final { public: static constexpr auto const & num{"26"}; public: static constexpr auto const & val{"ZZ"}; };

Esto le permitirá iterar sobre los parámetros y consultar sus propiedades usando un código genérico:

template<t_Param param> typename ::std::enable_if<t_ParamIsEnabled<param>::value>::type Echo(void) { ::std::cout << t_ParamTrait<param>::val << ":" << t_ParamTrait<param>::num << ::std::endl; } template<t_Param param> typename ::std::enable_if<!t_ParamIsEnabled<param>::value>::type Echo(void) { // Do nothing } template<int param_index = 0> void Echo_All(void) { Echo<static_cast<t_Param>(param_index)>(); Echo_All<param_index + 1>(); } template<> void Echo_All<static_cast<int>(t_Param::end)>(void) { // Do nothing. } int main() { Echo_All(); return 0; }

compilador en línea


Me encontré con el mismo tipo de necesidad hace mucho tiempo.

Mi solución fue usar el preprocesador, pero no obtener la respuesta "dentro del código".

Por ejemplo, clang++ -dM -E test.cpp generará todas las macros. (En ese momento, usé gcc , pero la misma técnica funciona para GCC, CLang y CL.EXE de Visual Studio ... los modificadores del compilador pueden variar).

Ahh, drat, que también incluye todas las macros predefinidas.

Así que produciría un archivo de "lista negra" de las macros predefinidas que no me importaban, y luego lo usaría para filtrar esos resultados (usando grep -v ).

El otro problema que encontré fue que a veces alguien #undef IMPORTANT_MACRO que luego se perdería en el volcado. Para esas situaciones infrecuentes ... y luego comenzaron los asesinatos .