Suppose I'm using some C library which has a function:
int foo(char* str);
and I know for a fact that foo() does not modify the memory pointed to by str. It's just poorly written and doesn't bother to declare str being constant.
Now, in my C++ code, I currently have:
extern "C" int foo(char* str);
and I use it like so:
foo(const_cast<char*>("Hello world"));
My question: Is it safe - in principle, from a language-lawyering perspective, and in practice - for me to write:
extern "C" int foo(const char* str);
and skip the const_cast'ing?
If it is not safe, please explain why.
Note: I am specifically interested in the case of C++98 code (yes, woe is me), so if you're assuming a later version of the language standard, please say so.
No.
-- From language side:
After reading the dcl.link I think exactly how the interoperability works between C and C++ is not exactly specified, with many "no diagnostic required" cases. The most important part is:
Because they refer to the same function, I believe a sane assumption would be that the declaration of a identifier with C language linkage on C++ side has to be compatible with the declaration of that symbol on C side. In C++ there is no concept of "compatible types", in C++ two declarations have to be identical (after transformations), making the restriction actually more strict.
From C++ side, we read c++draft basic#link-11:
Because the declaration
int foo(const char *str)with C language linkage in a C++ translation unit is not identical to the declarationint foo(char *str)declared in C translation unit (thus it has C language linkage), the behavior is undefined (with famous "no diagnostic required").From C side (I think this is not even needed - the C++ side is enough to make the program have undefined behavior. anyway), the most important part would be C99 6.7.5.3p15:
Because from C99 6.7.5.1p2:
and C99 6.7.3p9:
So because
charis not compatible withconst char, thusconst char *is not compatible withchar *, thusint foo(const char *)is not compatible withint foo(char*). Calling such a function (C99 6.5.2.2p9) would be undefined behavior (you may see also C99 J.2)-- From practical side:
I do not believe will be able to find a compiler+architecture combination where one translation unit sees
int foo(const char *)and the other translation unit defines a functionint foo(char *) { /* some stuff */ }and it would "not work".Theoretically, an insane implementation may use a different register to pass a
const char*argument and a different one to pass achar*argument, which I hope would be well documented in that insane architecture ABI and compiler. If that's so, wrong registers will be used for parameters, it will "not work".Still, using a simple wrapper costs nothing: