c++pointersboostmultidimensional-arrayboost-multi-array

pointers to a class in dynamically allocated boost multi_array, not compiling


I'm working with boost::multi_array.

I want an object of class "world" to have an array named "chunk" of type "octreenode". Previously I had an ordinary one-dimensional array, and this worked fine. Now I'm trying to move to using a 3D array with Boost's multi_array functionality, and I'm really not sure what I'm doing wrong.

Simplified code:

class world {
public:

  typedef boost::multi_array<octreenode, 3> planetchunkarray;  // a boost_multi for chunks
  typedef planetchunkarray::index index;
  planetchunkarray *chunk;

  world(double x,double y,double z,
        int widtheast, int widthnorth, int height) :
        originx(x), originy(y), originz(z),
        chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height) {

    chunk = new planetchunkarray(boost::extents[chunksnorth][chunkseast][chunksup]);
    planetchunkarray::extent_gen extents;

    for (int cz = 0; cz < chunksnorth; ++cz) {
      for (int cx = 0; cx < chunkseast; ++cx) {
        for (int cy = 0; cy < chunksup; ++cy) {
          (*chunk)[cz][cx][cy] = new octreenode(1,72);
        }
      }
    }
  }
};

After which if I attempt to make the assignment

root->planet[0]->chunk[0][0][0]->material = 4;

I get the error:

error: base operand of '->' has non-pointer type 'boost::detail::multi_array::sub_array<octreenode, 1u>'|

"octreenode" has the relevant constructor, and this line worked in identical syntax when it was just:

root->planet[0]->chunk[0]->material = 4;

(with a one-dimensional array). Similarly, while it compiled fine with a one-dimensional array, trying to pass the chunk to functions that expect a pointer to an "octreenode" object, such as:

compactoctree(root->planet[p]->chunk[cz][cx][cy], 0, 14);

generates the error

error: cannot convert 'boost::detail::multi_array::sub_array<octreenode, 1u>' to 'octreenode*' for argument '1' to 'short int compactoctree(octreenode*, int, int)'|

Would be very grateful for any suggestions, I'm sure I'm missing something obvious.


Solution

  • Your array is of value type (octreenode), not pointer type (octreenode*)

    Therefore you are not supposed to try to assign a pointer to a dynamically allocated octreenode (new is for heap allocation, by default).

    Instead, just assign a value:

          (*chunk)[cz][cx][cy] = octreenode(1,72);
    

    In fact, there's no reason to use new on the multi array in the first place either:

    UPDATE

    In the comments it has been raised that more things could be optimized and that you consider that useful additions to the answer about the compilation error.

    So here goes: if you indeed want to initialize all array elements with the exact same value,

    1. You can make the loops way more efficient by forgetting about the array shapes for a moment:

      std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
      

      If you know octreenode is a POD type, you could write

      std::uninitialzed_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
      

      but a smart library implementation would end up calling fill_n anyways (because there's no gain). You can use uninitialized_fill_n if octreenode is not a POD type, but it is trivially destructible.

    2. In fact, there's no reason to use new on the multi array in the first place either. You can just use the constructor initialization list to construct the multi_array member


    Live On Coliru

    #include <boost/multi_array.hpp>
    #include <type_traits>
    
    struct octreenode { int a; int b; };
    
    class world {
    public:
        world(double x, double y, double z, int widtheast, int widthnorth, int height)
                : 
                    originx(x), originy(y), originz(z), 
                    chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height),
                    chunk(boost::extents[chunksnorth][chunkseast][chunksup])
        {
            octreenode v = { 1, 72 };
            std::fill_n(chunk.data(), chunk.num_elements(), v);
        }
    
    private:
        double originx, originy, originz;
        int chunkseast, chunksnorth, chunksup;
    
        typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
        typedef planetchunkarray::index index;
        planetchunkarray chunk;
    };
    
    int main() {
        world w(1,2,3,4,5,6);
    }