在不知道宏的数量的情况下打印宏值

时间:2017-11-23 10:15:19

标签: c++ c-preprocessor

我的代码包含一个生成的文件(我事先并不知道它的内容),只有一个约定我和我的用户同意如何创建这个文件,以便我可以使用它。此文件类似于

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

我想打印所有宏值。我当前的代码看起来像

#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

我的问题是,如何迭代生成的文件中的宏,所以我不需要复制我的代码

3 个答案:

答案 0 :(得分:50)

首先,我们知道我们可以依靠Boost.Preprocessor来满足我们的循环需求。但是,生成的代码必须单独工作。遗憾的是,#ifdef无法通过宏扩展工作,因此无法在您的问题中生成代码。我们烤了吗?

还没有!我们可以利用您的宏都不存在或字符串文字这一事实。请考虑以下事项:

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

我们正在利用我们的老朋友这里最令人烦恼的解析。第二行可以用两种方式解释,具体取决于是否定义了MACRO1。没有它,它相当于:

char const *probe(char const *MACRO1);

...这是一个函数声明,其中MACRO1是参数的名称。但是,当MACRO1定义为"B"时,它将等同于:

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

...这是一个初始化为"B"的变量。然后我们可以打开我们刚刚生成的类型,看看是否发生了替换:

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

我们可以在这里使用if constexpr,但是std::cout可以输出一个函数指针(它将它转换为bool),因此死分支是有效的,并且编译器足够聪明完全优化它。

最后,我们回到Boost.Preprocessor为我们生成所有内容:

#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)

......瞧!

See it live on Coliru

注意:Coliru片段包含GCC和Clang的警告禁用,它警告我们这个可怜的朋友最麻烦的解析:(

答案 1 :(得分:3)

很久以前我遇到了同样的需求。

我的解决方案是使用预处理器,但不是“在代码中”得到答案。

例如,clang++ -dM -E test.cpp将输出所有宏。 (当时,我使用gcc,但相同的技术适用于GCC,CLang和Visual Studio的CL.EXE ......编译器开关可能会有所不同。)

啊,drat,它还包括所有预定义的宏。

所以我会生成一个我不关心的预定义宏的“黑名单”文件,然后用它来过滤掉那些结果(使用grep -v)。

我遇到的另一个问题是,有时会有人#undef IMPORTANT_MACRO在转储中丢失。对于那些罕见的情况...... 然后谋杀开始

答案 2 :(得分:0)

这个答案是在考虑follow-up question的情况下编写的。

C ++支持通用编程,通常不需要预处理器。在这种情况下,最好创建一组类型特征来声明需要处理的参数属性,从而减少预处理器对条件编译的作用(或者如果每次都应该生成此代码,则完全消除它):

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

这将允许您使用通用代码迭代参数并查询其属性:

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

online compiler