I would like to propose a standardised fixed size string type, fixed_string<N> as a thin wrapper of char[N+1].
The standard allows for implementations to store extra data in std::array, so that is why char[N+1] makes more sense than std::array for this purpose - the size should be exactly N+1 chars.
The name is debatable. Fixed has a number of meanings and I don't like the idea of someone thinking the objective is to "fix" something broken with std::string, but this name is common to many existing implementations. The +1 is also debatable. Given the number of functions in the wild that assume a null char termination, that extra char provides safety. Overwriting that final char is then classed as UB. Having non-null chars after the first null char is UB. Primary focus should be on providing intuitive interface that makes it easier to stick to defined behaviour.
I have worked with many 3rd party libraries involving low latency transport, and fixed size string implementations are to be found in the majority of those. I use fixed size strings in my own libraries, and I have also seen them in a variety of open source codebases. The benefits of it are the same that std::array has over std::vector, or that char[N] has over char*, in that its value is stack allocated and its length is encoded in its type. The upside of standardisation is to eventually introduce compatibility between such libraries, but the primary use for me is on easier, type safe wrapping of C interfaces (e.g. sockets) without resorting to explicit use of C string functions.
Main benefits:
- Easy and efficient to transmit/read. A struct containing them can be reinterpret_casted to a char array and sent over a socket or written to a file directly, assuming the struct is packed. This is quite common when using UDP, for example, but through a char[N] interface (and with all the C functions that come with it).
- Efficient to process. The string data is held within the body of any struct holding it, so you benefit from cache locality and stack allocation. The max size is known, so a lower bound search for the null char will suffice in larger strings (hence non-null chars after the first null char being UB).
- Type safety. There is are many cases that runtime size checking is unnecessary, such as conversion from smaller/larger fixed strings (via a fixed size memcpy, rather than an strcpy, with conversion from larger strings truncating the input). Concatenation of a fixed_string<N> and a fixed_string<M> results in a fixed_string<N+M>, though this will require a search for the null char in the left operand.
The interface should have much of the functionality we enjoy with std::string. End iterators can come in two varieties: end() which is the first null char, and fixed_end() which is begin() + N + 1, which can also enjoy a view through the ranges interface.
I have an interface in mind, but I'm looking for criticism and further ideas to the above before proposing the interface itself. I'm aware that this idea is far from unique (that's actually the main reason I believe standardisation is beneficial) and there are a variety of tried and tested approaches.
Thanks,
Jake