std::wprint / std::wprintln
Document Number: D3673R0
Date: 2026-05-23
Project: ISO/IEC JTC1/SC22/WG21 14882: Programming Language - C++
Audience: Library Evolution Working Group Incubator (LEWGI, SG18), SG16
Reply-To: Tymi <[email protected]>
Abstract
C++23 introducedstd::printandstd::printlnfunction templates. While most standard library text formatting facilities support wide strings, these functions currently do not. This paper proposes the addition of wide-string overloads of these functions, acceptingstd::wformat_string, to provide consistent support for wide character formatted output.
Introduction
This proposal extends the interface of the standard printing utilities by introducing wide string
overloads of std::print and std::println.
The addition allows integration with systems that use wide characters and supports codebases where wide character strings are the preferred representation.
Revision History
- R0: Initial proposal.
Motivation and Scope
C++23 introduced std::print and std::println function templates, which
provide efficient formatted text output. These functions are currently limited to narrow string
formats.
Wide character strings are widely used throughout many codebases, particularly on Windows and in specialised environments that prefer wider encoding. The lack of direct wide-string support in the formatted printing utilities causes confusion, leads to inconsistencies across APIs and does not support modern C++ features.
This proposal aims to fix these problems by providing overloads for std::print and
std::println that accept std::wformat_args, ensuring developers can
use the formatted output facilities with wide character strings.
Impact on the Standard
New Functionality
This proposal introduces additional function template overloads, No existing functionality is removed or changed; the impact is additive only.
Proposed Wording
void vprint_unicode(wostream& os, wstring_view fmt, wformat_args args);
void vprint_nonunicode(wostream& os, wstring_view fmt, wformat_args args);
Effects: Behaves as a formatted output function of os, except that:
- Failure to generate output is reported as specified below, and
- Any exception thrown by the call to
vformatis propagated without regard to the value ofos.exceptions()and without turning onios_base::badbitin the error state ofos.
After constructing a sentry object, the function initialises an automatic variable via:
wstring out = vformat(os.getloc(), fmt, args);
If the function is vprint_unicode and os is a stream that
refers to
a terminal capable of displaying Unicode (determined in an implementation-defined
manner),
it writes out to the terminal using the native Unicode API; if out
contains invalid code units, the behaviour is undefined and implementations are encouraged to
diagnose it. If the native Unicode API is used, the function flushes os before
writing
out. Otherwise (if os is not such a stream or the function is
vprint_nonunicode), inserts the character sequence
[out.begin(), out.end()) into os. If writing to the terminal or
inserting
into os fails, calls os.setstate(ios_base::badbit) (which may throw
ios_base::failure).
Recommended practice: For vprint_unicode, if invoking the native Unicode
API
requires transcoding, implementations should substitute invalid code units with
u+fffd
replacement character per the Unicode Standard, Chapter 3.9 u+fffd
Substitution in Conversion.
void vprint_unicode(FILE* stream, wstring_view fmt, wformat_args args);
void vprint_nonunicode(FILE* stream, wstring_view fmt, wformat_args args);
Preconditions: stream is a valid pointer to an output C wide
stream.
Effects: The function initialises an automatic variable via:
wstring out = vformat(fmt, args);
If stream refers to a terminal capable of displaying Unicode, writes
out to
the terminal using the native Unicode API; if out contains invalid code units, the
behaviour is undefined and implementations are encouraged to diagnose it. Otherwise writes
out to stream unchanged. If the native Unicode API is used, the
function
flushes stream before writing out.
- Note 1: On POSIX and Windows,
streamreferring to a terminal means thatisatty(fileno(stream))andGetConsoleMode(_get_osfhandle(_fileno(stream)), ...)return nonzero. - Note 2: On Windows, the native Unicode API is
WriteConsoleW.
Throws: Any exception thrown by the call to vformat.
system_error if writing to the terminal or stream fails. May throw
bad_alloc.
Recommended practice: If invoking the native Unicode API requires transcoding,
implementations should substitute invalid code units with u+fffd replacement
character
per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion.
Templates
template<class... Args>
void print(wformat_string<Args...> fmt, Args&&... args);
Effects: Equivalent to:
print(stdout, fmt, std::forward<Args>(args)...);
template<class... Args>
void print(FILE* stream, wformat_string<Args...> fmt, Args&&... args);
Effects: If the ordinary literal encoding is UTF-8, UTF-16 or UTF-32, equivalent to:
vprint_unicode(stream, fmt.str, make_wformat_args(std::forward<Args>(args)...));
Otherwise, equivalent to:
vprint_nonunicode(stream, fmt.str, make_wformat_args(std::forward<Args>(args)...));
template<class... Args>
void println(wformat_string<Args...> fmt, Args&&... args);
Effects: Equivalent to:
println(stdout, fmt, std::forward<Args>(args)...);
template<class... Args>
void println(FILE* stream, wformat_string<Args...> fmt, Args&&... args);
Effects: Equivalent to:
print(stream, L"{}\n", format(fmt, std::forward<Args>(args)...));
void vprint_unicode(wstring_view fmt, wformat_args args);
Effects: Equivalent to:
vprint_unicode(stdout, fmt, args);
Technical Specification
namespace std
{
template <typename... Args>
void print(wformat_string<Args...> fmt, Args&&... args);
template <typename... Args>
void print(FILE* stream, wformat_string<Args...> fmt, Args&&... args);
template <typename... Args>
void println(wformat_string<Args...> fmt, Args&&... args);
template <typename... Args>
void println(FILE* stream, wformat_string<Args...> fmt, Args&&... args);
void vprint_unicode(wostream& os, wstring_view fmt, wformat_args args);
void vprint_nonunicode(wostream& os, wstring_view fmt, wformat_args args);
}
Implementation Experience
A proof of concept is done for MSVC.
Design Decisions
An alternative design would introduce distinct functions named wprint and
wprintln, aligning with the C naming style.
Rejected because:
- Consistency with
std::print/std::format - Reduced API surface complexity
Support for char8_t, char16_t, char32_t is outside scope.
Support for UTF-N character strings
References
Appendix A. Examples
WinAPI
int main(void)
{
std::wstring locale{};
locale.resize(LOCALE_NAME_MAX_LENGTH);
locale.resize(GetUserDefaultLocaleName(locale.data(), locale.size()) - 1);
std::println(L"The System Locale is {}", locale);
}
Unicode characters in paths
void PrintPath(std::filesystem::path path)
{
std::println(L"Path = {}", path);
}
Displaying emojis
void PrintPath(std::filesystem::path path)
{
std::println(L"Path = {}", path.wstring());
}
int main(void)
{
SetConsoleOutputCP(CP_UTF8);
std::wstring locale{};
locale.resize(LOCALE_NAME_MAX_LENGTH);
locale.resize(GetUserDefaultLocaleName(locale.data(),
static_cast<DWORD>(locale.size())) - 1);
std::println(L"The System Locale is {}", locale);
std::println(L"Pair: 🍋\x200D🟩");
static_assert(sizeof(L"🍋\x200D🟩") == 12);
PrintPath(std::filesystem::current_path());
}
Expected output:
The System Locale is en-GB Pair: 🍋🟩 Path = F:\source\P3673\P3673