c++ 使用实际元素初始化boost::multi_array的干净方法

wr98u20j  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(129)

我正在寻找清晰的语法糖来从显式值初始化boost::multi_array

  1. double g[5][5] = {
  2. {-0.0009 , 0.003799 , 0.00666 , 0.00374 , 0.00186 },
  3. {-0.0008 , 0.0176 , 0.0619 , 0.0159 , 0.00324 },
  4. {0.00099 , 0.0475 , 0.666 , 0.0376 , 0.00758 },
  5. {0.00242 , 0.02189 , 0.0624 , 0.0192 , 0.0008 },
  6. {0.00182 , 0.00404 , 0.00479 , 0.00924 , 0.00189 }};
  7. boost::multi_array_ref<double,2> mg((double*)g,boost::extents[5][5]);

字符串
我不喜欢这样,因为它需要2个变量而不是1个,三倍冗余的[5][5]尺寸(大小可以通过嵌套的花括号列表推断),以及从double[][]double*的转换。
我确实喜欢这样一个事实,即没有从gmg的复制,而且g的初始化方式令人赏心悦目(即嵌套的、结构化的初始化列表,具有最少的多余字符)。

zpjtge22

zpjtge221#

有几个可用的选项。所有这些都需要一些模板魔法;它们在语法表达和效率上有所不同。如果boost::multi_array和朋友们实际上提供了一些更有用的构造函数,生活会更容易,但遗憾的是,截至本文撰写时,情况并非如此。

1)使用扁平initializer_list

这个基本选项消除了一些冗余,并使语法糖相当好。它使用一个helper函数,该函数接受initializer_list<t>,将其转储到std::vector中,并使用该函数首先创建const_multi_array_ref,然后将其深度复制到multi-array中。

  1. #include <boost/multi_array.hpp>
  2. #include <cassert>
  3. #include <initializer_list>
  4. #include <vector>
  5. // Helper class to determine the full number of elements in the
  6. // multi-dimensional array
  7. template <std::size_t... vs> struct ArraySize;
  8. template <std::size_t v, std::size_t... vs> struct ArraySize<v, vs...>
  9. { static constexpr std::size_t size = v * ArraySize<vs...>::size; };
  10. template <> struct ArraySize<>
  11. { static constexpr std::size_t size = 1; };
  12. // Creates your multi_array
  13. template <typename T, int... dims>
  14. boost::multi_array<T, sizeof...(dims)>
  15. makeMultiArray(std::initializer_list<T> l)
  16. {
  17. constexpr std::size_t asize = ArraySize<dims...>::size;
  18. assert(l.size() == asize); // could be a static assert in C++14
  19. // Dump data into a vector (because it has the right kind of ctor)
  20. const std::vector<T> a(l);
  21. // This can be used in a multi_array_ref ctor.
  22. boost::const_multi_array_ref<T, sizeof...(dims)> mar(
  23. &a[0],
  24. std::array<int, sizeof...(dims)>{dims...});
  25. // Finally, deep-copy it into the structure we can return.
  26. return boost::multi_array<T, sizeof...(dims)>(mar);
  27. }
  28. // Usage example
  29. auto mg = makeMultiArray<double, 5, 5>({
  30. -0.0009, 0.003799, 0.00666, 0.00374, 0.00186,
  31. -0.0008, 0.0176, 0.0619, 0.0159, 0.00324,
  32. 0.00099, 0.0475, 0.666, 0.0376, 0.00758,
  33. 0.00242, 0.02189, 0.0624, 0.0192, 0.0008,
  34. 0.00182, 0.00404, 0.00479, 0.00924, 0.00189});

字符串
在这个版本中,initializer_list的适当大小只在运行时检查,但我认为在C++14中std::initializer_list::size()将是constexpr,这应该允许您使用static_assert

  • 优点:声明和维度中的冗余消失了。
  • 缺点:至少创建一个副本;使用一个不那么可读的平面列表。

2)从C数组初始化

这一个更接近于你的原始数组,但是你需要单独定义数组-我不认为你可以直接将它作为一个函数参数提供给一个冗余的类型转换。优点是因为你首先构建了一个标准的C数组,你可以在你的multi_array_ref中重用它,而不需要复制。与第一个选项相比,你需要一些额外的构造:CArray用于从模板参数构造C数组类型。

  1. // CArray<double,1,2,3>::type is double[1][2][3]
  2. template <typename T, std::size_t... vs> struct CArray;
  3. template <typename T, std::size_t v, std::size_t... vs> struct CArray<T, v, vs...>
  4. { typedef typename CArray<T, vs...>::type type[v]; };
  5. template <typename T> struct CArray<T> { typedef T type; };
  6. // Creates a multi_array_ref
  7. template <typename T, int... dims>
  8. boost::multi_array_ref<T, sizeof...(dims)>
  9. makeMultiArray(typename CArray<T, dims...>::type l)
  10. {
  11. constexpr std::size_t asize = ArraySize<dims...>::size;
  12. return boost::multi_array_ref<T, sizeof...(dims)>(
  13. reinterpret_cast<double*>(l),
  14. std::array<int, sizeof...(dims)>{dims...});
  15. }
  16. // Usage example
  17. double g[5][5] =
  18. { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
  19. { -0.0008, 0.0176, 0.0619, 0.0159, 0.00324 },
  20. { 0.00099, 0.0475, 0.666, 0.0376, 0.00758 },
  21. { 0.00242, 0.02189, 0.0624, 0.0192, 0.0008 },
  22. { 0.00182, 0.00404, 0.00479, 0.00924, 0.00189 } };
  23. auto mg = makeMultiArray<double, 5, 5>(g);

  • 优点:保持初始化器的层次结构,以提高可读性;避免复制。
  • 缺点:仍然有一些你想摆脱的冗余。

3)使用嵌套initializer_list s

除了上面的代码,我们还需要一种方法来构造嵌套的initializer_list s,并将它们复制到一个数组中。

  1. // Nested initializer lists
  2. template <typename T, std::size_t level> struct NestedInitializerList
  3. {
  4. typedef std::initializer_list<typename NestedInitializerList<T, level-1>::type> type;
  5. };
  6. template <typename T> struct NestedInitializerList<T, 1>
  7. {
  8. typedef std::initializer_list<T> type;
  9. };
  10. // Helpers which fill the array from a nested initializer_list
  11. template <typename T>
  12. void fillArray(const T& l, typename CArray<T>::type& a)
  13. {
  14. a = l;
  15. }
  16. template <typename T, int dim, int... dims>
  17. void fillArray(typename NestedInitializerList<T, sizeof...(dims)+1>::type l,
  18. typename CArray<T, dim, dims...>::type& a)
  19. {
  20. assert(l.size() == dim); // could be a static assert in C++14
  21. int i=0;
  22. for (auto it = l.begin(); it != l.end(); ++it, ++i)
  23. {
  24. fillArray<T, dims...>(*it, a[i]);
  25. }
  26. }
  27. // Creates your multi_array
  28. template <typename T, int... dims>
  29. boost::multi_array<T, sizeof...(dims)>
  30. makeMultiArray(typename NestedInitializerList<T, sizeof...(dims)>::type l)
  31. {
  32. typename CArray<T, dims...>::type a; // Multidimensional C array
  33. fillArray<T, dims...>(l, a); // Fill from l
  34. // Turn into multi_array_ref.
  35. boost::const_multi_array_ref<T, sizeof...(dims)> mar(
  36. reinterpret_cast<const double*>(a),
  37. std::array<int, sizeof...(dims)>{dims...});
  38. // Finally, deep-copy it into the structure we can return.
  39. return boost::multi_array<T, sizeof...(dims)>(mar);
  40. }
  41. // Usage example
  42. auto mg = makeMultiArray<double, 5, 5>(
  43. { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
  44. { -0.0008, 0.0176, 0.0619, 0.0159, 0.00324 },
  45. { 0.00099, 0.0475, 0.666, 0.0376, 0.00758 },
  46. { 0.00242, 0.02189, 0.0624, 0.0192, 0.0008 },
  47. { 0.00182, 0.00404, 0.00479, 0.00924, 0.00189 } });


最后一个来自this article的一点灵感。

  • 优点:用法完全符合您的要求;没有冗余,清晰的分层初始化器结构
  • 缺点:fillArray()例程是递归的,因此效率较低(我预计编译器也无法优化它)。
展开查看全部

相关问题