How to have a unique pointer with 32 byte aligned?

I want to change my current version to a new version using make_unique function.
Reason: I found make_unique much better than std::unique_ptr's constructor. Also make_unique handles the deleter more automatically.

How can I make my make_unique with 32 aligned possible?

1
2
3
4
5
6
  // My current version
char* data_malloc = static_cast<char*>(std::aligned_alloc(32, size));
unique_ptr<char[]> data = std::unique_ptr<char[]>(data_malloc);

// what I want, in such style
unique_ptr<char[]> data = make_unique<char[]>(size);
Note that your current version has undefined behavior. std::unique_ptr<T> uses std::default_deleter, which uses delete to de-allocate memory. However, the counterpart to std::aligned_alloc is std::free.

Here's an untested version of the API for arrays. I used Boost.Align to avoid writing a deleter, but that's relatively easy and the other fundamentals are present in the standard library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <boost/align.hpp>
#include <memory>
#include <cstdint>
#include <cassert>
#include <utility>

constexpr bool is_power_of_2(std::size_t n)
{ return n && !(n & (n - 1)); }

template <typename T>
  using unique_aligned_ptr = 
    std::unique_ptr<T, boost::alignment::aligned_delete>;
      
struct alignment_t
{ 
  constexpr explicit alignment_t(std::size_t align)
    : value(align)
  { assert(is_power_of_2(align)); }
  
  std::size_t value;
};

// Thanks cppreference
template<class T> struct is_bounded_array: std::false_type {};
template<class T, std::size_t N>
  struct is_bounded_array<T[N]> : std::true_type {};
  
template<class T> struct is_unbounded_array : std::false_type {};
template<class T> struct is_unbounded_array<T[]> : std::true_type {};
  
template <typename T,
          typename = std::enable_if_t<is_unbounded_array<T>::value>>
  unique_aligned_ptr<T> make_unique_aligned
    (alignment_t alignment, std::size_t size)
  {
    using array_element_type = std::remove_extent_t<T>;
      
    void* storage = boost::alignment::aligned_alloc
      (alignment.value, size * sizeof(array_element_type));
    if (! storage)
      throw std::bad_alloc(); 
 
    try 
    {
      return unique_aligned_ptr<T> 
        (new(storage) array_element_type[size]());
    } catch(...) { boost::alignment::aligned_free(storage); throw; }
  }
  
template <typename T, typename... Args,
          typename = std::enable_if_t<is_bounded_array<T>::value>>
  void make_unique_aligned(alignment_t, Args&&...) = delete;
  
int main()
{
    auto px = make_unique_aligned<double[]>(alignment_t{64}, 32);
}
Last edited on
Thanks.
Could you explain the code little bit?
Because we thought make_unique could do the work (?) Can it?
We thought make_unique could do the work (?) Can it?

Mostly.

When I wrote the above program, I forgot about C++17's support for over-aligned new:
make_unique can be coerced into working by
- over-aligning the type being constructed (i.e., by applying alignas) and using C++17's alignment-aware operator new; or
- writing class-specific overloads for operator new/new[]/delete/delete[].

The first option is ideal, if it is acceptable.

For example:
1
2
3
// all objects of type A are (over-)aligned on a 32-byte boundary. 
struct alignas(32) A { double x[4]; }; 
auto result = std::make_unique<A[]>(4);
result will be properly aligned, but only since C++17. This is because make_unique evaluates a new-expression which calls the alignment-aware allocation function void* operator new( std::size_t count, std::align_val_t al). Before C++17, there is no such allocation function, and new will not respect over-alignment.

Unlike adjusting a type's alignment requirement, make_unique_aligned() offers the ability to control alignment on a per-object basis at run-time.

Further, the presence of alignment-aware operator new obviates the need for a special deleter.
Last edited on
Topic archived. No new replies allowed.