I am following Stefanus Du Toit's hourglass pattern, that is, implementing a C API in C++ and then wrapping it in C++ again. This is very similar to the pimpl idiom, and it is also transparent to the user, but prevents more ABI-related issues and allows for a wider range of foreign language bindings.
As in the pointer-to-implementation approach, the underlying object's size and layout is not known by the outsiders at compile-time, so the memory in which it resides has to be dynamically allocated (mostly). However, unlike in the pimpl case, in which the object has been fully defined at the allocation point, here its properties are completely hidden behind an opaque pointer.
Memory obtained with std::malloc
is "suitably aligned for any scalar type", which makes it unsuitable for the task. I'm not sure about the new-expression. Quoted from the section Allocation of the linked page:
In addition, if the new-expression is used to allocate an array of char or an array unsigned char, it may request additional memory from the allocation function if necessary to guarantee correct alignment of objects of all types no larger than the requested array size, if one is later placed into the allocated array.
Does this mean that the following code is compliant?
size_t object_size ( void ); // return sizeof(internal_object);
size_t object_alignment ( void ); // return alignof(internal_object);
void object_construct ( void * p ); // new (p) internal_object();
void object_destruct ( void * p ); // static_cast<internal_object *>(p)->~internal_object();
/* The memory block that p points to is correctly aligned
for objects of all types no larger than object_size() */
auto p = new char[ object_size() ];
object_construct( p );
object_destruct( p );
delete[] p;
If it is not, how to dynamically allocate properly-aligned memory?
You can use dynamic memory allocation only for arrays that are local to the MATLAB Function block. You cannot use dynamic memory allocation for: Input and output signals. Variable-size input and output signals must have an upper bound.
Also, it is a general query if we can allocate memory for the objects dynamically in C++? Yes, we can dynamically allocate objects also. Whenever a new object is created, constructor, a member function of a class is called. Whenever the object goes out of scope, destructor, a class member function is called.
The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate.
The dynamic memory requested by our program is allocated by the system from the memory heap.
I can't find where the standard guarantees your proposed code to work. First, I cannot find the part of the standard that backs what you've quoted from CppReference.com, but even if we take that claim on faith, it still only says that it may allocate additional space. If it doesn't, you're sunk.
The standard does speak to the alignment of memory returned by operator new[]
: "The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type …." (C++03, §3.7.2.1/2; C++11, §3.7.4.1/2) However, in the context where you're planning to allocate the memory, the type you plan to store in it isn't a complete type. And besides, the result of operator new[]
isn't necessarily the same as the result of the new-expression new char[…]
; the latter is allowed to allocate additional space for its own array bookkeeping.
You could use C++11's std::align
. To guarantee that you allocate space that can be aligned to the required amount, you'd have to allocate object_size() + object_alignment() - 1
bytes, but in practice, allocating only object_size()
bytes will probably be fine. Thus, you might try using std::align
something like this:
size_t buffer_size = object_size();
void* p = operator new(buffer_size);
void* original_p = p;
if (!std::align(object_alignment(), object_size(), p, buffer_size) {
// Allocating the minimum wasn't enough. Allocate more and try again.
operator delete(p);
buffer_size = object_size() + object_alignment() - 1;
p = operator new(buffer_size);
original_p = p;
if (!std::align(object_alignment(), object_size(), p, buffer_size)) {
// still failed. invoke error-handler
operator delete(p);
}
}
object_construct(p);
object_destruct(p);
operator delete(original_p);
The allocator described in another question accomplishes much the same thing. It's templated on the type of object being allocated, which you don't have access to, but it's not required to be that way. The only times it uses its template type argument are to evaluate sizeof
and alignof
, which you already have from your object_size
and object_alignment
functions.
That seems like a lot to require from consumers of your library. It would be much more convenient for them if you moved the allocation behind the API as well:
void* object_construct() {
return new internal_object();
}
Make sure to move the destruction, too, by calling delete
, not just the destructor.
That makes any alignment questions go away because the only module that really needs to know it is the one that already knows everything else about the type being allocated.
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