When I call posix_memalign
to allocate aligned memory for an object of type Foo
in my C++ code, I am required to do a reinterpret_cast
of the address of that pointer to void**
.
In general when I encounter this situation, it implies that I am missing some language feature. That is, it feels like I am calling malloc
in c++
when I should be calling new
.
,
Is there a type-aware new
equivalent for aligned memory allocation in c++
?
I will start with the core advice first.
Foo* aligned_foo() {
void* raw = 0;
if(posix_memalign(&raw, 8, sizeof(Foo)))
return 0; // we could throw or somehow communicate the failure instead
try{
return new(raw) Foo();
}catch(...){
free(raw);
throw;
}
}
then when you are done with the Foo* foo
, do a foo->~Foo(); free(foo);
instead of delete
.
Note the lack of reinterpret_cast
s.
Here is an attempt to make it generic:
// note: stateless. Deleting a derived with a base without virtual ~base a bad idea:
template<class T>
struct free_then_delete {
void operator()(T*t)const{
if(!t)return;
t->~T();
free(t);
};
};
template<class T>
using aligned_ptr=std::unique_ptr<T,free_then_delete<T>>;
// the raw version. Dangerous, because the `T*` requires special deletion:
template<class T,class...Args>
T* make_aligned_raw_ptr(size_t alignment, Args&&...args) {
void* raw = 0;
if(int err = posix_memalign(&raw, alignment, sizeof(T)))
{
if (err==ENOMEM)
throw std::bad_alloc{};
return 0; // other possibility is bad alignment: not an exception, just an error
}
try {
// returns a T*
return new(raw) T(std::forward<Args>(args)...);
} catch(...) { // the constructor threw, so clean up the memory:
free(raw);
throw;
}
}
template<class T,class...Args> // ,class... Args optional
aligned_ptr<T> make_aligned_ptr(size_t alignment=8, Args&&...args){
T* t = make_aligned_raw_ptr<T>(alignment, std::forward<Args>(args)...);
if (t)
return aligned_ptr<T>(t);
else
return nullptr;
}
The unique_ptr
alias aligned_ptr
bundles the destroyer along with the pointer -- as this data requires destruction and free, not delete, this makes it clear. You can still .release()
the pointer out, but you still have to do the steps.
Actually, you don't want to do a reinterpret_cast
because then your Foo
constructor isn't called. If you need to allocate memory from a special place, you then call placement new to construct the object in that memory:
void* alloc;
posix_memalign(&alloc, 8, sizeof(Foo));
Foo* foo = new (foo) Foo();
The only other way (pre C++11) would be overriding the new
operator for your class. That works if you have a particular class that always requires this special allocation:
class Foo {
void* operator new(size_t size) {
void* newobj;
posix_memalign(&newobj, 8, sizeof(Foo));
return newobj;
}
};
Then anytime you call new Foo()
it will invoke this allocator. See http://en.cppreference.com/w/cpp/memory/new/operator_new for more information. Overriding operator new
and operator delete
can be done for individual classes or globally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With