Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
josuttis_nm_c20_the_complete_guide.pdf
Скачиваний:
44
Добавлен:
27.03.2023
Размер:
5.85 Mб
Скачать

10.4 Internationalization

329

std::wstring and std::basic_string<wchar_t, traits, allocator>

std::wstring_view and std::basic_string_view<wchar_t, traits>

Note that the format string and arguments for them must have the same character type:

auto ws1 = std::format("{}", L"K\u00F6ln");

// compile-time ERROR

std::wstring ws2 = std::format(L"{}", L"K\u00F6ln");

// OK

Specifiers for Pointers

For pointer types, the default format specifier is p, which usually writes the address in hexadecimal notation with the prefix 0x. On platforms that have no type uintptr_t the format is implementation-defined:

void* ptr = ... ;

 

std::format("{}", ptr)

// usually yields a value such as 0x7ff688ee64

std::format("{:p}", ptr)

// usually yields a value such as 0x7ff688ee64

Note that only the following pointer types are supported:

void* and const void*

std::nullptr_t

Thus, you can either pass nullptr or a raw pointer, which you have to cast to type (const) void*:

int i = 42;

 

std::format("{}", &i)

// compile-time error

std::format("{}", static_cast<void*>(&i))

// OK (e.g., 0x7ff688ee64)

std::format("{:p}", static_cast<void*>(&i))

// OK (e.g., 0x7ff688ee64)

std::format("{}", static_cast<const void*>("hi"))

// OK (e.g., 0x7ff688ee64)

std::format("{}", nullptr)

// OK (usually 0x0)

std::format("{:p}", nullptr)

// OK (usually 0x0)

10.4 Internationalization

If L is specified for a format, the locale-specific notation is used:

For bool, the locale strings on std::numpunct::truename and std::numpunct::falsename are used.

For integral values, the locale-dependent thousands separator character is used.

For floating-point values, the locale-dependent decimal point and thousands separator characters are used.

For several notations of types in the chrono library (durations, timepoints, etc), their locale-specific formats are used.

To activate the locale-specific notation, you also have to pass a locale to std::format(). For example:

// initialize a locale for “German in Germany”:

#ifdef _MSC_VER

std::locale locG{"deu_deu.1252"}; #else

std::locale locG{"de_DE"}; #endif

330

Chapter 10: Formatted Output

// use it for formatting:

std::format(locG, "{0} {0:L}", 1000.7) // yields 1000.7 1.000,7

See format/formatgerman.cpp for a complete example.

Note that the locale is used only if you use the locale specifier L. Without this, the default locale "C" is used, which uses the American formatting.

Alternatively, you can set the global locale and use the L specifier:

std::locale::global(locG);

// set German locale globally

std::format("{0} {0:L}", 1000.7)

// yields 1000.7 1.000,7

You might have to create your own locale (usually based on an existing locale with modified facets). For example:

format/formatbool.cpp

#include <iostream> #include <locale> #include <format>

// define facet for German bool names:

class GermanBoolNames : public std::numpunct_byname<char> { public:

GermanBoolNames (const std::string& name) : std::numpunct_byname<char>(name) {

}

protected:

virtual std::string do_truename() const { return "wahr";

}

virtual std::string do_falsename() const { return "falsch";

}

};

int main()

{

// create locale with German bool names: std::locale locBool{std::cin.getloc(),

new GermanBoolNames{""}};

// use locale to print Boolean values:

} std::cout << std::format(locBool, "{0} {0:L}\n", false); // false falsch

The program has the following output: false falsch

10.5 Error Handling

331

To print values with wide-character strings (which is an issue especially with Visual C++), both the format string and the arguments have to be wide-character strings. For example:

std::wstring city = L"K\u00F6ln";

// K¨oln

auto ws1 = std::format("{}", city);

// compile-time ERROR

std::wstring ws2 = std::format(L"{}", city);

// OK: ws2 is std::wstring

std::wcout << ws2 << '\n';

// OK

Strings of types char8_t (UTF-8 characters), char16_t, and char32_t are not supported, yet.

10.5 Error Handling

Ideally, C++ compilers should detect bugs at compile time rather than at runtime. Because string literals are known at compile time, C++ can check for format violations when string literals are used as format strings and does so in std::format():4

std::format("{:d}",

42)

// OK

std::format("{:s}",

42)

// compile-time ERROR

If you pass a format string that has already been initialized or computed, the formatting library handles format errors as follows:

std::format(), std::format_to(), and format_to_n() take only format strings known at compile time:

String literals

constexpr character pointers

Compile-time strings that can be converted to a compile-time string view

To use format strings computed at runtime, use

std::vformat()

std::vformat_to()

For std::formatted_size(), you can only use format strings known at compile time.

For example:

 

const char* fmt1 = "{:d}";

// runtime format string

std::format(fmt1, 42);

// compile-time ERROR

std::vformat(fmt1, std::make_format_args(42));

// OK

constexpr const char* fmt2 = "{:d}";

// compile-time format string

std::format(fmt2, 42);

// OK

Using fmt1 does not compile because the passed argument is not a compile-time string and std::format() is used. However, using fmt1 with std::vformat() works fine (but you have to convert all arguments with std::make_format_args()). Using fmt2 does compile when you pass it to std::format() because it is initialized as a compile-time string.

4The requirement to check format strings at compile time was proposed with http://wg21.link/p2216r3 and accepted as a fix against C++20 after C++20 was standardized.

332

Chapter 10: Formatted Output

If you want to use multiple arguments with std::vformat(), you have to pass them all to one call of std::make_format_args():

const char* fmt3 = "{} {}";

std::vformat(fmt3, std::make_format_args(x, y))

If a format failure is detected at runtime, an exception of type std::format_error is thrown. This new standard exception type is derived from std::runtime_error and offers the usual API of standard exceptions to initialize the exception with a string for the error message you get by calling what().

For example:

try {

const char* fmt4 = "{:s}";

std::vformat(fmt4, std::make_format_args(42)) // throws std::format_error

}

catch (const std::format_error& e) {

std::cerr << "FORMATTING EXCEPTION: " << e.what() << std::endl;

}