1

In my MFC based application this code:

std::string stdString("MappingName");
std::cout << "as String: " << stdString << std::endl;
CString cString(stdString.c_str());
std::cout << "as CString: " << cString << std::endl;

Produces this output:

as String: MappingName
as CString: 01DEA060

The value of the CString is different every time I run it, but the length seems to be constant. Some other results are 042219B0 and 042C4378.

I've tried every variation discussed in this thread, with the results being the same. I've also tried to change the Character Set in the Visual Studio project from Use Unicode Character Set to Use Multi-Byte Character Set, again with no effect.

What could be the reason the conversion fails?

Edit: More tests show that the value of the std::string doesn't seem to make a difference:

tmp as String: The quick brown fox jumps over the lazy dog
tmp as CString: 00EAAF88

I've also set the Character Set to Not Set which didn't help either.

Jdv
  • 962
  • 10
  • 34

2 Answers2

6

The problem is printing not conversion.

CString can implicilty convert to TCHAR const*. When built with unicode enabled, this is wchar_t const*.

std::cout has no wchar_t const* overload for <<. It does have a void const* overload.

The void pointer overload prints a pointer address in hex.

Cast to CStringA before printing:

std::cout << "as CString: " << static_cast<CStringA>(cString) << std::endl;

Or print using wcout.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 3
    _"Cast to CStringA"_ ... or simply create a `CStringA` variable in the first place to avoid the cast. – zett42 Aug 19 '18 at 17:35
  • 1
    Especially, since the cast invokes a *lossy* conversion. It may be safe when round-tripping from ANSI to Unicode back to ANSI, but in general, it isn't. – IInspectable Aug 19 '18 at 21:13
  • @iinsp it is the best cout can handle, however. – Yakk - Adam Nevraumont Aug 19 '18 at 22:50
  • Presumably, `cout` is just used as an example. It's not the real code. – IInspectable Aug 19 '18 at 22:59
  • @iinsp some other interface with `void const*` and `char const*` APIs that interprets the void case as a pointer address interpreted in hex as a string value thus getting confused by the `wchar_t const*` conversion operator of `CString` could, I guess, be the real problem. But I doubt it. This question, as I read it, is about the narrow collision of Microsofts implicit cast to wide character and the permissive interface of cout streaming causing a confusing resulting behaviour. – Yakk - Adam Nevraumont Aug 20 '18 at 00:51
3

The issue is a combination of two aspects:

  • Mixture of string types with explicit encoding (std::string) and generic-text mapping (CString vs. CStringA/CStringW).
  • CString's ability to convert between encodings through conversion c'tors.

The std::string converted to the CString just fine, presumably constructing a CStringW object. Since there is no operator<< overload for the std::ostream that takes a wchar_t const* (which CStringW implicitly converts to), it merely prints the address, matching the generic void const* overload.

The solution here is to take the generic-text mappings out of the picture, and construct a CString specialization matching the source encoding (ANSI for std::string, i.e. CStringA):

std::string stdString("MappingName");
std::cout << "as String: " << stdString << std::endl;
CStringA cString(stdString.c_str());
std::cout << "as CString: " << cString.GetString() << std::endl;

To get notified about implicit conversions when constructing CStrings, you can #define the _CSTRING_DISABLE_NARROW_WIDE_CONVERSION proprocessor symbol. This will in turn generate compiler errors in case you are trying to invoke any of the conversion c'tors.

IInspectable
  • 46,945
  • 8
  • 85
  • 181