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/qfilesystemiterator_p.h
|
||||
io/qfilesystemmetadata_p.h
|
||||
io/qfloat16format.h
|
||||
io/qfsfileengine.cpp io/qfsfileengine_p.h
|
||||
io/qfsfileengine_iterator.cpp io/qfsfileengine_iterator_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/qtemporarydir.cpp io/qtemporarydir.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/qurlidna.cpp
|
||||
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)
|
||||
add_subdirectory(qzip)
|
||||
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