[libc++][print] Adds ostream overloads. (#73262)
Finishes implementation of - P2093R14 Formatted output - P2539R4 Should the output of std::print to a terminal be synchronized with the underlying stream? Differential Revision: https://reviews.llvm.org/D156609
This commit is contained in:
@@ -344,7 +344,7 @@ Status
|
||||
--------------------------------------------------- -----------------
|
||||
``__cpp_lib_out_ptr`` *unimplemented*
|
||||
--------------------------------------------------- -----------------
|
||||
``__cpp_lib_print`` *unimplemented*
|
||||
``__cpp_lib_print`` ``202207L``
|
||||
--------------------------------------------------- -----------------
|
||||
``__cpp_lib_ranges_as_const`` *unimplemented*
|
||||
--------------------------------------------------- -----------------
|
||||
|
||||
@@ -28,6 +28,29 @@ The Standard allows implementations to automatically update the
|
||||
This offers a way for users to update the *remote time zone database* and
|
||||
give them full control over the process.
|
||||
|
||||
|
||||
`[ostream.formatted.print]/3 <http://eel.is/c++draft/ostream.formatted.print#3>`_ A terminal capable of displaying Unicode
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
The Standard specifies that the manner in which a stream is determined to refer
|
||||
to a terminal capable of displaying Unicode is implementation-defined. This is
|
||||
used for ``std::print`` and similar functions taking an ``ostream&`` argument.
|
||||
|
||||
Libc++ determines that a stream is Unicode-capable terminal by:
|
||||
|
||||
* First it determines whether the stream's ``rdbuf()`` has an underlying
|
||||
``FILE*``. This is ``true`` in the following cases:
|
||||
|
||||
* The stream is ``std::cout``, ``std::cerr``, or ``std::clog``.
|
||||
|
||||
* A ``std::basic_filebuf<CharT, Traits>`` derived from ``std::filebuf``.
|
||||
|
||||
* The way to determine whether this ``FILE*`` refers to a terminal capable of
|
||||
displaying Unicode is the same as specified for `void vprint_unicode(FILE*
|
||||
stream, string_view fmt, format_args args);
|
||||
<http://eel.is/c++draft/print.fun#7>`_. This function is used for other
|
||||
``std::print`` overloads that don't take an ``ostream&`` argument.
|
||||
|
||||
Listed in the index of implementation-defined behavior
|
||||
======================================================
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ What's New in Libc++ 18.0.0?
|
||||
|
||||
Implemented Papers
|
||||
------------------
|
||||
- P2093R14 Formatted output
|
||||
- P2539R4 Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?
|
||||
|
||||
- P2497R0 - Testing for success or failure of ``<charconv>`` functions
|
||||
- P2697R1 - Interfacing ``bitset`` with ``string_view``
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
|
||||
"`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
|
||||
"`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
|
||||
"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","|In Progress|"
|
||||
"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","","|Complete|","18.0"
|
||||
"`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
|
||||
"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
|
||||
"`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
|
||||
@@ -99,7 +99,7 @@
|
||||
"`P2167R3 <https://wg21.link/P2167R3>`__","LWG", "Improved Proposed Wording for LWG 2114", "November 2022","","",""
|
||||
"`P2396R1 <https://wg21.link/P2396R1>`__","LWG", "Concurrency TS 2 fixes ", "November 2022","","","|concurrency TS|"
|
||||
"`P2505R5 <https://wg21.link/P2505R5>`__","LWG", "Monadic Functions for ``std::expected``", "November 2022","|Complete|","17.0",""
|
||||
"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|In Progress|","","|format|"
|
||||
"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|Complete|","18.0","|format|"
|
||||
"`P2602R2 <https://wg21.link/P2602R2>`__","LWG", "Poison Pills are Too Toxic", "November 2022","","","|ranges|"
|
||||
"`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
|
||||
"","","","","","",""
|
||||
|
||||
|
@@ -5,11 +5,11 @@ Number,Name,Standard,Assignee,Status,First released version
|
||||
`P1868 <https://wg21.link/P1868>`_,"width: clarifying units of width and precision in std::format (Implements the unicode support.)","C++20",Mark de Wever,|Complete|,14.0
|
||||
`P2216 <https://wg21.link/P2216>`_,"std::format improvements","C++20",Mark de Wever,|Complete|,15.0
|
||||
`P2418 <https://wg21.link/P2418>`__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|,15.0
|
||||
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|In Progress|
|
||||
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|Complete|,"18.0"
|
||||
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","C++23","Mark de Wever","|Complete|",16.0
|
||||
"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|",15.0
|
||||
"`P2585R0 <https://wg21.link/P2585R0>`__","Improving default container formatting","C++23","Mark de Wever","|Complete|",17.0
|
||||
"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever","|In Progress|"
|
||||
"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever","|Complete|","18.0"
|
||||
"`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","C++23","Mark de Wever",""
|
||||
"`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","C++23","Mark de Wever","|Complete|",17.0
|
||||
"`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","C++23","Mark de Wever","|Complete|",17.0
|
||||
|
||||
|
@@ -49,4 +49,4 @@ Section,Description,Dependencies,Assignee,Status,First released version
|
||||
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output"
|
||||
`[print.fun] <https://wg21.link/print.fun>`__,"Output to ``stdout``",,Mark de Wever,|Complete|, 17.0
|
||||
`[print.fun] <https://wg21.link/print.fun>`__,"Output to ``FILE*``",,Mark de Wever,|Complete|, 17.0
|
||||
`[ostream.formatted.print] <https://wg21.link/ostream.formatted.print>`__,"Output to ``ostream``",,Mark de Wever
|
||||
`[ostream.formatted.print] <https://wg21.link/ostream.formatted.print>`__,"Output to ``ostream``",,Mark de Wever,|Complete|, 18.0
|
||||
|
||||
|
@@ -136,6 +136,12 @@
|
||||
# define _LIBCPP_AVAILABILITY_HAS_TZDB 1
|
||||
# define _LIBCPP_AVAILABILITY_TZDB
|
||||
|
||||
// This controls the availability of C++23 <print>, which
|
||||
// has a dependency on the built library (it needs access to
|
||||
// the underlying buffer types of std::cout, std::cerr, and std::clog.
|
||||
# define _LIBCPP_AVAILABILITY_HAS_PRINT 1
|
||||
# define _LIBCPP_AVAILABILITY_PRINT
|
||||
|
||||
// Enable additional explicit instantiations of iostreams components. This
|
||||
// reduces the number of weak definitions generated in programs that use
|
||||
// iostreams by providing a single strong definition in the shared library.
|
||||
@@ -258,6 +264,9 @@
|
||||
# define _LIBCPP_AVAILABILITY_HAS_TZDB 0
|
||||
# define _LIBCPP_AVAILABILITY_TZDB __attribute__((unavailable))
|
||||
|
||||
# define _LIBCPP_AVAILABILITY_HAS_PRINT 0
|
||||
# define _LIBCPP_AVAILABILITY_PRINT __attribute__((unavailable))
|
||||
|
||||
// clang-format off
|
||||
# if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 120000) || \
|
||||
(defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 150000) || \
|
||||
|
||||
@@ -280,6 +280,8 @@ private:
|
||||
|
||||
bool __read_mode();
|
||||
void __write_mode();
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
|
||||
};
|
||||
|
||||
template <class _CharT, class _Traits>
|
||||
|
||||
@@ -159,13 +159,24 @@ basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, cons
|
||||
template<class traits>
|
||||
basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, const char32_t*) = delete; // since C++20
|
||||
|
||||
// [ostream.formatted.print], print functions
|
||||
template<class... Args> // since C++23
|
||||
void print(ostream& os, format_string<Args...> fmt, Args&&... args);
|
||||
template<class... Args> // since C++23
|
||||
void println(ostream& os, format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
void vprint_unicode(ostream& os, string_view fmt, format_args args); // since C++23
|
||||
void vprint_nonunicode(ostream& os, string_view fmt, format_args args); // since C++23
|
||||
} // std
|
||||
|
||||
*/
|
||||
|
||||
#include <__assert> // all public C++ headers provide the assertion handler
|
||||
#include <__availability>
|
||||
#include <__config>
|
||||
#include <__exception/operations.h>
|
||||
#include <__format/format_args.h>
|
||||
#include <__format/format_functions.h>
|
||||
#include <__fwd/ostream.h>
|
||||
#include <__memory/shared_ptr.h>
|
||||
#include <__memory/unique_ptr.h>
|
||||
@@ -176,10 +187,13 @@ basic_ostream<wchar_t, traits>& operator<<(basic_ostream<wchar_t, traits>&, cons
|
||||
#include <__type_traits/void_t.h>
|
||||
#include <__utility/declval.h>
|
||||
#include <bitset>
|
||||
#include <cstdio>
|
||||
#include <ios>
|
||||
#include <locale>
|
||||
#include <new>
|
||||
#include <print>
|
||||
#include <streambuf>
|
||||
#include <string_view>
|
||||
#include <version>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
@@ -1005,6 +1019,151 @@ extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_ostream<char>;
|
||||
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS basic_ostream<wchar_t>;
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER >= 23
|
||||
|
||||
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
|
||||
_LIBCPP_HIDE_FROM_ABI inline void
|
||||
__vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
|
||||
// [ostream.formatted.print]/3
|
||||
// Effects: Behaves as a formatted output function
|
||||
// ([ostream.formatted.reqmts]) 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 initializes an automatic
|
||||
// variable via
|
||||
// string out = vformat(os.getloc(), fmt, args);
|
||||
|
||||
ostream::sentry __s(__os);
|
||||
if (__s) {
|
||||
string __o = std::vformat(__os.getloc(), __fmt, __args);
|
||||
if (__write_nl)
|
||||
__o += '\n';
|
||||
|
||||
const char* __str = __o.data();
|
||||
size_t __len = __o.size();
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
try {
|
||||
# endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
typedef ostreambuf_iterator<char> _Ip;
|
||||
if (std::__pad_and_output(
|
||||
_Ip(__os),
|
||||
__str,
|
||||
(__os.flags() & ios_base::adjustfield) == ios_base::left ? __str + __len : __str,
|
||||
__str + __len,
|
||||
__os,
|
||||
__os.fill())
|
||||
.failed())
|
||||
__os.setstate(ios_base::badbit | ios_base::failbit);
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
} catch (...) {
|
||||
__os.__set_badbit_and_consider_rethrow();
|
||||
}
|
||||
# endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
}
|
||||
|
||||
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
|
||||
_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) {
|
||||
std::__vprint_nonunicode(__os, __fmt, __args, false);
|
||||
}
|
||||
|
||||
// Returns the FILE* associated with the __os.
|
||||
// Returns a nullptr when no FILE* is associated with __os.
|
||||
// This function is in the dylib since the type of the buffer associated
|
||||
// with std::cout, std::cerr, and std::clog is only known in the dylib.
|
||||
//
|
||||
// This function implements part of the implementation-defined behavior
|
||||
// of [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to
|
||||
// a terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API;
|
||||
// Whether the returned FILE* is "a terminal capable of displaying Unicode"
|
||||
// is determined in the same way as the print(FILE*, ...) overloads.
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os);
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_UNICODE
|
||||
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
|
||||
__vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
|
||||
FILE* __file = std::__get_ostream_file(__os);
|
||||
if (!__file || !__print::__is_terminal(__file))
|
||||
return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
|
||||
|
||||
// [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to a
|
||||
// terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API; if out contains invalid code units, the behavior is
|
||||
// undefined and implementations are encouraged to diagnose it. If the
|
||||
// native Unicode API is used, the function flushes os before writing out.
|
||||
//
|
||||
// This is the path for the native API, start with flushing.
|
||||
__os.flush();
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
try {
|
||||
# endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
ostream::sentry __s(__os);
|
||||
if (__s) {
|
||||
# ifndef _LIBCPP_WIN32API
|
||||
__print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
|
||||
# elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
|
||||
__print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
|
||||
# else
|
||||
# error "Windows builds with wchar_t disabled are not supported."
|
||||
# endif
|
||||
}
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
} catch (...) {
|
||||
__os.__set_badbit_and_consider_rethrow();
|
||||
}
|
||||
# endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI inline void
|
||||
vprint_unicode(ostream& __os, string_view __fmt, format_args __args) {
|
||||
std::__vprint_unicode(__os, __fmt, __args, false);
|
||||
}
|
||||
# endif // _LIBCPP_HAS_NO_UNICODE
|
||||
|
||||
template <class... _Args>
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
|
||||
print(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args) {
|
||||
# ifndef _LIBCPP_HAS_NO_UNICODE
|
||||
if constexpr (__print::__use_unicode)
|
||||
std::__vprint_unicode(__os, __fmt.get(), std::make_format_args(__args...), false);
|
||||
else
|
||||
std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), false);
|
||||
# else // _LIBCPP_HAS_NO_UNICODE
|
||||
std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), false);
|
||||
# endif // _LIBCPP_HAS_NO_UNICODE
|
||||
}
|
||||
|
||||
template <class... _Args>
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_HIDE_FROM_ABI void
|
||||
println(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args) {
|
||||
# ifndef _LIBCPP_HAS_NO_UNICODE
|
||||
// Note the wording in the Standard is inefficient. The output of
|
||||
// std::format is a std::string which is then copied. This solution
|
||||
// just appends a newline at the end of the output.
|
||||
if constexpr (__print::__use_unicode)
|
||||
std::__vprint_unicode(__os, __fmt.get(), std::make_format_args(__args...), true);
|
||||
else
|
||||
std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), true);
|
||||
# else // _LIBCPP_HAS_NO_UNICODE
|
||||
std::__vprint_nonunicode(__os, __fmt.get(), std::make_format_args(__args...), true);
|
||||
# endif // _LIBCPP_HAS_NO_UNICODE
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 23
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace std {
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _LIBCPP_WIN32API
|
||||
_LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
|
||||
|
||||
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
@@ -75,7 +75,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
|
||||
_LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
|
||||
# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // _LIBCPP_WIN32API
|
||||
|
||||
#if _LIBCPP_STD_VER >= 23
|
||||
|
||||
@@ -198,7 +198,11 @@ inline constexpr bool __use_unicode = true;
|
||||
# endif
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI inline bool __is_terminal(FILE* __stream) {
|
||||
# ifdef _WIN32
|
||||
// The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
|
||||
// the behavior in the test. This is not part of the public API.
|
||||
# ifdef _LIBCPP_TESTING_PRINT_IS_TERMINAL
|
||||
return _LIBCPP_TESTING_PRINT_IS_TERMINAL(__stream);
|
||||
# elif defined(_LIBCPP_WIN32API)
|
||||
return std::__is_windows_terminal(__stream);
|
||||
# elif __has_include(<unistd.h>)
|
||||
return isatty(fileno(__stream));
|
||||
@@ -271,7 +275,7 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
|
||||
// the behavior in the test. This is not part of the public API.
|
||||
# ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
|
||||
_LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
|
||||
# elif defined(_WIN32)
|
||||
# elif defined(_LIBCPP_WIN32API)
|
||||
std::__write_to_windows_console(__stream, __view);
|
||||
# else
|
||||
std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
|
||||
@@ -309,7 +313,7 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
|
||||
// so there the call can be forwarded to the non_unicode API. On
|
||||
// Windows there is a different API. This API requires transcoding.
|
||||
|
||||
# ifndef _WIN32
|
||||
# ifndef _LIBCPP_WIN32API
|
||||
__print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
|
||||
# elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
|
||||
__print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
|
||||
|
||||
@@ -454,7 +454,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
|
||||
# undef __cpp_lib_optional
|
||||
# define __cpp_lib_optional 202110L
|
||||
// # define __cpp_lib_out_ptr 202106L
|
||||
// # define __cpp_lib_print 202207L
|
||||
# define __cpp_lib_print 202207L
|
||||
// # define __cpp_lib_ranges_as_const 202207L
|
||||
# define __cpp_lib_ranges_as_rvalue 202207L
|
||||
// # define __cpp_lib_ranges_chunk 202202L
|
||||
|
||||
@@ -1465,6 +1465,7 @@
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
|
||||
|
||||
@@ -506,6 +506,7 @@
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
|
||||
@@ -506,6 +506,7 @@
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
|
||||
|
||||
@@ -1465,6 +1465,7 @@
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '__ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
|
||||
|
||||
@@ -1160,6 +1160,7 @@
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
|
||||
@@ -2025,4 +2026,4 @@
|
||||
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
|
||||
@@ -1158,6 +1158,7 @@
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
|
||||
|
||||
@@ -1130,6 +1130,7 @@
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
|
||||
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
|
||||
|
||||
@@ -27,13 +27,14 @@ export namespace std {
|
||||
# endif
|
||||
using std::operator<<;
|
||||
|
||||
# if 0
|
||||
# if _LIBCPP_STD_VER >= 23
|
||||
// [ostream.formatted.print], print functions
|
||||
using std::print;
|
||||
using std::println;
|
||||
|
||||
using std::vprint_nonunicode;
|
||||
using std::vprint_unicode;
|
||||
# endif
|
||||
# endif // _LIBCPP_STD_VER >= 23
|
||||
|
||||
#endif // _LIBCPP_HAS_NO_LOCALIZATION
|
||||
} // namespace std
|
||||
|
||||
@@ -89,6 +89,7 @@ if (LIBCXX_ENABLE_LOCALIZATION)
|
||||
ios.instantiations.cpp
|
||||
iostream.cpp
|
||||
locale.cpp
|
||||
ostream.cpp
|
||||
regex.cpp
|
||||
strstream.cpp
|
||||
)
|
||||
|
||||
42
libcxx/src/ostream.cpp
Normal file
42
libcxx/src/ostream.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <__availability>
|
||||
#include <__config>
|
||||
#ifndef _LIBCPP_HAS_NO_FILESYSTEM
|
||||
# include <fstream>
|
||||
#endif
|
||||
#include <ostream>
|
||||
|
||||
#include "std_stream.h"
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_AVAILABILITY_PRINT _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os) {
|
||||
// dynamic_cast requires RTTI, this only affects users whose vendor builds
|
||||
// the dylib with RTTI disabled. It does not affect users who build with RTTI
|
||||
// disabled but use a dylib where the RTTI is enabled.
|
||||
//
|
||||
// Returning a nullptr means the stream is not considered a terminal and the
|
||||
// special terminal handling is not done. The terminal handling is mainly of
|
||||
// importance on Windows.
|
||||
#ifndef _LIBCPP_HAS_NO_RTTI
|
||||
auto* __rdbuf = __os.rdbuf();
|
||||
# ifndef _LIBCPP_HAS_NO_FILESYSTEM
|
||||
if (auto* __buffer = dynamic_cast<filebuf*>(__rdbuf))
|
||||
return __buffer->__file_;
|
||||
# endif
|
||||
|
||||
if (auto* __buffer = dynamic_cast<__stdoutbuf<char>*>(__rdbuf))
|
||||
return __buffer->__file_;
|
||||
#endif // _LIBCPP_HAS_NO_RTTI
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
@@ -269,6 +269,8 @@ private:
|
||||
|
||||
__stdoutbuf(const __stdoutbuf&);
|
||||
__stdoutbuf& operator=(const __stdoutbuf&);
|
||||
|
||||
_LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
|
||||
};
|
||||
|
||||
template <class _CharT>
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: no-filesystem, no-rtti
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
// XFAIL: availability-print-missing
|
||||
|
||||
// Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_IS_TERMINAL
|
||||
// XFAIL: clang-modules-build
|
||||
// <ostream>
|
||||
|
||||
// Tests the implementation of
|
||||
// void __vprint_unicode(ostream& os, string_view fmt,
|
||||
// format_args args, bool write_nl);
|
||||
|
||||
// In the library when std::cout is redirected to a file it is no longer
|
||||
// considered a terminal and the special terminal handling is no longer
|
||||
// executed. By testing this function we can "force" emulate a terminal.
|
||||
// Note write_nl is tested by the public API.
|
||||
|
||||
#include <cstdio>
|
||||
bool is_terminal(FILE*);
|
||||
#define _LIBCPP_TESTING_PRINT_IS_TERMINAL ::is_terminal
|
||||
|
||||
#include "filesystem_test_helper.h"
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
scoped_test_env env;
|
||||
std::string filename = env.create_file("output.txt");
|
||||
|
||||
int is_terminal_calls = 0;
|
||||
bool is_terminal_result = false;
|
||||
bool is_terminal(FILE*) {
|
||||
++is_terminal_calls;
|
||||
return is_terminal_result;
|
||||
}
|
||||
|
||||
// When the stream is not a file stream, cout, clog, or cerr the stream does not
|
||||
// considered to be backed by a FILE*. Then the stream should never check
|
||||
// whether it's a terminal.
|
||||
static void test_is_terminal_not_a_file_stream() {
|
||||
is_terminal_calls = 0;
|
||||
is_terminal_result = false;
|
||||
{
|
||||
std::stringstream stream;
|
||||
std::print(stream, "test");
|
||||
}
|
||||
{
|
||||
std::ostringstream stream;
|
||||
std::print(stream, "test");
|
||||
}
|
||||
assert(is_terminal_calls == 0);
|
||||
}
|
||||
|
||||
// When the stream is a file stream, its FILE* may be a terminal. Validate this
|
||||
// is tested.
|
||||
static void test_is_terminal_file_stream() {
|
||||
is_terminal_calls = 0;
|
||||
is_terminal_result = false;
|
||||
{
|
||||
std::fstream stream(filename);
|
||||
assert(stream.is_open());
|
||||
assert(stream.good());
|
||||
std::print(stream, "test");
|
||||
assert(is_terminal_calls == 1);
|
||||
}
|
||||
{
|
||||
std::ofstream stream(filename);
|
||||
assert(stream.is_open());
|
||||
assert(stream.good());
|
||||
std::print(stream, "test");
|
||||
assert(is_terminal_calls == 2);
|
||||
}
|
||||
}
|
||||
|
||||
// The same as above, but this time test for derived classes.
|
||||
static void test_is_terminal_rdbuf_derived_from_filebuf() {
|
||||
struct my_filebuf : public std::filebuf {};
|
||||
|
||||
is_terminal_calls = 0;
|
||||
is_terminal_result = false;
|
||||
|
||||
my_filebuf buf;
|
||||
buf.open(filename, std::ios_base::out);
|
||||
assert(buf.is_open());
|
||||
|
||||
std::ostream stream(&buf);
|
||||
std::print(stream, "test");
|
||||
assert(is_terminal_calls == 1);
|
||||
}
|
||||
|
||||
// When the stream is cout, clog, or cerr, its FILE* may be a terminal. Validate
|
||||
// this is tested.
|
||||
static void test_is_terminal_std_cout_cerr_clog() {
|
||||
is_terminal_calls = 0;
|
||||
is_terminal_result = false;
|
||||
{
|
||||
std::print(std::cout, "test");
|
||||
assert(is_terminal_calls == 1);
|
||||
}
|
||||
{
|
||||
std::print(std::cerr, "test");
|
||||
assert(is_terminal_calls == 2);
|
||||
}
|
||||
{
|
||||
std::print(std::clog, "test");
|
||||
assert(is_terminal_calls == 3);
|
||||
}
|
||||
}
|
||||
|
||||
// When the stream's FILE* is a terminal the contents need to be flushed before
|
||||
// writing to the stream.
|
||||
static void test_is_terminal_is_flushed() {
|
||||
struct sync_counter : public std::filebuf {
|
||||
sync_counter() {
|
||||
open(filename, std::ios_base::out);
|
||||
assert(is_open());
|
||||
}
|
||||
int sync_calls = 0;
|
||||
|
||||
protected:
|
||||
int virtual sync() {
|
||||
++sync_calls;
|
||||
return std::basic_streambuf<char>::sync();
|
||||
}
|
||||
};
|
||||
|
||||
is_terminal_result = false;
|
||||
|
||||
sync_counter buf;
|
||||
std::ostream stream(&buf);
|
||||
|
||||
// Not a terminal sync is not called.
|
||||
std::print(stream, "");
|
||||
assert(buf.sync_calls == 0);
|
||||
|
||||
// A terminal sync is called.
|
||||
is_terminal_result = true;
|
||||
std::print(stream, "");
|
||||
assert(buf.sync_calls == 1); // only called from the destructor of the sentry
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test_is_terminal_not_a_file_stream();
|
||||
test_is_terminal_file_stream();
|
||||
test_is_terminal_rdbuf_derived_from_filebuf();
|
||||
test_is_terminal_std_cout_cerr_clog();
|
||||
|
||||
test_is_terminal_is_flushed();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -576,26 +576,29 @@ optional typeinfo
|
||||
optional utility
|
||||
optional variant
|
||||
optional version
|
||||
ostream array
|
||||
ostream atomic
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream concepts
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstdio
|
||||
ostream cstdlib
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream ios
|
||||
ostream iosfwd
|
||||
ostream iterator
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream type_traits
|
||||
ostream typeinfo
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -581,26 +581,29 @@ optional typeinfo
|
||||
optional utility
|
||||
optional variant
|
||||
optional version
|
||||
ostream array
|
||||
ostream atomic
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream concepts
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstdio
|
||||
ostream cstdlib
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream ios
|
||||
ostream iosfwd
|
||||
ostream iterator
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream type_traits
|
||||
ostream typeinfo
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -583,26 +583,29 @@ optional typeinfo
|
||||
optional utility
|
||||
optional variant
|
||||
optional version
|
||||
ostream array
|
||||
ostream atomic
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream concepts
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstdio
|
||||
ostream cstdlib
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream ios
|
||||
ostream iosfwd
|
||||
ostream iterator
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream type_traits
|
||||
ostream typeinfo
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -583,26 +583,29 @@ optional typeinfo
|
||||
optional utility
|
||||
optional variant
|
||||
optional version
|
||||
ostream array
|
||||
ostream atomic
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream concepts
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstdio
|
||||
ostream cstdlib
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream ios
|
||||
ostream iosfwd
|
||||
ostream iterator
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream type_traits
|
||||
ostream typeinfo
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -588,26 +588,29 @@ optional typeinfo
|
||||
optional utility
|
||||
optional variant
|
||||
optional version
|
||||
ostream array
|
||||
ostream atomic
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream concepts
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstdio
|
||||
ostream cstdlib
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream ios
|
||||
ostream iosfwd
|
||||
ostream iterator
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream type_traits
|
||||
ostream typeinfo
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -398,19 +398,23 @@ optional initializer_list
|
||||
optional limits
|
||||
optional new
|
||||
optional version
|
||||
ostream array
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream cstdio
|
||||
ostream ios
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream typeinfo
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
@@ -398,19 +398,23 @@ optional initializer_list
|
||||
optional limits
|
||||
optional new
|
||||
optional version
|
||||
ostream array
|
||||
ostream bitset
|
||||
ostream cerrno
|
||||
ostream cmath
|
||||
ostream cstddef
|
||||
ostream cstdint
|
||||
ostream cstring
|
||||
ostream initializer_list
|
||||
ostream cstdio
|
||||
ostream ios
|
||||
ostream limits
|
||||
ostream locale
|
||||
ostream new
|
||||
ostream optional
|
||||
ostream print
|
||||
ostream stdexcept
|
||||
ostream streambuf
|
||||
ostream string
|
||||
ostream typeinfo
|
||||
ostream string_view
|
||||
ostream tuple
|
||||
ostream version
|
||||
print array
|
||||
print cerrno
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,193 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO PRINT Investigate see https://reviews.llvm.org/D156585
|
||||
// UNSUPPORTED: no-filesystem
|
||||
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
// XFAIL: availability-print-missing
|
||||
|
||||
// <ostream>
|
||||
|
||||
// template<class... Args>
|
||||
// void print(ostream& os, format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
// [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to
|
||||
// a terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API;
|
||||
// This is tested in
|
||||
// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "print_tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
std::stringstream sstr;
|
||||
std::print(sstr, fmt, std::forward<Args>(args)...);
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
|
||||
// After P2216 most exceptions thrown by std::format become ill-formed.
|
||||
// Therefore this tests does nothing.
|
||||
// A basic ill-formed test is done in format.verify.cpp
|
||||
// The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
|
||||
};
|
||||
// [ostream.formatted.print]/3.2
|
||||
// ...
|
||||
// After constructing a sentry object, the function initializes an automatic variable via
|
||||
// string out = vformat(os.getloc(), fmt, args);
|
||||
// This means if both
|
||||
// - creating a sentry fails
|
||||
// - the formatting fails
|
||||
// the first one "wins" and the format_error is not thrown.
|
||||
static void test_sentry_failure() {
|
||||
// In order for the creation of a sentry to fail a tied stream's
|
||||
// sync operation should fail.
|
||||
struct sync_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual sync() { return -1; }
|
||||
};
|
||||
sync_failure buf_tied;
|
||||
std::ostream os_tied(&buf_tied);
|
||||
os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
std::stringstream os;
|
||||
os.tie(&os_tied);
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "valid"));
|
||||
os_tied.clear();
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "throws exception at run-time {0:{0}}", -10));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.setstate(std::stringstream::failbit);
|
||||
std::print(os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", -10);
|
||||
}
|
||||
|
||||
// [ostream.formatted.print]/3.2
|
||||
// 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.
|
||||
// Most invalid format strings are checked at compile-time. An invalid
|
||||
// value for the width can only be tested run-time.
|
||||
static void test_format_exception() {
|
||||
std::stringstream sstr;
|
||||
assert(sstr.good());
|
||||
|
||||
TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::goodbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::print(sstr, "no output {0:{0}}", -10));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
}
|
||||
|
||||
static void test_write_failure() {
|
||||
// Stream that fails to write a single character.
|
||||
struct overflow_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual overflow(int) { return std::char_traits<char>::eof(); }
|
||||
};
|
||||
overflow_failure buf;
|
||||
std::ostream os(&buf);
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::print(os, "valid"));
|
||||
os.clear();
|
||||
// When the parser would directly write to the output instead of
|
||||
// formatting first it would fail writing the first character 't' of
|
||||
// the string and result in a std::ios_base::failure exception.
|
||||
TEST_THROWS_TYPE(std::format_error, std::print(os, "throws exception at run-time {0:{0}}", -10));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.clear();
|
||||
std::print(os, "valid");
|
||||
assert(os.fail());
|
||||
}
|
||||
|
||||
static void test_stream_formatting() {
|
||||
std::stringstream sstr;
|
||||
auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
sstr.str("");
|
||||
std::print(sstr, fmt, std::forward<Args>(args)...);
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
test("hello", "{}", "hello");
|
||||
|
||||
sstr.width(10);
|
||||
test(" hello", "{}", "hello");
|
||||
|
||||
sstr.fill('+');
|
||||
|
||||
sstr.width(10);
|
||||
test("+++++hello", "{}", "hello");
|
||||
|
||||
// *** Test embedded NUL character ***
|
||||
using namespace std::literals;
|
||||
sstr.width(15);
|
||||
test("++++hello\0world"sv, "hello{}{}", '\0', "world");
|
||||
|
||||
// *** Test Unicode ***
|
||||
// Streams count code units not code points
|
||||
// 2-byte code points
|
||||
sstr.width(5);
|
||||
test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
|
||||
sstr.width(5);
|
||||
test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
|
||||
|
||||
// 3-byte code points
|
||||
sstr.width(5);
|
||||
test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
|
||||
sstr.width(5);
|
||||
test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
|
||||
|
||||
// 4-byte code points
|
||||
sstr.width(5);
|
||||
test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
|
||||
sstr.width(5);
|
||||
test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
print_tests(test_file, test_exception);
|
||||
|
||||
test_sentry_failure();
|
||||
test_format_exception();
|
||||
test_write_failure();
|
||||
test_stream_formatting();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
|
||||
#define TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
|
||||
|
||||
template <class TestFunction, class ExceptionTest>
|
||||
void print_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
// *** Test escaping ***
|
||||
|
||||
check("{", "{{");
|
||||
check("{:^}", "{{:^}}");
|
||||
check("{: ^}", "{{:{}^}}", ' ');
|
||||
check("{:{}^}", "{{:{{}}^}}");
|
||||
check("{:{ }^}", "{{:{{{}}}^}}", ' ');
|
||||
|
||||
// *** Test argument ID ***
|
||||
check("hello false true", "hello {0:} {1:}", false, true);
|
||||
check("hello true false", "hello {1:} {0:}", false, true);
|
||||
|
||||
// *** Test many arguments ***
|
||||
check(
|
||||
"1234567890\t1234567890",
|
||||
"{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}",
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
0);
|
||||
|
||||
// *** Test embedded NUL character ***
|
||||
using namespace std::literals;
|
||||
check("hello\0world"sv, "hello{}{}", '\0', "world");
|
||||
check("hello\0world"sv, "hello\0{}"sv, "world");
|
||||
check("hello\0world"sv, "hello{}", "\0world"sv);
|
||||
|
||||
// *** Test Unicode ***
|
||||
// 2-byte code points
|
||||
check("\u00a1"sv, "{}"sv, "\u00a1"); // INVERTED EXCLAMATION MARK
|
||||
check("\u07ff"sv, "{:}"sv, "\u07ff"); // NKO TAMAN SIGN
|
||||
|
||||
// 3-byte code points
|
||||
check("\u0800"sv, "{}"sv, "\u0800"); // SAMARITAN LETTER ALAF
|
||||
check("\ufffd"sv, "{}"sv, "\ufffd"); // REPLACEMENT CHARACTER
|
||||
|
||||
// 4-byte code points
|
||||
check("\U00010000"sv, "{}"sv, "\U00010000"); // LINEAR B SYLLABLE B008 A
|
||||
check("\U0010FFFF"sv, "{}"sv, "\U0010FFFF"); // Undefined Character
|
||||
|
||||
// *** Test invalid format strings ***
|
||||
check_exception("The format string terminates at a '{'", "{");
|
||||
check_exception("The replacement field misses a terminating '}'", "{:", 42);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", "}");
|
||||
check_exception("The format string contains an invalid escape sequence", "{:}-}", 42);
|
||||
|
||||
check_exception("The format string contains an invalid escape sequence", "} ");
|
||||
check_exception("The argument index starts with an invalid character", "{-", 42);
|
||||
check_exception("The argument index value is too large for the number of arguments supplied", "hello {}");
|
||||
check_exception("The argument index value is too large for the number of arguments supplied", "hello {0}");
|
||||
check_exception("The argument index value is too large for the number of arguments supplied", "hello {1}", 42);
|
||||
}
|
||||
|
||||
#endif // TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_OUTPUT_STREAMS_OSTREAM_FORMATTED_OSTREAM_FORMATTED_PRINT_PRINT_TESTS_H
|
||||
@@ -0,0 +1,63 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO PRINT Investigate see https://reviews.llvm.org/D156585
|
||||
// UNSUPPORTED: no-filesystem
|
||||
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
// XFAIL: availability-print-missing
|
||||
|
||||
// <ostream>
|
||||
|
||||
// template<class... Args>
|
||||
// void println(ostream& os, format_string<Args...> fmt, Args&&... args);
|
||||
|
||||
// [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to
|
||||
// a terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API;
|
||||
// This is tested in
|
||||
// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "print_tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test_file = []<class... Args>(std::string_view e, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
std::string expected = std::string{e} + '\n';
|
||||
|
||||
std::stringstream sstr;
|
||||
std::println(sstr, fmt, std::forward<Args>(args)...);
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
|
||||
// After P2216 most exceptions thrown by std::format become ill-formed.
|
||||
// Therefore this tests does nothing.
|
||||
// A basic ill-formed test is done in format.verify.cpp
|
||||
// The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
print_tests(test_file, test_exception);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO PRINT Investigate see https://reviews.llvm.org/D156585
|
||||
// UNSUPPORTED: no-filesystem
|
||||
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
// XFAIL: availability-print-missing
|
||||
|
||||
// <ostream>
|
||||
|
||||
// void vprint_nonunicode(ostream& os, string_view fmt, format_args args);
|
||||
|
||||
// [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to
|
||||
// a terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API;
|
||||
// This is tested in
|
||||
// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "print_tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
std::stringstream sstr;
|
||||
std::vprint_nonunicode(sstr, fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
auto test_exception = []< class... Args>([[maybe_unused]] std::string_view what,
|
||||
[[maybe_unused]] std::string_view fmt,
|
||||
[[maybe_unused]] Args&&... args) {
|
||||
TEST_VALIDATE_EXCEPTION(
|
||||
std::format_error,
|
||||
[&]([[maybe_unused]] const std::format_error& e) {
|
||||
TEST_LIBCPP_REQUIRE(
|
||||
e.what() == what,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
|
||||
},
|
||||
[&] {
|
||||
std::stringstream sstr;
|
||||
std::vprint_nonunicode(sstr, fmt, std::make_format_args(args...));
|
||||
}());
|
||||
};
|
||||
|
||||
// [ostream.formatted.print]/3.2
|
||||
// ...
|
||||
// After constructing a sentry object, the function initializes an automatic variable via
|
||||
// string out = vformat(os.getloc(), fmt, args);
|
||||
// This means if both
|
||||
// - creating a sentry fails
|
||||
// - the formatting fails
|
||||
// the first one "wins" and the format_error is not thrown.
|
||||
static void test_sentry_failure() {
|
||||
// In order for the creation of a sentry to fail a tied stream's
|
||||
// sync operation should fail.
|
||||
struct sync_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual sync() { return -1; }
|
||||
};
|
||||
sync_failure buf_tied;
|
||||
std::ostream os_tied(&buf_tied);
|
||||
os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
std::stringstream os;
|
||||
os.tie(&os_tied);
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_nonunicode(os, "valid", std::make_format_args()));
|
||||
os_tied.clear();
|
||||
[[maybe_unused]] int arg = -10;
|
||||
TEST_THROWS_TYPE(std::ios_base::failure,
|
||||
std::vprint_nonunicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.setstate(std::stringstream::failbit);
|
||||
std::vprint_nonunicode(
|
||||
os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", std::make_format_args(arg));
|
||||
}
|
||||
|
||||
// [ostream.formatted.print]/3.2
|
||||
// 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.
|
||||
// Most invalid format strings are checked at compile-time. An invalid
|
||||
// value for the width can only be tested run-time.
|
||||
static void test_format_exception() {
|
||||
std::stringstream sstr;
|
||||
assert(sstr.good());
|
||||
|
||||
[[maybe_unused]] int arg = -10;
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::goodbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_nonunicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
}
|
||||
|
||||
static void test_write_failure() {
|
||||
// Stream that fails to write a single character.
|
||||
struct overflow_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual overflow(int) { return std::char_traits<char>::eof(); }
|
||||
};
|
||||
overflow_failure buf;
|
||||
std::ostream os(&buf);
|
||||
[[maybe_unused]] int arg = -10;
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_nonunicode(os, "valid", std::make_format_args()));
|
||||
os.clear();
|
||||
// When the parser would directly write to the output instead of
|
||||
// formatting first it would fail writing the first character 't' of
|
||||
// the string and result in a std::ios_base::failure exception.
|
||||
TEST_THROWS_TYPE(std::format_error,
|
||||
std::vprint_nonunicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.clear();
|
||||
std::vprint_nonunicode(os, "valid", std::make_format_args());
|
||||
assert(os.fail());
|
||||
}
|
||||
|
||||
static void test_stream_formatting() {
|
||||
std::stringstream sstr;
|
||||
auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
sstr.str("");
|
||||
std::vprint_nonunicode(sstr, fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
test("hello", "{}", "hello");
|
||||
|
||||
sstr.width(10);
|
||||
test(" hello", "{}", "hello");
|
||||
|
||||
sstr.fill('+');
|
||||
|
||||
sstr.width(10);
|
||||
test("+++++hello", "{}", "hello");
|
||||
|
||||
// *** Test embedded NUL character ***
|
||||
using namespace std::literals;
|
||||
sstr.width(15);
|
||||
test("++++hello\0world"sv, "hello{}{}", '\0', "world");
|
||||
|
||||
// *** Test Unicode ***
|
||||
// Streams count code units not code points
|
||||
// 2-byte code points
|
||||
sstr.width(5);
|
||||
test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
|
||||
sstr.width(5);
|
||||
test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
|
||||
|
||||
// 3-byte code points
|
||||
sstr.width(5);
|
||||
test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
|
||||
sstr.width(5);
|
||||
test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
|
||||
|
||||
// 4-byte code points
|
||||
sstr.width(5);
|
||||
test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
|
||||
sstr.width(5);
|
||||
test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
print_tests(test_file, test_exception);
|
||||
|
||||
test_sentry_failure();
|
||||
test_format_exception();
|
||||
test_write_failure();
|
||||
test_stream_formatting();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO PRINT Investigate see https://reviews.llvm.org/D156585
|
||||
// UNSUPPORTED: no-filesystem
|
||||
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
// XFAIL: availability-print-missing
|
||||
|
||||
// <ostream>
|
||||
|
||||
// void vprint_unicode(ostream& os, string_view fmt, format_args args);
|
||||
// [ostream.formatted.print]/3
|
||||
// If the function is vprint_unicode and os is a stream that refers to
|
||||
// a terminal capable of displaying Unicode which is determined in an
|
||||
// implementation-defined manner, writes out to the terminal using the
|
||||
// native Unicode API;
|
||||
// This is tested in
|
||||
// test/libcxx/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "print_tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test_file = []<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
std::stringstream sstr;
|
||||
std::vprint_unicode(sstr, fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
auto test_exception = []< class... Args>([[maybe_unused]] std::string_view what,
|
||||
[[maybe_unused]] std::string_view fmt,
|
||||
[[maybe_unused]] Args&&... args) {
|
||||
TEST_VALIDATE_EXCEPTION(
|
||||
std::format_error,
|
||||
[&]([[maybe_unused]] const std::format_error& e) {
|
||||
TEST_LIBCPP_REQUIRE(
|
||||
e.what() == what,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
|
||||
},
|
||||
[&] {
|
||||
std::stringstream sstr;
|
||||
std::vprint_unicode(sstr, fmt, std::make_format_args(args...));
|
||||
}());
|
||||
};
|
||||
|
||||
// [ostream.formatted.print]/3.2
|
||||
// ...
|
||||
// After constructing a sentry object, the function initializes an automatic variable via
|
||||
// string out = vformat(os.getloc(), fmt, args);
|
||||
// This means if both
|
||||
// - creating a sentry fails
|
||||
// - the formatting fails
|
||||
// the first one "wins" and the format_error is not thrown.
|
||||
static void test_sentry_failure() {
|
||||
// In order for the creation of a sentry to fail a tied stream's
|
||||
// sync operation should fail.
|
||||
struct sync_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual sync() { return -1; }
|
||||
};
|
||||
sync_failure buf_tied;
|
||||
std::ostream os_tied(&buf_tied);
|
||||
os_tied.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
std::stringstream os;
|
||||
os.tie(&os_tied);
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
[[maybe_unused]] int arg = -10;
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_unicode(os, "valid", std::make_format_args()));
|
||||
os_tied.clear();
|
||||
TEST_THROWS_TYPE(std::ios_base::failure,
|
||||
std::vprint_unicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.setstate(std::stringstream::failbit);
|
||||
std::vprint_unicode(
|
||||
os, "not called when the os.good() is false, so no exception is thrown {0:{0}}", std::make_format_args(arg));
|
||||
}
|
||||
|
||||
// [ostream.formatted.print]/3.2
|
||||
// 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.
|
||||
// Most invalid format strings are checked at compile-time. An invalid
|
||||
// value for the width can only be tested run-time.
|
||||
static void test_format_exception() {
|
||||
std::stringstream sstr;
|
||||
assert(sstr.good());
|
||||
|
||||
[[maybe_unused]] int arg = -10;
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::goodbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
|
||||
sstr.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
TEST_THROWS_TYPE(std::format_error, std::vprint_unicode(sstr, "no output {0:{0}}", std::make_format_args(arg)));
|
||||
assert(sstr.good());
|
||||
assert(sstr.str().empty());
|
||||
}
|
||||
|
||||
static void test_write_failure() {
|
||||
// Stream that fails to write a single character.
|
||||
struct overflow_failure : public std::basic_streambuf<char> {
|
||||
protected:
|
||||
int virtual overflow(int) { return std::char_traits<char>::eof(); }
|
||||
};
|
||||
overflow_failure buf;
|
||||
std::ostream os(&buf);
|
||||
os.exceptions(std::stringstream::failbit | std::stringstream::badbit | std::stringstream::eofbit);
|
||||
|
||||
TEST_THROWS_TYPE(std::ios_base::failure, std::vprint_unicode(os, "valid", std::make_format_args()));
|
||||
os.clear();
|
||||
// When the parser would directly write to the output instead of
|
||||
// formatting first it would fail writing the first character 't' of
|
||||
// the string and result in a std::ios_base::failure exception.
|
||||
[[maybe_unused]] int arg = -10;
|
||||
TEST_THROWS_TYPE(
|
||||
std::format_error, std::vprint_unicode(os, "throws exception at run-time {0:{0}}", std::make_format_args(arg)));
|
||||
|
||||
os.exceptions(std::stringstream::goodbit);
|
||||
os.clear();
|
||||
std::vprint_unicode(os, "valid", std::make_format_args());
|
||||
assert(os.fail());
|
||||
}
|
||||
|
||||
static void test_stream_formatting() {
|
||||
std::stringstream sstr;
|
||||
auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
|
||||
sstr.str("");
|
||||
std::vprint_unicode(sstr, fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::string out = sstr.str();
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
test("hello", "{}", "hello");
|
||||
|
||||
sstr.width(10);
|
||||
test(" hello", "{}", "hello");
|
||||
|
||||
sstr.fill('+');
|
||||
|
||||
sstr.width(10);
|
||||
test("+++++hello", "{}", "hello");
|
||||
|
||||
// *** Test embedded NUL character ***
|
||||
using namespace std::literals;
|
||||
sstr.width(15);
|
||||
test("++++hello\0world"sv, "hello{}{}", '\0', "world");
|
||||
|
||||
// *** Test Unicode ***
|
||||
// Streams count code units not code points
|
||||
// 2-byte code points
|
||||
sstr.width(5);
|
||||
test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
|
||||
sstr.width(5);
|
||||
test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
|
||||
|
||||
// 3-byte code points
|
||||
sstr.width(5);
|
||||
test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
|
||||
sstr.width(5);
|
||||
test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
|
||||
|
||||
// 4-byte code points
|
||||
sstr.width(5);
|
||||
test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
|
||||
sstr.width(5);
|
||||
test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
print_tests(test_file, test_exception);
|
||||
|
||||
test_sentry_failure();
|
||||
test_format_exception();
|
||||
test_write_failure();
|
||||
test_stream_formatting();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -89,17 +89,11 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
|
||||
#elif TEST_STD_VER > 23
|
||||
@@ -117,17 +111,11 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
|
||||
#endif // TEST_STD_VER > 23
|
||||
|
||||
@@ -50,32 +50,20 @@
|
||||
|
||||
#elif TEST_STD_VER == 23
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
|
||||
#elif TEST_STD_VER > 23
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
|
||||
#endif // TEST_STD_VER > 23
|
||||
|
||||
@@ -5243,17 +5243,11 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++23"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++23"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_quoted_string_io
|
||||
@@ -6950,17 +6944,11 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
# else // _LIBCPP_VERSION
|
||||
# ifdef __cpp_lib_print
|
||||
# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_print
|
||||
# error "__cpp_lib_print should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_print != 202207L
|
||||
# error "__cpp_lib_print should have the value 202207L in c++26"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_quoted_string_io
|
||||
|
||||
@@ -875,7 +875,6 @@ feature_test_macros = [
|
||||
"name": "__cpp_lib_print",
|
||||
"values": {"c++23": 202207},
|
||||
"headers": ["ostream", "print"],
|
||||
"unimplemented": True,
|
||||
},
|
||||
{
|
||||
"name": "__cpp_lib_quoted_string_io",
|
||||
|
||||
@@ -586,4 +586,13 @@ DEFAULT_FEATURES += [
|
||||
cfg.available_features,
|
||||
),
|
||||
),
|
||||
# Tests that require support for <print> and std::print in <ostream> in the built library.
|
||||
Feature(
|
||||
name="availability-print-missing",
|
||||
when=lambda cfg: BooleanExpression.evaluate(
|
||||
# TODO(ldionne) Please provide the correct value.
|
||||
"stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}}",
|
||||
cfg.available_features,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user