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?
C API
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();
C++ wrapper
/* 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?