Lets say I have a function to get data into an std vector:
void getData(std::vector<int> &toBeFilled) {
// Push data into "toBeFilled"
}
Now I want to send this data to another function, that should free the data when finished:
void useData(int* data)
{
// Do something with the data...
delete[] data;
}
Both functions (getData and useData) are fixed and cannot be changed. This works fine when copying the data once:
{
std::vector<int> data;
getData(data);
int *heapData = new int[data.size()];
memcpy(heapData, data.data(), data.size()*sizeof(int));
useData(heapData);
data.clear();
}
However, this memcpy operation is expensive and not really required, since the data is already on the heap. Is it possible to directly extract and use the data allocated by the std vector? Something like (pseudocode):
{
std::vector<int> data;
getData(data);
useData(data.data());
data.clearNoDelete();
}
Edit:
The example maybe doesn't make too much sense, since it is possible to just free the vector after the function call to useData. However, in the real code, useData is not a function but a class that receives the data, and this class lives longer than the vector...
No.
The API you're using has a contract that states it takes ownership of the data you provide it, and that this data is provided through a pointer. This basically rules out using standard vectors.
Vector will always assuredly free the memory it allocated and safely destroy the elements it contains. That is part of its guaranteed contract and you cannot turn that off.
You have to make a copy of the data if you wish to take ownership of them... or move each element out into your own container. Or start with your own new[] in the first place (ugh) though you can at least wrap all this in some class that mimics std::vector and becomes non-owning.
Here's a horrible hack which should allow you to do what you need, but it relies on Undefined Behaviour doing the simplest thing it can. The idea is to create your own allocator which is layout-compatible with std::allocator and type-pun the vector:
template <class T>
struct CheatingAllocator : std::allocator<T>
{
using typename std::allocator<T>::pointer;
using typename std::allocator<T>::size_type;
void deallocate(pointer p, size_type n) { /* no-op */ }
// Do not add ANY data members!!
};
{
std::vector<int, CheatingAllocator<int>> data;
getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
useData(data.data());
// data actually uses your own allocator, so it will not deallocate anything
}
Note that it's as hacky and unsafe as hacks go. It relies on the memory layout not changing and it relies of std::allocator using new[] inside its allocate function. I wouldn't use this in production code myself, but I believe it is a (desperate) solution.
#TonyD correctly pointed out in the comments that std::allocator is quite likely to not use new[] internally. Therefore, the above would most likely fail on the delete[] inside useData(). The same #TonyD also made a good point about using reserve() to (hopefully) prevent reallocation inside getData(). So the updated code would look like this:
template <class T>
struct CheatingAllocator : std::allocator<T>
{
using typename std::allocator<T>::pointer;
using typename std::allocator<T>::size_type;
pointer allocate(size_type n) { return new T[n]; }
void deallocate(pointer p, size_type n) { /* no-op */ }
// Do not add ANY data members!!
};
{
std::vector<int, CheatingAllocator<int>> data;
data.reserve(value_such_that_getData_will_not_need_to_reallocate);
getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
useData(data.data());
// data actually uses your own allocator, so it will not deallocate anything
}
Related
I am using some C-library in my C++ code. The library wants me to allocate some amount of the memory and pass the pointer to the library. Unfortunately, the exact required memory size is not known in advance, so the library also requires me to provide C-callback with the following signature:
void* callback_realloc(void* ptr, size_t new_size);
where ptr is previously passed memory and new_size is required size, and the callback must return the pointer to newly allocated memory. There is no direct way to store the allocator state. Instead, I need to rely on pointer arithmetic somehow as the following:
template<class T>
struct o_s {
std::aligned_storage_t<sizeof(T), alignof(T)> data;
};
template<class Alloc>
struct o_i: private Alloc {
std::size_t allocated_size;
const Alloc& get_allocator() const { return *this; }
};
template<class T>
struct o: public o_i, public o_s<T> {
void* ptr() {
return &data;
}
// Additionally, override class operator new and operator delete...
};
void* my_callback(void* ptr, size_t new_size) {
auto meta = static_cast<o*>(reinterpret_cast<o_s<char>*>(ptr));
// access to the allocator state ...
}
Then sizeof(o_i) + initial_size memory is allocated and ptr() is passed to the C library.
At this point, I understand that I am not the first person in the world who needs this pattern. Unfortunately (and surprisingly) I have not found anything suitable for this in Boost or STL. I would like to use ready implementation to avoid possible underwater rocks.
The simplest solution is to allocate the memory using std::malloc, and use std::realloc as the callback. C API such as this are the case where using those makes sense in C++.
You don't necessarily have to use std::malloc however. You can implement a custom allocator if you want to. Using some of the allocated storage is one way of storing allocation metadata, and it's an efficient way. That's not necessary either though, since you can also store the metadata separately in a map-like structure.
I don't think you'd find an idiomatic way to do this in C++, as this is a C idiom.
Conceptually, you have two ways of going about this:
Store the metadata alongside the buffer you've allocated (as you seem to be doing). I feel using double inheritance etc. is a bit overkill here, when you could just allocate a char buffer of sizeof(size_t)+allocation_size and use the first part for your metadata.
Allocate and return to the API a raw buffer as needed, and use a separate static data structure to manage this with a map of ptr->allocation size. I suppose this is what malloc/realloc is doing behind the scenes anyway.
Both are valid, and the one you chose depends on the specific details of your application. When dealing with memory it's ok to actually deal with memory, be it pointer arithmetic or what not.
The important thing is probably to keep the ugly bit to a single location, and provide a C++ style API to this on the C++ side of things, so that the client code isn't exposed to implementation details.
My teammates are writing a fixed-size implementation of std::vector for a safety-critical application. We're not allowed to use heap allocation, so they created a simple array wrapper like this:
template <typename T, size_t NUM_ITEMS>
class Vector
{
public:
void push_back(const T& val);
...more vector methods
private:
// Internal storage
T storage_[NUM_ITEMS];
...implementation
};
A problem we encountered with this implementation is that it requires elements present default constructors (which is not a requirement of std::vector and created porting difficulties). I decided to hack on their implementation to make it behave more like std::vector and came up with this:
template <typename T, size_t NUM_ITEMS>
class Vector
{
public:
void push_back(const T& val);
...more vector methods
private:
// Internal storage
typedef T StorageType[NUM_ITEMS];
alignas(T) char storage_[NUM_ITEMS * sizeof(T)];
// Get correctly typed array reference
StorageType& get_storage() { return reinterpret_cast<T(&)[NUM_ITEMS]>(storage_); }
const StorageType& get_storage() const { return reinterpret_cast<const T(&)[NUM_ITEMS]>(storage_); }
};
I was then able to just search and replace storage_ with get_storage() and everything worked. An example implementation of push_back might then look like:
template <typename T, size_t NUM_ITEMS>
void Vector<T, NUM_ITEMS>::push_back(const T& val)
{
get_storage()[size_++] = val;
}
In fact, it worked so easily that it got me thinking.. Is this a good/safe use of reinterpret_cast? Is the code directly above a suitable alternative to placement new, or are there risks associated with copy/move assignment to an uninitialized object?
EDIT: In response to a comment by NathanOliver, I should add that we cannot use the STL, because we cannot compile it for our target environment, nor can we certify it.
The code you've shown is only safe for POD types (Plain Old Data), where the object's representation is trivial and thus assignment to an unconstructed object is ok.
If you want this to work in all generality (which i assume you do due to using a template), then for a type T it is undefined behavior to use the object prior to construction it. That is, you must construct the object before your e.g. assignment to that location. That means you need to call the constructor explicitly on demand. The following code block demonstrates an example of this:
template <typename T, size_t NUM_ITEMS>
void Vector<T, NUM_ITEMS>::push_back(const T& val)
{
// potentially an overflow test here
// explicitly call copy constructor to create the new object in the buffer
new (reinterpret_cast<T*>(storage_) + size_) T(val);
// in case that throws, only inc the size after that succeeds
++size_;
}
The above example demonstrates placement new, which takes the form new (void*) T(args...). It calls the constructor but does not actually perform an allocation. The visual difference is the inclusion of the void* argument to operator new itself, which is the address of the object to act on and call the constructor for.
And of course when you remove an element you'll need to destroy that explicitly as well. To do this for a type T, simply call the pseudo-method ~T() on the object. Under templated context the compiler will work out what this means, either an actual destructor call, or no-op for e.g. int or double. This is demonstrated below:
template<typename T, size_t NUM_ITEMS>
void Vector<T, NUM_ITEMS>::pop_back()
{
if (size_ > 0) // safety test, you might rather this throw, idk
{
// explicitly destroy the last item and dec count
// canonically, destructors should never throw (very bad)
reinterpret_cast<T*>(storage_)[--size_].~T();
}
}
Also, I would avoid returning a refernce to an array in your get_storage() method, as it has length information and would seem to imply that all elements are valid (constructed) objects, which of course they're not. I suggest you provide methods for getting a pointer to the start of the contiguous array of constructed objects, and another method for getting the number of constructed objects. These are the .data() and .size() methods of e.g. std::vector<T>, which would make use of your class less jarring to seasoned C++ users.
Is this a good/safe use of reinterpret_cast?
Is the code directly above a suitable alternative to placement new
No. No.
or are there risks associated with copy/move assignment to an uninitialized object?
Yes. The behaviour is undefined.
Assuming memory is uninitialised, copying the vector has undefined behaviour.
No object of type T has started its lifetime at the memory location. This is super bad when T is not trivial.
The reinterpretation violates the strict aliasing rules.
First is fixed by value-initialising the storage. Or by making the vector non-copyable and non-movable.
Second is fixed by using placement new.
Third is technically fixed by using using the pointer returned by placement new, but you can avoid storing that pointer by std::laundering after reinterpreting the storage.
Motivation
Imagining that there is a very large class A, and I wanna store it in a vector and I also want the vector to be copyable. Thus, smart pointer like std::shared_ptr and std::unique_ptr would not be nice choices. I know I could store objects of A in std::vector<A> directly. But as I said, A is a very large class. And since elements of std::vector<A> reside continuously in memory, there is a big chance that std::vector<A> needs to reallocate memory if I update it frequently. So, I need a "smart pointer" (let's say copyable_ptr) which will copy the underlying resource when copying the smart pointer itself. Then I could use std::vector<copyable_ptr<A>> to manage objects of A.
Here is a snippet template class of copyable_ptr:
template <typename T> class copyable_ptr {
public:
explicit copyable_ptr(T *p) : p(p) {}
copyable_ptr(copyable_ptr const &rhs) : p(new T(*rhs.p)) {}
copyable_ptr(copyable_ptr &&rhs) : p(rhs.p) { rhs.p = nullptr; }
~copyable_ptr() { delete p; }
copyable_ptr &operator=(copyable_ptr rhs) {
swap(rhs);
return *this;
}
void swap(copyable_ptr &rhs) {
using std::swap;
swap(p, rhs.p);
}
T &operator*() const { return *p; }
T *operator->() const { return &this->operator*(); }
/* other members */
private:
T *p;
};
template <typename T>
inline void swap(copyable_ptr<T> &lhs, copyable_ptr<T> &rhs) {
lhs.swap(rhs);
}
Question
Is there similar class that I can use directly or should I write my own or Is there any other appropriate solutions to store big class in container?
Edit
I think I should explain myself more clearly. A is a very large class, and I wanna use std::vector to manage objects of A. Basically, there are four options to do the job:
std::vector<std::shared_ptr<A>> a1; // #1
std::vector<std::unique_ptr<A>> a2; // #2
std::vector<A *> a3; // #3
std::vector<A> a4; // #4
I do not want to share objects of A, which makes #1 inappropriate. The vector will not be copyable in #2, and I have to take care of memory management manually
in #3. So, only #4 lefts.
However, what I don't like about #4 is when size of a4 reaches its capacity, reallocating happens. Double size of a4 will be reserved (as I said, A is a very large class, isn't it inappropriate to reserve so much continuous memory in heap just for potential usage?). And What's worse is we perhaps need to move every single element of a4 to the newly allocated memory from old memory location. Another backward of #4 that I forgot to mention when I asked the question is it's not polymorphic support.
So, if I use std::vector<copyable_ptr<A>> to manage objects of A, all the problems I mentioned above seems be handled properly. All you need is a well designed copyable_ptr class, and that's what I'm looking for.
Yes, there is such a thing: it's called the class itself.
The whole purpose of a smart pointer is to provide a reference-counted handle for an object, that can be passed around, without copying the underlying object, and automatically destroy the object when the reference count goes down to 0, and the last smart pointer referencing the object goes out of scope, and it gets destroyed (with certain variations of reference-counting semantics between shared_ptr, unique_ptr, et al).
If you want to have some kind of a pointer to an object that copies it, when copying the pointer, you don't need a pointer for that. Just use the object directly, by value. Every time you copy it, you're guaranteed to copy the object, by definition.
Having some kind of pointer, around, adds absolutely nothing of value. You want to use the object directly, copy it when needed, etc... Just use the object.
As far as I know, there is no such smart pointer in the standard library. Your copyable_ptr seems to do the job. I've used such things in the past, though for polymorphic classes instead of just really big classes.
Not exactly the answer to question, but if this:
However, what I don't like about #4 is when size of a4 reaches its capacity, reallocating happens.
is your main concern and if your are Ok with storing your classes in non-contiguous memory with slightly slower direct access by index, you can use std::deque instead of std::vector.
I tried this:
....
vector<players*> player;
for (int i = 0; i<10; i++)
{
player.push_back(new players());
}
...
And I wonder if I need to free memory for the vector? If so, how?
If you need to store pointers to things in a container, you should either store some sort of smart pointer (like std::tr1::shared_ptr or boost::shared_ptr) or use a container designed for storing pointers, like those found in the Boost Pointer Container Library.
If you do store bare pointers, you need to remember to delete the pointers in the container before the container is destroyed. This includes any time that an exception is thrown that might cause the container to be destroyed. Getting this right is tedious, bug-prone, and wholly unnecessary given the facilities mentioned in the previous paragraph.
Yes, you do need to delete them yourself. The vector is only going to "destruct" the pointers (which does nothing).
If you can, use the Boost pointer containers library, and you won't have to worry about it. However, if you can't you need to wrap the container up. Consider an exception is thrown between the time the container is populated and the time its elements are deleted. You will not execute the element delete code and leak.
A simple wrapper might look like:
struct default_deleter
{
template <typename T>
void operator()(T* pPtr)
{
delete pPtr;
}
};
template <typename T, typename Deleter = default_deleter>
struct container_wrapper
{
typedef T container_type;
typedef Deleter deleter_type;
container_wrapper(container_type pContainer = container_type()) :
container(pContainer)
{}
~container_wrapper(void)
{
std::for_each(container.begin(), container.end(), deleter_type());
}
container_type container;
};
Use it like:
typedef std::vector<int*> vec_intptr;
typedef container_wrapper<vec_intptr> vec;
vec v;
v.container.push_back(new int); // and never worry about it again
This is a simple wrapper. Any pop_back(), erase(), etc. operations will have the wrong effect. I strongly suggest using Boost.
One may think of using a container of auto_ptr. Contrarily, this is a bad idea; the copy-semantics of an auto_ptr prevent it from working. The best option is to get rid of the dynamic allocation, if possible.
since you are creating new players(), you will have to delete them. probably best to iterate through the vector deleting the players and then clean up your vector.
Is this the best way to make a variable sized struct in C++? I don't want to use vector because the length doesn't change after initialization.
struct Packet
{
unsigned int bytelength;
unsigned int data[];
};
Packet* CreatePacket(unsigned int length)
{
Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
output->bytelength = length;
return output;
}
Edit: renamed variable names and changed code to be more correct.
Some thoughts on what you're doing:
Using the C-style variable length struct idiom allows you to perform one free store allocation per packet, which is half as many as would be required if struct Packet contained a std::vector. If you are allocating a very large number of packets, then performing half as many free store allocations/deallocations may very well be significant. If you are also doing network accesses, then the time spent waiting for the network will probably be more significant.
This structure represents a packet. Are you planning to read/write from a socket directly into a struct Packet? If so, you probably need to consider byte order. Are you going to have to convert from host to network byte order when sending packets, and vice versa when receiving packets? If so, then you could byte-swap the data in place in your variable length struct. If you converted this to use a vector, it would make sense to write methods for serializing / deserializing the packet. These methods would transfer it to/from a contiguous buffer, taking byte order into account.
Likewise, you may need to take alignment and packing into account.
You can never subclass Packet. If you did, then the subclass's member variables would overlap with the array.
Instead of malloc and free, you could use Packet* p = ::operator new(size) and ::operator delete(p), since struct Packet is a POD type and does not currently benefit from having its default constructor and its destructor called. The (potential) benefit of doing so is that the global operator new handles errors using the global new-handler and/or exceptions, if that matters to you.
It is possible to make the variable length struct idiom work with the new and delete operators, but not well. You could create a custom operator new that takes an array length by implementing static void* operator new(size_t size, unsigned int bitlength), but you would still have to set the bitlength member variable. If you did this with a constructor, you could use the slightly redundant expression Packet* p = new(len) Packet(len) to allocate a packet. The only benefit I see compared to using global operator new and operator delete would be that clients of your code could just call delete p instead of ::operator delete(p). Wrapping the allocation/deallocation in separate functions (instead of calling delete p directly) is fine as long as they get called correctly.
If you never add a constructor/destructor, assignment operators or virtual functions to your structure using malloc/free for allocation is safe.
It's frowned upon in c++ circles, but I consider the usage of it okay if you document it in the code.
Some comments to your code:
struct Packet
{
unsigned int bitlength;
unsigned int data[];
};
If I remember right declaring an array without a length is non-standard. It works on most compilers but may give you a warning. If you want to be compliant declare your array of length 1.
Packet* CreatePacket(unsigned int length)
{
Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
output->bitlength = length;
return output;
}
This works, but you don't take the size of the structure into account. The code will break once you add new members to your structure. Better do it this way:
Packet* CreatePacket(unsigned int length)
{
size_t s = sizeof (Packed) - sizeof (Packed.data);
Packet *output = (Packet*) malloc(s + length * sizeof(unsigned int));
output->bitlength = length;
return output;
}
And write a comment into your packet structure definition that data must be the last member.
Btw - allocating the structure and the data with a single allocation is a good thing. You halve the number of allocations that way, and you improve the locality of data as well. This can improve the performance quite a bit if you allocate lots of packages.
Unfortunately c++ does not provide a good mechanism to do this, so you often end up with such malloc/free hacks in real world applications.
This is OK (and was standard practice for C).
But this is not a good idea for C++.
This is because the compiler generates a whole set of other methods automatically for you around the class. These methods do not understand that you have cheated.
For Example:
void copyRHSToLeft(Packet& lhs,Packet& rhs)
{
lhs = rhs; // The compiler generated code for assignement kicks in here.
// Are your objects going to cope correctly??
}
Packet* a = CreatePacket(3);
Packet* b = CreatePacket(5);
copyRHSToLeft(*a,*b);
Use the std::vector<> it is much safer and works correctly.
I would also bet it is just as efficient as your implementation after the optimizer kicks in.
Alternatively boost contains a fixed size array:
http://www.boost.org/doc/libs/1_38_0/doc/html/array.html
You can use the "C" method if you want but for safety make it so the compiler won't try to copy it:
struct Packet
{
unsigned int bytelength;
unsigned int data[];
private:
// Will cause compiler error if you misuse this struct
void Packet(const Packet&);
void operator=(const Packet&);
};
I'd probably just stick with using a vector<> unless the minimal extra overhead (probably a single extra word or pointer over your implementation) is really posing a problem. There's nothing that says you have to resize() a vector once it's been constructed.
However, there are several The advantages of going with vector<>:
it already handles copy, assignment & destruction properly - if you roll your own you need to ensure you handle these correctly
all the iterator support is there - again, you don't have to roll your own.
everybody already knows how to use it
If you really want to prevent the array from growing once constructed, you might want to consider having your own class that inherits from vector<> privately or has a vector<> member and only expose via methods that just thunk to the vector methods those bits of vector that you want clients to be able to use. That should help get you going quickly with pretty good assurance that leaks and what not are not there. If you do this and find that the small overhead of vector is not working for you, you can reimplement that class without the help of vector and your client code shouldn't need to change.
There are already many good thoughts mentioned here. But one is missing. Flexible Arrays are part of C99 and thus aren't part of C++, although some C++ compiler may provide this functionality there is no guarantee for that. If you find a way to use them in C++ in an acceptable way, but you have a compiler that doesn't support it, you perhaps can fallback to the "classical" way
If you are truly doing C++, there is no practical difference between a class and a struct except the default member visibility - classes have private visibility by default while structs have public visibility by default. The following are equivalent:
struct PacketStruct
{
unsigned int bitlength;
unsigned int data[];
};
class PacketClass
{
public:
unsigned int bitlength;
unsigned int data[];
};
The point is, you don't need the CreatePacket(). You can simply initialize the struct object with a constructor.
struct Packet
{
unsigned long bytelength;
unsigned char data[];
Packet(unsigned long length = 256) // default constructor replaces CreatePacket()
: bytelength(length),
data(new unsigned char[length])
{
}
~Packet() // destructor to avoid memory leak
{
delete [] data;
}
};
A few things to note. In C++, use new instead of malloc. I've taken some liberty and changed bitlength to bytelength. If this class represents a network packet, you'll be much better off dealing with bytes instead of bits (in my opinion). The data array is an array of unsigned char, not unsigned int. Again, this is based on my assumption that this class represents a network packet. The constructor allows you to create a Packet like this:
Packet p; // default packet with 256-byte data array
Packet p(1024); // packet with 1024-byte data array
The destructor is called automatically when the Packet instance goes out of scope and prevents a memory leak.
You probably want something lighter than a vector for high performances. You also want to be very specific about the size of your packet to be cross-platform. But you don't want to bother about memory leaks either.
Fortunately the boost library did most of the hard part:
struct packet
{
boost::uint32_t _size;
boost::scoped_array<unsigned char> _data;
packet() : _size(0) {}
explicit packet(packet boost::uint32_t s) : _size(s), _data(new unsigned char [s]) {}
explicit packet(const void * const d, boost::uint32_t s) : _size(s), _data(new unsigned char [s])
{
std::memcpy(_data, static_cast<const unsigned char * const>(d), _size);
}
};
typedef boost::shared_ptr<packet> packet_ptr;
packet_ptr build_packet(const void const * data, boost::uint32_t s)
{
return packet_ptr(new packet(data, s));
}
There's nothing whatsoever wrong with using vector for arrays of unknown size that will be fixed after initialization. IMHO, that's exactly what vectors are for. Once you have it initialized, you can pretend the thing is an array, and it should behave the same (including time behavior).
Disclaimer: I wrote a small library to explore this concept: https://github.com/ppetr/refcounted-var-sized-class
We want to allocate a single block of memory for a data structure of type T and an array of elements of type A. In most cases A will be just char.
For this let's define a RAII class to allocate and deallocate such a memory block. This poses several difficulties:
C++ allocators don't provide such API. Therefore we need to allocate plain chars and place the structure in the block ourselves. For this std::aligned_storage will be helpful.
The memory block must be properly aligned. Because in C++11 there doesn't seem to be API for allocating an aligned block, we need to slightly over-allocate by alignof(T) - 1 bytes and then use std::align.
// Owns a block of memory large enough to store a properly aligned instance of
// `T` and additional `size` number of elements of type `A`.
template <typename T, typename A = char>
class Placement {
public:
// Allocates memory for a properly aligned instance of `T`, plus additional
// array of `size` elements of `A`.
explicit Placement(size_t size)
: size_(size),
allocation_(std::allocator<char>().allocate(AllocatedBytes())) {
static_assert(std::is_trivial<Placeholder>::value);
}
Placement(Placement const&) = delete;
Placement(Placement&& other) {
allocation_ = other.allocation_;
size_ = other.size_;
other.allocation_ = nullptr;
}
~Placement() {
if (allocation_) {
std::allocator<char>().deallocate(allocation_, AllocatedBytes());
}
}
// Returns a pointer to an uninitialized memory area available for an
// instance of `T`.
T* Node() const { return reinterpret_cast<T*>(&AsPlaceholder()->node); }
// Returns a pointer to an uninitialized memory area available for
// holding `size` (specified in the constructor) elements of `A`.
A* Array() const { return reinterpret_cast<A*>(&AsPlaceholder()->array); }
size_t Size() { return size_; }
private:
// Holds a properly aligned instance of `T` and an array of length 1 of `A`.
struct Placeholder {
typename std::aligned_storage<sizeof(T), alignof(T)>::type node;
// The array type must be the last one in the struct.
typename std::aligned_storage<sizeof(A[1]), alignof(A[1])>::type array;
};
Placeholder* AsPlaceholder() const {
void* ptr = allocation_;
size_t space = sizeof(Placeholder) + alignof(Placeholder) - 1;
ptr = std::align(alignof(Placeholder), sizeof(Placeholder), ptr, space);
assert(ptr != nullptr);
return reinterpret_cast<Placeholder*>(ptr);
}
size_t AllocatedBytes() {
// We might need to shift the placement of for up to `alignof(Placeholder) - 1` bytes.
// Therefore allocate this many additional bytes.
return sizeof(Placeholder) + alignof(Placeholder) - 1 +
(size_ - 1) * sizeof(A);
}
size_t size_;
char* allocation_;
};
Once we've dealt with the problem of memory allocation, we can define a wrapper class that initializes T and an array of A in an allocated memory block.
template <typename T, typename A = char,
typename std::enable_if<!std::is_destructible<A>{} ||
std::is_trivially_destructible<A>{},
bool>::type = true>
class VarSized {
public:
// Initializes an instance of `T` with an array of `A` in a memory block
// provided by `placement`. Callings a constructor of `T`, providing a
// pointer to `A*` and its length as the first two arguments, and then
// passing `args` as additional arguments.
template <typename... Arg>
VarSized(Placement<T, A> placement, Arg&&... args)
: placement_(std::move(placement)) {
auto [aligned, array] = placement_.Addresses();
array = new (array) char[placement_.Size()];
new (aligned) T(array, placement_.Size(), std::forward<Arg>(args)...);
}
// Same as above, with initializing a `Placement` for `size` elements of `A`.
template <typename... Arg>
VarSized(size_t size, Arg&&... args)
: VarSized(Placement<T, A>(size), std::forward<Arg>(args)...) {}
~VarSized() { std::move(*this).Delete(); }
// Destroys this instance and returns the `Placement`, which can then be
// reused or destroyed as well (deallocating the memory block).
Placement<T, A> Delete() && {
// By moving out `placement_` before invoking `~T()` we ensure that it's
// destroyed even if `~T()` throws an exception.
Placement<T, A> placement(std::move(placement_));
(*this)->~T();
return placement;
}
T& operator*() const { return *placement_.Node(); }
const T* operator->() const { return &**this; }
private:
Placement<T, A> placement_;
};
This type is moveable, but obviously not copyable. We could provide a function to convert it into a shared_ptr with a custom deleter. But this will need to internally allocate another small block of memory for a reference counter (see also How is the std::tr1::shared_ptr implemented?).
This can be solved by introducing a specialized data type that will hold our Placement, a reference counter and a field with the actual data type in a single structure. For more details see my refcount_struct.h.
You should declare a pointer, not an array with an unspecified length.