1

I have a codebase with some ubiquitous data structure; and said structure has an std::string member. Now, for reasons, I want this codebase to work when std::string is unavailable, and in fact with no dynamic allocation of memory (at least not the usual way). I can also verify that that string member never has a string longer than M characters (and M is small).

Now, what should I replace std::string with, so that I don't have to do a lot of rewriting, on one hand; and that my constraints are satisfied on the other?

Note:

  • I can't move the computation of the string to compile-time.
  • It's ok if the solution only has a trivial constructor, a const char* constructor, or both.
  • Solutions involving the use of std::string_view may be relevant (but I'm not sure whether that would be useful).
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Sounds like you just want a `std::array`. You can wrap it in a class if you want to keep a `std::string`-like interface. Though beware that this change may make your type significantly less move friendly, less container friendly, and generally can significantly increase it's size. – François Andrieux Jun 18 '21 at 16:43
  • @FrançoisAndrieux Given that *"M is small"* and `std::string` is a largeish type (24..32 bytes), there might be no change, or even improvement in move friendliness and class size, depending on how small it is. – eerorika Jun 18 '21 at 16:48
  • You may just need a new string allocator. – Galik Jun 18 '21 at 16:49
  • 3
    Boost has something called [`static_string`](https://www.boost.org/doc/libs/1_75_0/libs/static_string/doc/html/index.html), with storage equivalent to an array, and an interface that strives to match `std::string`. – Drew Dormann Jun 18 '21 at 16:52
  • 1
    I also have a custom [small_string](https://github.com/Ambeco/mpd/blob/master/SmallContainers/small_string.hpp) which is apparently similar to boost `static_string`. – Mooing Duck Jun 18 '21 at 16:57

2 Answers2

3

char[M + 1] would probably be a decent, simple option to investigate. The amount of rewriting may be more or less depending on how you have used the string member so far.

If the "std::stringiness" of the member is used a lot, then you could possibly reduce the amount of rewrite by implementing a custom class that offers similar interface as std::string, but internally uses char[M + 1]. boost::static_string mentioned by Drew Dormann is a template for such class.

P.S. If you can still use std::string and are only restricted from using dynamic memory, then you could potentially keep using std::string with a custom allocator instead, as mentioned by Galik.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I was hoping to avoid "rolling my own" string-like class. `boost::static_string` could be relevant, but is perhaps a bit overkill, since its templated on a bunch of stuff; and I would need to either drag in all of boost, or rip it out of Boost. Might still do it. – einpoklum Jun 18 '21 at 17:00
  • 1
    @einpoklum: "The library is usable in two different modes: standalone and Boost dependent. This library defaults to Boost dependent mode; standalone mode is opt-in through the use of a configuration macro." https://www.boost.org/doc/libs/1_75_0/libs/static_string/doc/html/index.html `BOOST_STATIC_STRING_STANDALONE` – Mooing Duck Jun 18 '21 at 17:10
0

A minimum-hassle potential solution could utilize the specifics of your case, sacrificing generality for simplicity:

You wrote the string you want to replace is actually a member of another struct. This already allows using an array member of the struct, as @eerorika suggests, without worrying about array decay. But - arrays don't the interface of an std::string.

You said you didn't see how an std::string_view would be useful - and that's because there's no storage to go with it.

Well, combine the two! ... no, don't write a new class with both storage and a string interface. Rather, add a char [M+1] field to your struct, with a new field name, and use the original field name for a string_view looking at the array (with the appropriate length of course)!

This complicates construction and assignment a bit - i.e. it's no longer trivial member-wise construction - but you will then be able to pass this object around and use the same field as before like an std::string.

  struct ubiquitous {
     // other members
     char[M+1] string_data;
     std::string_view original_field;

     // apply rule of 5: ctor, move ctor, assignment, move assignment
     // and a trivial dtor (at least for your two fields)
     // ... so, a "rule of 4" in this case
  };

If you don't have a string_view available, i.e. it's pre-C++17 code, then you can get Martin Moene's back-ported string_view (GitHub).

einpoklum
  • 118,144
  • 57
  • 340
  • 684