[英]clean way to initialize boost::multi_array with actual elements
I'm looking for clean syntactic sugar to initialize a boost::multi_array
from explicit values. 我正在寻找干净的语法糖来从显式值初始化boost::multi_array
。 The best I could come up with was 我能想到的最好的是
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]);
I don't like this because it requires 2 variables instead of 1, the triply-redundant [5][5]
dimensioning (the size can be inferred by the nested curly bracket lists), and the cast from double[][]
to double*
. 我不喜欢这个,因为它需要2个变量而不是1个,三重冗余[5][5]
尺寸(大小可以通过嵌套的花括号列表推断),以及从double[][]
到的转换double*
。
I do like the fact that there isn't a copy performed from g
to mg
, and the way that g
is initialized is pleasing to the eye (ie nested, structured initializer lists with a minimum of superfluous characters). 我确实喜欢没有从g
到mg
执行复制的事实,并且g
初始化的方式令人赏心悦目(即嵌套的,结构化的初始化列表,其中包含最少的多余字符)。
There are a few options available. 有几个选项可供选择。 All of them require some template magic; 所有这些都需要一些模板魔术; they differ in their syntactic expressiveness and efficiency. 他们的句法表达能力和效率不同。 Life would be easier if boost::multi_array
and friends actually provided some more useful constructors, but alas, that is not the case as of this writing. 如果boost::multi_array
和朋友实际上提供了一些更有用的构造函数,那么生活会更容易,但唉,在撰写本文时并非如此。
initializer_list
1)使用flat initializer_list
This basic option removes some redundancy, and makes for reasonably good syntactic sugar. 这个基本选项消除了一些冗余,并提供了相当好的语法糖。 It uses a helper function that takes an initializer_list<t>
, dumps it into an std::vector
, and uses that to first create a const_multi_array_ref
which is then deep-copied into a multi-array
. 它使用一个辅助函数,它接受一个initializer_list<t>
,将它转储到一个std::vector
,并使用它首先创建一个const_multi_array_ref
,然后将其深度复制到一个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});
In this version, the appropriate size of the initializer_list
is only checked at runtime, but I think in C++14 std::initializer_list::size()
is going to be constexpr
, which should allow you to use a static_assert
. 在这个版本中, initializer_list
的适当大小仅在运行时检查,但我认为在C ++ 14中, std::initializer_list::size()
将是constexpr
,这应该允许你使用static_assert
。
This one is closer to your original, but you need to define the array separately – I don't think you can provide it straight as a function parameter with a redundant cast. 这个更接近你原来的,但是你需要单独定义数组 - 我认为你不能直接提供它作为带有冗余强制转换的函数参数。 The advantage is that since you first build a standard C array, you can just re-use that in your multi_array_ref
and you do not need a copy. 优点是,自从您首次构建标准C数组后,您可以在multi_array_ref
重复使用它,而不需要副本。 You need some additional constructs in comparison to the first option: CArray
is used to construct a C array type from the template parameters. 与第一个选项相比,您需要一些额外的构造: 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);
initializer_list
s 3)使用嵌套的initializer_list
This one seems more proper, but is less efficient. 这个似乎更合适,但效率较低。 In addition to the code above, we need a way to construct nested initializer_list
s, and copy them into an array. 除了上面的代码,我们还需要一种方法来构造嵌套的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 } });
The last one comes with a little bit of inspiration from this article . 最后一篇文章从本文中获得了一些灵感。
fillArray()
routine is recursive and therefore less efficient (I expect that the compiler won't be able to optimize it as well). Con: fillArray()
例程是递归的,因此效率较低(我希望编译器也不能优化它)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.