Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by SuperKoko
treuss : the third-party library may be quite complex but well documented.
In that case, the documentation would say if the data is modified or not.
Honestly, I find this scenario highly unlikely. Why would the interface state that it needs a modifyable object (or character string) and the documentation would state that it does not modify it. Could mean that either - The programmer wrote the incorrect interface on purpose, in which case I would not trust the quality of the library and not use it
- The programmer did not pay much attention to the interface definition, in which case I would doubt that he payed much attention to implementation details, so I would also not trust the quality of the library and not use it.
But of course nothing is impossible...
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by treuss
&str[0] does not guarantee that the string is null-terminated. Depending on the implementation of std::string it might not be.
I'm curious then how c_str() could possibly guarantee that the string is null-terminated. After all, it's a const function, so presumably does not modify the internal representation of the string. Its documentation indicates that it does not make a copy of the string either (or else why would it not have a non-const version). The way I see it in any practical implementation, the null must always be there tagged as an extra character at the end after the formal length.
Re: invalid conversion from `const char*' to `char*'
Quote:
Its documentation indicates that it does not make a copy of the string either (or else why would it not have a non-const version).
Why cant an implementation that does not internally use a null-terminated string copy the string data into a null-terminated string that is returned by c_str()?
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by googler
I'm curious then how c_str() could possibly guarantee that the string is null-terminated. After all, it's a const function, so presumably does not modify the internal representation of the string. Its documentation indicates that it does not make a copy of the string either (or else why would it not have a non-const version). The way I see it in any practical implementation, the null must always be there tagged as an extra character at the end after the formal length.
From roguewave documentation for basic_string:
Code:
basic_string (const charT * s,
const Allocator& a = Allocator());
Constructs a string containing all characters in s up to, but not including, a traits::eos() character.
I could not find a relevant section from the standards.. but for c_str it is guaranteed to returns a traits::eos() appended at the end.. here: (from standard)
Code:
const charT* c_str() const;
Returns: A pointer to the initial element of an array of length size() + 1
whose first size() elements equal the corresponding elements of the string
controlled by *this and whose last element is a null character specified by
traits::eos().
Also, it is not necessary that the internal representation of the sequence as for a basic_string and a char[] would be the same. I mean it does support random access operator (with string[i+1] being the next character in the sequence after string[i] as does for char_string[i+1]) but it is not necessary that it will have contiguous allocation as is expected for a character array or a C-style string. The called function has no way of knowing the internal representation of the controlled sequence .. and would hence not be correct... Use c_str(). Regards.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by laserlight
Why cant an implementation that does not internally use a null-terminated string copy the string data into a null-terminated string that is returned by c_str()?
Does it remember the pointer to the copy it made? If it does, it's violating the const contract of the function. If it doesn't, you have a memory leak.
More to the point, the description of c_str() and data() (under the Requires paragraphs) indicates that they wanted these functions to be able to return a pointer to the internal representation of the string. If they didn't, there would be no reason to return a "const char*" instead of a "char*" and require that the buffer be immutable. I mean, if you're going to waste space and make a copy anyway, you could as easily make your own copy without using c_str(). Plus, it would stand to reason that if it makes a copy, there would at the very least be a non-const version of c_str() that creates a mutable duplicate in addition to the one that creates an immutable duplicate. That's why I don't think they ever intended for these functions to be making duplicates.
Re: invalid conversion from `const char*' to `char*'
Quote:
Does it remember the pointer to the copy it made? If it does, it's violating the const contract of the function.
It must own the null-terminated string, obviously. Depending on how its done, I dont see it "violating the const contract of the function". In any case, even if the implementation were to change the internal string representation, the logical constness of the string need not change.
Quote:
the description of c_str() and data() (under the Requires paragraphs) indicates that they wanted these functions to be able to return a pointer to the internal representation of the string. If they didn't, there would be no reason to return a "const char*" instead of a "char*" and require that the buffer be immutable.
I disagree. I think that it allows for an implementation that returns a pointer to the internal representation of the string. The C++ Standard doesnt mandate the internal representation, only the external interface.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by googler
Does it remember the pointer to the copy it made? If it does, it's violating the const contract of the function. If it doesn't, you have a memory leak.
More to the point, the description of c_str() and data() (under the Requires paragraphs) indicates that they wanted these functions to be able to return a pointer to the internal representation of the string.
...with the difference, that c_str() guarantees the string to be null-terminated, while data does not. If they both were meant to return a pointer to the internal data buffer, there would not be the need for two functions.
Quote:
Originally Posted by googler
If they didn't, there would be no reason to return a "const char*" instead of a "char*" and require that the buffer be immutable. I mean, if you're going to waste space and make a copy anyway, you could as easily make your own copy without using c_str(). Plus, it would stand to reason that if it makes a copy, there would at the very least be a non-const version of c_str() that creates a mutable duplicate in addition to the one that creates an immutable duplicate. That's why I don't think they ever intended for these functions to be making duplicates.
What about an implementation, that only adds the null if c_str() is called? One could argue, that this would be a violation of the const definition of c_str(), but as the modification would be basically invisible from outside, I don't see it like that.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by googler
Does it remember the pointer to the copy it made? If it does, it's violating the const contract of the function. If it doesn't, you have a memory leak.
Firstly: The "const contract" is a high-level conceptual contract which specifies that a const method must not modify how the object will publically behaves in next calls to other methods. That is why mutable fields don't break that contract, if used correctly.
Secondly: If the string is implemented as containing a pointer to the data, this data is not const! I mean, even without const_cast you can modify a pointer within a const structure (the pointer has the const attribute but not the pointed data).
Quote:
Originally Posted by googler
More to the point, the description of c_str() and data() (under the Requires paragraphs) indicates that they wanted these functions to be able to return a pointer to the internal representation of the string. If they didn't, there would be no reason to return a "const char*" instead of a "char*" and require that the buffer be immutable
I think exactly the opposite.
If the standard required a specific representation it could have made the function return a char*.
But, since the implementation may be quite complex (for example, the string could be implemented as a list of bunch of memory), it is still possible for the implementation to provide a c_str function which copies all the data in a buffer, set a hidden pointer to point to that buffer in the string class, and return that pointer, and, then any modification of the string object would make the string object free the buffer (which is no more a correct representation of the string), and set the hidden pointer to NULL.
If a char* was returned, programmers could think that they are allowed to modify the buffer and that changes would be reflected to the real string, what is actually impossible with some implementations of strings.
Another example of std::string implementation that could not return a char*, is the Rogue Wave's implementation which uses a Copy-On-Write algorithm. Thus, the modification of one char* returned by a str() member would modify all other strings referring to the same data.
Even with naive implementations of std::string, putting a null-terminator only when c_str() is called, seems quite wise, to me.
Note that there could be a solution to permit usage of raw character array to access/modify data of a string.
This solution is to provide a lock method and an unlock method:
Code:
char* lock(); // returns a buffer to the string data that can be used to access/modify the string data.
// after a call to lock, you would not be able to access any function of std::string that could modify the string, otherwise behavior would be undefined.
void unlock(char* bufferThatWasReturnedByLock); // ensure that all changes made to the buffer are commited, and "unlocks" the string, allowing normal usage of string modifications methods.
With that mechanism, it would be possible for any implementation, to allocate a buffer, when lock is called, and to copy all the string data to it, and then to commit all modifications and free the buffer, when unlock is called.
lock and unlock functions could be fast for contiguous-non-COW implementations of std::string, and still work on other implementations.
However, that would be rarely useful.
That is why, c_str() is sufficient for almost all purposes.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by treuss
Honestly, I find this scenario highly unlikely. Why would the interface state that it needs a modifyable object (or character string) and the documentation would state that it does not modify it. Could mean that either
Yes, it is unlikely nowadays, but the const keyword didn't existed in K&R C of the first edition of The C Programming Language (in 1978).
Yes, I know that it is antediluvian, but the const keyword was not used in portable programs until 1989, and even after 1989, I guess that a few compilers were still not supporting the const keyword.
So, if the library is very old, it may be possible.
Of course, now we are in the third millenium, and a second ISO C standard is used...
Re: invalid conversion from `const char*' to `char*'
I found another interesting tidbit in the standard
Quote:
Originally Posted by C++ standard, section 21.3
6 References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:
— As an argument to non-member functions swap() (21.3.7.8), operator>>() (21.3.7.9), and getline() (21.3.7.9).
— As an argument to basic_string::swap().
— Calling data() and c_str() member functions.
— Calling non-const member functions, except operator[], at, begin, rbegin, end, and rend.
— Following construction or any of the above uses, except the forms of insert and erase that return iterators, the first call to non-const member functions operator[], at, begin, rbegin, end, or rend.
So it seems that the standard specifically envisions these so-called const member functions modifying the internal structure of string, and lumps them in with non-const member functions.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by SuperKoko
Yes, it is unlikely nowadays, but the const keyword didn't existed in K&R C of the first edition of The C Programming Language (in 1978).
Yes, I know that it is antediluvian, but the const keyword was not used in portable programs until 1989, and even after 1989, I guess that a few compilers were still not supporting the const keyword.
So, if the library is very old, it may be possible.
Thanks for the history lesson ;). I admit, I was not aware that there was a life before const.
Re: invalid conversion from `const char*' to `char*'
There are too many 3rd party libraries out there that are not const-correct. Tibco Rendezvous, which I used in 2004, is among them. It is most annoying.
We should have a campaign.
Your alternative to using const-cast is to use vector<char> internally then pass &v[0] which is non-const as long as your vector itself isn't const.
Re: invalid conversion from `const char*' to `char*'
Hi all! Looks like const gave a lot of headache to many of you.
Couldn't reply sooner 'cause was a bit hectic at work lately, so I've cumulated my replies in this post.
treuss:
- I recall a positive rating on your first post because was faster than mine and entirely correct. Will accept that you don't agree with my post but will not appreciate your negative rating with this reason.
- There are times when I'm simply told which libs to use and don't have the luxury to choose. Hell, one project I had was having the sole purpose of integrating a library into a certain environment. When I had doubts about a function implementation in that library, I could contact someone from the company that wrote it, but requesting changes to it was not "in the budget" and also meant updating several major existing clients. Conclusion: cast the bloody thing and move on to meet the deadline.
exterminator:
- My honest advice is to think outside the box once in a while. As much as we want to believe (me too) that "rules" are entirely described in "standards"... well, aren't.
- I suppose implementing polymorphism through patching virtual tables at runtime would be unacceptable, but I still love to do it.
- Last thing to clarify: indeed, you've reformulated same statement you've overlooked in my first post... but did sounded like a big No-No, and that is why I've replied you were wrong... wrong to say should never be used... because will not work (it worked for me many times)
ZuK:
- I'm very happy for you not having to work with "lazy" programmers (aren't we all?) which don't care too much about const / non-const params, but if I have to call a function that needs a filename to load some data and its parameter is defined as "char *", I would use const_cast without any doubt.
SuperKoko:
- Thanks a bunch for your post! I really liked that part saying "that is the purpose of const_cast", and appreciate your effort. Once again, many thanks!
Best regards to everyone,
Re: invalid conversion from `const char*' to `char*'
Both c_str() and data() must return contiguous buffers and they must be stored in within the string because the caller is not required to delete the returned pointer.
Therefore they can invoke "lazy evaluation" and have a mutable member. The fact that c_str() must put a 0 at the end of the data will also be of consequence where the buffer was not previously long enough to contain it.
So these two calls could invalidate your iterator.
Knowing that you have a string that contains a contiguous buffer of data and wanting to pass that buffer for write, you could technically use the evil const_cast on it. That might be something you would actually do in a performance-critical situation. vector<char> has the drawback of not using char_traits and therefore does not optimise the data copying. (You can optimise char_traits for unsigned char easily enough, by the way).
I would not want to see const_cast in my code unless it's hidden away somewhere.
Re: invalid conversion from `const char*' to `char*'
Quote:
Originally Posted by Bornish
treuss:
- There are times when I'm simply told which libs to use and don't have the luxury to choose. Hell, one project I had was having the sole purpose of integrating a library into a certain environment. When I had doubts about a function implementation in that library, I could contact someone from the company that wrote it, but requesting changes to it was not "in the budget" and also meant updating several major existing clients. Conclusion: cast the bloody thing and move on to meet the deadline.
A number, frequently stated by IT analysts, is that only about 20% of all software projects finish successfully. The other 80% exceed time or budget or both or get aborted. What you are writing is a good illustration of this: Not enough time and/or budget planned to ensure the quality of the libraries that are included - it comes down to the programmer to get out running software - the programmer uses const_cast's and hopes that they work.
Granted: In most cases const_casts will work. Just like not checking if malloc succeeded will work in most cases and many other things.
In short: I cannot blame any programmer for using const_casts in his programs to overcome old or buggy libraries, but but I stand to the point that it is not correct way to go.