-3

Is it legal to modify the result of std::string::op[]?

I would like to swap the contents of char* and std::string and could not do it. After going through the above link, I'm still unable to get the answer for my question.

This is what I am trying to do and could not succeed in swapping the string with char*. The code is verified on VS2010.

UPDATE 1 All the effort is to see if I could gain any performace and not gain any SO points. :-)
UPDATE 2 I mentioned in the comments why I intend to do this. Do people still believe that this requires to be on hold ?

void Swap(char* const& left, char* const& right)
{
    std::swap(const_cast<char*>(left), const_cast<char*>(right));
}



std::string ToString()
{
   std::string ret(1, ' ');
   char* str = new char[6]();
   strcpy(str, "Jagan");
   Swap(&ret[0], str);

    return ret;
}
Community
  • 1
  • 1
Jagannath
  • 3,995
  • 26
  • 30

4 Answers4

3

It cannot be done.

std::string provides no standard way for you to replace its internal data pointer.

In fact, not all implementations of std::string always have a pointer to a zero-terminated block of characters allocated using new[]. You are not supposed to know, and you're certainly not supposed to rely on it if you found out anyway (information hiding/implementation hiding).

Different std::string implementations might do any of the following:

  • keep the size separately from a non-zero-terminated char *. Other than classic C strings, instances of std::string are allowed to contain null characters.
  • the above, but shared between different std::strings with reference counts. You don't want to interfere here.
  • have a stack-allocated char [16] that is used for short strings instead of a heap pointer

This is why you're stuck with copying the contents of a std::string when you want to swap with a char* that you manage yourself.

wolfgang
  • 4,883
  • 22
  • 27
1

All the code &ret[0] does is create a temporary variable that is a pointer to the characters in ret. The call to swap sets this temporary variable to the pointer str, and str to this temporary variable. The result is that ret is unaffected and you leak the memory in str. To make it clearer, this code is equivalent to

char* str = new char[6]();
strcpy(str, "Jagan");
char* tmpPtr = &ret[0]
Swap(tmpPtr, str);

So you can see at the end of it tmpPtr is equal to the original value of str. There's simply no way for you to get to the internal char* pointer within str. And this is deliberate, because the wider question is why would you want to do this? Even if you could assign to the internal pointer of str, doing so would violate encapsulation -- you'd be assuming that the internal pointer was allocated using new for example. But stl containers can -- and do -- use their own internal allocation mechanisms, and ought to be free to change their implementations in the future without client code breaking. Just as you can change the implementations of your classes without your clients' code breaking.

TooTone
  • 7,129
  • 5
  • 34
  • 60
  • I know how to create a std::string. Just trying to understand if we can skip constructing and std::string – Jagannath Apr 11 '14 at 11:22
  • @Jagannath no, bottom line is you can't mess with the internals. Of course if you had another string you could use the [`string::swap`](http://www.cplusplus.com/reference/string/string/swap/) function, but I dont think that's what you want. – TooTone Apr 11 '14 at 11:24
  • @Jagannath I've modified my answer to address your comments and hopefully make it a bit clearer. – TooTone Apr 11 '14 at 11:35
  • OK. I got this information in an internal channel in my work place. I was just trying to be 100 % sure since this involves more audience. I'll accept the answer if I don't get any other. – Jagannath Apr 11 '14 at 11:41
  • @TooTone I think you [can](http://coliru.stacked-crooked.com/a/511b54f3aaade642) get to the internal buffer – 4pie0 Apr 11 '14 at 12:04
  • @privatedatapublicchannel2 yes you can certainly change the internal buffer, but you can't change the _pointer_ to the internal buffer – TooTone Apr 11 '14 at 12:10
  • @Jagannath it's a shame you didn't say in your question that you got the information from someone else at work, because I'd imagine then you could have presented it a bit differently and it is likely that you would have gotten fewer downvotes. – TooTone Apr 11 '14 at 15:12
1

Yes, as you saw in the linked post, you can modify the value of string[x] provided that x >= 0 && x < string.size() but the standard is quite clear that you can't modify anything else (and you certainly can't pass around &string[x] as a valid pointer-to-modifiable-null-terminated-string -- remember that string.c_str() and string.data() return a const char*.

You can, however, implement:

void swap(std::string& left, char* right) {
  auto right_size = std::strlen(right);
  auto left_size = left.size();
  assert(left_size <= right_size);
  std::string temp = std::move(left);
  left = right;
  std::copy(std::begin(temp), std::end(temp), right);
  right[left_size] = '\0';
}

inline void swap(char* left, std::string& right) {
  swap(right, left);
}

(live here)

Massa
  • 8,647
  • 2
  • 25
  • 26
  • `left = right;` would in my opinion be a lot simpler than `clear` followed by `copy` with a `back_inserter`. – wolfgang Apr 11 '14 at 11:22
  • How efficient is this compared to char str[] = "Jagan"; std::string data ( str ); – Jagannath Apr 11 '14 at 11:24
  • @wolfgang: you are absolutely right! (edited...) and I saw another optimization, by moving the string to `temp` instead of copying it. – Massa Apr 11 '14 at 11:29
  • The code is about swapping empty string with the one having data. – Jagannath Apr 11 '14 at 11:33
  • 1
    I don't want to downvote but this implementation does not make any sense. – Jagannath Apr 11 '14 at 11:47
  • @Jagannath just because in your example the `std::string` is not filled up (or it _is_ filled up with a single space) does not mean you'll always want to swap that way. Your question was `I would like to swap the contents of char* and std::string`... My implementation does exactly that... – Massa Apr 11 '14 at 12:04
  • @Jagannath: Take a look at the [live example](http://coliru.stacked-crooked.com/a/2c23998c492d33a1) and the [new, generic, live example](http://coliru.stacked-crooked.com/a/170d14c57227c549) that I made for you... – Massa Apr 11 '14 at 12:08
  • In your swap() , left = right will copy the data from right to left. I did not want this. I just wanted to understand what I was asking is possible or not. But that was the intention. – Jagannath Apr 11 '14 at 12:18
  • The definition of a swap is that you copy the data from the right side to the left _and_ from the left side to the right, so, I didn't understand the question from the beginning... – Massa Apr 11 '14 at 12:21
  • @Jagannath, if what you want was to break `std::string` encapsulation and substitute its "internal data pointer", then I'll refer you to wolfgang's answer below: there are some `std::string` implementations that **do not have** such pointer, or at least it is not valid all the time (because it has a "small strings buffer" allocated _inside_ the `std::string` object), or in some cases, the "internal pointer" is always allocated with a special allocator in a "string buffer pool" and can be invalidated at any given time, etc... – Massa Apr 11 '14 at 12:25
  • The definition of swap is to copy data from left to right and right to left is new to me. Learnt a new thing in SO. – Jagannath Apr 11 '14 at 14:06
  • @Jagannath Are you being sarcastic? Not a flamebait, I seriously can't tell. Assuming you are not being sarcastic, I see you updated your question, so I'll probably write another answer. – Massa Apr 11 '14 at 14:33
  • anyway, take a look at the most generic definition of `std::swap`: `template void swap(X& x, X& y) { X t = move(x); x = move(y); y = move(t); }` – Massa Apr 11 '14 at 14:45
  • @massa, swap is just swapping of pointers . No copying involved. Anyway thanks for taking time in coming up with the answers. Let me know if you tested your recent swap code, +1 for you. – Jagannath Apr 11 '14 at 15:14
  • Do you mean `x >= 0`? – ildjarn Apr 11 '14 at 21:29
  • @ildjarn Did not get it. – Jagannath Apr 11 '14 at 22:12
  • @Jagannath : That was directed at Massa. – ildjarn Apr 11 '14 at 22:39
  • @ildjarn I didn't get it also. Where could I have meant `x >= 0`? – Massa Apr 11 '14 at 23:49
  • @Jagannath about the [dictionary definition of swap](http://www.oxforddictionaries.com/definition/english/swap) and the [usage of swap as a C++11 idiom](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom)... I (finally!) understood what you wanted in your original question. – Massa Apr 12 '14 at 11:18
-1

What you are doing is wrong on so many levels.

To "swap" a std::string with a manually managed string pointed to by a char *, you need to manually manage that string, as in:

std::string one = ...;
char *two = ...;

// swap:
std::string tmp = two;
delete[] two;
two = new char[one.size() + 1];
memcpy(two, one.c_str(), one.size() + 1);
one = tmp;
wolfgang
  • 4,883
  • 22
  • 27
  • When I wanted a swap, I clearly did not want to allocate the same size of the source string. I can instead construct the string from existing data. – Jagannath Apr 11 '14 at 11:26
  • In a code 6 statements, pls tell me in how many levels the wrong things are ? Don't tell me about raw pointers, etc. This is sample code. – Jagannath Apr 11 '14 at 11:33
  • I've posted a separate answer based on my now-improved understanding of what you're trying to achieve. – wolfgang Apr 11 '14 at 11:35
  • To explain my regrettably unfriendly "many levels" comment (sorry about that): 1) trying to break abstraction of `std::string`; 2) trying to modify the result of `&...`; 3) lying to the compiler and declaring a parameter `const &` ; 4) using `const_cast` to modify something you've declared `const`. Forgetting to `delete[] str`. – wolfgang Apr 11 '14 at 11:41
  • 1) trying to break abstraction of std::string --> That was the intent anyways.2) trying to modify the result of &...; --> that's the intent. 3) lying to the compiler and declaring a parameter const & ; --> Otherwise the code does not compile. 4) using const_cast to modify something you've declared const -> Otherwise can't call swap 5) Forgetting to delete[] str -> Yes forgot. That's the sample code anyways. – Jagannath Apr 11 '14 at 11:43
  • @stefaanv: fixed the `[]`. The dynamic allocation assumption I took from the question. – wolfgang Apr 11 '14 at 11:43
  • @Jagannath: I get that now :-). In the beginning, the fact that this was a conscious attempt at doing that (for performance reasons?) got lost amid the bad things you did trying to achieve it. I guess the '-4' on your question is due to people thinking you really just wanted to swap the *values* of two strings and got carried away doing bad things. – wolfgang Apr 11 '14 at 11:49
  • Yes it is for performance reasons. We a third pary lib that gives an output in XML format and it gives char* and not std::string. The rest of the interfaces that deal with that lib take std::string. The data size can be min of 4 MB to 150 MB. – Jagannath Apr 11 '14 at 11:54
  • @Jagannath, can you edit (again) the question, expliciting what third party lib gives an output on a `char*`, and what interfaces are you using that take `std::string` and what do they do to them? Because your best option may be using the [Adapter pattern](http://sourcemaking.com/design_patterns/adapter/cpp/1) so you can bridge both parts of your design efficiently. – Massa Apr 11 '14 at 14:40
  • The third party lib is internal to the company and can't be modified. – Jagannath Apr 11 '14 at 15:17