libstdc++: Hide 128-bit int and float types behind handle for basic_format_arg visitation [PR108053]
Implement visit_format_arg and basic_format_arg::visit function, in terms of _M_visit_user member functions, that wraps any type stored inside basic_format_arg, that is not specified in the standard, into the handle. This affects __int128, unsigned __int128, PowerPC specific __ieee128 and __ibm128, and _Float128 for architectures where long double is not 128bits. The bfloat16, _Float16, _Float32, _Float32, and _Float128 for 128bits long double are not are not addressed, as they are transformed into a standard floating point types. For internal purposes __format::__visit_format_arg function is used, that provides an unmodified access to stored object. PR libstdc++/108053 libstdc++-v3/ChangeLog: * include/std/format (basic_format_arg::_M_visit_user): Helper function for wrapping extension types into handle. (visit_format_arg): Call `_M_visit_user` instead of `_M_visit`. (basic_format_arg::visit): As above. (__format::__visit_format_arg): Provides direct access to values stored in basic_format_arg. (__format::__int_from_arg): Use __format::__visit_format_arg instead of std::visit_format_arg. (_Formatting_scanner::_M_format_arg): As above. (_Checking_scanner::__do_vformat_to): As above. * testsuite/std/format/arguments/args.cc: New tests. * testsuite/std/format/string.cc: Test for using __int128 as width/precision. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
parent
77ef91d715
commit
22847ef193
3 changed files with 123 additions and 9 deletions
|
@ -3308,10 +3308,12 @@ namespace __format
|
|||
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
|
||||
|
||||
|
@ -3383,12 +3385,12 @@ namespace __format
|
|||
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:
|
||||
|
@ -3589,6 +3591,10 @@ namespace __format
|
|||
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;
|
||||
|
@ -3657,6 +3663,28 @@ namespace __format
|
|||
__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>
|
||||
|
@ -3664,12 +3692,19 @@ namespace __format
|
|||
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>
|
||||
|
@ -3701,7 +3736,7 @@ namespace __format
|
|||
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>
|
||||
|
@ -4154,7 +4189,7 @@ namespace __format
|
|||
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>)
|
||||
|
@ -4256,7 +4291,7 @@ namespace __format
|
|||
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>)
|
||||
{
|
||||
|
|
|
@ -148,9 +148,82 @@ test_member_visit()
|
|||
#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
|
||||
}
|
||||
|
|
|
@ -113,11 +113,14 @@ test_format_spec()
|
|||
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) );
|
||||
|
@ -126,11 +129,14 @@ test_format_spec()
|
|||
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) );
|
||||
|
|
Loading…
Add table
Reference in a new issue