Read only variable is not assignable - c++

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.

Related

Argument of type "const char **" is incompatible with parameter of type "const char *"

I get this error with this code:
string folder;
getline(cin, folder);
string folder2 = folder + "/index.txt";
const char* oldhtml[] = { folder2.c_str() };
folder2 = folder + "/index.html";
const char* newhtml[] = { folder2.c_str()};
rename(oldhtml, newhtml);
The error occurs with: rename(oldhtml, newhtml);
I am fairly new to C++. So if this is a simple fix I apologise
It seems you don't understand this line:
const char* oldhtml[] = { folder2.c_str() };
That declares an array of length 1. The array element is a pointer which is initialized with the result of folder2.c_str() (probably, pointing to the internal storage of the string).
However you then change folder2 on the next line. This invalidates the result of any previous calls to c_str, so oldhtml[0] is now a dangling pointer.
A better way to write this code is to use string all the way:
string oldhtml = folder + "/index.txt";
string newhtml = folder + "/index.html";
rename(oldhtml.c_str(), newhtml.c_str());
const char* oldhtml[] creates an array of char* (similar to const char**), basically, an array of string (many char*), when you want a string (one and only one char*).
To create a regular pointer, use:
const char* oldhtml or const char oldhtml[].
Use const char* oldhtml = folder2.c_str() instead: there's no need to have an array of const char*
Be aware though that oldhtml will only be valid for as long as folder2 is in scope and remains unaltered. Which is doesn't. You modify it later. Boom!
By the way, if rename changes either input parameter, then the program behaviour will be undefined.

Looking for the shortest string type conversion C++

I'm making an unmanaged C++ DLL which uses the C# managed DLL. I'm writting the C++ library as I need to use functions and headers defined in a software for which the C++ library can be added as an addon. But the things I want to make are so complex that my sparse knowledge of C++ would slow me down so I decided to do things in my favourite C# and connect the DLLs via COM and I was successful.
I'm somehow successful in making the code work, but less successful in keeping the code concise as I'm clearly not a professional C++ programmer.
The problem is with converting various string types. BSTR and const char * in particular.
The following code converst const char * to BSTR:
BSTR bstrt;
const char * someChar;
csharpInterfacedClassPointer->get_PropertyForSomeChars(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someChar = nstring;
The problem is, I have plenty of discrete someChars with corresponding discrete interface methods...the property method is generated from the C# interface so I can't change it. Each of the "someChar" requires the following three lines of code so for 30 discrete variables, I'd need to write 90 lines of code.
csharpInterfacedClassPointer->get_PropertyForSomeCharX(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someCharX = nstring;
The question is: how do write some shortcut for this so it'd fit just in one line?
I tried some sort of function with the "getter" function pointer and the someChar pointer.
typedef HRESULT (__stdcall *get_string_func)(BSTR * str); //getter function pointer
//the converting function
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
const size_t nsize = 1000;
char nstring[nsize];
BSTR bstrt;
bstrt = bstr_t(constCharString);
bstr_get_fx(&bstrt);
strcpy_s(nstring, (char *)bstrt);
constCharString = nstring;
}
//calling the function...as I thought that would work
ConvertAndAssign(sPtr->get_DataFile, someChar);
But then the compiler says some weird things aboud bound functions and how they are not allowed as pointers...I googled what does it mean and the solutions given required to alter the function definition but I can't do that since the definition is generated from the C# code (by regasm.exe).
Important note: I need to get the const char * type in the end because it is the required input type to the functions of the program for which I'm making the C++ DLL.
Disclaimer: it was a long time (7 years to be more precise) since I have touched C++/COM code for the last time.
Regarding binding an instance method to a function pointer check this SO question.
Another option is to use the IDispatch interface (if your COM component implement it)
Regarding ConvertAndAssign() implementation, IMO it has some issues. In order to make it easier to explain I have copied it bellow:
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
const size_t nsize = 1000;
char nstring[nsize];
BSTR bstrt;
bstrt = bstr_t(constCharString); // issue 1
bstr_get_fx(&bstrt);
strcpy_s(nstring, (char *)bstrt); // issue 2
constCharString = nstring; // issues 3 & 4
}
if your COM method returns a BSTR (i.e, it has an out parameter of type BSTR) you should not pass on a pre-allocated string otherwise you'll end up
leaking memory.
Casting bstr to char * will not work. BSTRs are Unicode strings. Also they are encoded such its length preceeds the actual characters.
If you are using ATL/MFC you can use one of the string conversion macros.
If you are NOT using ATL/MFC you can use WideCharToMultiByte() function or one of the "Smart" BSTR classes (CComBSTR in ATL, bstr_t, etc)
Assigning nstring to constCharString will have no effect on the outside string. If you are calling ConvertAndAssign like follows
char *outsideStr = NULL;
ConvertAndAssign(whatever, outsideStr);
Inside ConvertAndAssign() function consCharString will point to NULL in the begning. After the assignment constCharString does point to nstring but
outsideStr still points to NULL (remember, when you called ConvertAndAssign() function a copy of the pointer value was passed to it).
In order to get what you want you can either pass a reference or a pointer to a pointer:
void ConvertAndAssign(get_string_func bstr_get_fx, const char * &constCharString)
{
constCharString = nstring; // Assignment
}
or a pointer to a pointer:
char *outsideStr = NULL;
ConvertAndAssign(whatever, &outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char **constCharString)
{
.
.
.
*constCharString = nstring; // Assignment
}
After fixing the previous issue you'll hit another one: You cannot return the address of a local variable! When your code resumes after ConvertAndAssign() returns, this
address is not allocated for you anymore (it's part of the stack, so it may even look to be working, but I assure you, it is not; the slightest changes
in your code may break it)
To fix this you need to pass a pre-allocated string:
char outsideStr[1000];
ConvertAndAssign(whatever, outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
strcpy_s(constCharString, /* result str here */ );
}
or allocate a string in the heap.
Given all the above, one possible implementation for ConvertAndAssign() and its usage is as follow:
char outsideStr[1000];
ConvertAndAssign(whatever, outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
BSTR bstrt;
if(SUCCEEDED(bstr_get_fx(&bstrt)))
{
// Assumes constCharString points to a buffer large enough to hold the converted string.
strcpy_s(constCharString, CW2A(bstr)); // not completely correct since bstr may contain the byte 0 but I guess thats not your scenario.
SysFreeString(bstr);
}
else
{
// error handling.
constCharString[0] = 0;
}
}
This question can be deleted if any mod is going to read this. I realized I have a totally different problem. Thanks Vagaus for his effort.

NSString weakly holding const char * of std::string

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().

What's different about this variable assignment?

The following code attempts to put the contents of string c into arg[0].
const char **argv = new const char* [paramlist.size() + 2];
argv[0] = c.c_str();
This is another way to do it.
argv[0] = "someprogram"
I am noticing that later in my program, the second way works, but the first way causes an error. What could possibly be different? How could the first way be changed so that it works right?
This is where the problem occurs:
execvp(c.c_str(), (char **)argv);
If I change it to the following, then the problem doesn't occur. Why is that?
execvp(argv[0], (char **)argv);
In both ways you keep const char* pointers in argv[0]. So the only concern is whether pointers are valid and point to zero-terminated string.
"someprogram" pointer is valid and point to zero-terminated string during program execution.
c.c_str() pointer is guaranteed to be valid from the moment it is returned (by std::basic_string::c_str() function) to the moment string c is changed or destroyed. So if you access string c explicitly or implicitly with any non-const function (including its destructor) the pointer you stored into argv[0] will likely become invalid.
ADD:
Obviously to make argv[0] = c.c_str(); variant work correctly you have to just use argv only in the scope of std::string c (where c exist) and don't change c in any way after you make this assignment (argv[0] = c.c_str();).
You can use _strdup:
const char **argv = new const char* [paramlist.size() + 2];
argv[0] = _strdup(c.c_str());
_strdup allocates memory to store the copy of the string. When you are finished with the string, use free() to return the memory.
The _strdup function looks something like this:
char *_strdup (const char *s) {
char *d = (char *)(malloc (strlen (s) + 1));
if (d == NULL) return NULL;
strcpy (d,s);
return d;
}

Convert std::string to char * alternative

I have done a search in google and been told this is impossible as I can only get a static char * from a string, so I am looking for an alternative.
Here is the situation:
I have a .txt file that contains a list of other .txt files and some numbers, this is done so the program can be added to without recompilation. I use an ifstream to read the filenames into a string.
The function that they are required for is expecting a char * not a string and apparently this conversion is impossible.
I have access to this function but it calls another function with the char * so I think im stuck using a char *.
Does anyone know of a work around or another way of doing this?
In C++, I’d always do the following if a non-const char* is needed:
std::vector<char> buffer(str.length() + 1, '\0');
std::copy(str.begin(), str.end(), buffer.begin());
char* cstr = &buffer[0];
The first line creates a modifiable copy of our string that is guaranteed to reside in a contiguous memory block. The second line gets a pointer to the beginning of this buffer. Notice that the vector is one element bigger than the string to accomodate a null termination.
You can get a const char* to the string using c_str:
std::string str = "foo bar" ;
const char *ptr = str.c_str() ;
If you need just a char* you have to make a copy, e.g. doing:
char *cpy = new char[str.size()+1] ;
strcpy(cpy, str.c_str());
As previous posters have mentioned if the called function does in fact modify the string then you will need to copy it. However for future reference if you are simply dealing with an old c-style function that takes a char* but doesn't actually modfiy the argument, you can const-cast the result of the c_str() call.
void oldFn(char *c) { // doesn't modify c }
std::string tStr("asdf");
oldFn(const_cast< char* >(tStr.c_str());
There is c_str(); if you need a C compatible version of a std::string. See http://www.cppreference.com/wiki/string/basic_string/c_str
It's not static though but const. If your other function requires char* (without const) you can either cast away the constness (WARNING! Make sure the function doesn't modify the string) or create a local copy as codebolt suggested. Don't forget to delete the copy afterwards!
Can't you just pass the string as such to your function that takes a char*:
func(&string[0]);