In a project about a decade ago, we found that std::vector
's dynamic allocations caused a serious performance drain. In this case it was many small vectors allocated, so the quick solution was to write a vector-like class wrapping around a stack-based pre-allocated char
array used as the raw storage for its capacity. The result was a static_vector<typename T, std::size_t Max>
. Such a thing is easy enough to write if you know a few basics, and you can find quite a few such beasts on the web. In fact, boost has one, too, now.
Working on an embedded platform now, we happen to need a static_basic_string
. That would be a string that pre-allocates a fixed maximum amount of memory on the stack and uses that as its capacity.
At first I thought this should be fairly easy (it could be based it on the existing static_vector
, after all), but looking again at std::basic_string
's interface I am not so sure anymore. It is way more complex than std::vector
's interface. Especially implementing the family of find()
functions std::basic_string
comes with is more than just a tedious task.
That got me thinking again. After all, this is what allocators were created for: replace allocation based on new
and delete
with some other means. However, to say that the allocator interface is unwieldy would be an understatement. There are a few articles out there explaining it, but there is a reason I have seen so very few home-grown allocators in the last 15 years.
So here is my question:
If you had to implement a basic_string
lookalike, how would you do it?
- Write your own
static_basic_string
? - Write an allocator to pass to
std::basic_string
? - Do something I did not think of?
- Use something from boost I am not aware of?
As always, there is a rather essential restriction for us: Being on an embedded platform, we are tied to GCC 4.1.2, so we can only employ C++03, TR1, and boost 1.52.
The first question is: how much of the extra interface do you use? Most of
std::string
's additional interfaces can be trivially implemented using functions in<algorithm>
(e.g.std::find
,std::find_if
andstd::search
), and in a lot of cases, there are large chunks of it that won't be used anyway. Just implement it on an as needed basis.I don't think you can make this work with a custom allocator. The only way to get the memory "on stack" would be to declare it as a member of the custom allocator, which would create all sorts of problems when copying them. And allocators must be copiable, and the copies must be idempotent.
Perhaps you can find a free implementation on the net of
std::string
which uses the small string implementation; then modify it so that the cutoff size (beyond which it uses dynamic allocation) is larger than any strings you actually use. (There are several open-source implementations of the standard library available; the one delivered with g++ still uses COW, but I suspect that most of the others use SSO.)