1

I'm writing some code which uses SSE/AVX via intrinsics. Therefore, I need arrays that are guaranteed to be aligned. I am attempting to make these via _aligned_malloc with the following code:

template<class T>
std::shared_ptr<T> allocate_aligned( int arrayLength, int alignment )
{
   return std::shared_ptr<T>( (T*) _aligned_malloc( sizeof(T) * arrayLength, alignment ), [] (void* data) { _aligned_free( data ); } );
}

My question is, how can I reference the data in my arrays with the usual array index notation? I know unique_ptr has a specialization for arrays that calls delete[] for destruction and allows for array index notation (ie myArray[10] to access the 11th element of the array). I need to use a shared_ptr however.

This code is giving me problems:

void testFunction( std::shared_ptr<float[]>& input )
{
   float testVar = input[5]; // The array has more than 6 elements, this should work
}

Compiler output:

error C2676: binary '[' : 'std::shared_ptr<_Ty>' does not define this operator or a conversion to a type acceptable to the predefined operator
1>          with
1>          [
1>              _Ty=float []
1>          ]

Is there a way to do this? I am still pretty new to using smart pointers, so I might be screwing up something simple. Thanks for any help!

RyanP
  • 1,898
  • 15
  • 20
  • 1
    Aside: your allocation function doesn't construct the objects in your array, and the destructors won't get called upon cleanup. You should be careful to only use it on trivial types -- or better yet, do a `static_assert` in the function to ensure that `std::is_trivial::value` is `true` (I think that's the check you want to make). Or possibly even better, use SNIFAE to eliminate nontrivial types from the overload. Alternatively, change the function to construct and destroy objects appropriately. –  Apr 13 '15 at 19:59
  • Why no overloads for operator new / delete in the classes which like to be aligned? –  Apr 13 '15 at 20:03
  • 1
    I don't think you can use allocate_aligned with `float[]`, because sizeof cannot be applied to that. – typ1232 Apr 13 '15 at 20:04
  • @typ1232: You're correct, `T` should be simply `float`, and the return type `std::shared_ptr` – Ben Voigt Apr 13 '15 at 20:25
  • @Hurkyl I am just using this on floats, doubles, and complex arrays (complex implemented as a struct of 2 floats) so that shouldn't be an issue, but I should add a check in there. Good idea, thanks. – RyanP Apr 13 '15 at 20:30
  • @typ1232 Yea I need to fix the syntax, thanks! – RyanP Apr 13 '15 at 20:31
  • just related: [Why isn't there a std::shared_ptr specialisation?](http://stackoverflow.com/q/8947579/3953764) – Piotr Skotnicki Apr 13 '15 at 20:35

2 Answers2

2

What you exactly want is not actually possible in C++.

The reasons are simple: shared_ptr does not implement operator[] for them and operator[] must be implemented as a member.

However, you can get very close with one of three options:

  1. Simply use a vector with a member type of the correct alignment (e.g. __m128 from xmmintrin.h) and drop all the other work.

  2. Implement a class similar to shared_ptr yourself (possibly using std::shared_ptr under the hood)

  3. Extract the raw pointer when you need it (float testVar = input.get()[5];) and index it instead.

danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • hmm okay... i was afraid that might be the answer. i guess i'll have to think about how i want to do this. thank you! – RyanP Apr 13 '15 at 20:27
  • @RyanP Yeah, I ran into similar problems before. Let me suggest that using `vector<__m128>` makes things very simple (at least it did for me). – danielschemmel Apr 13 '15 at 20:30
  • I'm actually using the shared_ptr to hold large arrays of image data. So I need the image data to be aligned so that I don't need to use unaligned loads and stores. I suppose I could hold the images in a vector but I was hoping to not have to significantly alter the existing code base.. just make it a little less error-prone. – RyanP Apr 13 '15 at 20:35
0

For those facing a similar problem, the following may help. Instead of having a shared pointer to an array, use a shared pointer to a pointer. You can still use the index notation, but you need to dereference the shared pointer before that:

std::shared_ptr<int*> a = std::make_shared<int*>(new int[10]);
(*a)[0] = 5;
std::cout << (*a)[0] << std::endl;
S. K.
  • 433
  • 3
  • 9