Deleted the C tag, seeing that this was causing some confusion (it should not have started with this, sorry for any inconvenience there. C response is still welcome :)
In several things that I did, I found the need to create objects with dynamic size and static size, where the static part is your main members of the object, however the dynamic part is an array / buffer attached directly to the class, preserving memory continuity, thereby reducing the number of necessary distributions (these are objects that are not redistributable), and reducing fragmentation (although it may be more difficult to find a block with a sufficiently large size as the lower side, but this is much less common - whether this should happen at all - than fragmentation of the heap. It is also useful for embedded devices where memory is very expensive (however, I am not doing anything for embedded devices at present) and things like std :: string should be avoided or not used , as in the case of trivial unions.
In general, as I would do, it would (ab) use malloc (std :: string is not used specifically and for various reasons):
struct TextCache { uint_32 fFlags; uint_16 nXpos; uint_16 nYpos; TextCache* pNext; char pBuffer[0]; }; TextCache* pCache = (TextCache*)malloc(sizeof(TextCache) + (sizeof(char) * nLength));
This, however, is not too good with me, since at first I would like to do it using a new one, and therefore in a C ++ environment, and secondly, it looks awful: P
So, the next step was the C ++ boilerplate variable:
template <const size_t nSize> struct TextCache { uint_32 fFlags; uint_16 nXpos; uint_16 nYpos; TextCache<nSize>* pNext; char pBuffer[nSize]; };
This has a problem that saving a pointer to a variable-sized object becomes "impossible", so the following work around:
class DynamicObject {}; template <const size_t nSize> struct TextCache : DynamicObject {...};
However, this still requires casting, and the presence of pointers to DynamicObjects around the place becomes ambiguous when another object with a dynamic size follows from it (it also looks terrible and may suffer from an error that causes empty classes to still have size, although this is probably an archaic, extinct mistake ...).
Then there was the following:
class DynamicObject { void* operator new(size_t nSize, size_t nLength) { return malloc(nSize + nLength); } }; struct TextCache : DynamicObject {...};
which looks much better, but will interfere with objects that already have overloads of new ones (this may even affect the placement of new ones ...).
Finally, I came up with a new investigation:
inline TextCache* CreateTextCache(size_t nLength) { char* pNew = new char[sizeof(TextCache) + nLength]; return new(pNew) TextCache; }
This, however, is probably the worst idea so far for a number of reasons.
So are there any better ways to do this? or will one of the above versions be better or at least improved? Is it considered safe and / or bad programming practice?
As I said above, I try to avoid double distributions, because there should not be two distributions for this, and this leads to greatly simplified writing (serialization) of these things to files. The only exception to the double allocation requirement that I have is when the overhead is mostly zero. the only reason I ran into this is where I sequentially allocate memory from a fixed buffer ( using this system that I ran into) however this is also a special exception to prevent over-dense copying.