31 votes

Imprimer des valeurs de macro sans connaître le nombre de macros

J'ai du code qui inclut un fichier généré (je ne connais pas à l'avance son contenu), il y a juste une convention sur laquelle moi et mes utilisateurs sommes convenus de la façon de créer ce fichier afin que je puisse l'utiliser. Ce fichier ressemble à

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

Je souhaite imprimer toutes les valeurs des macros. Mon code actuel ressemble à

 #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
 

Ma question est de savoir comment répéter les macros dans le fichier généré afin que je n'aie pas tellement besoin de dupliquer mon code

55voto

Quentin Points 3904

Tout d'abord, nous savons que nous pouvons compter sur un coup de pouce.Préprocesseur pour notre boucle besoins. Toutefois, le code généré doit travailler sur son propre. Malheureusement, #ifdef ne peut pas travailler en raison de l'expansion des macros, donc il n'y a aucun moyen de générer du code dans votre question. Sommes-nous grillées?

Pas encore! Nous pouvons tirer avantage du fait que vos macros sont soit inexistants, soit une chaîne littérale. Considérez les points suivants:

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

Nous profitons de notre vieil ami, le plus grand d'analyser ici. La deuxième ligne peut être interprété de deux manières, selon que MACRO1 est défini. Sans elle, c'est équivalent à:

char const *probe(char const *MACRO1);

... qui est une déclaration de fonction où MACRO1 est le nom du paramètre. Mais, lorsqu' MACRO1 est "B", il devient équivalent à:

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

... qui est une variable initialisée à point à l' "B". On peut ensuite passer sur le type de ce que nous venons de produit pour voir si une substitution s'est produite:

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

Nous pourrions faire usage de l' if constexpr ici, mais std::cout pouvez sortie d'un pointeur de fonction (il convertit en bool) de sorte que la branche morte est valide, et le compilateur est assez intelligent pour optimiser complètement hors.

Enfin, nous reviendrons à coup de fouet.Préprocesseur pour générer ce genre de choses pour nous:

#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à!

Voir en direct sur Coliru

Remarque: le Coliru extrait comprend avertissement disablers pour GCC et Clang, qui mettent en garde contre notre pauvre pal la plus délicate à analyser :(

3voto

Eljay Points 1439

J'ai couru dans le même type de besoin il y a longtemps.

Ma solution a été d'utiliser le préprocesseur, mais ce n'est pas la réponse "dans le code".

Par exemple, clang++ -dM -E test.cpp seront de sortie de toutes les macros. (À l'époque, j'ai utilisé gcc, mais la même technique fonctionne pour GCC, CLang, et Visual Studio CL.EXE ... le compilateur commutateurs peuvent varier.)

Ahh, drat, qui comprend également toutes les macros prédéfinies.

Je voudrais donc produire une "liste noire" fichier de macros prédéfinies que je ne se soucient pas, et ensuite l'utiliser pour filtrer ces résultats (à l'aide d' grep -v).

L'autre problème que j'ai rencontré était que, parfois, quelqu'un serait - #undef IMPORTANT_MACRO qui aura donc manqué de l'image. Pour ces rares situations... et puis les meurtres ont commencé.

0voto

VTT Points 27056

Cette réponse est écrite en prenant une question de suivi en compte.

C++ a un support pour la programmation générique qui, souvent, élimine le besoin pour le préprocesseur. Dans ce cas, il serait préférable de faire un ensemble de traits de type de déclarer des propriétés de paramètres qui doivent être manipulés de la réduction du rôle de préprocesseur pour la compilation conditionnelle (ou l'éliminer complètement si ce code est censé être généré à chaque fois):

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"};
};

Cela vous permettra d'effectuer une itération sur les paramètres de requête et de leurs propriétés à l'aide de code générique:

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;
}

en ligne compilateur

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X