diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -22,13 +22,13 @@ #include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> #include <__memory/addressof.h> +#include <__memory/construct_at.h> #include <__utility/move.h> #include <__variant/monostate.h> #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION -#include -#include +# include #endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -44,20 +44,92 @@ class _LIBCPP_TEMPLATE_VIS basic_format_context; #ifndef _LIBCPP_HAS_NO_LOCALIZATION + +namespace __format { +// [format.context]/6 +// std::locale locale(); +// Returns: The locale passed to the formatting function if the latter +// takes one, and std::locale() otherwise. +// +// This class is a helper to implement this behaviour. The call to +// std::locale() is done once and only when needed. +class __locale { +public: + _LIBCPP_HIDE_FROM_ABI __locale() : __dummy_{'\0'}, __engaged_{false} {} + _LIBCPP_HIDE_FROM_ABI __locale(locale __loc) : __loc_{__loc}, __engaged_{true} {} + + _LIBCPP_HIDE_FROM_ABI ~__locale() { + if (__engaged_) + __loc_.~locale(); + } + + _LIBCPP_HIDE_FROM_ABI __locale(const __locale& __other) noexcept : __dummy_{'\0'}, __engaged_(__other.__engaged_) { + if (__engaged_) + std::construct_at(std::addressof(__loc_), __other.__loc_); + } + + _LIBCPP_HIDE_FROM_ABI __locale(__locale&& __other) noexcept : __dummy_{'\0'}, __engaged_(__other.__engaged_) { + // Note locale is not movable. + if (__engaged_) + __loc_ = __other.__loc_; + } + + _LIBCPP_HIDE_FROM_ABI __locale& operator=(const __locale& __other) noexcept { + __assign(__other); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI __locale& operator=(__locale&& __other) noexcept { + __assign(__other); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI locale __value() { + if (!__engaged_) { + std::construct_at(std::addressof(__loc_), locale()); + __engaged_ = true; + } + + return __loc_; + } + +private: + union { + char __dummy_; + locale __loc_; + }; + + bool __engaged_; + + _LIBCPP_HIDE_FROM_ABI void __assign(const __locale& __other) noexcept { + // Note locale is not movable. + if (__engaged_) { + if (__other.__engaged_) + __loc_ = __other.__loc_; + else { + __loc_.~locale(); + __engaged_ = false; + } + } else if (__other.__engaged_) { + std::construct_at(std::addressof(__loc_), __other.__loc_); + __engaged_ = true; + } + // Both not engaged do nothing. + } +}; +} // namespace __format + /** * Helper to create a basic_format_context. * * This is needed since the constructor is private. */ template -_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> -__format_context_create( - _OutIt __out_it, - basic_format_args> __args, - optional<_VSTD::locale>&& __loc = nullopt) { - return _VSTD::basic_format_context(_VSTD::move(__out_it), __args, _VSTD::move(__loc)); +_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> __format_context_create( + _OutIt __out_it, basic_format_args> __args, __format::__locale __loc = {}) { + return _VSTD::basic_format_context(_VSTD::move(__out_it), __args, __loc); } -#else +# else template _LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> __format_context_create( @@ -65,7 +137,7 @@ basic_format_args> __args) { return _VSTD::basic_format_context(_VSTD::move(__out_it), __args); } -#endif +# endif using format_context = basic_format_context>, @@ -95,12 +167,8 @@ return __args_.get(__id); } #ifndef _LIBCPP_HAS_NO_LOCALIZATION - _LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { - if (!__loc_) - __loc_ = _VSTD::locale{}; - return *__loc_; - } -#endif + _LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { return __loc_.__value(); } +# endif _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); } _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); } @@ -109,30 +177,17 @@ basic_format_args __args_; #ifndef _LIBCPP_HAS_NO_LOCALIZATION - // The Standard doesn't specify how the locale is stored. - // [format.context]/6 - // std::locale locale(); - // Returns: The locale passed to the formatting function if the latter - // takes one, and std::locale() otherwise. - // This is done by storing the locale of the constructor in this optional. If - // locale() is called and the optional has no value the value will be created. - // This allows the implementation to lazily create the locale. - // TODO FMT Validate whether lazy creation is the best solution. - optional<_VSTD::locale> __loc_; + __format::__locale __loc_; template friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> - __format_context_create(__OutIt, basic_format_args>, - optional<_VSTD::locale>&&); + __format_context_create(__OutIt, basic_format_args>, __format::__locale __loc); // Note: the Standard doesn't specify the required constructors. - _LIBCPP_HIDE_FROM_ABI - explicit basic_format_context(_OutIt __out_it, - basic_format_args __args, - optional<_VSTD::locale>&& __loc) - : __out_it_(_VSTD::move(__out_it)), __args_(__args), - __loc_(_VSTD::move(__loc)) {} -#else + _LIBCPP_HIDE_FROM_ABI explicit basic_format_context( + _OutIt __out_it, basic_format_args __args, __format::__locale __loc) + : __out_it_(_VSTD::move(__out_it)), __args_(__args), __loc_(__loc) {} +# else template friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> __format_context_create(__OutIt, basic_format_args>); @@ -141,7 +196,7 @@ explicit basic_format_context(_OutIt __out_it, basic_format_args __args) : __out_it_(_VSTD::move(__out_it)), __args_(__args) {} -#endif +# endif }; // A specialization for __retarget_buffer diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -910,7 +910,6 @@ module format_args { private header "__format/format_args.h" } module format_context { private header "__format/format_context.h" - export optional export locale export __locale } diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -328,7 +328,6 @@ format initializer_list format limits format locale -format optional format queue format stack format stdexcept diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -328,7 +328,6 @@ format initializer_list format limits format locale -format optional format queue format stack format stdexcept diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -330,7 +330,6 @@ format initializer_list format limits format locale -format optional format queue format stack format stdexcept diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -330,7 +330,6 @@ format initializer_list format limits format locale -format optional format queue format stack format stdexcept diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -120,7 +120,6 @@ chrono ctime chrono limits chrono locale -chrono optional chrono ostream chrono ratio chrono sstream @@ -337,7 +336,6 @@ format initializer_list format limits format locale -format optional format queue format stack format stdexcept diff --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv --- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv @@ -73,7 +73,6 @@ chrono limits chrono locale chrono new -chrono optional chrono ostream chrono ratio chrono sstream @@ -227,7 +226,6 @@ format limits format locale format new -format optional format queue format stack format stdexcept