-5

Edit: Solutions must compile against Microsoft Visual Studio 2012.

I want to use a known string length to declare another string of the same length.

The reasoning is the second string will act as a container for operation done to the first string which must be non volatile with regards to it.

e.g.

const string messy "a bunch of letters";

string dostuff(string sentence) {
    string organised NNN?????  // Idk, just needs the same size.
    for ( x = 0; x < NNN?; x++) {
        organised[x] = sentence[x]++; // Doesn't matter what this does.
    }
}

In both cases above, the declaration and the exit condition, the NNN? stands for the length of 'messy'.

How do I discover the length at compile time?

Georgina Davenport
  • 189
  • 1
  • 2
  • 11

2 Answers2

3

std::string has two constructors which could fit your purposes.

The first, a copy constructor:

string organised(sentence);

The second, a constructor which takes a character and a count. You could initialize a string with a temporary character.

string organised(sentence.length(), '_'); 

Alternatively, you can:

  • Use an empty string and append (+=) text to it as you go along, or
  • Use a std::stringstream for the same purpose.

the stringstream will likely be more efficient.

Overall, I would prefer the copy constructor if the length is known.

Rotem
  • 21,452
  • 6
  • 62
  • 109
  • Well, so far I've just done "string stringname = CONSTANTSTRING;" it's annoying though because I don't need or even want to assign it, I just want to allocate equal memory, the contents are irrelevant, it's just a container. I also want to use the length in a loop for x = 0 to stringlength. How can I find the length at compile time? – Georgina Davenport Mar 21 '16 at 16:58
  • Perhaps this question would be more suited for your needs http://stackoverflow.com/questions/12541739/determining-the-length-of-a-string-literal – Rotem Mar 21 '16 at 17:00
  • It kind of works, but it kind of doesn't. I like to avoid defines if I can, it's bad practice. Even when I do though I can't use that define in a sizeof preprocessor directive, I don't know why. It forces me to use the full string literal. It doesn't work how the answer suggests it does you point me to. This defeats the use of the #define anyway, because I can't use it. Have I beaten stack exchange? Nobody seems to be able to answer this, even though it sounds like a very basic question? – Georgina Davenport Mar 21 '16 at 17:23
2

std::string isn't a compile time type (it can't be a constexpr), so you can't use it directly to determine the length at compile time.

You could initialize a constexpr char[] and then use sizeof on that:

constexpr char messychar[] = "a bunch of letters";
// - 1 to avoid including NUL terminator which std::string doesn't care about
constexpr size_t messylen = sizeof(messychar) / sizeof(messychar[0]) - 1;
const string messy(messychar);

and use that, but frankly, that's pretty ugly; the length would be compile time, but organized would need to use the count and char constructor that would still be performed on each call, allocating and initializing only to have the contents replaced in the loop.

While it's not compile time, you'd avoid that initialization cost by just using reserve and += to build the new string, which with the #define could be done in an ugly but likely efficient way as:

constexpr char messychar[] = "a bunch of letters";
constexpr size_t messylen = sizeof(messychar) / sizeof(messychar[0]) - 1;
// messy itself may not be needed, but if it is, it's initialized optimally
// by using the compile time calculated length, so there is no need to scan for
// NUL terminators, and it can reserve the necessary space in the initial alloc
const string messy(messychar, messylen);

string dostuff(string sentence) {
    string organised;
    organized.reserve(messylen);
    for (size_t x = 0; x < messylen; x++) {
        organised += sentence[x]++; // Doesn't matter what this does.
    }
}

This avoids setting organised's values more than once, allocating more than once (well, possibly twice if initial construction performs it) per call, and only performs a single read/write pass of sentence, no full read followed by read/write or the like. It also makes the loop constraint a compile time value, so the compiler has the opportunity to unroll the loop (though there is no guarantee of this, and even if it happens, it may not be helpful).

Also note: In your example, you mutate sentence, but it's accepted by value, so you're mutating the local copy, not the caller copy. If mutation of the caller value is required, accept it by reference, and if mutation is not required, accept by const reference to avoid a copy on every call (I understand the example code was filler, just mentioning this).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Note: All of this assumes the compiler isn't doing clever stuff with `std::string`, which isn't necessarily a valid assumption; given how important `std::string` is, it would be reasonable for compilers to go beyond the requirements of the language standard and make certain features of `std::string` work like `constexpr` for global `const std::string`s. So all the ugliness may not really gain you anything; I'd strongly recommend avoiding the obsession with compile time calculation until you have strong evidence of a bottleneck. – ShadowRanger Mar 21 '16 at 17:30
  • That's very informative and I'll definitely use it, thank you. The real needling thing about this whole project is it must compile against MSVS 2012. :-( You don't need to tell me how tragic that is, I already know. 2012 has almost no C++11 features, so we are expected to keep it simple. This means I've had to throw away a lot of things which worked fine at home on my Unix C++14 system. it also means.... I can't use constexpr. :-( Any ideas for a C++98 compatible variant? – Georgina Davenport Mar 21 '16 at 19:35
  • @GeorginaDavenport: If `constexpr` is out, then you're stuck with `#define` constants or macros, e.g. `const char messychar[] = "a bunch of letters";`, `#define MESSYLEN (sizeof(messychar) / sizeof(messychar[0]) - 1)`, then using `MESSYLEN` in the same places as `messylen` in the C++11 code (or inline the definition yourself by hand). Not as pure, but if you want compile time behavior, there is just far less of that guaranteed in the spec pre-C++11. – ShadowRanger Mar 21 '16 at 19:52
  • Thanks shadow, that's what I was afraid of. #defines are so inelegant, but currently that is the solution I'm using. I'll edit the OP to reflect it and any new ideas will be most welcome, thank you. – Georgina Davenport Mar 21 '16 at 20:34
  • @ShadowRanger You don't really need `constexpr` or define macros for `sizeof(messychar) / sizeof(messychar[0]) - 1`. That is a constant expression that can be used as an array size. – juanchopanza Mar 21 '16 at 22:48
  • @juanchopanza: The problem is that with a `constexpr` or `#define`, you're repeating yourself, and the risk with repeating is that a subsequent update that should apply to both locations only gets applied to one. Obviously a `#define` for a given value can just be manually substituted each place it's used (that's effectively what most `#define`s do, modulo some weirdness with complex macros), but it's a good idea to adhere to the principle of [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself) when you can. – ShadowRanger Mar 22 '16 at 00:53
  • 1
    @ShadowRanger I meant `const` will do here. I there really is no need for `constexpr` or `#define`. `const size_t len = sizeof(messychar) / sizeof(messychar[0]) - 1;` will do fine, in this case anyway. – juanchopanza Mar 22 '16 at 06:29