我的代码包含一个生成的文件(我事先并不知道它的内容),只有一个约定我和我的用户同意如何创建这个文件,以便我可以使用它。此文件类似于
#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
我的问题是,如何迭代生成的文件中的宏,所以我不需要复制我的代码
答案 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)
......瞧!
注意: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;
}