I want to define a struct, e.g. type
, such that sizeof(type)
is no less than some value.
Motivation:
I have a vector std::vector<type>
and I will remove some elements from it. Also, I have saved the indexes of some elements to other places, thus I want just mark it as not used and reuse it in the future. This leads me to save the next available position as a list in erased positions. As a result, sizeof(type)
should be no less than sizeof(size_t)
and type
should be properly aligned as well.
Possible Solutions:
boost::variant<type, size_t>
This has two problems from my point of view. If I use boost::get<type>
, the performance will decrease significantly. If I use boost::apply_visitor
, the syntax would be weird and the performance also decreases according to my profile.
union{type t; size_t s;}
This of course works except for two shortfalls. Firstly, the syntax to refer the member of type
would be more messy. Secondly, I have to define constructor, copy constructor, etc. for this union.
Extend type
by char[sizeof(size_t) - sizeof(type)]
This almost fulfills my requirements. However, this risks of zero length array which is not supported by the c++ standard and possibly wrong alignment.
Since I won't use type
as size_t
often, I'd like to just ensure I can use reinterpret_cast<size_t>
when needed.
Complements
After reading the comments, I think the best solution for my problem should be boost::variant
. But I am still wondering is there a way to combine the benefits of solution 2 and 3, i.e.
a. I can access members of type
without changes.
b. Get the guarantee that reinterpret_cast<size_t>
works.
The minimum size must be one byte.
The sizeof for a struct is not always equal to the sum of sizeof of each individual member. This is because of the padding added by the compiler to avoid alignment issues. Padding is only added when a structure member is followed by a member with a larger size or at the end of the structure.
In 32 bit processor, it can access 4 bytes at a time which means word size is 4 bytes. Similarly in a 64 bit processor, it can access 8 bytes at a time which means word size is 8 bytes. Structure padding is used to save number of CPU cycles. Let's see what compiler is giving using the sizeof() operator.
The size of the entire structure is 8 bytes. On knowing the structured padding, it is easier to redesign or rewrite the structure.
You can mitigate the concerns about solution 3 with something like:
struct data
{
// ...
};
template<class T, bool> class pad_;
template<class T> class pad_<T, true> { char dummy[sizeof(T) - sizeof(data)]; };
template<class T> class pad_<T, false> {};
template<class T> using pad = pad_<T, (sizeof(T) > sizeof(data))>;
class type : public data, pad<size_t>
{
// ...
};
This code:
pad
could be completely optimized out from type
layout when sizeof(data) >= sizeof(size_t)
Though this being an interesting problem the design itself seams questionable.
When inserting a new element items marked unused are considered first before growing the vector. It means that the relative order of items is unpredictable. If that's being acceptable you could have just used a vector of (smart) pointers.
Typically a vector is inefficient when removing items from the middle. Since the order doesn't matter it is possible to swap the element being removed with the last element and pop the last element.
All elements are of the same size; allocating them using a pool could be faster then using the system allocator.
A pool basically allocates memory in big chunks and hands out smaller chunks on request. A pool usually stores the free list in yet unallocated chunks to track available memory (the same very idea described in the question). There are some good implementations readily available (from Boost and other sources).
Concerning the original design it is cumbersome to enumerate elements in the vector since real elements are mixed with "holes", the logic is going to be obfuscated with additional checks.
Probably there is some sold reasoning behind the original design; unfortunately @user1535111 is not telling the details.
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