5

Say I have

#include <boost/multi_array.hpp>
using intArray3D = boost::multi_array<int, 3>;

and I want to create a bunch of intArray3Ds with the same shape:

auto my_shape = boost::extents[3][4][5];
intArray3D xs(my_shape), ys(my_shape), zs(my_shape);

It's easy enough to use auto to assign boost::extents[3][4][5] into a variable, but how can I concretely figure out the underlying type?

rayhem
  • 741
  • 1
  • 8
  • 23
  • 1
    Do you want to know it for the sake of curiosity, or do you want to then code something with that ? – Quentin Oct 05 '17 at 22:49
  • 1
    In your case it's [`boost::detail::multi_array::extent_gen<3>`](https://github.com/boostorg/multi_array/blob/83c3738519a442c619f9ef661335acde6878b7d8/include/boost/multi_array/extent_gen.hpp#L28), but why do you care? – Praetorian Oct 05 '17 at 22:57
  • @Quentin Little of both, actually. I'm building a class that needs a couple such `multi_array`s of unknown-but-calculable size and I thought storing such an `extent` somewhere might make their construction easier. None of the ways I thought to store it worked (aside from `auto`) so my curiosity led me to ask! – rayhem Oct 05 '17 at 23:05

3 Answers3

4

Most importantly,

  1. you don't have to know
  2. you don't have to use extents either

Many things are acceptible as long they satisfy the documented criteria:

enter image description here

The Collection concept is documented in that link

Live On Coliru

#include <boost/multi_array.hpp>
#include <iostream>
using intArray3D = boost::multi_array<int, 3>;

void dump_shape(intArray3D const& arr) {
    for (unsigned dim = 0; dim < arr.dimensionality; ++dim)
        std::cout << arr.shape()[dim] << " ";
    std::cout << "\n";
}

int main() {
    {
        auto my_shape = boost::extents[3][4][5];
        intArray3D xs(my_shape), ys(my_shape), zs(my_shape);
        dump_shape(xs); dump_shape(ys); dump_shape(zs);
    }

    {
        std::array<int, 3> my_shape { 3, 4, 5 };
        intArray3D xs(my_shape), ys(my_shape), zs(my_shape);
        dump_shape(xs); dump_shape(ys); dump_shape(zs);
    }

    {
        std::vector<int> my_shape { 3, 4, 5 };
        intArray3D xs(my_shape), ys(my_shape), zs(my_shape);
        dump_shape(xs); dump_shape(ys); dump_shape(zs);
    }

}

Prints

3 4 5 
3 4 5 
3 4 5 
3 4 5 
3 4 5 
3 4 5 
3 4 5 
3 4 5 
3 4 5 
sehe
  • 374,641
  • 47
  • 450
  • 633
3

The documentation mentions:

template gen_type<Ranges>::type

  • This type generator is used to specify the result of Ranges chained calls to extent_gen::operator[].

where gen_type is a member of boost::multi_array_types::extent_gen (and boost::multi_array_types::extent_gen is also the type of the global helper object boost::extents).

You can also see that constructors which accept a set of extents are specified in this way (at least for purposes of public documentation). For example,

namespace boost {

template <typename ValueType, 
          std::size_t NumDims, 
          typename Allocator = std::allocator<ValueType> >
class multi_array {

...

  typedef multi_array_types::extent_gen         extent_gen;

...

  explicit multi_array(extent_gen::gen_type<NumDims>::type ranges,
                       const storage_order_type& store = c_storage_order(),
                       const Allocator& alloc = Allocator());

So you could rewrite that line of your code without using auto as:

boost::multi_array_types::extent_gen::gen_type<3>::type my_shape =
    boost::extents[3][4][5];

This is a bit silly for a local variable, but maybe you want to store a set of extents in a class or something like that. If so, this is the way to do it according to the officially documented interface.

(As noted in comments, the actual type this typedef resolves to involves boost::internal::, but you should never use anything from an "internal" namespace in your code, because that is subject to change in future versions.)

Community
  • 1
  • 1
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I don't see why all the complication is necessary. The documentation clearly states that any container is allowed. That includes something like `std::array<>`. `extent_gen` is merely an internal type for syntactic sugar. (See [my answer](https://stackoverflow.com/a/46596425/85371)) – sehe Oct 06 '17 at 00:04
  • 1
    That's a very good point. I'm not that familiar with `multi_array`, and focused too much on the question about what `extents[3][4][5]` is without even looking at the actual arrays they're used for. – aschepler Oct 06 '17 at 00:09
0

I would store a

template<class T>
using factory=std::function< T() >;

then when I want to create many arrays:

auto my_shape = boost::extents[3][4][5];
factory<intArray3D> shaper = [my_shape]{ return intArray3D(my_shape); };
intArray3D xs(shaper()), ys(shaper()), zs(shaper());

this eliminates dependence on the exact type of boost extents.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524