Document Number: D3673R0
Date: 2026-05-23

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 introduced std::print and std::println function 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, accepting std::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

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 vformat is propagated without regard to the value of os.exceptions() and without turning on ios_base::badbit in the error state of os.

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, stream referring to a terminal means that isatty(fileno(stream)) and GetConsoleMode(_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:

  1. Consistency with std::print / std::format
  2. 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