用表达式包装可变参数宏中的每个元素

时间:2015-03-24 03:22:23

标签: c++ c++11 macros c-preprocessor boost-preprocessor

问题:

我正在寻找一个可变的宏

#define WRAP(token, ...) 

当使用令牌和N个参数调用

WRAP(decltype, a, b, c)

将扩展为包含在令牌

中的参数的逗号分隔列表
decltype(a), decltype(b), decltype(c)

这将允许我写下类似的内容:

#define MACRO(...) \
    Class< WRAP(decltype, __VA_ARGS__) >::Function();

如果我按如下方式打电话:

MACRO(a, b, c)

它会导致:

Class<decltype(a), decltype(b), decltype(c)>::Function(0;

我不知道如何实现这一目标。可能吗?也许用BOOST_PP或其他一些?

动机:

我有一个用于记录的宏:

#define LOG(fmt, ...) \
    logger::instance().push(__FUNCTION__, fmt, __VA_ARGS__);

我有一个可变的类模板,可以验证参数是否与提供的格式字符串匹配

template<typename... Ts>
struct Format
{
    template<std::size_t N>
    static constexpr bool check(const char (&fmt)[N], std::size_t n);
};

如果我定义一个具有固定数量参数的宏,我可以调用我的格式检查功能:

#define LOG(fmt, a) \
    static_assert(Format<decltype(a1)>::check(fmt, 0), ""); \
    logger::instance().push(__FUNCTION__, fmt, a);

但是,如果我使用一个可变参数宏,这对多于一个参数不起作用:

#define LOG(fmt, ...) \
    static_assert(Format<decltype(__VA_ARGS__)>::check(fmt, 0), ""); \
    logger::instance().push(__FUNCTION__, fmt, __VA_ARGS__);

那是因为decltype(__VA_ARGS__)显然是无效的语法。

要解决此问题,我需要将__VA_ARGS__扩展为decltype(a1), decltype(a2), decltype(a3)

Variadic功能模板?

我尝试使用constexpr可变参数函数模板实现此目的,但我无法将fmt传递给static_assert,因为此时它不再是字符串文字:

template<size_t N, typename... Ts>
constexpr void check(const char (&fmt)[N], const Ts&...)
{
    static_assert(Format<Ts...>::check(fmt, 0), "");
}

尝试调用此检查功能

check("%s", "hello world"); 

无法编译:

main.cpp:216:46:   in constexpr expansion of ‘Format<T, Ts ...>::check<7ul>((* & fmt), 0ul)’
main.cpp:216:5: error: ‘fmt’ is not a constant expression

1 个答案:

答案 0 :(得分:3)

Boost.PP非常简单:

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define COMMA_SEP(r, token, i, e) BOOST_PP_COMMA_IF(i) token(e)
#define WRAP(token, ...) BOOST_PP_SEQ_FOR_EACH_I(COMMA_SEP, token, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

修改:如果__VA_ARGS__为空,则上述工作无效,要解决此问题,请参阅this solution,但请注意解决方案已赢得&#39 ; t适用于MSVC,因为它们的预处理器是非标准的,如果你想支持MSVC,只需使用BOOST_PP_IS_EMPTY代替ISEMPTY