While maintaining old code that uses libmxl2 I get the following error:
error: invalid static_cast
from type ‘const char [8]’
to type ‘xmlChar* {aka unsigned char*}’
in the following piece of code:
xmlNodePtr messageNode = doc->children; // The "Message" node
if(xmlStrcmp(messageNode->name, static_cast<xmlChar*>("Message"))) ... blabla
(same error happens with reinterpret_cast)
Obviously I can resolve with an old style cast (xmlChar*) but what is the proper way of doing this recently in C++?
I use gcc 4.7.2 (and this code was last time compiled 5 years ago :) without this error, but do not ask the version of the old compiler :) )
after some combination of casts this works:
const_cast<xmlChar*>(reinterpret_cast<const xmlChar *>("Message"))
Get a newer version of libxml2. The current version of the API has the following signature:
int xmlStrcmp (const xmlChar * str1,
const xmlChar * str2)
Which is compatible with your const string literal.
As you're stuck with an older version of libxml2 you'll have to const_cast away the constness of the literal and depend on the library not trying to modify the string that you pass it. You'll also need a reinterpret_cast as "regular" chars are usually signed instead of unsigned, e.g.
xmlStrcmp(messageNode->name, reinterpret_cast<xmlChar*>(const_cast<char*>("Message")))
By the way, the reason that you need a reinterpret_cast instead of a plain old static_cast is because you are casting a pointer type to a pointer type pointing at a differently typed object.
As has been stated here a couple of times, you can get the newest version of the lib which is const correct
If that is not an option for any reason, then what you need is a const_cast instead of a static_cast
Your code should look like
if(xmlStrcmp(messageNode->name,
const_cast<xmlChar*>(reinterpret_cast<const xmlChar *>("Message"))))
You can tell this is the case, by looking at the error message
error: invalid static_cast
from type ‘const char [8]’
to type ‘xmlChar* {aka unsigned char*}’
the only difference between the two incompatible types is the const modifier
xmlChar after some research is typedef to unsigned char. Therefore,
A solution is :
// use the implicit conversion to 'char*' to cast away constness:
char* message = "Message";
// explicitly cast to 'unsigned char*'
unsigned char* in = reinterpret_cast<unsigned char*>(message);
xmlChar* test = in;
The removal of constness is usually a bad idea since string literals are not modifiable, but sometimes it is necessary when dealing with legacy libraries that are not const-correct.
The conversion from char* to unsigned char* is safe because all objects can be treated as an array of char, unsigned char, or signed char in C++.
Related
I'm working on updating a very large codebase to be able to use gcc4.3 and ran into this issue. The question has been asked several times, however I think my situation is a little unique and Haven't been able to get a good explanation out of it.
The error I get is
error: deprecated conversion from string constant to 'realchar {aka char*}'
realchar refers to a typedef I have:
typedef char* realchar;
public:
ClassNameIsMe(const realchar name="UsyncBndLNQueue");
changing realChar to be simply char* removes the warning. however shouldn't that be equivalent since it is a typedef pointing to char*?
The problem is that const realchar name is const pointer to char, i.e it is equivalent to:
char * name const
instead of pointer to const char, which would be equivalent to:
const char * name
So the warning is valid.
See details here:
Q: typedef pointer const weirdness
Q: Is typedef'ing a pointer type considered bad practice?
I do have a "C" function
find_register(char *name)
which is called from a "C++" routine. The first tip to call it was
find_register("%ebx")
It results in a warning
deprecated conversion from string constant to 'char*'
OK, I corrected it to
find_register(string("%ebx").c_str())
which results in the error message
"invalid conversion from 'const char*' to 'char*'
Finally,
find_register((char*)string("%ebx").c_str())
is accepted without error message and warnings.
My questions:
1./ The probable reason of the error message is that the possibility of changing a 'const char*' is left open. It is OK, but in the first case the less sophisticated version allows the same, and converting a string constant to 'char *' is a legal, but deprecated conversion. Is there any deeper reason behind?
2./ Is there any simple method to do what I want? (the perfect (for the compiler) code is hardly readable for the programmer)
The reasoning lies in the original roots of C++ language, back in the time when certain amount of backward compatibility with C was considered important.
In C language string literals, despite being non-modifiable, have type char [N]. For this reason they are implicitly convertible to type char * in C. In C++ string literals have type const char[N]. Formally, const char[N] is not implicitly convertible to char *. But for aforementioned C compatibility reasons the original C++ specification (C++98) allowed implicit conversion of string literals to char *. This exception was made for immediate string literals only, as a form of special treatment provided to string literals.
But eventually the matter of C compatibility became unimportant and this special treatment was deprecated in C++03. So, this is what the compiler is telling you. In your find_register("%ebx") call it agrees to convert the immediate string literal to char *, but warns you that this conversion is deprecated. In C++11 this implicit conversion is outlawed entirely.
In all other contexts (not an immediate string literal), implicit conversion of const char [N] or const char * to char * is prohibited and has always been prohibited. This is why your
find_register(string("%ebx").c_str())`
variant has no chance of compiling.
As for working around this restriction... If you are sure that find_register(char *name) does not attempt to change the data pointed by name (and you cannot just change the find_register's parameter type to const char *name), then
find_register(const_cast<char *>("%ebx"))
is a fairly acceptable solution (with an appropriate accompanying comment). Of course, if find_register is a modifying function, then you'll have to do something like
char reg[] = "%ebx";
find_register(reg);
The prototype for the function find_register(char*) indicates that it may change the parameter since it is just a pointer that is passed. You do not mention if you have the source code of that function so I assume you don't otherwise it would be better to change that function to accept a char const * provided it doesn't change the contents.
Otherwise just pass a modifiable array:
char arg[] = "%ebx";
find_register(arg);
to write:
find_register((char*)string("%ebx").c_str())
is dangerous, what if the function does indeed modify the string e.g. strtok? But if you insist at least try to use the C++ style of casting instead of C casting (in this case find_register(const_cast<char*>("%ebx"))).
Side note: personally I find it easier to read to avoid using the C-way of putting const when possible. So instead of writing const char * write char const * and char * const when the pointer is constant i.e. const always goes to the left - it is more consistent.
If you are passing string literals to find_register(char *name), then the function prototype should be find_register(const char *name), as string literals are not modifiable.
With this conversion, C++ will happily let you shoot yourself in the foot and try to modify name, which gives me a segmentation fault, as in the following code:
void modifyIllegally(char* name) {
name[0]='d';
}
int main() {
modifyIllegally("abc");
}
Edit: If you don't control the API of the C program and are sure it won't change the char*, you can disable this warning with compiler flags. For gcc the flag is -Wno-write-strings.
I am writing a program (well... using sample code form Tektronix) to test the message exchange over the network to our spectrum analyser in our lab.
A header file called "decl-32.h" has been provided, and in the sample code is the following line, along with the error it produces:
ibwrt(GpibDevice, "SYSTem:ERRor:ALL?", strlen("SYSTem:ERRor:ALL?"));
"invalid conversion from 'const void*' to 'PVOID {aka void*}' [-fpermissive]"
Within the header file, the following line of code is highlighted as erroneous, along with the following error:
extern int __stdcall ibwrt (int ud, PVOID buf, long cnt);
"initializing argument 2 of 'int ibwrt(int, PVOID, long int)' [-fpermissive]"
The problem is that I am unfamiliar with such complex variable definitions, uses and conversions.
Could somebody be kind enough to offer me some advice? I'm sure this would be relevant to many others who are unfamiliar with such variable types and conversions, etc.
Thank you in advance!
The second parameter to ibwrt is PVOID which is a typedef for void*. In C++, pointer types are implicitly convertible to void*, but, as with all other types, the conversion is not allowed to drop a qulifier. That is, conversion from const char* (which is the type string literals decay to) to void* is illegal. Hence the error.
In C language, which is where the code is coming from, string literals decay to char* and your line will compile as is. The reason is historical - early implementations of C didn't have const keyword.
To fix it, you can cast the const away with a const_cast:
const char* s = "SYSTem:ERRor:ALL?";
ibwrt(GpibDevice, const_cast<char*>(s), strlen("SYSTem:ERRor:ALL?"));
You need to trust the function that it'll not attempt to modify the string literal through the pointer you passed it, otherwise it would invoke undefined behaviour. Seems like a safe assumption in this case (or maybe not, the parameter is named buf, mind you!), but if you want to be sure, do a copy of the string like #MikeSeymour shows in his answer.
The problem is that string literals are immutable, and this function requires a non-const pointer to the data.
If the function is guaranteed not to modify the data, and the lack of const is just an oversight, then you can cast to the required type:
ibwrt(GpibDevice, const_cast<char*>("SYSTem:ERRor:ALL?"), strlen("SYSTem:ERRor:ALL?"));
If it might modify the data, then this will give undefined behaviour, and you'll need a local copy of the string:
const char message[] = "SYSTem:ERRor:ALL?";
ibwrt(GpibDevice, message, strlen(message));
(You could specify the length as the less obvious but perhaps more efficient sizeof message - 1)
I've come across bizarre error related to reinterpret_cast. Just look at below code:
int* var;
reinterpret_cast<void const **>(&var);
error in VSC++2010: error C2440: 'reinterpret_cast' : cannot convert from 'int ** ' to 'const void ** '
error in gcc 4.1.2: reinterpret_cast from type ‘int** ’ to type ‘const void** ’ casts away constness
error in gcc 4.6.2: reinterpret_cast from type ‘int** ’ to type ‘const void** ’ casts away qualifiers
Does anyone have a clue why compilers say that I'm casting const away. Me, and few of my work colleagues have no idea what's wrong with it.
Thanks for help!
Section 5.2.10 of the C++03 standard talks about what a reinterpret_cast can do. It explicitly states "The reinterpret_cast operator shall not cast away constness".
Casting away constness is defined in section 5.2.11 of the C++03 standard. The notation used there is a little confusing, but it basically states that casting between two types "casts away constness" if there is no implicit conversion for the given qualification.
In your case, you are trying to convert an int ** to a void const**. The compiler asks "Can I implicitly convert between T ** and T const**?", and the answer is no, so it says that you are casting away constness.
The logic here is that reinterpret_cast is made to handle changing types, not changing qualifiers (that's what const_cast is for). So if you are asking it to do something you would need const_cast for, it refuses.
To add/remove const, use const_cast.
To deal with confusing casting errors, do things one step at a time:
int* var;
int** v2 = &var;
int const** v3 = const_cast<int const**>(v2);
void const** v4 = reinterpret_cast<void const**>(v3);
Note that a int const** and a int** are very different types, and converting between them is dangerous -- more dangerous than a void* <-> int*.
Suppose you have an int** bob. You then pass it to a function that takes a int const** alice through a const_cast.
In that function, they assign a pointer to an int stored in read-only memory to the *alice -- perfectly legal.
Outside the function, you check that bob and *bob are valid, then assign to **bob, and you just tried to write to read-only memory.
Can someone explain this to me:
char* a;
unsigned char* b;
b = a;
// error: invalid conversion from ‘char*’ to ‘unsigned char*’
b = static_cast<unsigned char*>(a);
// error: invalid static_cast from type ‘char*’ to type ‘unsigned char*’
b = static_cast<unsigned char*>(static_cast<void*>(a));
// everything is fine
What makes the difference between cast 2 and 3? And are there any pitfalls if the approach from 3 is used for other (more complex) types?
[edit]
As some mentioned bad design, etc...
This simple example comes from an image library which gives me the pointer to the image data as char*. Clearly image intensities are always positive so I need to interpret it as unsigned char data.
static_cast<void*> annihilate the purpose of type checking as you say that now it points on "something you don't know the type of". Then the compiler have to trust you and when you say static_cast<unsigned char*> on your new void* then he'll just try to do his job as you ask explicitely.
You'd better use reinterpret_cast<> if you really must use a cast here (as it's obvioulsy showing a design problem here).
Your third approach works because C++ allows a void pointer to be casted to T* via static_cast (and back again) but is more restrictive with other pointer types for safety reasons. char and unsigned char are two distinct types. This calls for a reinterpret_cast.
C++ tries to be a little bit more restrictive to type casting than C, so it doesn't let you convert chars to unsigned chars using static_cast (note that you will lose sign information). However, the type void* is special, as C++ cannot make any assumption for it, and has to rely on the compiler telling it the exact type (hence the third cast works).
As for your second question, of course there are a lot of pitfals on using void*. Usually, you don't have to use it, as the C++ type system, templates, etc. is rich enough to not to have to rely in that "unknown type". Also, if you really need to use it, you have to be very careful with casts to and from void* controlling that types inserted and obtained are really the same (for example, not pointer to subclasses, etc.)
static_cast between pointers works correct only if one of pointers is void or that's casting between objects of classes, where one class is inherited by another.
The difference between 2 and 3 is that in 3, you're explicitly telling the compiler to stop checking you by casting to void*. If the approach from 3 is used for, pretty much anything that isn't a direct primitive integral type, you will invoke undefined behaviour. You may well invoke undefined behaviour in #3 anyway. If it doesn't cast implicitly, it's almost certainly a bad idea unless you really know what's going on, and if you cast a void* back to something that wasn't it's original type, you will get undefined behaviour.
Casts between pointers require reinterpret_cast, with the exception of void*:
Casts from any pointer to to void* are implicit, so you don't need to explicitly cast:
char* pch;
void* p = pch;
Casts from void* to any other pointer only require a static_cast:
unsigned char* pi = static_cast<unsigned char*>(p);
beware, when you cast to void* you lose any type information.
what you are trying to do is incorrect, and false, and error prone and misleading. that's why the compilator returned a compilation error :-)
a simple example
char* pChar = NULL; // you should always initalize your variable when you declare them
unsigned char* pUnsignedChar = NULL; // you should always initalize your variable when you declare them
char aChar = -128;
pChar = &aChar;
pUnsignedChar = static_cast<unsigned char*>(static_cast<void*>(pChar));
then, though pUnsignedChar == pChar nonethless we have *pUnsignedChar == 255 and *pChar == -128.
i do believe this is bad joke, thus bad code.