I have a class that defined a user defined operator for a TCHAR*, like so
CMyClass::operator const TCHAR*() const
{
// returns text as const TCHAR*
}
I want to be able to do something like
CMyClass myClass;
_tprintf(_T("%s"), myClass);
or even
_tprintf(_T("%s"), CMyClass(value));
But when trying, printf always prints (null) instead of the value. I have also tried a normal char* operator, as well variations with const etc.
It only works correctly if I explicitly call the operator or do a cast, like
_tprintf(_T("%s\n"), (const TCHAR*)myClass);
_tprintf(_T("%s\n"), myClass.operator const TCHAR *());
However, I don't want to cast. How can this be achieved?
Note, that a possibility is to create a function that has a parameter of const TCHAR*, so that it forcible calls the operator TCHAR*, but this I also don't want to implement.
The C++ Standard says that implicit conversions like this are not applied to ellipsis parameters - how would the compiler know what conversion to apply? You will have to perform the conversion explicitly yourself, or better yet stop using printf.
Avoid conversion operators. They rarely do what you want, and then explicit calls are painful. Rename operator const TCHAR*() const to TCHAR *str() const.
Conversion operators are called when the compiler wants to convert a value to another type. This works for functions that take defined parameters of specific types. It doesn't work for variadic functions like printf() with ... in the function declaration. These functions take the arguments and then work with them, so the conversion operator is never called.
To be specific, when the compiler sees printf("%s", foo), it passes foo, whatever it is, to printf(), which will have to assume it's suitable for a %s format as it is. No conversion operator will be called (although certain arithmetic promotions will take place).
Conversion operators in general cause problems. By having that operator in that class, you've complicated function overload resolution, since the compiler can interpret a CMyClass as if it were a TCHAR *. This can cause unexpected results, either causing code to compile when you really didn't want it to, or selecting the wrong overloaded function. (For example, given CMyClass cmc;, the expression cmc + 10 is suddenly legal, since TCHAR * + int is perfectly legitimate pointer arithmetic.) The usual practice is to label such conversions explicit.
Casting is the right thing to do if you want to use printf style APIs and rely on a conversion operator (and I am not going to argue here whether or not you should use these features). However, I would use static cast, e.g. _tprintf(_T("%s\n"), static_cast<const TCHAR*>(myClass));
Related
I have a class SpecialString. It has an operator overload / conversion function it uses any time it's passed off as a const char*. It then returns a normal c-string.
class SpecialString
{
...
operator char* () const { return mCStr; }
...
};
This used to work a long time ago (literally 19 years ago) when I passed these directly into printf(). The compiler was smart enough to know that argument was meant to be a char* and it used the conversion function, but the now g++ complains.
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", str1, str2);
error: cannot pass object of non-POD type 'SPECIALSTRING' (aka 'SpecialString') through variadic method; call will abort at runtime [-Wnon-pod-varargs]
Is there any way to get this to work again without changing the code? I can add a deref operator overload function that returns the c-string and pass the SpecialString objects around like this.
class SpecialString
{
...
operator CHAR* () const { return mCStr; }
char* operator * () const { return mCStr; }
...
};
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", *str1, *str2);
But I'd prefer not to because this requires manually changing thousands of lines of code.
You could disable the warning, if you don't want to be informed about it... but that's a bad idea.
The behaviour of the program is undefined, you should fix it and that requires changing the code. You can use the exising conversion operator with static_cast, or you can use your unary * operator idea, which is going to be terser.
Even less change would be required if you used unary + instead which doesn't require introducing an overload, since it will invoke the implicit conversion instead. That may add some confusion to the reader of the code though.
Since you don't want to modify the existing code, you can write a "hack" instead. More specifically, a bunch of overloads to printf() that patch the existing code.
For example:
int printf(const char* f, const SpecialString& a, const SpecialString& b)
{
return printf(f, (const char*)a, (const char*)b);
}
With this function declared in your header, every call to printf() with those specific parameters will use this function instead of the "real" printf() you're familiar with, and perform the needed conversions.
I presume you have quite a few combinations of printf() calls in your code envolving SpecialString, so you may have to write a bunch of different overloads, and this is ugly af to say the least but it does fit your requirement.
As mentioned in another comment, it has always been undefined behavior that happens to work in your case.
With Microsoft CString class, it seems like the undefined behavior was so used (as it happen to work), that now the layout is defined in a way that it will still works. See How can CString be passed to format string %s?.
In our code base, I try to fix code when I modify a file to explicitly do the conversion by calling GetString()
There are a few things you could do:
Fix existing code everywhere you get the warning.
In that case, a named function like c_str or GetString is preferable to a conversion operator to avoid explicit casting (for ex. static_cast or even worst C-style case (const char *). The deref operator might be an acceptable compromise.
Use some formatting library
<iosteam>
fmt: https://github.com/fmtlib/fmt
many other choices (search C++ formatting library or something similar)
Use variadic template function so that conversion could be done.
If you only use a few types (int, double, string) and rarely more than 2 or 3 parameters, defining overloads might also be a possibility.
Not recommended: Hack your class to works again.
Have you done any change to your class definition that cause it to break or only upgrade the compiler version or change compiler options?
Such hack is working with undefined behavior so you must figure out how your compiler works and the code won't be portable.
For it to works the class must have the size of a pointer and the data itself must be compatible with a pointer. Thus essentially, the data must consist of a single pointer (no v-table or other stuff).
Side note: I think that one should avoid defining its own string class. In most case, standard C++ string should be used (or string view). If you need additional functions, I would recommend your to do write stand-alone function in a namespace like StringUtilities for example. That way, you avoid converting back and forth between your own string and standard string (or some library string like MFC, Qt or something else).
I'm having a weird problem with an overloaded function one of my work colleagues made, but we're both C++/QT newbies and can't figure out the reason for such a situation.
The situation is as follows:
We have an overloaded function:
foo(bool arg1 = false);
foo(const QVariant &arg1, const QString &arg2 = NULL, bool arg3 = false);
On the first one we have only one optional bool argument passed as value;
On the second we have one QVariant reference, an optional qstring reference and a bool.
What happens on runtime is the when I call for example:
foo("some_c++_string");
instead of using the 2nd one and casting the string to QVariant, it uses the first one and probably ignores the argument! The weird thing is that it doesn't even complain about not existing an overload for foo(char *) on compile time for example!
But if we do:
foo(QString("some_qt_string"));
it goes to the second overload as expected.
So, the question is: Why in the world does it decide to go for an overload which accepts one optional argument not even of the same type instead of using the second one and trying to use the string argument as a QVariant?
It probably has something to do with passing the arguments as reference and giving them default values, but I've tried several combinations and always went wrong.
Thanks for your time
That's because of the ordering of implicit conversions. Converting a string literal to a bool requires only a standard conversion sequence: an array-to-pointer conversion (obtaining const char*) and a boolean conversion (obtaining bool).
On the other hand, converting a string literal to a QVariant requires a user-defined conversion sequence, since it involves a class (QVariant).
And per C++11 13.3.3.2/2,
When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion
sequence or an ellipsis conversion sequence
Which means that the first overload is strictly better and is thus chosen.
I'd wager you're building with Visual Studio, by the way. Otherwise, the construct
foo(QString("some_qt_string"));
wouldn't even compile. That's because QString("some_qt_string") creates a temporary object, and in standard C++, temporaries cannot bind to non-const lvalue references (which your second overload takes). However, Visual Studio allows that as an extension.
Why in the world does it decide to go for an overload which accepts one optional argument not even of the same type instead of using the second one and trying to use the string argument as a QVariant?
Looking at the functions signature :
foo(bool arg1 = false);
foo(QVariant &arg1, QString &arg2 = NULL, bool arg3 = false);
The 2nd overload takes an lvalue. If you pass a const char*, it would require creating a QVariant rvalue.
On the other hand, there is a function overload that takes a bool value, which is a better match, and is called.
You also said that if you do this :
foo(QString("some_qt_string"));
the second is called. But it shouldn't even compile. You are passing rvalue to a function which takes a lvalue. I am not sure which compiler you use, but that is certainly wrong. Put maximum possible warning level and see what happens. I do hope you are not ignoring compiler warnings ;)
The simplest solution (and maybe the best) is NOT to overload the function. Or at least not to give default arguments.
The second overload takes a QVariant by reference as it's first argument. You did not pass a QVariant, so this overload can never be taken. In fact, when you pass a QString as first argument, the second overload is still not viable and you should get a compiler error. My guess is that you are compiling with Microsoft VC and what you are observing is non-standard behavior that allows you to take an l-value reference of a temporary.
I'm currently porting a C++ application to a slightly restricted environment. The application uses the STL, string and stream classes. I'm rewriting simplified versions of these that will play nicely in my environment.
What I'm concerned about is that my application is compiling even without all the necessary operator definitions. For example, for my string classes I defined:
string operator+ (const string& lhs, const string& rhs);
and this was enough. However, I noticed there were often cases that had mystring + "some constant string" and this isn't defined in my code anywhere. When I explicitly added it it was used:
string operator+ (const string& lhs, const char* rhs);
What was going on before that? It compiled successfully before I added the second function. Surely the compiler wouldn't be able to infer how to concatenate c-style strings to my string class.
I'm getting strange behaviour in my program now and I'm wondering if it's due to other operators left undefined. Is there any way to enforce the compiler to require such operator definitions if it's needed by the program?
P.S. My string class is in a unique namespace and unrelated to std::
It's impossible to be certain without seeing the rest of your code, but in this case it's probably not too hard to guess either. You almost certainly have a constructor for your string that takes a char const * as its parameter, so what's happening is that the compiler is using that ctor to convert the char const * to a string, then using string operator+ (const string& lhs, const string& rhs); to concatenate that.
Allowing this to happen is one of (if not the) primary reason for overloading these operators with globals instead of member functions. As member functions they can convert the right operand, but not the left.
When you passed a const char*, a string object was probably constructed and passed to operator+. If you step through the code in a debugger you will probably be able to verify the constructor is being called.
You probably, have a constructor in your class which takes const char * as an input parameter, Most likely this constructor is used for implicit coversions and the weird behavior you see.
Declare your constructor which takes const char * as explicit, this would disable its use for implicit conversions where you do not intend it to be used.
Does your 'string' class have a one argument constructor that takes a const char*? Is that single argument constructor marked as 'explicit'?
If it is not explicit, the the most likely answer is that the compiler is constructing a temporary string object from the char* via your converting constructor, and then using that temporary string to call your two argument addition operator.
The compiler can't know how to convert your string to a std::string or char* unless you specify how.
Look for conversion constructors or cast operators in your class declaration.
class MyString
{
MyString(char*);
MyString(std::string);
operator std::string ();
operator char*();
};
These will be called implicitly.
You can specify the explicit keyword for your constructor to prevent this from happening.
If this doesn't solve the problem, you must have the operators overloaded somewhere, best find them stepping through the code with a debugger.
In C++ is it possible to define conversion operators which are not class members? I know how to do that for regular operators (such as +), but not for conversion operators.
Here is my use case: I work with a C Library which hands me out a PA_Unichar *, where the library defines PA_Unichar to be a 16-bit int. It is actually a string coded in UTF-16. I want to convert it to a std::string coded in UTF-8. I have all the conversion code ready and working, and I am only missing the syntactic sugar that would allow me to write:
PA_Unichar *libOutput = theLibraryFunction();
std::string myString = libOutput;
(usually in one line without the temp variable).
Also worth noting:
I know that std::string doesn't define implicit conversion from char* and I know why. The same reason might apply here, but that's beside the point.
I do have a ustring, subclass of std::string that defines the right conversion operator from PA_Unichar*. It works but this means using ustring variables instead of std::string and that then requires conversion to std::string when I use those strings with other libraries. So that doesn't help very much.
Using an assignment operator doesn't work as those must be class members.
So is it possible to define implicit conversion operators between two types you don't control (in my case PA_Unichar* and std::string), which may or may not be class types?
If not what could be workarounds?
What's wrong with a free function?
std::string convert(PA_Unichar *libOutput);
std::string myString = convert(theLibraryFunction());
Edit answering to the comment:
As DrPizza says: Everybody else is trying to plug the holes opened up by implicit conversions through replacing them with those explicit conversion which you call "visual clutter".
As to the temporary string: Just wait for the next compiler version. It's likely to come with rvalue references and its std::string implementation will implement move semantics on top of that, which eliminates the copy. I have yet to see a cheaper way to speedup your code than than by simply upgrading to a new compiler version.
I don't think you can define "global" conversion operators. The standards say that conversion functions are special member functions. I would propse the following if I could consider the following syntax sugar:
struct mystring : public string
{
mystring(PA_Unichar * utf16_string)
{
// All string functionality is present in your mystring.
// All what you have to do is to write the conversion process.
string::operator=("Hello World!");
string::push_back('!');
// any string operation...
}
};
Be aware that polymorphic behavior of this class is broken. As long as you don't create an object of it through a pointer of type string* though, you are in the safe-side! So, this code is perfect:
mystring str(....);
As said before, the following code is broken!
string* str = new mystring(....);
....
delete str; // only deleting "string", but not "mystring" part of the object
// std::string doesn't define virtual destructor
Implicit conversions are the devil, anyway. Make it explicit with a converting function call.
No, you can't. What you could do as an alternative is to create a conversion constructor in the target class (not your case, as you want to convert to std::string - unless you derive it). But I agree to the other answers, I think an implicit conversion is not recommended in this case - especially because you're not converting from an object but from a pointer. Better to have a free function, your code will be easier to understand and the next programmer to inherit the code will for sure thank you.
std::string provides const char* c_str ( ) const which:
Get C string equivalent
Generates a null-terminated sequence
of characters (c-string) with the same
content as the string object and
returns it as a pointer to an array of
characters.
A terminating null character is
automatically appended.
The returned array points to an
internal location with the required
storage space for this sequence of
characters plus its terminating
null-character, but the values in this
array should not be modified in the
program and are only granted to remain
unchanged until the next call to a
non-constant member function of the
string object.
Why don't they just define operator const char*() const {return c_str();}?
From the C++ Programming Language 20.3.7 (emphasis mine):
Conversion to a C-style string could have been provided by an operator const char*() rather than c_str(). This would have provided the convenience of an implicit conversion at the cost of surprises in cases in which such a conversion was unexpected.
I see at least two problems with the implicit conversion:
Even the explicit conversion that c_str() provides is dangerous enough as is. I've seen a lot of cases where the pointer was stored to be used after the lifetime of the original string object had ended (or the object was modified thus invalidating the pointer). With the explicit call to c_str() you hopefully are aware of these issues. But with the implicit conversion it would be very easy to cause undefined behavior as in:
const char *filename = string("/tmp/") + name;
ofstream tmpfile(filename); // UB
The conversion would also happen in some cases where you wouldn't expect it and the semantics are surprising to say the least:
string name;
if (name) // always true
;
name-2; // pointer arithmetic + UB
These could be avoided by some means but why get into this trouble in the first place?
Because implicit conversions almost never behave as you expect. They can give surprising results in overload resolution, so it's usually better to provide an explicit conversion as std::string does.
The Josuttis book says the following:
This is for safety reasons to prevent unintended type conversions that result in strange behavior (type char * often has strange behavior) and ambiguities (for example, in an expression that combines a string and a C-string it would be possible to convert string into char * and vice versa).
I addition to the rationale provided in the specification (unexpected surprises), if you're mixing C API calls with std::string, you really need to get into the habit of using the ::c_str() method. If you ever call a varargs function (eg: printf, or equivalent) which requires a const char*, and you pass a std::string directly (without calling the extraction method), you won't get a compile error (no type checking for varargs functions), but you will get a runtime error (class layout is not binary identical to a const char*).
Incidentally, CString (in MFC) takes the opposite approach: it has an implicit cast, and the class layout is binary-compatible with const char* (or const w_char*, if compiling for wide character strings, ie: "Unicode").
That's probably because this conversion would have surprising and peculiar semantics. Particularly, the fourth paragraph you quote.
Another reason is that there is an implicit conversion const char* -> string, and this would be just the converse, which would mean strange behavior wrt overload resolution (you shouldn't make both implicit conversions A->B and B->A).
Because C-style strings are a source of bugs and many security problems it's way better to make the conversion explicitly.