You might want something akin to a string_view: A light object which refers to a portion of a string, but does not own the storage. It might just be a wrapper around pair of char pointers, or a char pointer and a length. Most of the time a function can simply return a string_view (by value) instead of a string (by reference), and the string_view doesn't care how the storage is managed. The specific template instantiation of a string knows how to construct a string_view from its stack or heap-based storage, and that can be hidden inside the function, thereby hiding consumers of the function from any concern about the string's specific template type.
This works particularly well if you're treating strings as mostly immutable. Appending to a string, for example, would actually entail creating a new string of appropriate size, copying the contents of the first, and then added on the contents being appended. Otherwise, a string_view isn't going to provide the consumer a sufficient interface to perform an append operation, because knowledge of the string storage has been hidden. You could instead make your string_view hold a polymorphic reference to the original string instead of just a couple of char pointers, so that it could forward a request to append onto the string that actually knows about its own storage, but using polymorphism for that purpose seems suspect to me.
I had considered something similar to this, although I will admit to not knowing that string_view was a thing. The idea being that you would make the memory elsewhere, and then attach a 'handler' class of some kind to it by passing in a pointer to the external memory during construction, and possibly the size. However, this just doesn't seem like the most elegant solution.
Having said that I have used this solution in multiple projects before, like attaching an 'editor' class to some memory, and then using that to interact with the memory. It is very nice and works quite well, and easily avoids the issues of instruction cache pollution. However, I'm hoping to find a different solution this time.
Okay. If the data is statically allocated on the stack, what do you want to happen when you are returning this from a function? It to be copied to a newly stack allocated copy in the calling function?
Generally, one would return a reference to MyString, but it depends on context.
I personally would use a stack-based allocator. That way I could use it with string, vector, pretty much any container within reason.
This seems to be the most reasonable thing to do. The only issue comes if you use many different static sizes you will run into more instruction cache misses. In the extreme it might actually slow things down! I was thinking perhaps you could export some of the code that is exactly the same across different allocators into a separate base-class and inherit from that. I'm not sure if that would help however, as I'm not super versed in how instructions are stored in memory.
So, in summery we have 3 different solutions I think:
1) Use char* and forget about c++ for this.
2) Use class that is constructed with a pointer to the externally allocated memory, and its size.
3) Use a static allocator.
The only problem with 3, which is my current favorite, is instruction cache pollution if you go overboard. This may be completely unavoidable with this solution, but I'm all ears if someone knows of a way to reduce this side-effect. Seeing as we might be crating a completely different set of instructions for each memory size, and that's not cool.
I think perhaps 2 could also be used very happily. It would effectively be like wrapping the string manipulation functions into a class, although not very OOP, it is probably the fastest and most problem free solution.