One C++ "ish" way to tackle this is to describe the buffer itself as a "trivially-copyable" (C++11 lingo, was "POD" for "plain old data" in C++98 & 2003) struct, with the microscopic exception that it has a private contructor to prevent instantiation. Then construct a pointer object for that struct. Here's a complete-but-trivial program with that idea:
#include <cstdlib>
#include <cstring>
struct MyBuffer
{
int length;
char data[1];
private:
MyBuffer() {}
MyBuffer& operator =(MyBuffer& other) { return other; }
};
class MyBufferPointer
{
MyBuffer *bufptr_;
static std::size_t getsize(std::size_t array_size)
{
return sizeof (MyBuffer) + array_size * sizeof (char);
}
static MyBuffer *getbuf(std::size_t array_length)
{
std::size_t sz = getsize(array_length);
return static_cast<MyBuffer*>( malloc(sz) );
}
public:
MyBufferPointer() { bufptr_ = NULL; }
MyBufferPointer(std::size_t array_length)
{
bufptr_ = getbuf(array_length);
bufptr_->length = array_length;
}
MyBufferPointer(const MyBufferPointer &other)
{
const MyBuffer *op = other.bufptr_;
if (op == NULL)
{
bufptr_ = NULL;
}
else
{
bufptr_ = getbuf(op->length);
bufptr_->length = op->length;
std::size_t sz = op->length * sizeof op->data[0];
std::memmove( bufptr_->data, op->data, sz );
}
}
MyBufferPointer& operator =(const MyBufferPointer &other)
{
const MyBuffer *op = other.bufptr_;
if (op == NULL)
{
bufptr_ = NULL;
}
else
{
bufptr_ = getbuf(op->length);
bufptr_->length = op->length;
std::size_t sz = op->length * sizeof op->data[0];
std::memmove( bufptr_->data, op->data, sz );
}
return *this;
}
~MyBufferPointer() { if (bufptr_) free(bufptr_); }
std::size_t size() const
{
return bufptr_ ? bufptr_->length : 0;
}
// conventience operations for access to the data array:
char &operator [](std::size_t index) { return bufptr_->data[index]; }
char at(size_t index) const { return bufptr_->data[index]; }
MyBuffer* c_buffer() { return bufptr_; }
};
#include <iostream>
using namespace std;
int main()
{
MyBufferPointer bufp;
cout << "bufp().size() = " << bufp.size()
<< ", c_buffer=" << bufp.c_buffer() << endl;
bufp = MyBufferPointer(100);
cout << "bufp().size() = " << bufp.size()
<< ", c_buffer=" << bufp.c_buffer() << endl;
return 0;
}
The MyBuffer struct is the layout for the C data area, only with private constructor and assignment operator declarations to prevent instantiation or attempt to copy (neither of which will work properly, either in C or C++.) The MyBufferPointer class encapsulates that as a C++ style char[] array, overloading the [] operator.
This still uses malloc(), not new. The memory image needed to satisfy those C APIs you mentioned needs the variable-length struct, and you can't get that in a standard C++ class created by new. This just provides a C++ wrapper to give a single point of struct creation in that class (in the static member functions getsize() and getbuf()); and guaranteed deletion of the buffer when the pointer goes out of scope. You could add resize(), to_string(), substring() or whatever methods you want.
The performance should be identical to the C struct accessed by an ordinary pointer, after optimization, since the methods are declared in-class and simple enough to be inlined.