gpt4 book ai didi

c++ - 是否可以创建一个生成转发函数的可变参数宏?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:07:02 25 4
gpt4 key购买 nike

我很确定这个问题的答案是否定的,但我很高兴被证明是错误的

假设我有一个可变模板函数

template <typename... Args>
void log(Args&&... args) { ... }

...而且我还有宏来生成具体的日志记录函数。喜欢:

#define LOG_2(fname, arg0_type, arg0_name, arg1_type, arg1_name) \
void fname(arg0_type arg0_name, arg1_type arg1_name) { \
log(std::forward<arg0_type>(arg0_name), std::forward<arg1_type>(arg1_name)); }

名为 LOG_0LOG_10 的宏也有类似的代码。

我用它们来生成有两个目的的特殊日志记录函数。首先,他们为参数赋予强类型,其次,他们赋予它们名称,这些名称可用作代码完成提示。示例:

LOG_2(progress_log, const char*, msg, float, part)
...
progress_log("Loading: ", complete/total);

然后除了 args 的强类型之外,我得到以下很好的代码完成提示:

code completion hint

那么,是否可以在宏中删除显式元数并编写具有完全相同效果但使用可变参数宏的内容? ...并且也不是使用计数器通过将适当的数字与 LOG_ 连接来调用底层 arity-macros 之一的宏。

我知道我可以计算参数,但大多数代码完成工具完全无法扩展计数宏,尤其是在可能的参数数量很大(比如 10)的情况下。因此,代码完成效果丢失了。如果我愿意放弃代码完成提示效果,那么我宁愿使用类似 LOG_FUN(progress_log, void(const char*, float)) 的东西,它也可以使用生成编译错误,但可以没有参数名称作为提示

最佳答案

Boost.Preprocessor当你真的想用预处理器宏做一些花哨的事情时,这个库就是为这种情况而制作的。不过,它可能会变得有点难看。

#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/arithmetic.hpp>
#include <boost/preprocessor/punctuation.hpp>
#include <boost/preprocessor/logical.hpp>
#include <utility>

template <typename... Args>
void log(Args&&...) {}

#define DEF_LOG_COMMA_BEFORE_PAIR(i) \
BOOST_PP_COMMA_IF(BOOST_PP_AND(i, BOOST_PP_NOT(BOOST_PP_MOD(i,2))))

#define DEF_LOG_EXPAND_PARAM(r, data, i, elem) \
DEF_LOG_COMMA_BEFORE_PAIR(i) elem

#define DEF_LOG_EXPAND_CALL(r, data, i, elem) \
DEF_LOG_COMMA_BEFORE_PAIR(i) \
BOOST_PP_IF( BOOST_PP_MOD(i,2), \
(elem), \
std::forward<elem> )

#define DEF_LOG_FUNC(name, ...) \
inline void name( \
BOOST_PP_SEQ_FOR_EACH_I(DEF_LOG_EXPAND_PARAM, ~, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
) { log( \
BOOST_PP_SEQ_FOR_EACH_I(DEF_LOG_EXPAND_CALL, ~, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
); }

DEF_LOG_FUNC(progress_log, const char*, msg, float, part)

int main() {
progress_log("Progress", 0.25);
}

要将其分开,请先查看 DEF_LOG_COMMA_BEFORE_PAIR宏。如果参数i,它只是一个扩展为逗号的助手是非零的偶数,否则展开为零。通过将它放在转换一个 vararg 参数的结果之前,我们每隔一项就得到一个逗号。 (After 可能看起来更自然,但是在最后一项之后排除逗号会比这种排除第一项之前的逗号的方法复杂一点。) 注意 BOOST_PP_COMMA_IF 用于避免过早扩展为逗号,这会混淆其他 Boost 宏。

跳转到主宏的定义DEF_LOG_FUNC ,你会看到它使用 BOOST_PP_SEQ_FOR_EACH_I 两次。这是一个将提供的宏应用于传递的参数序列中的每个术语的工具。给定的宏必须接受四个参数:

  • r : 当你需要多级预处理器循环时使用

  • data : 一系列标记直接通过 BOOST_PP_SEQ_FOR_EACH_I对宏的每次调用

  • i : 序列元素从零开始的索引

  • elem : 实际序列元素

我们的宏实际上都不需要 data , 所以我们只传递 ~作为 BOOST_PP_SEQ_FOR_EACH_I 的虚拟标记使用。我们传递给 BOOST_PP_SEQ_FOR_EACH_I 的两个辅助宏是 DEF_LOG_EXPAND_PARAM用于构建函数参数列表和 DEF_LOG_EXPAND_CALL用于构建实际的参数列表 log功能。 DEF_LOG_EXPAND_PARAM使用DEF_LOG_COMMA_BEFORE_PAIR,除了添加逗号外不需要做任何事情 helper 。 DEF_LOG_EXPAND_CALL需要对偶数和奇数参数做不同的事情,所以除了 DEF_LOG_COMMA_BEFORE_PAIR工具,它使用 BOOST_PP_IF 将偶数(类型)参数转换为 std::forward<elem>(elem) 的奇数(参数)参数.

因此,根据需要,预处理器替换

DEF_LOG_FUNC(progress_log, const char*, msg, float, part)

作为

inline void progress_log(
const char* msg , float part
) { log(
std::forward<const char*> (msg) , std::forward<float> (part)
); }

关于c++ - 是否可以创建一个生成转发函数的可变参数宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48241509/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com