HeapBlock change has broken my 2D arrays


#1

The addition of the JUCE_PREVENT_HEAP_ALLOCATION macro to HeapBlock has broken my 2D arrays.

What used to work:

[code]// Member variable
OwnedArray<HeapBlock> array2d;

// In method…
array2d.add(new HeapBlock(2)); //Outer dimension dynamic, inner dimension = 2
[/code]

The last line now produces the following compile errors:

error C2248: 'juce::HeapBlock<ElementType>::operator new' : cannot access private member declared in class 'juce::HeapBlock<ElementType>' error C2248: 'juce::HeapBlock<ElementType>::operator delete' : cannot access private member declared in class 'juce::HeapBlock<ElementType>'
Any suggestions?


#2

use std::pair
or a std::vector

There is no reason to use HeapBlock if you don’t intend to use it for what it was designed for: not allocating memory


#3

Well, it must be said that using OwnedArray was actually jules’ original suggestion, but he obviously has a valid reason for making the allocator private; HeapBlock is intended to be used as stack/member objects to remove the risk of leaking memory after all.

Since you’re using them as arrays anyway, you could just use Array instead of HeapBlock?


#4

http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=7153&hilit=array#p40313 8)


#5

[quote=“haydxn”]Well, it must be said that using OwnedArray was actually jules’ original suggestion, but he obviously has a valid reason for making the allocator private; HeapBlock is intended to be used as stack/member objects to remove the risk of leaking memory after all.

Since you’re using them as arrays anyway, you could just use Array instead of HeapBlock?[/quote]

Thanks for the suggestion, but I need to be able to de-reference an element of the outer array so I can pass the inner array as double* (as an argument to a library function). HeapBlock allowed me to do that nicely. Is it safe to use getRawDataPointer() on Array to achieve the same thing?

Thanks, I probably should have linked to that!


#6

Maybe an Array? (In retrospect, I wish I’d made MemoryBlock templated, which would make it perfect for this…)

Or, if your inner dimension is a constant, the most efficient way might be something like this:

struct DataBlock { double data [whateverTheFixedSizeIs]; }; Array <DataBlock> foo;

(But be careful to use Array::getReference to access the members, to avoid copying the arrays).

In fact, Array<HeapBlock> might work (as long as you don’t use any methods that require a copy constructor). If you’re using C++11 it’d be more likely to compile, as it’ll be able to use move constructors where possible.


#7

Thanks Jules.

I’ve tried using Array and have finally got it to work (after many hours of thought and experimentation!). Example code:

Array<MemoryBlock> my2DArray; const int innerArraySize = 5; for (int x=0; x<2; x++) { MemoryBlock mb (sizeof(double)*innerArraySize); my2DArray.add(mb); //double* myInnerArray = static_cast<double*>(my2DArray[x].getData()); <- causes heap allocation error on second iteration double* myInnerArray = static_cast<double*>(my2DArray.getReference(x).getData()); for (int y=0; y<5; y++) myInnerArray[y] = (x+1)*y; }

Some questions:
[list][]Do I need to do anything to destroy the MemoryBlocks when it comes time to clear the Array?[/]
[]How about a new TemplatedMemoryBlock class? Or would that be too much bloat? :)[/]
[]I can’t get my head around my2DArray[x].getData() vs my2DArray.getReference(x).getData()
[list][
]Both compile, but the first causes a heap allocation error the second time the MemoryBlock is added to the array[/]
[
]From what Jules noted above, the second method avoids copying the inner array - but I don’t exactly understand what’s going on here[/][/list][/][/list]

I liked the idea of the struct approach for the case where the inner array is a fixed size, but I ran into trouble trying to cast from the struct to double* (invalid type conversion). This doesn’t worry me too much though, as most of my needs are for a non-fixed size so I’ll just use the Array method instead.

Lastly with Array<HeapBlock>, the compiler didn’t like the operator[] method:


#8

operator[] returns the object by value, so creates a copy. When your object is a massive lump of data, you obviously never want to create a copy of it (and if your object is a HeapBlock it’s impossible to copy it), so you should never use operator[], but use getReference instead.

If you make sure you don’t call operator[], then using a HeapBlock should be possible (and would be my recommendation if it does indeed compile).


#9

Try as I might, I can’t get it to compile with Array<HeapBlock>. I can’t even declare it without getting the private member error!


#10

Fair enough, it’d probably only be possible with a c++11 compiler. Just stick to MemoryBlock then.


#11

I currently have some float arrays the old fashion way (new, delete… ) in several processing classes.
I wanna go RAII but I’m a bit lost… but I’m not sure I should be :slight_smile:

Andrew, is there any reasons you use Array not AudioSampleBuffer ? (just because you need doubles or something else I’m missing?)

How do you guys go when dealing with buffers that may be resized (channels, length) ?
Do you use AudioSampleBuffer in all your processing classes (e.g. delay lines… ), or …?

as well, for 2d arrays of doubles or integers, do you use Array or is there something ‘cleaner’ ?


#12

In case it’s of use, I’ve recently had to make a kind of 2D array for my game engine’s entity/component table.

Because of my specific use-case, I’ve called it Table rather than ‘Array2D’, but feel free to rename it if you wish!

The nature of a 2D array makes it a little less suitable for the same kind of interface as Array; technically, it’s probably got more in common with ArrayAllocationBase (if you think about it, it makes sense - e.g. an ‘add’ function which automatically increases the allocation doesn’t really fit in when you have 2 axes to worry about). As such, you do need to specify a capacity, but it is designed to automatically shift stuff around as you would expect it to (in order to keep everything in the same place!).

Here’s the code to stick in a header…

EDIT: Removed old code, since it’s been replaced by this

Another note - I purposely opted to not use ‘row’ and ‘column’ terminology. Due to the way in which 2D data is stored (one axis iterates contiguously, the other by stride), the ‘orientation’ of the table is something best determined on a case-by-case basis. For instance, your data model may be conceptually easier to understand with one particular aspect representing ‘columns’, but that may also be the axis that would benefit (computationally) from being contiguous.

Because of this, I’ve chosen to refer to one axis as defining ‘layers’, and the other axis crosses through those layers; instead of ‘x,y’ or ‘col,row’, the indices are ‘position,layer’, and the dimensions are specified as layer length ('width) * layer count (‘height’). I’m not especially happy with it, but I didn’t want to waste too much time worrying about it so left it as it is!

Hope this helps!

[EDIT] i should point out this is a basic implementation - it’s currently not got any thread safety, and doesn’t have much beyond the basics. Oh, and it assumes uber-primitive types, since it doesn’t construct/destruct! (yep, it is just about the allocation at the moment). I’m in the midst of refactoring it (e.g. making this a TableAllocationBase, and creating a Table class with insert/remove row/column etc…, adding thread safety - and committing to a row-major design!)


#13

Yes, I needed doubles.

[quote=“lalala”]How do you guys go when dealing with buffers that may be resized (channels, length) ?
Do you use AudioSampleBuffer in all your processing classes (e.g. delay lines… ), or …?[/quote]
I use AudioSampleBuffer as much as possible, but sometimes use HeapBlock for single channel stuff (e.g. parameter dezipping).


#14

[quote=“haydxn”]In case it’s of use, I’ve recently had to make a kind of 2D array for my game engine’s entity/component table.

Here’s the code to stick in a header…

[EDIT] i should point out this is a basic implementation - it’s currently not got any thread safety, and doesn’t have much beyond the basics. Oh, and it assumes uber-primitive types, since it doesn’t construct/destruct! (yep, it is just about the allocation at the moment). I’m in the midst of refactoring it (e.g. making this a TableAllocationBase, and creating a Table class with insert/remove row/column etc…, adding thread safety - and committing to a row-major design!)[/quote]

Thanks for sharing haydxn - looks useful :smiley:


#15

I’m just about done with some massive changes to it :slight_smile: it’s a lot nicer now - I’ll post it in the useful tools forum shortly!


#16

There we go!. Hope it’s useful - feel free to request additions, I’m sure I’ll end up needing more stuff from it anyway :slight_smile:


#17

thanks andrew for your answer about AudioSampleBuffer, and thanks for sharing that haydxn !