1

I'm trying to define a Data object that contains its size, followed by size bytes of data.

Something like:

struct Data {
    size_t size;
    char data[1];
    
    static void* operator new( std::size_t size, size_t dataSize ) {
        return ::operator new( size+dataSize-1 );
    }
    static void operator delete( void* data ){
        ::operator delete( data );
    }
    
    Data( size_t size ) : size(size) {
        std::memset( data, 0, size );
    }
};

This works, and I can allocate it with placement new:

Data* data = new (3) Data( 3 );

I'd like to create a std::unique_ptr<Data> and, as good practice, I'd prefer to use std::make_uinque, over a raw new.

Is it possible to call std::make_unique<Data> passing it the data for placement-new?

Any version of the C++ standard is fine.

ShadowRanger
  • 108,619
  • 9
  • 124
  • 184
Helloer
  • 341
  • 1
  • 11
  • `Data` is probably 16 bytes in size, consisting of 8 bytes `size`, 1 byte `data`, and 7 bytes padding, which is probably throwing off your calculations. I think `data` just flat out doesn't belong inside the class; you should get at the extra allocated space with something like `static_cast(this) + sizeof(*this)`. Also, you should *really* think about making `Data`'s constructors `private` (`void horror() { Data d(10); }`... bone chilling), at which point `std::make_unique` doesn't even work anymore and you have to provide your own factory methods. – HTNW Jan 05 '21 at 03:11
  • About the size of `Data` you're right. I can use `struct __attribute__((packed)) Data` though. About the private constructor, I completely agree. I was trying to keep the example short here. – Helloer Jan 05 '21 at 03:26

1 Answers1

2

There is no standard way to do this, because standard C++ doesn't provide any real support for VLAs/flexible array members. make_unique was written solely for either:

  1. A single object of fixed size (it has no mechanism to provide information about the "real" size, it just uses new, which assumes sizeof T is correct and complete)
  2. An array of objects of fixed size (by definition an array must have objects of fixed size, or indexing doesn't work)

I suppose in theory you could make your own version that supported quasi-flexible array members, but there is zero support for this in the C++ standard library. std::make_unique doesn't even have an allocator-aware variant; it's not going out of its way to support unusual use cases. std::allocate_shared (the allocator aware version of std::make_shared) might be forced to support this in ridiculous ways (you'd essentially need to write custom allocators that also knew how much extra memory an allocation request should provide), and there's a proposal for an allocator-aware version of std::make_unique, but again, making this work would be an exercise in insanity.

You say "as good practice, I'd prefer to use std::make_unique, over a raw new", but flexible array members in C++ are already bad practice; there is essentially zero support for them because they break all sorts of assumptions that much of the C++ language and library rely on. If you must do it, write your own factory methods to perform the work; sure, using raw new is frowned on, but confining it to a single API in your code that guarantees the result is a managed pointer before passing it outside that API limits the "damage".

ShadowRanger
  • 108,619
  • 9
  • 124
  • 184