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

问题描述 投票:0回答:1

我正在寻找干净的语法糖来从显式值初始化

boost::multi_array
。我能想到的最好的办法是

double g[5][5] = {
{-0.0009    ,  0.003799 ,    0.00666   ,   0.00374   ,   0.00186 },
{-0.0008    ,  0.0176   ,    0.0619    ,   0.0159    ,   0.00324 },
{0.00099    ,  0.0475   ,    0.666     ,   0.0376    ,   0.00758 },
{0.00242    ,  0.02189  ,    0.0624    ,   0.0192    ,   0.0008 },
{0.00182    ,  0.00404  ,    0.00479   ,   0.00924   ,   0.00189 }};

boost::multi_array_ref<double,2> mg((double*)g,boost::extents[5][5]);

我不喜欢这个,因为它需要 2 个变量而不是 1 个,三重冗余

[5][5]
尺寸标注(大小可以通过嵌套的大括号列表推断),以及从
double[][]
double*
的转换。

我确实喜欢这样一个事实,即没有从

g
mg
执行复制,并且
g
的初始化方式令人赏心悦目(即嵌套的结构化初始化列表,其中多余的字符最少) ).

c++ boost boost-multi-array
1个回答
2
投票

有几个选项可用。所有这些都需要一些模板魔法;它们的不同之处在于句法表达力和效率。如果

boost::multi_array
和朋友们实际上提供了一些更有用的构造函数,生活会更容易,但遗憾的是,在撰写本文时情况并非如此。

1) 使用平板
initializer_list

这个基本选项消除了一些冗余,并形成了相当好的语法糖。它使用一个辅助函数,该函数接受

initializer_list<t>
,将其转储到
std::vector
,并使用它首先创建一个
const_multi_array_ref
,然后将其深度复制到
multi-array

#include <boost/multi_array.hpp>
#include <cassert>
#include <initializer_list>
#include <vector>

// Helper class to determine the full number of elements in the
// multi-dimensional array
template <std::size_t... vs> struct ArraySize;
template <std::size_t v, std::size_t... vs> struct ArraySize<v, vs...>
{ static constexpr std::size_t size = v * ArraySize<vs...>::size; };
template <> struct ArraySize<>
{ static constexpr std::size_t size = 1; };

// Creates your multi_array
template <typename T, int... dims>
boost::multi_array<T, sizeof...(dims)>
makeMultiArray(std::initializer_list<T> l)
{
  constexpr std::size_t asize = ArraySize<dims...>::size;
  assert(l.size() == asize); // could be a static assert in C++14

  // Dump data into a vector (because it has the right kind of ctor)
  const std::vector<T> a(l);
  // This can be used in a multi_array_ref ctor.
  boost::const_multi_array_ref<T, sizeof...(dims)> mar(
    &a[0],
    std::array<int, sizeof...(dims)>{dims...});
  // Finally, deep-copy it into the structure we can return.
  return boost::multi_array<T, sizeof...(dims)>(mar);
}

// Usage example
auto mg = makeMultiArray<double, 5, 5>({
  -0.0009, 0.003799, 0.00666, 0.00374, 0.00186,
  -0.0008, 0.0176,   0.0619,  0.0159,  0.00324,
  0.00099, 0.0475,   0.666,   0.0376,  0.00758,
  0.00242, 0.02189,  0.0624,  0.0192,  0.0008,
  0.00182, 0.00404,  0.00479, 0.00924, 0.00189});

在此版本中,仅在运行时检查

initializer_list
的适当大小,但我认为在 C++14 中
std::initializer_list::size()
将是
constexpr
,这应该允许您使用
static_assert

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

2) 从 C 数组初始化

这个更接近您的原始数组,但您需要单独定义数组 - 我认为您不能直接将其作为具有冗余转换的函数参数提供。优点是,由于您首先构建了一个标准 C 数组,因此您可以在您的

multi_array_ref
中重复使用它,并且不需要副本。与第一个选项相比,您需要一些额外的构造:
CArray
用于从模板参数构造 C 数组类型。

// CArray<double,1,2,3>::type is double[1][2][3]
template <typename T, std::size_t... vs> struct CArray;
template <typename T, std::size_t v, std::size_t... vs> struct CArray<T, v, vs...>
{ typedef typename CArray<T, vs...>::type type[v]; };
template <typename T> struct CArray<T> { typedef T type; };

// Creates a multi_array_ref
template <typename T, int... dims>
boost::multi_array_ref<T, sizeof...(dims)>
makeMultiArray(typename CArray<T, dims...>::type l)
{
  constexpr std::size_t asize = ArraySize<dims...>::size;
  return boost::multi_array_ref<T, sizeof...(dims)>(
           reinterpret_cast<double*>(l),
           std::array<int, sizeof...(dims)>{dims...});
}

// Usage example
double g[5][5] =
  { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
    { -0.0008, 0.0176,   0.0619,  0.0159,  0.00324 },
    { 0.00099, 0.0475,   0.666,   0.0376,  0.00758 },
    { 0.00242, 0.02189,  0.0624,  0.0192,  0.0008  },
    { 0.00182, 0.00404,  0.00479, 0.00924, 0.00189 } };
auto mg = makeMultiArray<double, 5, 5>(g);
  • Pro:保留初始化程序的层次结构,以获得更好的可读性;避免复制。
  • 缺点:仍然有一些你想要摆脱的冗余。

3)使用嵌套
initializer_list
s

这个看起来比较合适,但是效率较低。除了上面的代码之外,我们还需要一种方法来构造嵌套的

initializer_list
,并将它们复制到数组中。

// Nested initializer lists
template <typename T, std::size_t level> struct NestedInitializerList
{
  typedef std::initializer_list<typename NestedInitializerList<T, level-1>::type> type;
};
template <typename T> struct NestedInitializerList<T, 1>
{
  typedef std::initializer_list<T> type;
};

// Helpers which fill the array from a nested initializer_list
template <typename T>
void fillArray(const T& l, typename CArray<T>::type& a)
{
  a = l;
}
template <typename T, int dim, int... dims>
void fillArray(typename NestedInitializerList<T, sizeof...(dims)+1>::type l,
               typename CArray<T, dim, dims...>::type& a)
{
  assert(l.size() == dim); // could be a static assert in C++14
  int i=0;
  for (auto it = l.begin(); it != l.end(); ++it, ++i)
  {
    fillArray<T, dims...>(*it, a[i]);
  }
}

// Creates your multi_array
template <typename T, int... dims>
boost::multi_array<T, sizeof...(dims)>
makeMultiArray(typename NestedInitializerList<T, sizeof...(dims)>::type l)
{
  typename CArray<T, dims...>::type a; // Multidimensional C array
  fillArray<T, dims...>(l, a);         // Fill from l
  // Turn into multi_array_ref.
  boost::const_multi_array_ref<T, sizeof...(dims)> mar(
    reinterpret_cast<const double*>(a),
    std::array<int, sizeof...(dims)>{dims...});
  // Finally, deep-copy it into the structure we can return.
  return boost::multi_array<T, sizeof...(dims)>(mar);
}

// Usage example
auto mg = makeMultiArray<double, 5, 5>(
  { { -0.0009, 0.003799, 0.00666, 0.00374, 0.00186 },
    { -0.0008, 0.0176,   0.0619,  0.0159,  0.00324 },
    { 0.00099, 0.0475,   0.666,   0.0376,  0.00758 },
    { 0.00242, 0.02189,  0.0624,  0.0192,  0.0008  },
    { 0.00182, 0.00404,  0.00479, 0.00924, 0.00189 } });

最后一篇文章的灵感来自于这篇文章

  • Pro:用法完全如你所愿;无冗余,清晰的分层初始化结构
  • 缺点:
    fillArray()
    例程是递归的,因此效率较低(我预计编译器也无法优化它)。
© www.soinside.com 2019 - 2024. All rights reserved.