template<typename _Context, typename... _Args>
class _Arg_store;
+ template<typename _Visitor, typename _Ctx>
+ decltype(auto) __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
+
template<typename _Ch, typename _Tp>
consteval _Arg_t
__to_arg_t_enum() noexcept;
-
} // namespace __format
/// @endcond
template<typename _Visitor>
decltype(auto)
visit(this basic_format_arg __arg, _Visitor&& __vis)
- { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
+ { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
template<typename _Res, typename _Visitor>
_Res
visit(this basic_format_arg __arg, _Visitor&& __vis)
- { return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type); }
+ { return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type); }
#endif
private:
friend decltype(auto)
visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>);
+ template<typename _Visitor, typename _Ctx>
+ friend decltype(auto)
+ __format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>);
+
template<typename _Ch, typename _Tp>
friend consteval __format::_Arg_t
__format::__to_arg_t_enum() noexcept;
__builtin_unreachable();
}
}
+
+ template<typename _Visitor>
+ decltype(auto)
+ _M_visit_user(_Visitor&& __vis, __format::_Arg_t __type)
+ {
+ return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
+ {
+ constexpr bool __user_facing = __is_one_of<_Tp,
+ monostate, bool, _CharT,
+ int, unsigned int, long long int, unsigned long long int,
+ float, double, long double,
+ const _CharT*, basic_string_view<_CharT>,
+ const void*, handle>::value;
+ if constexpr (__user_facing)
+ return std::forward<_Visitor>(__vis)(__val);
+ else
+ {
+ handle __h(__val);
+ return std::forward<_Visitor>(__vis)(__h);
+ }
+ }, __type);
+ }
};
template<typename _Visitor, typename _Context>
inline decltype(auto)
visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg)
{
- return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
+ return __arg._M_visit_user(std::forward<_Visitor>(__vis), __arg._M_type);
}
/// @cond undocumented
namespace __format
{
+ template<typename _Visitor, typename _Ctx>
+ inline decltype(auto)
+ __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg)
+ {
+ return __arg._M_visit(std::forward<_Visitor>(__vis), __arg._M_type);
+ }
+
struct _WidthPrecVisitor
{
template<typename _Tp>
template<typename _Context>
inline size_t
__int_from_arg(const basic_format_arg<_Context>& __arg)
- { return std::visit_format_arg(_WidthPrecVisitor(), __arg); }
+ { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); }
// Pack _Arg_t enum values into a single 60-bit integer.
template<int _Bits, size_t _Nm>
using _Context = basic_format_context<_Out, _CharT>;
using handle = typename basic_format_arg<_Context>::handle;
- std::visit_format_arg([this](auto& __arg) {
+ __format::__visit_format_arg([this](auto& __arg) {
using _Type = remove_reference_t<decltype(__arg)>;
using _Formatter = typename _Context::template formatter_type<_Type>;
if constexpr (is_same_v<_Type, monostate>)
if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
{
bool __done = false;
- std::visit_format_arg([&](auto& __arg) {
+ __format::__visit_format_arg([&](auto& __arg) {
using _Tp = remove_cvref_t<decltype(__arg)>;
if constexpr (is_same_v<_Tp, bool>)
{
#endif
}
+template<typename T>
+void test_visited_as_handle()
+{
+ T v{};
+ auto store = std::make_format_args(v);
+ std::format_args args = store;
+
+ constexpr auto is_handle = [](auto arg) {
+ return std::is_same_v<decltype(arg), decltype(args.get(0))::handle>;
+ };
+ VERIFY( std::visit_format_arg(is_handle, args.get(0)) );
+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
+ VERIFY( args.get(0).visit(is_handle) );
+#endif
+}
+
+template<typename E, typename S>
+void test_visited_as()
+{
+ auto v = static_cast<S>(1.0);
+ auto store = std::make_format_args(v);
+ std::format_args args = store;
+
+ auto is_expected_val = [v](auto arg) {
+ if constexpr (std::is_same_v<decltype(arg), E>)
+ return arg == static_cast<E>(v);
+ return false;
+ };
+ VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) );
+#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
+ VERIFY( args.get(0).visit(is_expected_val) );
+#endif
+}
+
+template<typename T>
+concept can_format = std::is_default_constructible_v<std::formatter<T, char>>;
+
int main()
{
test_empty();
test_args();
test_member_visit();
+
+#ifdef __SIZEOF_INT128__
+ test_visited_as_handle<__int128>();
+ test_visited_as_handle<unsigned __int128>();
+#endif
+// TODO: This should be visited as handle.
+#ifdef __STDCPP_FLOAT16_T__
+ if constexpr (can_format<_Float16>)
+ test_visited_as<float, _Float16>();
+#endif
+#ifdef __STDCPP_BFLOAT16_T__
+ if constexpr (can_format<__gnu_cxx::__bfloat16_t>)
+ test_visited_as<float, __gnu_cxx::__bfloat16_t>();
+#endif
+#ifdef __FLT32_DIG__
+ if constexpr (can_format<_Float32>)
+ test_visited_as<float, _Float32>();
+#endif
+#ifdef __FLT64_DIG__
+ if constexpr (can_format<_Float64>)
+ test_visited_as<double, _Float64>();
+#endif
+#ifdef __FLT128_DIG__
+ if constexpr (can_format<_Float128>)
+# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128
+ test_visited_as<long double, _Float128>();
+# else
+ test_visited_as_handle<_Float128>();
+# endif
+#endif
+#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
+ if constexpr (!std::is_same_v<__ieee128, long double>)
+ test_visited_as_handle<__ieee128>();
+ if constexpr (!std::is_same_v<__ibm128, long double>)
+ test_visited_as_handle<__ibm128>();
+#endif
}
VERIFY( ! is_format_string_for("{:0c}", 'c') );
VERIFY( ! is_format_string_for("{:0s}", true) );
- // Dynamic width arg must be an integer type.
+ // Dynamic width arg must be a standar integer type.
VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) );
VERIFY( ! is_format_string_for("{:{}d}", 1, true) );
VERIFY( ! is_format_string_for("{:{}d}", 1, "str") );
VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) );
+#ifdef __SIZEOF_INT128__
+ VERIFY( ! is_format_string_for("{:{}d}", 1, static_cast<__int128>(1)) );
+#endif
// Precision only valid for string and floating-point types.
VERIFY( ! is_format_string_for("{:.3d}", 1) );
VERIFY( ! is_format_string_for("{:3.3s}", 'c') );
VERIFY( ! is_format_string_for("{:3.3p}", nullptr) );
- // Dynamic precision arg must be an integer type.
+ // Dynamic precision arg must be a standard integer type.
VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) );
VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) );
VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") );
VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) );
+#ifdef __SIZEOF_INT128__
+ VERIFY( ! is_format_string_for("{:{}f}", 1.0, static_cast<unsigned __int128>(1)) );
+#endif
// Invalid presentation types for integers.
VERIFY( ! is_format_string_for("{:f}", 1) );