Add std::format support for qfloat16
The qfloat16 formatter is based of the std::formatter<float>. It is added in a separate header file qfloat16format.h, so that the users could include it only when they really need. After the C++20 standard was released, it had one important addition that requires the format string to be parsed at compile-time. This feature is covered by __cpp_lib_format == 202106L. The older implementations have slightly different signatures of std::formatter::format() and std::formatter::parse() methods. To avoid ugly macros, Qt only supports std::format if the compilers implement __cpp_lib_format >= 202106L. This commit also defines QT_SUPPORTS_STD_FORMAT macro to avoid constant repetition of if (defined(__cpp_lib_format) && (__cpp_lib_format >= 202106L)) checks. [ChangeLog][QtCore][qfloat16] Added std::format support for qfloat16. The supported formatting options are similar to those of std::formatter for float. Fixes: QTBUG-104654 Change-Id: I3a0b077a55fbb46573de8b7bcd288b4ef9541953 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
3c81fc5d73
commit
ffac33964d
@ -111,6 +111,7 @@ qt_internal_add_module(Core
|
|||||||
io/qfilesystementry.cpp io/qfilesystementry_p.h
|
io/qfilesystementry.cpp io/qfilesystementry_p.h
|
||||||
io/qfilesystemiterator_p.h
|
io/qfilesystemiterator_p.h
|
||||||
io/qfilesystemmetadata_p.h
|
io/qfilesystemmetadata_p.h
|
||||||
|
io/qfloat16format.h
|
||||||
io/qfsfileengine.cpp io/qfsfileengine_p.h
|
io/qfsfileengine.cpp io/qfsfileengine_p.h
|
||||||
io/qfsfileengine_iterator.cpp io/qfsfileengine_iterator_p.h
|
io/qfsfileengine_iterator.cpp io/qfsfileengine_iterator_p.h
|
||||||
io/qiodevice.cpp io/qiodevice.h io/qiodevice_p.h
|
io/qiodevice.cpp io/qiodevice.h io/qiodevice_p.h
|
||||||
@ -127,6 +128,7 @@ qt_internal_add_module(Core
|
|||||||
io/qstorageinfo.cpp io/qstorageinfo.h io/qstorageinfo_p.h
|
io/qstorageinfo.cpp io/qstorageinfo.h io/qstorageinfo_p.h
|
||||||
io/qtemporarydir.cpp io/qtemporarydir.h
|
io/qtemporarydir.cpp io/qtemporarydir.h
|
||||||
io/qtemporaryfile.cpp io/qtemporaryfile.h io/qtemporaryfile_p.h
|
io/qtemporaryfile.cpp io/qtemporaryfile.h io/qtemporaryfile_p.h
|
||||||
|
io/qtformat_impl.h
|
||||||
io/qurl.cpp io/qurl.h io/qurl_p.h
|
io/qurl.cpp io/qurl.h io/qurl_p.h
|
||||||
io/qurlidna.cpp
|
io/qurlidna.cpp
|
||||||
io/qurlquery.cpp io/qurlquery.h
|
io/qurlquery.cpp io/qurlquery.h
|
||||||
|
31
src/corelib/io/qfloat16format.h
Normal file
31
src/corelib/io/qfloat16format.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QFLOAT16FORMAT_H
|
||||||
|
#define QFLOAT16FORMAT_H
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#pragma qt_class(QFloat16Format)
|
||||||
|
#pragma qt_sync_skip_header_check
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
#include <QtCore/qtformat_impl.h>
|
||||||
|
|
||||||
|
#ifdef QT_SUPPORTS_STD_FORMAT
|
||||||
|
|
||||||
|
#include <QtCore/qfloat16.h>
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
struct std::formatter<QT_PREPEND_NAMESPACE(qfloat16), CharT> : std::formatter<float, CharT>
|
||||||
|
{
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(QT_PREPEND_NAMESPACE(qfloat16) val, FormatContext &ctx) const
|
||||||
|
{
|
||||||
|
return std::formatter<float, CharT>::format(float(val), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QT_SUPPORTS_STD_FORMAT
|
||||||
|
|
||||||
|
#endif // QFLOAT16FORMAT_H
|
25
src/corelib/io/qtformat_impl.h
Normal file
25
src/corelib/io/qtformat_impl.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
|
#ifndef QTFORMAT_IMPL_H
|
||||||
|
#define QTFORMAT_IMPL_H
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#pragma qt_no_master_include
|
||||||
|
#pragma qt_sync_skip_header_check
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/qsystemdetection.h>
|
||||||
|
#include <QtCore/qtconfigmacros.h>
|
||||||
|
|
||||||
|
#if __has_include(<format>)
|
||||||
|
# include <format>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__cpp_lib_format) && (__cpp_lib_format >= 202106L))
|
||||||
|
|
||||||
|
#define QT_SUPPORTS_STD_FORMAT 1
|
||||||
|
|
||||||
|
#endif // __cpp_lib_format
|
||||||
|
|
||||||
|
#endif // QTFORMAT_IMPL_H
|
@ -68,3 +68,9 @@ endif()
|
|||||||
if(QT_FEATURE_private_tests)
|
if(QT_FEATURE_private_tests)
|
||||||
add_subdirectory(qzip)
|
add_subdirectory(qzip)
|
||||||
endif()
|
endif()
|
||||||
|
if(NOT (MACOS AND "$ENV{QT_BUILD_ENVIRONMENT}" STREQUAL "ci"))
|
||||||
|
# On macOS the new features require at least macOS 13.3,
|
||||||
|
# but we also run the tests on older OS versions.
|
||||||
|
# So just skip macOS on CI for now.
|
||||||
|
add_subdirectory(qfloat16format)
|
||||||
|
endif()
|
||||||
|
21
tests/auto/corelib/io/qfloat16format/CMakeLists.txt
Normal file
21
tests/auto/corelib/io/qfloat16format/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(tst_qfloat16format LANGUAGES CXX)
|
||||||
|
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.3)
|
||||||
|
|
||||||
|
qt_internal_add_test(tst_qfloat16format
|
||||||
|
SOURCES
|
||||||
|
tst_qfloat16format.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(tst_qfloat16format
|
||||||
|
PROPERTIES
|
||||||
|
CXX_STANDARD 20
|
||||||
|
CXX_STANDARD_REQUIRED OFF
|
||||||
|
)
|
168
tests/auto/corelib/io/qfloat16format/tst_qfloat16format.cpp
Normal file
168
tests/auto/corelib/io/qfloat16format/tst_qfloat16format.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <QtCore/qcompilerdetection.h>
|
||||||
|
#include <QtCore/qdebug.h>
|
||||||
|
#include <QtCore/qfloat16format.h>
|
||||||
|
#include <QtCore/qstring.h>
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
class tst_QFloat16Format : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void formatCompileTime();
|
||||||
|
void format_data();
|
||||||
|
void format();
|
||||||
|
void formatMultiArg();
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QFloat16Format::initTestCase()
|
||||||
|
{
|
||||||
|
#ifndef QT_SUPPORTS_STD_FORMAT
|
||||||
|
QSKIP("This test requires std::format support!");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QFloat16Format::formatCompileTime()
|
||||||
|
{
|
||||||
|
#ifdef QT_SUPPORTS_STD_FORMAT
|
||||||
|
// Starting from __cpp_lib_format == 202106L,
|
||||||
|
// std::format requires the format string to be evaluated at compile-time,
|
||||||
|
// so check it here.
|
||||||
|
|
||||||
|
const qfloat16 val{1.234f};
|
||||||
|
std::locale loc{"C"};
|
||||||
|
|
||||||
|
// char
|
||||||
|
std::string buffer;
|
||||||
|
std::format_to(std::back_inserter(buffer), "{}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), "{:*>15.7f}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), "{:*^+#15.7g}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), "{:*<-#15.7A}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), "{:*^ 15.7e}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), loc, "{:*^10.3Lf}", val);
|
||||||
|
std::format_to(std::back_inserter(buffer), loc, "{:*< 10.7LE}", val);
|
||||||
|
|
||||||
|
// wchar_t
|
||||||
|
std::wstring wbuffer;
|
||||||
|
std::format_to(std::back_inserter(wbuffer), L"{}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), L"{:*>15.7f}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), L"{:*^+#15.7g}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), L"{:*<-#15.7A}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), L"{:*^ 15.7e}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), loc, L"{:*^10.3Lf}", val);
|
||||||
|
std::format_to(std::back_inserter(wbuffer), loc, L"{:*< 10.7LE}", val);
|
||||||
|
#endif // QT_SUPPORTS_STD_FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QFloat16Format::format_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("format");
|
||||||
|
QTest::addColumn<qfloat16>("value");
|
||||||
|
QTest::addColumn<QByteArray>("locName");
|
||||||
|
QTest::addColumn<QString>("expectedString");
|
||||||
|
|
||||||
|
auto row = [](const QString &format, qfloat16 val, const QString &expected,
|
||||||
|
const QByteArray &loc = QByteArray())
|
||||||
|
{
|
||||||
|
QString str;
|
||||||
|
QDebug dbg(&str);
|
||||||
|
dbg.nospace().noquote() << format;
|
||||||
|
if (!loc.isEmpty())
|
||||||
|
dbg.nospace().noquote() << "_" << loc;
|
||||||
|
|
||||||
|
QTest::newRow(qPrintable(str)) << format << val << loc << expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray loc;
|
||||||
|
#if defined(Q_CC_MSVC_ONLY)
|
||||||
|
loc = "de-DE"_ba;
|
||||||
|
#elif !defined(Q_CC_MINGW) // minGW has only C and POSIX locales
|
||||||
|
loc = "de_DE"_ba;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
row(u"{}"_s, qfloat16(1.f), u"1"_s);
|
||||||
|
row(u"{:#}"_s, qfloat16(1.f), u"1."_s);
|
||||||
|
row(u"{:f}"_s, qfloat16(1.f), u"1.000000"_s);
|
||||||
|
row(u"{:*>10.2a}"_s, qfloat16(-1.23f), u"**-1.3bp+0"_s);
|
||||||
|
if (!loc.isEmpty()) {
|
||||||
|
row(u"{:+Lf}"_s, qfloat16(1.f), u"+1,000000"_s, loc);
|
||||||
|
row(u"{:*^10.3LF}"_s, qfloat16(-0.1234f), u"**-0,123**"_s, loc);
|
||||||
|
row(u"{:*^#10.4Lg}"_s, qfloat16(-1.f), u"**-1,000**"_s, loc);
|
||||||
|
row(u"{:*<14.3LE}"_s, qfloat16(-0.1234f), u"-1,234E-01****"_s, loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QFloat16Format::format()
|
||||||
|
{
|
||||||
|
#ifdef QT_SUPPORTS_STD_FORMAT
|
||||||
|
QFETCH(const QString, format);
|
||||||
|
QFETCH(const qfloat16, value);
|
||||||
|
QFETCH(const QByteArray, locName);
|
||||||
|
QFETCH(const QString, expectedString);
|
||||||
|
|
||||||
|
// char
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
const auto formatStr = format.toStdString();
|
||||||
|
if (!locName.isEmpty()) {
|
||||||
|
std::locale loc(locName.constData());
|
||||||
|
std::vformat_to(std::back_inserter(buffer), loc, formatStr,
|
||||||
|
std::make_format_args(value));
|
||||||
|
} else {
|
||||||
|
std::vformat_to(std::back_inserter(buffer), formatStr,
|
||||||
|
std::make_format_args(value));
|
||||||
|
}
|
||||||
|
const QString actualString = QString::fromStdString(buffer);
|
||||||
|
QCOMPARE_EQ(actualString, expectedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wchar_t
|
||||||
|
{
|
||||||
|
std::wstring buffer;
|
||||||
|
const auto formatStr = format.toStdWString();
|
||||||
|
if (!locName.isEmpty()) {
|
||||||
|
std::locale loc(locName.constData());
|
||||||
|
std::vformat_to(std::back_inserter(buffer), loc, formatStr,
|
||||||
|
std::make_wformat_args(value));
|
||||||
|
} else {
|
||||||
|
std::vformat_to(std::back_inserter(buffer), formatStr,
|
||||||
|
std::make_wformat_args(value));
|
||||||
|
}
|
||||||
|
const QString actualString = QString::fromStdWString(buffer);
|
||||||
|
QCOMPARE_EQ(actualString, expectedString);
|
||||||
|
}
|
||||||
|
#endif // QT_SUPPORTS_STD_FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QFloat16Format::formatMultiArg()
|
||||||
|
{
|
||||||
|
#ifdef QT_SUPPORTS_STD_FORMAT
|
||||||
|
const qfloat16 v1{-0.1234f};
|
||||||
|
const qfloat16 v2{5.67f};
|
||||||
|
|
||||||
|
const QString expectedString = u"**+5.67**_*****-1.234E-01"_s;
|
||||||
|
// char
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
std::format_to(std::back_inserter(buffer), "{1:*^+9.2f}_{0:*>15.3E}", v1, v2);
|
||||||
|
QCOMPARE_EQ(QString::fromStdString(buffer), expectedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wchar_t
|
||||||
|
{
|
||||||
|
std::wstring buffer;
|
||||||
|
std::format_to(std::back_inserter(buffer), L"{1:*^+9.2f}_{0:*>15.3E}", v1, v2);
|
||||||
|
QCOMPARE_EQ(QString::fromStdWString(buffer), expectedString);
|
||||||
|
}
|
||||||
|
#endif // QT_SUPPORTS_STD_FORMAT
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QFloat16Format)
|
||||||
|
#include "tst_qfloat16format.moc"
|
Loading…
x
Reference in New Issue
Block a user