What's the safest way for a NSString to weakly contain a const char * belonging to a std::string? Both examples below work on a simple test, in logs, and as presented in a NSTableView, but I'm concerned about strange behavior down to road. It may be the extra null character of c_str() is simply ignored (because of the length parameter passed) and either will work fine.
Given:
std::string const * stdstring = new std::string("Let's see if this works");
Then:
NSString * aStr = [[NSString alloc] initWithBytesNoCopy:
stdstring->data() length: stdstring->length()
encoding:NSUTF8StringEncoding freeWhenDone:NO];
or:
NSString * aStr2 = [[NSString alloc] initWithBytesNoCopy:
stdstring->c_str() length: stdstring->length()
encoding:NSUTF8StringEncoding freeWhenDone:NO];
or something else?
The documentation for initWithBytesNoCopy:length:... clearly states that the length will be the number of bytes used, so the null termination character will always be ignored. Hence the contents of the memory returned by data() and c_str() is equally suitable.
With that in mind:
The lifetime guarantees of the memory returned by std::string's data() and c_str() functions are identical - they will survive until you call a non-const member function on the string object. It depends on the implementation whether the internal data structure is already a null-terminated character array, so in general, data() will be cheaper or identical in complexity to c_str(). I'd therefore go for data().
Related
I am thinking this is a later Xcode issue (I am using Xcode 11) as this code was fine previously in older versions.
In my main.mm file I have the following;
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * path = [[NSBundle mainBundle] pathForResource: #"fd" ofType: #"dat"];
NSData* image = [NSData dataWithContentsOfFile:path];
const char* szPathName = [path UTF8String];
const char* pos = strrchr (szPathName, '/');
//char* pos = strrchr (szPathName, '/');
*pos = 0;
if (CreateFaceFatted(szPathName) == 0)
{
NSLog(#"Init Dictionary failed");
}
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
I have changed the char* pos = strrchr (szPathName, '/'); to const char* pos = strrchr (szPathName, '/'); as otherwise it would throw a
Cannot initialize a variable of type 'char *' with an rvalue of type 'const char *'
so by doing the const char instead, I at least could proceed further, however on the following part;
*pos = 0;
I receive an error Read only variable is not assignable
I have a small understanding of the c++ code mixed with obj-c but I am struggling to figure a way to get this to compile
You declared the pointer szPathName as a pointer to constant data
const char* szPathName = [path UTF8String];
So you may not change the pointed data using the pointer pos
*pos = 0;
that shall be also declared with the qualifier const because the function strchr returns a constant pointer.
const char* pos = strrchr (szPathName, '/');
Make the pointer szPathName to point to non-constant data.
It doesn't appear that you've done anything since your previous question, SO does really require you show some effort.
Let's step through your original code to hopefully help understanding of the issue:
I am thinking this is a later Xcode issue (I am using Xcode 11) as this code was fine previously in older versions.
It's not an Xcode issue per se, your code was always wrong but fortunately for you producing the desired result, it is just that error and warning messages have improved.
const char* szPathName = [path UTF8String];
This line takes an NSString value referenced by path, calls the method UTF8String on it which returns a pointer to a C string value, and stores that reference in szPathName. From Objective-C you will know that an NSString value is immutable, you cannot change the characters in the string. The UTF8String method returns a pointer to a constant C string value and so szPathName has the type const char * – NSString and const char * are the types in Objective-C and C respectively for variables which store references to constant strings; similarly NSMutableString and char * are the types for references to mutable strings.
char* pos = strrchr (szPathName, '/');
This line of code searches for the rightmost / in the C string and returns a pointer to it. As you found from your last question strrchr() in C++ returns a const char * if passed a const char *. This line of code was always wrong but it seems an earlier compiler did not report the error, which is to type pos as a pointer to a mutable string which it is not. Just as assigning an NSString * value to an NSMutableString * typed variable in Objective-C does not make the referenced string mutable, assigning a const char * value to an char * typed variable in C does not make the reference C string mutable.
*pos = 0;
This line is correct, as the type of pos is char *, but also incorrect as the value stored in pos happens to be a pointer to an immutable C string.
Your code has previously worked as C is lax when it comes to mutability/immutabilty, and fortunately writing into a C string stored inside an instance of NSString (see UTF8String documentation) didn't cause any problems.
You are not going to solve your problem but adding or removing const in various places, doing so may result in code which appears to work but it could easily break at any time.
As suggested to you in your last question rather than try to fix the C code a line at a time, especially given your declared lack of knowledge of C(++), you would be better off looking at what this code is trying to do and to code that as much as possible in Objective-C.
So what is the aim of the code?
Start with a path to a file stored in path, as an NSString value
Determine the parent folder/directory of that file
Pass that parent folder as a constant C string value to the function CreateFaceFatted
You already have the code to obtain the file path and store it in path, so step 1 requires no work.
You also know how to produce a pointer to a constant C string from an NSString value – use UTF8String. So step 3 is covered.
That leaves step 2. Your current code tries to do it after the conversion to a C string, and that you don't know how to do. But you do know Objective-C, is there a method/property on NSString which takes a file path and returns the path of the containing folder/directory? A good place to look would be the documentation.
Hopefully that will help you understand your issue and hence get you quickly to the solution.
I want to know more about programming and after a bit of googling I found how to convert a string to a const char.
String text1;
What I do not understand is why c_str() works,
const char *text2 = text1.c_str();
contrary to toCharArray()?
const char *text2 = text1.toCharArray();
or
const char text2 = text1.toCharArray();
The latter is more logical to me as I want to convert a string to a char, and then turn it into a const char. But that doesn't work because one is a string, the other is a char. The former, as I understand, converts the string to a C-type string and then turns it into a const char. Here, the string suddenly isn't an issue anymore oO
.
a) Why does it need a C-type string conversion and why does it work only then?
b) Why is the pointer needed?
c) Why does a simple toCharArray() not work?
.
Or do I do something terribly wrong?
Thanks heaps.
I am using PlatformIO with Arduino platform.
If you need to modify the returned c-style string in any way, or have it persist after you modify the original String, you should use toCharArray.
If you only need a null-terminated c-style string to pass as a read-only parameter to a function, use c_str.
Arduino reference for String.toCharArray()
Arduino reference for String.c_str()
The interface (and implementation) of toCharArray is shown below, from source
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
{ getBytes((unsigned char *)buf, bufsize, index); }
So your first issue is that you're trying to use it incorrectly. toCharArray will COPY the underlying characters of your String into a buffer that you provide. This must be extra space that you have allocated, either in a buffer on the stack, or in some other writable area of memory. You would do it like this.
String str = "I am a string!";
char buf[5];
str.toCharArray(buf, 5);
// buf is now "I am\0"
// or you can start at a later index, here index 5
str.toCharArray(buf, 5, 5);
// buf is now "a st\0"
// we can also change characters in the buffer
buf[1] = 'X';
// buf is now "aXst\0"
// modifying the original String does not invalidate the buffer
str = "Je suis une chaine!";
// buf is still "aXst\0"
This allows you to copy a string partially, or at a later index, or anything you want. Most importantly, this array you copy into is mutable. We can change it, and since it's a copy, it doesn't affect the original String we copied it from. This flexibility comes with a cost. First, we have to have a large enough buffer, which may not be known at compile time, and takes up memory. Second, that copying takes time to do.
But what if we're calling a function that just wants to read a c-style string as input? It doesn't need to modify it at all?
That's where c_str() comes in. The String object has an underlying c-string type array (yes, null terminator and all). c_str() simply returns a const char* to this array. We make it const so that we don't accidentally change it. An object's underlying data should not be changed by random functions outside of its control.
This is the ENTIRE code for c_str():
const char* c_str() const { return buffer; }
You already know how to use it, but to illustrate a difference:
String str = "I am another string!";
const char* c = str.c_str();
// c[1] = 'X'; // error, cannot modify a const object
// modifying the original string may reallocate the underlying buffer
str = "Je suis une autre chaine!";
// dereferencing c now may point to invalid memory
Since c_str() simply returns the underlying data pointer, it's fast. But we don't want other functions to be allowed to modify this data, so it's const.
I've been trying to convert a const char to a char for the past 30 minutes.
Here's what I've got.
string atr;
getline(cin,atr); // Start off with a string because getline takes nothing else.
const char *buffA = atr.c_str(); // Create a const char of the string converted to a const char.
char *buff = ""; // Create a new char to hold the converted result.
strcat(buff,buffA); // Do the conversion.
parseargs(buff); // Pass the data on.
However, I get an unhandled exception. I have no idea why. I literally just typed 'try' into the console as my only argument.
Try using C++ instead of C idioms:
std::vector<char> data(atr.begin(), atr.end());
data.push_back('\0');
parseargs(&data[0]);
There are two things wrong with your code. First, you
initialize a char* with a string literal. This uses
a deprecated convertion; the type of a string literal is char
const[] (which converts to char const*, not to char*),
because any attempt to modify the literal is undefined behavior.
The second is that your string literal is only one char long,
so even if you could write to it, unless atr was empty, you're
writing beyond the end of the buffer.
You don't tell us anything about parseargs. If it doesn't
modify it's argument, you can just pass it atr.c_str(), and be
done with it. (If it's a legacy function which ignores const,
you may have to use a const_cast here.) If it does modify its
argument (say because it uses strtok), then you'll have to
explicitly push a '\0' onto the end of atr, and then pass it
&atr[0]. (Not a particularly clean solution, but if you're
not using atr afterwards, it should work.)
Both your contents of buff and buffA are in read-only memory of the process.
You will actually need to new your buff like
char* buff = new char[32];
This provides memory from the free-store and you can then strcat the string from buffA to buff.
You should prefer strncat, though to avoid buffer-overruns and delete your buff eventually.
This
char *buff = ""; // Create a new char to hold the converted result.
creates a char * that points to (probably read-only) memory of about 1 byte in extent. This:
strcat(buff,buffA); // Do the conversion.
attempts to overwrite that (probably read-only) memory of 1 or so bytes with an arbitrary string.
The chances are this will promptly crash. If the memory is read only, it will crash immediately. If the memory is not read only it will stomp over random data, resulting in very undefined behaviour.
Why on earth do you want to do that? Does parseArgs actually need a modifiable string? It's parsing arguments, it shouldn't need to change them. If it's really necessary, use a std::vector<char> and pass the address of the first element and hope that all it does is poke the contents of the array, rather than (say) running over the end.
I am new to C++, and haven't quite grasped all the concepts yet, so i am perplexed at why this function does not work. I am currently not at home, so i cannot post the compiler error just yet, i will do it as soon as i get home.
Here is the function.
const char * ConvertToChar(std::string input1, std::string input2) {
// Create a string that you want converted
std::stringstream ss;
// Streams the two strings together
ss << input1 << input2;
// outputs it into a string
std::string msg = ss.str();
//Creating the character the string will go in; be sure it is large enough so you don't overflow the array
cont char * cstr[80];
//Copies the string into the char array. Thus allowing it to be used elsewhere.
strcpy(cstr, msg.c_str());
return * cstr;
}
It is made to concatenate and convert two strings together to return a const char *. That is because the function i want to use it with requires a const char pointer to be passed through.
The code returns a pointer to a local (stack) variable. When the caller gets this pointer that local variable doesn't exist any more. This is often called dangling reference.
If you want to convert std::string to a c-style string use std::string::c_str().
So, to concatenate two strings and get a c-style string do:
std::string input1 = ...;
std::string input2 = ...;
// concatenate
std::string s = input1 + input2;
// get a c-style string
char const* cstr = s.c_str();
// cstr becomes invalid when s is changed or destroyed
Without knowing what the error is, it's hard to say, but this
line:
const char* cstr[80];
seems wrong: it creates an array of 80 pointers; when it
implicitly converts to a pointer, the type will be char
const**, which should give an error when it is passed as an
argument to strcpy, and the dereference in the return
statement is the same as if you wrote cstr[0], and returns the
first pointer in the array—since the contents of the array
have never been initialized, this is undefined behavior.
Before you go any further, you have to define what the function
should return—not only its type, but where the pointed to
memory will reside. There are three possible solutions to this:
Use a local static for the buffer:
This solution was
frequently used in early C, and is still present in a number of
functions in the C library. It has two major defects: 1)
successive calls will overwrite the results, so the client code
must make its own copy before calling the function again, and 2)
it isn't thread safe. (The second issue can be avoided by using
thread local storage.) In cases like yours, it also has the
problem that the buffer must be big enough for the data, which
probably requires dynamic allocation, which adds to the
complexity.
Return a pointer to dynamically allocated memory:
This works well in theory, but requires the client code to free
the memory. This must be rigorously documented, and is
extremely error prone.
Require the client code to provide the buffer:
This is probably the best solution in modern code, but it does
mean that you need extra parameters for the address and the
length of the buffer.
In addition to this: there's no need to use std::ostringstream
if all you're doing is concatenating; just add the two strings.
Whatever solution you use, verify that the results will fit.
I have been working with C++ strings and trying to load char * strings into std::string by using C functions such as strcpy(). Since strcpy() takes char * as a parameter, I have to cast it which goes something like this:
std::string destination;
unsigned char *source;
strcpy((char*)destination.c_str(), (char*)source);
The code works fine and when I run the program in a debugger, the value of *source is stored in destination, but for some odd reason it won't print out with the statement
std::cout << destination;
I noticed that if I use
std::cout << destination.c_str();
The value prints out correctly and all is well. Why does this happen? Is there a better method of copying an unsigned char* or char* into a std::string (stringstreams?) This seems to only happen when I specify the string as foo.c_str() in a copying operation.
Edit: To answer the question "why would you do this?", I am using strcpy() as a plain example. There are other times that it's more complex than assignment. For example, having to copy only X amount of string A into string B using strncpy() or passing a std::string to a function from a C library that takes a char * as a parameter for a buffer.
Here's what you want
std::string destination = source;
What you're doing is wrong on so many levels... you're writing over the inner representation of a std::string... I mean... not cool man... it's much more complex than that, arrays being resized, read-only memory... the works.
This is not a good idea at all for two reasons:
destination.c_str() is a const pointer and casting away it's const and writing to it is undefined behavior.
You haven't set the size of the string, meaning that it won't even necessealy have a large enough buffer to hold the string which is likely to cause an access violation.
std::string has a constructor which allows it to be constructed from a char* so simply write:
std::string destination = source
Well what you are doing is undefined behavior. Your c_str() returns a const char * and is not meant to be assigned to. Why not use the defined constructor or assignment operator.
std::string defines an implicit conversion from const char* to std::string... so use that.
You decided to cast away an error as c_str() returns a const char*, i.e., it does not allow for writing to its underlying buffer. You did everything you could to get around that and it didn't work (you shouldn't be surprised at this).
c_str() returns a const char* for good reason. You have no idea if this pointer points to the string's underlying buffer. You have no idea if this pointer points to a memory block large enough to hold your new string. The library is using its interface to tell you exactly how the return value of c_str() should be used and you're ignoring that completely.
Do not do what you are doing!!!
I repeat!
DO NOT DO WHAT YOU ARE DOING!!!
That it seems to sort of work when you do some weird things is a consequence of how the string class was implemented. You are almost certainly writing in memory you shouldn't be and a bunch of other bogus stuff.
When you need to interact with a C function that writes to a buffer there's two basic methods:
std::string read_from_sock(int sock) {
char buffer[1024] = "";
int recv = read(sock, buffer, 1024);
if (recv > 0) {
return std::string(buffer, buffer + recv);
}
return std::string();
}
Or you might try the peek method:
std::string read_from_sock(int sock) {
int recv = read(sock, 0, 0, MSG_PEEK);
if (recv > 0) {
std::vector<char> buf(recv);
recv = read(sock, &buf[0], recv, 0);
return std::string(buf.begin(), buf.end());
}
return std::string();
}
Of course, these are not very robust versions...but they illustrate the point.
First you should note that the value returned by c_str is a const char* and must not be modified. Actually it even does not have to point to the internal buffer of string.
In response to your edit:
having to copy only X amount of string A into string B using strncpy()
If string A is a char array, and string B is std::string, and strlen(A) >= X, then you can do this:
B.assign(A, A + X);
passing a std::string to a function from a C library that takes a char
* as a parameter for a buffer
If the parameter is actually const char *, you can use c_str() for that. But if it is just plain char *, and you are using a C++11 compliant compiler, then you can do the following:
c_function(&B[0]);
However, you need to ensure that there is room in the string for the data(same as if you were using a plain c-string), which you can do with a call to the resize() function. If the function writes an unspecified amount of characters to the string as a null-terminated c-string, then you will probably want to truncate the string afterward, like this:
B.resize(B.find('\0'));
The reason you can safely do this in a C++11 compiler and not a C++03 compiler is that in C++03, strings were not guaranteed by the standard to be contiguous, but in C++11, they are. If you want the guarantee in C++03, then you can use std::vector<char> instead.