C++ 如何使用笛卡尔积将向量元组转换为向量元组?

C++ 如何使用笛卡尔积将向量元组转换为向量元组?,c++,c++11,vector,tuples,cartesian-product,C++,C++11,Vector,Tuples,Cartesian Product,为了使用std::tuple构造函数对类进行单元测试,我想为构造函数参数生成一个特殊情况加上随机值的示例。假设我有一个std::vector到std::vector的std::tuple,其中每个Ti都不同,我如何将其转换为所有std::tuple组合的完整笛卡尔积的std::vector 具体来说,我想要一个可变函数模板,它看起来像这样: template<typename... Args> std::vector<std::tuple<Args...> cart

为了使用std::tuple构造函数对类进行单元测试,我想为构造函数参数生成一个特殊情况加上随机值的示例。假设我有一个std::vector到std::vector的std::tuple,其中每个Ti都不同,我如何将其转换为所有std::tuple组合的完整笛卡尔积的std::vector

具体来说,我想要一个可变函数模板,它看起来像这样:

template<typename... Args>
std::vector<std::tuple<Args...> cartesian_product(std::vector<Args>...)
{
    // template magic or fat mulitple loops?
}
// some type to be tested
class MyType
{
    MyType(std::tuple<int, bool, std::string>);
    // bla
};

// test values for each constructor argument
std::tuple< std::vector<int>, std::vector<bool>, std::vector<std::string> > input {
    { 1, 2, 3}, { false, true}, { "Hello", "World"}
};

// should become 3 x 2 x 2 = 12 cases { {1, false, "Hello"}, ... , {3, true, "World"} } 
std::vector< std::tuple<int, bool, std::string> > test_cases = cartesian_product( input );

// can write flat single loop over all cases
for (auto t: test_cases) {
    BOOST_CHECK(MyType(t).my_test());
}
可以这样使用:

template<typename... Args>
std::vector<std::tuple<Args...> cartesian_product(std::vector<Args>...)
{
    // template magic or fat mulitple loops?
}
// some type to be tested
class MyType
{
    MyType(std::tuple<int, bool, std::string>);
    // bla
};

// test values for each constructor argument
std::tuple< std::vector<int>, std::vector<bool>, std::vector<std::string> > input {
    { 1, 2, 3}, { false, true}, { "Hello", "World"}
};

// should become 3 x 2 x 2 = 12 cases { {1, false, "Hello"}, ... , {3, true, "World"} } 
std::vector< std::tuple<int, bool, std::string> > test_cases = cartesian_product( input );

// can write flat single loop over all cases
for (auto t: test_cases) {
    BOOST_CHECK(MyType(t).my_test());
}
有没有现成的Boost库可以做到这一点?为此编写可变模板的过程有多复杂

没那么棘手:

#include <cstddef>
#include <utility>
#include <vector>
#include <tuple>
#include <string>
#include <iostream>

using std::size_t;

template<size_t...> struct seq {};
template<size_t Min, size_t Max, size_t... s>
struct make_seq:make_seq< Min, Max-1, Max-1, s... > {};
template<size_t Min, size_t... s>
struct make_seq< Min, Min, s... > {
    typedef seq<s...> type;
};
template<size_t Max, size_t Min=0>
using MakeSeq = typename make_seq<Min, Max>::type;

size_t product_size() {
  return 1;
}
template<typename... Sizes>
size_t product_size( size_t x, Sizes... tail ) {
  return x * product_size(tail...);
}
namespace details {
  template<typename max_iterator, typename Lambda>
  void for_each_index( max_iterator mbegin, max_iterator mend, Lambda&& f, std::vector<size_t>& idx ) {
    if (mbegin == mend) {
      f(idx);
    } else {
      for (size_t i = 0; i < *mbegin; ++i) {
        idx.push_back(i);
        for_each_index(mbegin+1, mend, f, idx);
        idx.pop_back();
      }
    }
  }
  template<typename Lambda>
  void for_each_index( std::vector<size_t> const& maxes, Lambda&& f ) {
    std::vector<size_t> idx;
    details::for_each_index( maxes.begin(), maxes.end(), f, idx );
  }
  template<size_t... s, typename... Ts>
  std::vector< std::tuple<Ts...> > does_it_blend( seq<s...>, std::tuple< std::vector<Ts>... >const& input ) {
    std::vector< std::tuple<Ts...> > retval;
    retval.reserve( product_size( std::get<s>(input).size()... ) );
    std::vector<size_t> maxes = {
      (std::get<s>(input).size())...
    };
    for_each_index( maxes, [&](std::vector<size_t> const& idx){
      retval.emplace_back( std::get<s>(input)[idx[s]]... );
    });
    return retval;
  }
}
template<typename... Ts>
std::vector< std::tuple<Ts...> > does_it_blend( std::tuple< std::vector<Ts>... >const& input ) {
  return details::does_it_blend( MakeSeq< sizeof...(Ts) >(), input );
}

int main() {
  std::tuple< std::vector<int>, std::vector<bool>, std::vector<std::string> > input {
    { 1, 2, 3}, { false, true}, { "Hello", "World"}
  };

  // should become 3 x 2 x 2 = 12 cases { {1, false, "Hello"}, ... , {3, true, "World"} } 
  std::vector< std::tuple<int, bool, std::string> > test_cases = does_it_blend( input );

  for( auto&& x:test_cases ) {
    std::cout << std::get<0>(x) << "," << std::get<1>(x) << "," << std::get<2>(x) << "\n";
  }
}
这里我创建了一个函数,它对可能的索引进行笛卡尔积,然后直接在输出容器中创建元组

我还需要保留输出大小

现在代码更少了

没那么棘手:

#include <cstddef>
#include <utility>
#include <vector>
#include <tuple>
#include <string>
#include <iostream>

using std::size_t;

template<size_t...> struct seq {};
template<size_t Min, size_t Max, size_t... s>
struct make_seq:make_seq< Min, Max-1, Max-1, s... > {};
template<size_t Min, size_t... s>
struct make_seq< Min, Min, s... > {
    typedef seq<s...> type;
};
template<size_t Max, size_t Min=0>
using MakeSeq = typename make_seq<Min, Max>::type;

size_t product_size() {
  return 1;
}
template<typename... Sizes>
size_t product_size( size_t x, Sizes... tail ) {
  return x * product_size(tail...);
}
namespace details {
  template<typename max_iterator, typename Lambda>
  void for_each_index( max_iterator mbegin, max_iterator mend, Lambda&& f, std::vector<size_t>& idx ) {
    if (mbegin == mend) {
      f(idx);
    } else {
      for (size_t i = 0; i < *mbegin; ++i) {
        idx.push_back(i);
        for_each_index(mbegin+1, mend, f, idx);
        idx.pop_back();
      }
    }
  }
  template<typename Lambda>
  void for_each_index( std::vector<size_t> const& maxes, Lambda&& f ) {
    std::vector<size_t> idx;
    details::for_each_index( maxes.begin(), maxes.end(), f, idx );
  }
  template<size_t... s, typename... Ts>
  std::vector< std::tuple<Ts...> > does_it_blend( seq<s...>, std::tuple< std::vector<Ts>... >const& input ) {
    std::vector< std::tuple<Ts...> > retval;
    retval.reserve( product_size( std::get<s>(input).size()... ) );
    std::vector<size_t> maxes = {
      (std::get<s>(input).size())...
    };
    for_each_index( maxes, [&](std::vector<size_t> const& idx){
      retval.emplace_back( std::get<s>(input)[idx[s]]... );
    });
    return retval;
  }
}
template<typename... Ts>
std::vector< std::tuple<Ts...> > does_it_blend( std::tuple< std::vector<Ts>... >const& input ) {
  return details::does_it_blend( MakeSeq< sizeof...(Ts) >(), input );
}

int main() {
  std::tuple< std::vector<int>, std::vector<bool>, std::vector<std::string> > input {
    { 1, 2, 3}, { false, true}, { "Hello", "World"}
  };

  // should become 3 x 2 x 2 = 12 cases { {1, false, "Hello"}, ... , {3, true, "World"} } 
  std::vector< std::tuple<int, bool, std::string> > test_cases = does_it_blend( input );

  for( auto&& x:test_cases ) {
    std::cout << std::get<0>(x) << "," << std::get<1>(x) << "," << std::get<2>(x) << "\n";
  }
}
这里我创建了一个函数,它对可能的索引进行笛卡尔积,然后直接在输出容器中创建元组

我还需要保留输出大小

现在代码更少了。

这有帮助吗

#include <vector>
#include <tuple>
#include <type_traits>

template <typename... T>
struct transpose {};

template <typename... T>
struct transpose<std::tuple<std::vector<T>...>>
{
    using type = std::vector<std::tuple<T...>>;
};

template <typename... T>
struct transpose<std::vector<std::tuple<T...>>>
{
    using type = std::tuple<std::vector<T>...>;
};

int main()
{
    std::tuple<std::vector<int>, std::vector<bool>> var;

    static_assert(
        std::is_same<
            transpose<decltype(var)>::type,
            std::vector<std::tuple<int, bool>>
       >::value, ""
    );
}
这有用吗

#include <vector>
#include <tuple>
#include <type_traits>

template <typename... T>
struct transpose {};

template <typename... T>
struct transpose<std::tuple<std::vector<T>...>>
{
    using type = std::vector<std::tuple<T...>>;
};

template <typename... T>
struct transpose<std::vector<std::tuple<T...>>>
{
    using type = std::tuple<std::vector<T>...>;
};

int main()
{
    std::tuple<std::vector<int>, std::vector<bool>> var;

    static_assert(
        std::is_same<
            transpose<decltype(var)>::type,
            std::vector<std::tuple<int, bool>>
       >::value, ""
    );
}


@zch太好了,我甚至浏览了那个,但不知怎么的,它看起来不像是完整的一般解,而只是2参数情况下的一个。很抱歉错过了这一点,它看似简单!。这是一般性的,我将在示例中添加第三维,以使其更清晰,即使在评论中提到了它。@zch我测试了它,它完全符合我的要求。真的很酷。@zch太棒了,我甚至浏览了那个,但不知怎么的,它看起来不像完整的一般解,而只是2参数情况下的一个。很抱歉错过了这一点,它看似简单!。这是一般性的,我将在示例中添加第三维,以使其更清晰,即使在评论中提到了它。@zch我测试了它,它完全符合我的要求。真的很酷的把戏。+1谢谢你的回答!与@zch的一个相比,它的行数占你的行数的40%,它有什么优势呢?@rhalbersma,我还没有读过那个。一个优点是,我将结果直接放入输出std::vector,而该代码在第一次检查时会进行大量移动和复制。当然,我接受它,很高兴知道它可以以不同的方式完成。@rhalbersma模式非常相似:我在索引中建立叉积,她直接在生成的元组中执行此操作。两个cross\u imp重载几乎与我的for\u索引一一对应,而且它是否与她的cross\u混合我有一些额外的样板文件。如果我用一组可变的size\t maxes替换maxes向量,情况会更相似。好笑+谢谢你的回答!与@zch的一个相比,它的行数占你的行数的40%,它有什么优势呢?@rhalbersma,我还没有读过那个。一个优点是,我将结果直接放入输出std::vector,而该代码在第一次检查时会进行大量移动和复制。当然,我接受它,很高兴知道它可以以不同的方式完成。@rhalbersma模式非常相似:我在索引中建立叉积,她直接在生成的元组中执行此操作。两个cross\u imp重载几乎与我的for\u索引一一对应,而且它是否与她的cross\u混合我有一些额外的样板文件。如果我用一组可变的size\t maxes替换maxes向量,情况会更相似。有趣的这将转换类型,但我将如何使用它转换示例主函数中的实际值,以及如何将其用于笛卡尔积?@rhalbersma我将尝试稍后添加一个编辑。这将转换类型,但如何使用它转换示例主函数中的实际值,这将如何用于笛卡尔积?@rhalbersma我将在稍后尝试添加一个编辑。