I recall some "detail" code in Boost that does exactly what I want:
allocate a probably-good-enough size on the stack but switch to heap if
needed. But I can't remember where I saw it.
Looking around, I came across mentions of Boost.AutoBuffer by Thorsten
Ottosen, but all links are dead.
I have been a bit busy, but I plan to get it going soon.
It seems that there is an unofficial version in
boost/signals2/auto_buffer.hpp
regards
-Thorsten
Thanks. I found that copy, and it had your name on it too, so I figure it must be the
same code.
I like the feature of making an "uninitialized" expansion, too. In particular, for
dealing with "out" parameters of OS API calls or legacy code, I want to naturally give my
buffer as a vector.data(). But it is annoying to me that it needs to zero out the whole
buffer just to pass it to code that is expecting uninitialized area for output, anyway.
Making "uninitialized" part of this class is natural since the hopefull stack space is
used in the same context. And being your own brand new class, it doesn't fiddle with any
standard library code to achieve it. <g>.
However, I think the existing public API could be improved a little in this manner. Here
is what I ended up doing:
auto_buffer buf;
buf.uninitialized_grow (buf.capacity()); // start out with as much as fits on the
stack (256 elements).
int result= ::GetWindowTextW(hWnd,buf.data(),buf.size());
I had to figure out that capacity was the correct way to get the stack-based size, but
that's a documentation thing. I called uninitialized_grow rather than
uninitialized_resize because I know it was originally zero; the passing of the delta which
looks like the new size too bothers me a little. But my real point is that I can't
command an uninitialized size on the constructor. The normal constructor arguments work
the same as vector, and can take an initial size and a value that defaults to T(). It
would be much clearer and simpler for this use case to say what I want directly on the
constructor. I think it would be natural to give a special flag where the value would
normally go:
auto_buffer buf1 (500 /*count*/, L' ' /* value */); // same as std::vector
auto_buffer buf2 (500, non_initialized); // looks like a "value", but
overloaded ctor.
and it would be unambiguous to give the non_initialized flag alone, taking the maximum
pre-allocated size.
auto_buffer buf3 (non_initialized);
And, as a further trick, it is a cheap failsafe to initialize just the last element with a
sentinel value. This is not as inefficient as needlessly initializing the whole buffer,
but adds a cheap fallback just in case the call that was supposed to write to the buffer
didn't to anything.
auto_buffer buf4 (non_initialized, L'\0'); // the last index is set to zero
In general, any signature that takes a T after the special non_initialized_t takes it to
be a sentinal initialization, and two such arguments are for last and first index (only).
Or, since the same value ought to be used for both, maybe it always does first and last,
or uses different special flag names for non_initialized-except-for...
With the ctor signature it is clear what is being done, and I don't need to explicitly
reference the stack-based size separately from the template argument that gives it. And,
it generates simple direct code to set it up that way rather than setting it and changing
it around again. I don't think the optimizer would figure out that capacity() at this
point means the stack-based size, and then optimize out the large case from the resize code!
Meanwhile, should the class support uninitialized sizing ONLY for types that support it?
That is, PODs, primitives, classes without destructors, and in general those that are
marked as such via a metaprogramming type trait.