3
votes

My question is related to this older question Format specifiers for uint8_t, uint16_t, ...?

Just to recap the original question was related to how to use the specifiers for uint8_t, uint16_t, uint32_t and uint64_t with a scanf?

The answer to the question was as follows:

sscanf (line, "Value of integer: %" SCNd32 "\n", &my_integer);

But does anyone know how to do this but resulting in a wide string?

ie

std::wstring line;
swscanf (line.c_str(), L"Value of integer: %" SCNd16 L"\n", &my_integer);

The sbove line gives me a concatenating error. I believe because the SCNd16 is just not intended for a widestring?

Currently my solution is to create the std::string in the original answer and then convert it to a wide string

sscanf_s(line.c_str(), "%" SCNd16 "\n", &sixteenBitInteger)
// code here to check for EOF and EINVAL
//then I convert it 
typedef std::codecvt_utf8<wchar_t> ConverterType; 
std::wstring_convert<ConverterType, wchar_t> converter;
std::wstring convertedString = converter.from_bytes(line);

but it's rather ugly and I am sure there must be a more polished way to do this conversion? If it helps to understand my use, I am using the uint16_t type to store the port number for a web server but I want to be able to convert it to a wide string as that is the expected display type. I am also using C++11 if that changes the answer at all and I do have access to the boost libraries although I would rather not use them.

2
SCNd16 is just a define, so if for you it is not critical to have cross-platform/architecture code just lookup its value and enter after % in your swscanf string.lonewasp

2 Answers

4
votes

This is a VS2013 compiler bug. Since it has been closed as "fixed", maybe it'll work in VS2015 (don't have the preview installed to give it a try).

The line of code you have

swscanf (line.c_str(), L"Value of integer: %" SCNd16 L"\n", &my_integer);

is well formed, because even if SCNd16 expands to a string literal that lacks the L prefix, the standard says that if out of two adjacent string literals, one lacks an encoding prefix, it is treated as if it has the same encoding prefix as the other.

§2.14.5/14 [lex.string]

In translation phase 6 (2.2), adjacent string literals are concatenated. If both string literals have the same encoding-prefix, the resulting concatenated string literal has that encoding-prefix. If one string literal has no encoding-prefix, it is treated as a string literal of the same encoding-prefix as the other operand. ...


Typically, you can use the preprocessor to widen strings by using token concatenation. For instance, defining a set of macros like this

#define WIDEN_(x) L##x
#define WIDEN(x) WIDEN_(x)

and converting the offending line of code to

swscanf (line.c_str(), L"Value of integer: %" WIDEN(SCNd16) L"\n", &my_integer);

would fix the problem, but it doesn't on VS2013 because of an implementation detail. The SCNd16 macro actually expands into two separate string literals - "h" "d". So the above macro widens the first literal, but not the second, and you run into the same (bogus) error.

Your options are to either hardcode the string "hd" or go with the runtime conversion solution you've shown.

0
votes

A pure guess as I don't have time at the moment to try it.

Can you use the preprocesser to token paste the wide-string L to the front of the expanded SCNd16?