【问题标题】:How to get name for each argument in variadic macros?如何获取可变参数宏中每个参数的名称?
【发布时间】:2017-10-13 08:09:06
【问题描述】:

可以创建一个宏 str(a),它将使用其参数 (a) 及其字符串化名称 (#a),例如:

#include <iostream>

#define str(a) #a, " ", a

int main()
{
    int i = 5;
    float f = 4.5;
    const char* s = "string";

    auto l = [] (const auto&... p) { (std::cout << ... << p) << std::endl; };

    l(str(i));
    l(str(f));
    l(str(s));
}

Example.

有没有一种简单的方法来打印 variable 数量的参数,每个参数的名称都在前面?即从以下实现PREPEND_EACH_ARG_WITH_HASH_ARG

#include <iostream>
#include <tuple>

template <typename ... Ts>
void print_all(const Ts&... ts)
{
    (std::cout << ... << ts) << std::endl;
}

#define PREPEND_EACH_ARG_WITH_HASH_ARG(...) // how to implement '#a, " ", a' here?
#define PRINT_ALL(...) print_all(PREPEND_EACH_ARG_WITH_HASH_ARG(__VA_ARGS__))

int main()
{
    auto a = 10;
    auto b = 20.1;
    auto c = "string";
    auto d = 'c';
    PRINT_ALL(a, b, c, d);
}

Example.

【问题讨论】:

    标签: c++ macros c++14 c++17


    【解决方案1】:

    Boost.Preprocessor 库绝对是一个很好的解决方案。但是,如果您不想依赖外部库,则可以执行以下操作:

    #include <iostream>
    
    #define str(a) #a, " = ", a
    
    #define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
    #define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
    
    #define CONCAT_IMPL( x, y ) x##y
    #define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
    
    // to verify, run the preprocessor alone (g++ -E):
    #define PREPEND_EACH_ARG_WITH_HASH_ARG_1(a) str(a)
    #define PREPEND_EACH_ARG_WITH_HASH_ARG_2(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_1(__VA_ARGS__)
    #define PREPEND_EACH_ARG_WITH_HASH_ARG_3(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_2(__VA_ARGS__)
    #define PREPEND_EACH_ARG_WITH_HASH_ARG_4(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_3(__VA_ARGS__)
    #define PREPEND_EACH_ARG_WITH_HASH_ARG_5(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_4(__VA_ARGS__)
    #define PREPEND_EACH_ARG_WITH_HASH_ARG(...) MACRO_CONCAT(PREPEND_EACH_ARG_WITH_HASH_ARG_, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__) 
    #define PRINT_ALL(...) print_all(PREPEND_EACH_ARG_WITH_HASH_ARG(__VA_ARGS__))
    
    template<typename T>
    void print_impl(const T& t)
    {
       std::cout << t;
    }
    
    template<typename T, typename... Ts>
    void print_impl(const T& t, const Ts&... ts)
    {
       std::cout << t;
       print_impl(ts...);
    }
    
    
    template <typename ... Ts>
    void print_all(const Ts&... ts)
    {
       print_impl(ts...);
       std::cout << std::endl;
    }
    
    int main()
    {
       auto a = 10;
       auto b = 20.1;
       auto c = "string";
       auto d = 'c';
       PRINT_ALL(a, b, c, d);
    }
    

    这里的想法是使用VA_NUM_ARGS 计算参数的数量,并使用其结果调用正确的PREPEND_EACH_ARG_WITH_HASH_ARG_# 宏,该宏将为__VA_ARGS__ 中的每个参数“递归”调用下一个PREPEND_EACH_ARG_WITH_HASH_ARG_# .

    一个小缺点是它可以接受的参数数量有限,但另一方面,这很容易扩展。

    【讨论】:

    • IOW,自己重新实现那一点 Boost.Preprocessor :-)
    • @Angew 或多或少是的 :) 我不确定 Boost 库是如何实现的(它可能比我的示例更智能)。我通常喜欢独立于第三方库,所以我认为值得一提的解决方案。
    • @Banan,感谢您的解释,但是接受了对 boost 的引用作为答案,因为它更“简单”:)
    【解决方案2】:

    如果你可以使用Boost.Preprocessor,你可以这样做:

    #define PROCESS_ONE_ELEMENT(r, unused, idx, elem) \
      BOOST_PP_COMMA_IF(idx) BOOST_PP_STRINGIZE(elem) , " " , elem
    
    #define PRINT_ALL(...) \
      print_all(BOOST_PP_SEQ_FOR_EACH_I(PROCESS_ONE_ELEMENT, %%, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))
    

    [Live example]

    我使用%% 作为“未使用值”的占位符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-27
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      相关资源
      最近更新 更多