这是我的解决方案,使用 C++11 可变参数模板和包扩展。
我的“foobar”编译为 constexpr 函数,所以我认为这非常有效:p
至于优雅,你可以评判,但我觉得还不错。
基本上,这个想法是用函数式编程方式替换for 循环进行迭代,我们只是寻求显式地构建我们想要首先迭代的集合。然后可以以更直接的方式将该代码推广到任意维度。
除了<array> 标头之外,代码完全独立。
它使用 gcc 4.8.2 和 clang 3.6 按照 C++11 标准为我编译。
注意:如果您想对维度是运行时参数的代码使用相同的技术,基本上您要做的就是使用类似 std::vector<std::vector<size_t>> 的东西将下面的 Cartesian_Product 元函数重新实现为运行时函数.然后,您可以通过获取dim 笛卡尔积的数量来构建您的迭代集,并使用单个 for 循环对其进行迭代以填充结果。
#include <array>
#include <iostream>
/////////////////////////////////////////////
// The point class from problem statement
/////////////////////////////////////////////
template <size_t dims> class Point { public: double data[dims]; };
// Some basic type list and meta function objects to work with
// List of indices
template<size_t... sl>
struct StList {
static constexpr size_t length = sizeof...(sl);
};
// Metafunction to compute a volume
template <typename T>
struct Compute_Volume;
template<size_t s>
struct Compute_Volume<StList<s>> {
static constexpr size_t value = s;
};
template <size_t s1, size_t s2, size_t... sl>
struct Compute_Volume<StList<s1, s2, sl...>> {
static constexpr size_t value = s1 * Compute_Volume<StList<s2, sl...>>::value;
};
// Concatenate
template<typename TL, typename TR>
struct Concat;
template<size_t... SL, size_t... SR>
struct Concat<StList<SL...>, StList<SR...>> {
typedef StList<SL..., SR...> type;
};
template<typename TL, typename TR>
using Concat_t = typename Concat<TL, TR>::type;
// Meta function to check if a typelist is component-wise less than another typelist
// For testing purposes
template <typename TL, typename TR>
struct all_less_than;
template <size_t l1, size_t... SL, size_t r1, size_t... SR>
struct all_less_than<StList<l1, SL...>, StList<r1, SR...>> {
static constexpr bool value = (l1 < r1) && all_less_than<StList<SL...>, StList<SR...>>::value;
};
template<>
struct all_less_than<StList<>, StList<>> {
static constexpr bool value = true;
};
/////////////////////////////////////////////////////////////////////////////
// constexpr template function for the point initializations you described
/////////////////////////////////////////////////////////////////////////////
template <typename index, typename dims>
struct point_maker;
template <size_t... idx, size_t... dim>
struct point_maker<StList<idx...>, StList<dim...>> {
static_assert(sizeof...(idx) == sizeof...(dim), "misuse of 'point_maker' template, idx and dim must have same number of coordinates");
static_assert(all_less_than<StList<idx...>, StList<dim...>>::value, "misuse of 'point_maker' template, idx is out of bounds");
static constexpr Point<sizeof...(idx)> make_point() {
return {{ ((idx + 0.5) / static_cast<double>(dim))... }};
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// Now we need to abstract the for loops. We need a little more infrastructure for this.
//////////////////////////////////////////////////////////////////////////////////////////
// A basic typelist object
template <typename... tl>
struct TypeList {
static constexpr size_t length = sizeof...(tl);
};
// Specialization for the Concat metafunction
template<typename... TL, typename... TR>
struct Concat<TypeList<TL...>, TypeList<TR...>> {
typedef TypeList<TL..., TR...> type;
};
// Metafunction to compute the cartesian product of two lists of lists, and evaluate the `Concat` metafunction for each pair.
template <typename S, typename T>
struct Cartesian_Product;
template <typename s1, typename s2, typename... sl, typename T>
struct Cartesian_Product<TypeList<s1, s2, sl...>, T> {
typedef Concat_t<typename Cartesian_Product<TypeList<s1>, T>::type, typename Cartesian_Product<TypeList<s2, sl...>, T>::type> type;
};
template <typename s, typename t1, typename t2, typename... tl>
struct Cartesian_Product<TypeList<s>, TypeList<t1, t2, tl...>> {
typedef Concat_t<typename Cartesian_Product<TypeList<s>, TypeList<t1>>::type, typename Cartesian_Product<TypeList<s>, TypeList<t2, tl...>>::type> type;
};
template <typename s, typename t>
struct Cartesian_Product<TypeList<s>, TypeList<t>> {
typedef TypeList<Concat_t<s, t>> type;
};
template <typename S, typename T>
using Cartesian_Product_t = typename Cartesian_Product<S, T>::type;
// Some unit tests for the above :)
static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1>, StList<2>>, TypeList<StList<3>, StList<4>>>, TypeList<StList<1,3>, StList<1,4>, StList<2,3>, StList<2,4>>>::value , "cartesian product not working");
static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1,5>, StList<2>>, TypeList<StList<3>, StList<4>>>, TypeList<StList<1,5,3>, StList<1,5,4>, StList<2,3>, StList<2,4>>>::value , "cartesian product not working");
static_assert( std::is_same<Cartesian_Product_t<TypeList<StList<1,5>, StList<2>>, TypeList<StList<>>>, TypeList<StList<1,5>, StList<2>>>::value , "cartesian product not working");
// Metafunction to count from 0 to n
// Count from zero to n-1, and make a sequence of singleton sets containing the numbers
template<size_t s>
struct Count {
typedef Concat_t<typename Count<s-1>::type, TypeList<StList<s-1>>> type;
};
template<>
struct Count<0> {
typedef TypeList<> type;
};
template<size_t s>
using Count_t = typename Count<s>::type;
// Metafunction to abstract a series of for loops, generating the list of all the index tuples the collection of loops would generate
template <typename T>
struct Volume_Maker;
template <>
struct Volume_Maker<StList<>> {
typedef TypeList<StList<>> type;
};
template <size_t s, size_t... sl>
struct Volume_Maker<StList<s, sl...>> {
typedef Cartesian_Product_t<Count_t<s>, typename Volume_Maker<StList<sl...>>::type> type;
};
template <typename T>
using Volume_t = typename Volume_Maker<T>::type;
// Some more quick unit tests
template <typename T>
struct Volume_Test {
static_assert( Volume_t<T>::length == Compute_Volume<T>::value, "volume computation mismatch");
};
Volume_Test<StList<1,2,3>> test1{};
Volume_Test<StList<1,1,1>> test2{};
Volume_Test<StList<1>> test3{};
Volume_Test<StList<7,6,8>> test4{};
/////////////////
// Grand finale
/////////////////
template <typename dim_list, typename idx_list = Volume_t<dim_list>>
struct foobar_helper;
template <size_t... dim, typename... idx>
struct foobar_helper<StList<dim...>, TypeList<idx...>> {
typedef StList<dim...> dim_list;
typedef std::array<Point<sizeof...(dim)>, sizeof...(idx)> array_type;
static constexpr array_type make_array() {
return {{ point_maker<idx, dim_list>::make_point()... }};
}
};
template <size_t... dim>
constexpr std::array<Point<sizeof...(dim)>, Compute_Volume<StList<dim...>>::value> foobar() {
return foobar_helper<StList<dim...>>::make_array();
}
/////////////////
// Example usage
/////////////////
template <size_t ndim>
std::ostream & operator << (std::ostream & s, const Point<ndim> & p) {
s << "[ ";
for (size_t i = 0; i < ndim; ++i) {
if (i) { s << ", "; }
s << p.data[i];
}
s << " ]";
return s;
}
template<size_t ndim, size_t N>
void print_array(std::ostream & s, const std::array<Point<ndim>, N> & a) {
for (size_t i = 0; i < N; ++i) {
s << a[i] << std::endl;
}
}
int main() {
constexpr auto array1 = foobar<2,2,2>();
print_array(std::cout, array1);
constexpr auto array2 = foobar<3,3,3,3>();
print_array(std::cout, array2);
}