我如何迭代打包variadic模板参数列表?

我试图找到一个方法来迭代一个pack variadic模板参数列表。 现在和所有迭代一样,您需要某种方法来了解打包列表中有多少个参数,更重要的是,如何从一个打包参数列表中单独获取数据。

总的想法是迭代列表,将int类型的所有数据存储到一个向量中,将所有类型为char *的数据存储到向量中,并将所有类型为float的数据存储到向量中。 在这个过程中,还需要一个单独的向量来存储参数进入顺序的个别字符。例如,当您push_back(a_float)时,您也正在执行一个push_back('f'),它只是存储一个单独的字符知道数据的顺序。 我也可以在这里使用std :: string,并简单地使用+ =。 该矢量仅用作示例。

现在这个东西的设计方式是使用宏来构造函数本身,尽管有恶意,但它是必需的,因为这是一个实验。 因此,使用递归调用实际上是不可能的,因为实际实现将容纳所有这些将在编译时被扩展; 你不能复制一个宏。

尽管做了所有可能的尝试,但我仍然坚持要弄清楚如何实际做到这一点。 所以相反,我使用更复杂的方法,包括构建一个类型,并将该类型传递给可变模板,将其扩展到一个向量中,然后对其进行迭代。 不过,我不想要像这样调用函数:

foo(arg(1), arg(2.0f), arg("three");

所以真正的问题是没有这样的我该怎么办? 为了让大家更好地理解代码实际在做什么,我粘贴了我目前使用的乐观方法。

struct any {
  void do_i(int   e) { INT    = e; }
  void do_f(float e) { FLOAT  = e; }
  void do_s(char* e) { STRING = e; }

  int   INT;
  float FLOAT;
  char *STRING;
};


template<typename T> struct get        { T      operator()(const any& t) { return T();      } };
template<>           struct get<int>   { int    operator()(const any& t) { return t.INT;    } };
template<>           struct get<float> { float  operator()(const any& t) { return t.FLOAT;  } };
template<>           struct get<char*> { char*  operator()(const any& t) { return t.STRING; } };

#define def(name)                                  
  template<typename... T>                          
  auto name (T... argv) -> any {                   
   std::initializer_list<any> argin = { argv... }; 
    std::vector<any> args = argin;
#define get(name,T)  get<T>()(args[name])
#define end }

any arg(int   a) { any arg; arg.INT    = a; return arg; }
any arg(float f) { any arg; arg.FLOAT  = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }

我知道这很讨厌,但这是一个纯粹的实验,不会在生产代码中使用。 这完全是一个想法。 它可能可以做得更好。 但是你将如何使用这个系统的例子:

def(foo)
  int data = get(0, int);
  std::cout << data << std::endl;
end

看起来很像python。 它也可以工作,但唯一的问题是你如何调用这个函数。 下面是一个简单的例子:

foo(arg(1000));

我需要构建一个新的任何类型,这是非常美观的,但不是说这些宏都不是。 除了这一点,我只想选择做:foo(1000);

我知道它可以完成,我只需要某种迭代方法,或者更重要的是一些std :: get方法用于打包variadic模板参数列表。 我肯定可以完成。

另外值得注意的是,我很清楚这不是类型友好的,因为我只支持int,float,char *,并且这对我很好。 我不需要其他任何东西,并且我将添加检查以使用type_traits来验证传递的参数确实是正确的,如果数据不正确,则会产生编译时错误。 这纯粹不是问题。 除了这些POD类型,我也不需要支持任何东西。

如果我能够得到一些建设性的帮助,这将非常受欢迎,反对关于我纯粹不合逻辑和愚蠢地使用宏和POD类型的争论。 我很清楚代码是多​​么的脆弱和破碎。 这是一个很好的实验,我可以稍后纠正非POD数据的问题,并使其更具类型安全性和可用性。

感谢您的承诺,我期待着提供帮助。


如果您想将参数封装到any ,您可以使用以下设置。 我也让any类更有用,尽管它在技术上不是any类。

#include <vector>
#include <iostream>

struct any {
  enum type {Int, Float, String};
  any(int   e) { m_data.INT    = e; m_type = Int;}
  any(float e) { m_data.FLOAT  = e; m_type = Float;}
  any(char* e) { m_data.STRING = e; m_type = String;}
  type get_type() const { return m_type; }
  int get_int() const { return m_data.INT; }
  float get_float() const { return m_data.FLOAT; }
  char* get_string() const { return m_data.STRING; }
private:
  type m_type;
  union {
    int   INT;
    float FLOAT;
    char *STRING;
  } m_data;
};

template <class ...Args>
void foo_imp(const Args&... args)
{
    std::vector<any> vec = {args...};
    for (unsigned i = 0; i < vec.size(); ++i) {
        switch (vec[i].get_type()) {
            case any::Int: std::cout << vec[i].get_int() << 'n'; break;
            case any::Float: std::cout << vec[i].get_float() << 'n'; break;
            case any::String: std::cout << vec[i].get_string() << 'n'; break;
        }
    }
}

template <class ...Args>
void foo(Args... args)
{
    foo_imp(any(args)...);  //pass each arg to any constructor, and call foo_imp with resulting any objects
}

int main()
{
    char s[] = "Hello";
    foo(1, 3.4f, s);
}

然而,编写函数来访问变量模板函数中的第n个参数并将函数应用于每个参数是可能的,这可能是您做任何事情的更好方法。


这不是通常使用Variadic模板的方式,完全不是。

根据语言规则,不可能对变量包进行迭代,因此您需要转向递归。

class Stock
{
public:
  bool isInt(size_t i) { return _indexes.at(i).first == Int; }
  int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }

  // push (a)
  template <typename... Args>
  void push(int i, Args... args) {
    _indexes.push_back(std::make_pair(Int, _ints.size()));
    _ints.push_back(i);
    this->push(args...);
  }

  // push (b)
  template <typename... Args>
  void push(float f, Args... args) {
    _indexes.push_back(std::make_pair(Float, _floats.size()));
    _floats.push_back(f);
    this->push(args...);
  }

private:
  // push (c)
  void push() {}

  enum Type { Int, Float; };
  typedef size_t Index;

  std::vector<std::pair<Type,Index>> _indexes;
  std::vector<int> _ints;
  std::vector<float> _floats;
};

示例(在行动中),假设我们有Stock stock;

  • stock.push(1, 3.2f, 4, 5, 4.2f); 被解析为(a),因为第一个参数是一个int
  • this->push(args...)被扩展到this->push(3.2f, 4, 5, 4.2f); ,这是解决(二)作为第一个参数是一个float
  • this->push(args...)扩展为this->push(4, 5, 4.2f); ,它被解析为(a),因为第一个参数是一个int
  • this->push(args...)扩展为this->push(5, 4.2f); ,它被解析为(a),因为第一个参数是一个int
  • this->push(args...)扩展为this->push(4.2f); ,这是解决(二)作为第一个参数是一个float
  • this->push(args...)扩展为this->push(); ,因为没有参数,所以解决了(c),从而结束了递归
  • 从而:

  • 添加另一个类型来处理就像添加另一个重载一样简单,改变第一个类型(例如, std::string const&
  • 如果传递了完全不同的类型(比如Foo ),则不能选择超载,从而导致编译时错误。
  • 一个警告:自动转换意味着double选择超载(b), short选择超载(a)。 如果不需要,那么就需要引入SFINAE,这使得方法稍微复杂一些(以及它们的签名至少),例如:

    template <typename T, typename... Args>
    typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);
    

    其中is_int会是这样的:

    template <typename T> struct is_int { static bool constexpr value = false; };
    template <> struct is_int<int> { static bool constexpr value = true; };
    

    不过,另一种选择是考虑变体类型。 例如:

    typedef boost::variant<int, float, std::string> Variant;
    

    它已经存在,所有的实用程序,它可以存储在一个vector ,复制等等,并且看起来非常像你所需要的,即使它不使用可变模板。


    您可以通过在{}之间使用参数包对其进行初始化来创建容器。 只要参数的类型是同类的或至少可以转换为您的容器的元素类型,它就可以工作。 (用g ++ 4.6.1测试)

    #include <array>
    
    template <class... Params>
    void f(Params... params) {
        std::array<int, sizeof...(params)> list = {params...};
    }
    
    链接地址: http://www.djcxy.com/p/51573.html

    上一篇: How can I iterate over a packed variadic template argument list?

    下一篇: C++0x lambdas with template parameters?