【问题标题】:Using boost preprocessor to call a variadic template iteratively使用 boost 预处理器迭代调用可变参数模板
【发布时间】:2016-01-27 08:05:14
【问题描述】:

假设我有一个可变参数模板:

template<typename... Args>
class Foo;

这个可变参数模板递归地生成另一个模板,直到它到达最后一级的一个参数Foo。现在我想要一个宏,例如Bar(...),当我调用它时,我会得到这样的结果:

Bar(float, int, string, vector<int>)
// expands to
Macro(Foo<float, int, string, vector<int>>)
Macro(Foo<int, string, vector<int>>)
Macro(Foo<string, vector<int>>)
Macro(Foo<vector<int>>)

Macro(...) 是另一个在这个类上做某事的宏。我希望能够使用 Boost Preprocessor 来减少我必须编写的代码。

请给我一些建议,帮助我编写这样的宏。

【问题讨论】:

  • 这是可能的,但这听起来很像XY Problem
  • BarMacro 是不同的宏吗?您是否希望为可变参数模板的每个调用扩展调用 Macro 宏?
  • 感谢您的建议。虽然我不完全同意你的观点,但我改变了问题以避免看起来像一个 XY 问题。
  • @nabla 您的两个问题的答案都是肯定的。它们是不同的,Macro 必须为每个版本调用。
  • @MohsenTamiz 我认为this slight variation 更有意义。

标签: c++ boost macros boost-preprocessor


【解决方案1】:

我不知道这是否是解决您的问题的最佳方法,但这可以满足您的需求:

#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define CALL_MACRO(_,__,SEQ) Macro(Foo<BOOST_PP_SEQ_ENUM(SEQ)>)

#define GENERATE_MACRO_INVOCATIONS(SEQS) BOOST_PP_SEQ_FOR_EACH(CALL_MACRO,_,SEQS)

#define GENERATE_DESCENDING_SEQUENCES(_,INDEX,DATA) (BOOST_PP_SEQ_REST_N(INDEX,DATA))

#define BAR_IMPL(SEQ) GENERATE_MACRO_INVOCATIONS(BOOST_PP_REPEAT_FROM_TO(0,BOOST_PP_SEQ_SIZE(SEQ),GENERATE_DESCENDING_SEQUENCES, SEQ))


#define Bar(...) BAR_IMPL(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Bar(float, int, string, vector<int>)
  • 您最初有一个可变参数数据:float, int, string, vector&lt;int&gt;
  • BOOST_PP_VARIADIC_TO_SEQ 将其转换为:(float)(int)(string)(vector&lt;int&gt;)
  • BOOST_PP_REPEAT_FROM_TO 调用宏 GENERATE_DESCENDING_SEQUENCES BOOST_PP_SEQ_SIZE(SEQ) 次,序列为数据,索引从 0 开始。
  • BOOST_PP_SEQ_REST_N(INDEX,DATA)DATA 中删除INDEX 的第一个元素并返回其余元素。这个结果放在一对括号内。
  • 在调用 REPEAT 之后,您有一个序列序列:

    ((float)(int)(string)(vector))((int)(string)(vector))( (字符串)(向量))((向量))

  • BOOST_PP_SEQ_FOR_EACH 使用序列序列中的每个元素调用CALL_MACRO

  • 最后BOOST_PP_SEQ_ENUM 接受一个序列并返回其元素,用逗号分隔。

Preprocessed on Coliru

【讨论】:

    【解决方案2】:

    更新:根据 cmets 编辑答案。现在调用BOOST_EXPORT_CLASS 宏而不是我定义的macro() 函数。我没有使用BOOST_EXPORT_CLASS 对此进行测试,但是,我通过在模板扩展的每个级别访问Foo&lt;...&gt; 的类型进行了模拟。我能够为不同的扩展访问每种不同的类型,所以我假设BOOST_EXPORT_CLASS 所做的任何事情都应该有效。

    我认为这现在可以满足您的需求:

    #define BAR(...) Foo<__VA_ARGS__>()
    
    // Variadic definition of Foo
    template <typename... Args>
    struct Foo;
    
    // Specialize Foo for the case that there is at least on template parameter
    template <typename Arg, typename... Args>
    struct Foo<Arg, Args...> : Foo<Args...> {
      using type = Foo<Arg, Args...>;
      Foo() : Foo<Args...>(){
        BOOST_EXPORT_CLASS(type)
      }
    };
    
    // Terminating case for Foo
    template <>
    struct Foo<> {
      using type = Foo<>;
      Foo() { BOOST_EXPORT_CLASS(type) }
    };
    
    int main() {
      BAR(int, float, double);
    }
    

    为了测试这在理论上是否可行,我定义了以下宏:

    #define MACRO(x) test<x>();
    

    并将BOOST_EXPORT_CLASS(type) 替换为MACRO(type)。函数test如下:

    template <typename T>
    void test() {
      std::cout << typeid(T).name() << "\n";
    }
    

    运行代码打印以下内容(我已经添加了 cmets),这表明模板在宏中展开并且应该出现在 BOOST_EXPORT_CLASS 中,如问题所示:

    3FooIJEE      // Foo<>
    3FooIJdEE     // Foo<double>
    3FooIJfdEE    // Foo<float, double>
    3FooIJifdEE   // Foo<int, float, double>
    

    这是代码Live Demo的现场演示。

    需要注意的是,扩展方向与OP指定的方向相反,即:

    Macro(Foo<>)
    Macro(Foo<double>)
    Macro(Foo<float, double>)
    Macro(Foo<int, float, double>)
    

    【讨论】:

    • 也许我必须更多地了解Macro,这是我必须使用的另一个宏。具体来说,这是我想用来导出每种类型的类的BOOST_EXPORT_CLASS 宏。所以我认为你的答案是无效的,它是一个模板,不是我想要的。
    • @nabla 你假设Macro 是可以简化为函数(constexpr 或其他)的东西,这似乎不是 OP 的情况。例如,Macro 可以扩展为声明。
    • @MohsenTamiz,对不起,我根据我从问题中理解的内容做了一个假设。请查看更新的答案——希望这能满足您的要求。由于我没有尝试使用 boost,请告诉我它是否有效。
    • @Elektito,是的,那是我的错误。请查看更新后的答案——我认为这符合 OP 的要求。
    • 假设这个宏声明了一个新类(例如#define Macro(name) class name{}),所以在这种情况下它不能在构造函数中。
    猜你喜欢
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多