[librpbase] Initial switch to libfmt for string formatting.

This will replace printf()-style functions in most cases, and will
replace all uses of rp_sprintf() and related.

NOTE: We need to use FMT_STRING() [which we're abbreviating FSTR] if
compiling without C++20 support; otherwise, string format checking won't
be done. We're not targetting C++20 at the moment.

Also, string format checking can't be done when using gettext. This also
applied to printf(), so it's not a big deal per se.

NOTE: Support for C++-style format strings (std::print) was added in
gettext-0.22, so gettext-0.22 will be required in order to update the
.pot and .po files.

FIXME: libfmt has its own "PACKED" definition, which conflicts with our
own. We should rename our "PACKED" to "RP_PACKED".

TODO: Add an internal copy of libfmt for Windows.
This commit is contained in:
David Korth 2025-01-20 19:30:55 -05:00
parent 59816111fc
commit 64e860f545
16 changed files with 197 additions and 75 deletions

View File

@ -172,6 +172,7 @@ INCLUDE(CheckTinyXML2)
INCLUDE(CheckZSTD)
INCLUDE(CheckLZ4)
INCLUDE(CheckLZO)
INCLUDE(CheckLibfmt)
# Reference: https://cmake.org/Wiki/RecipeAddUninstallTarget
########### Add uninstall target ###############

View File

@ -0,0 +1,54 @@
# Check for libfmt.
# If libfmt isn't found, extlib/libfmt/ will be used instead.
# TODO: Add internal libfmt.
FIND_PACKAGE(Fmt REQUIRED)
SET(Fmt_LIBRARY "fmt::fmt")
IF(0)
IF(NOT USE_INTERNAL_FMT)
IF(Fmt_LIBRARY MATCHES "^fmt$" OR Fmt_LIBRARY MATCHES "^fmt")
# Internal libfmt was previously in use.
UNSET(Fmt_FOUND)
UNSET(HAVE_Fmt)
UNSET(Fmt_LIBRARY CACHE)
UNSET(Fmt_LIBRARIES CACHE)
ENDIF()
# Check for libfmt.
FIND_PACKAGE(Fmt)
IF(Fmt_FOUND)
# Found system libfmt.
SET(HAVE_Fmt 1)
ELSE()
# System libfmt was not found.
MESSAGE(STATUS "Using the internal copy of libfmt since a system version was not found.")
SET(USE_INTERNAL_FMT ON CACHE BOOL "Use the internal copy of libfmt" FORCE)
ENDIF()
ELSE()
MESSAGE(STATUS "Using the internal copy of libfmt.")
ENDIF(NOT USE_INTERNAL_FMT)
IF(USE_INTERNAL_FMT)
# Using the internal libfmt library.
SET(Fmt_FOUND 1)
SET(HAVE_Fmt 1)
SET(Fmt_VERSION 11.1.1 CACHE INTERNAL "libfmt version" FORCE)
# FIXME: When was it changed from LIBRARY to LIBRARIES?
IF(WIN32 OR APPLE)
# Using DLLs on Windows and Mac OS X.
SET(USE_INTERNAL_FMT_DLL ON)
SET(Fmt_LIBRARY fmt CACHE INTERNAL "libfmt library" FORCE)
ELSE()
# Using static linking on other systems.
SET(USE_INTERNAL_FMT_DLL OFF)
SET(Fmt_LIBRARY fmt CACHE INTERNAL "libfmt library" FORCE)
ENDIF()
SET(Fmt_LIBRARIES ${Fmt_LIBRARY} CACHE INTERNAL "libfmt libraries" FORCE)
# FIXME: When was it changed from DIR to DIRS?
SET(Fmt_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libfmt)
SET(Fmt_INCLUDE_DIRS ${Fmt_INCLUDE_DIR})
ELSE(USE_INTERNAL_FMT)
SET(USE_INTERNAL_FMT_DLL OFF)
ENDIF(USE_INTERNAL_FMT)
ENDIF(0)

View File

@ -63,6 +63,8 @@ CheckOptions:
performance-inefficient-vector-operation.VectorLikeClasses: '::std::vector;QList;QByteARray;QByteArrayList;QItemSelection;QQueue;QStringList'
misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: '1'
modernize-avoid-c-arrays.AllowStringArrays: 'true'
modernize-use-std-print.PrintfLikeFunctions: 'printf;rp_sprintf'
modernize-use-std-print.ReplacementPrintFunction: 'fmt::print'
readability-function-cognitive-complexity.IgnoreMacros: 'true'
readability-implicit-bool-conversion.AllowPointerConditions: 'true'
readability-simplify-subscript-expr.Types: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::std::span;QByteArray;QString'

View File

@ -257,6 +257,9 @@ FOREACH(_target ${TARGETS})
TARGET_LINK_LIBRARIES(${_target} PRIVATE ${NETTLE_LIBRARY})
TARGET_INCLUDE_DIRECTORIES(${_target} PRIVATE ${NETTLE_INCLUDE_DIRS})
ENDIF(NETTLE_FOUND)
IF(Fmt_FOUND)
TARGET_LINK_LIBRARIES(${_target} PRIVATE ${Fmt_LIBRARY})
ENDIF(Fmt_FOUND)
IF(WIN32)
# libwin32common

View File

@ -97,7 +97,7 @@ string RomDataPrivate::getURL_GameTDB(
const char *region, const char *gameID,
const char *ext)
{
return rp_sprintf("https://art.gametdb.com/%s/%s/%s/%s%s",
return fmt::format(FSTR("https://art.gametdb.com/{:s}/{:s}/{:s}/{:s}{:s}"),
system, type, region, gameID, ext);
}
@ -115,7 +115,7 @@ string RomDataPrivate::getCacheKey_GameTDB(
const char *region, const char *gameID,
const char *ext)
{
return rp_sprintf("%s/%s/%s/%s%s",
return fmt::format(FSTR("{:s}/{:s}/{:s}/{:s}{:s}"),
system, type, region, gameID, ext);
}
@ -135,9 +135,9 @@ string RomDataPrivate::getURL_RPDB(
{
// Game ID may need to be urlencoded.
const string gameID_urlencode = LibCacheCommon::urlencode(gameID);
return rp_sprintf("https://rpdb.gerbilsoft.com/%s/%s/%s%s%s%s",
return fmt::format(FSTR("https://rpdb.gerbilsoft.com/{:s}/{:s}/{:s}{:s}{:s}{:s}"),
system, type, (region ? region : ""), (region ? "/" : ""),
gameID_urlencode.c_str(), ext);
gameID_urlencode, ext);
}
/**
@ -154,7 +154,7 @@ string RomDataPrivate::getCacheKey_RPDB(
const char *region, const char *gameID,
const char *ext)
{
return rp_sprintf("%s/%s/%s%s%s%s",
return fmt::format(FSTR("{:s}/{:s}/{:s}{:s}{:s}{:s}"),
system, type, (region ? region : ""), (region ? "/" : ""),
gameID, ext);
}

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase) *
* RomFields.cpp: ROM fields class. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -462,7 +462,7 @@ string RomFields::ageRatingDecode(AgeRatingsCountry country, uint16_t rating)
} else {
// No string rating.
// Print the numeric value.
str = rp_sprintf("%u",
str = fmt::format(FSTR("{:d}"),
static_cast<unsigned int>(rating) & RomFields::AGEBF_MIN_AGE_MASK);
}
@ -514,7 +514,7 @@ string RomFields::ageRatingsDecode(const age_ratings_t *age_ratings, bool newlin
} else {
// Invalid age rating organization.
// Use the numeric index.
str += rp_sprintf("%u", i);
str += fmt::format(FSTR("{:d}"), i);
}
str += '=';
str += ageRatingDecode((AgeRatingsCountry)i, rating);
@ -956,23 +956,25 @@ int RomFields::addField_string_numeric(const char *name, uint32_t val, Base base
if (!name)
return -1;
const char *fmtstr;
string s;
switch (base) {
case Base::Dec:
default:
fmtstr = "%0*u";
s = fmt::format(FSTR("{:0>{}d}"), val, digits);
break;
case Base::Hex:
fmtstr = (!(flags & STRF_HEX_LOWER)) ? "0x%0*X" : "0x%0*x";
if (unlikely(flags & STRF_HEX_LOWER)) {
s = fmt::format(FSTR("0x{:0>{}x}"), val, digits);
} else {
s = fmt::format(FSTR("0x{:0>{}X}"), val, digits);
}
break;
case Base::Oct:
fmtstr = "0%0*o";
s = fmt::format(FSTR("0{:0>{}o}"), val, digits);
break;
}
char buf[64];
snprintf(buf, sizeof(buf), fmtstr, digits, val);
return addField_string(name, buf, flags);
return addField_string(name, s, flags);
}
/**
@ -1058,16 +1060,20 @@ int RomFields::addField_string_address_range(const char *name,
}
// Address range
char buf[128];
int len = snprintf(buf, sizeof(buf),
(!(flags & STRF_HEX_LOWER)) ? "0x%0*X - 0x%0*X" : "0x%0*x - 0x%0*x",
digits, start, digits, end);
if (suffix && suffix[0] != 0 && (len > 0 && len < 126)) {
string s;
s.reserve(digits * 2 + (suffix ? 8+4 : 4));
if (unlikely(flags & STRF_HEX_LOWER)) {
s = fmt::format(FSTR("0x{:0>{}x} - 0x{:0>{}x}"), start, digits, end, digits);
} else {
s = fmt::format(FSTR("0x{:0>{}X} - 0x{:0>{}X}"), start, digits, end, digits);
}
if (suffix && suffix[0] != 0 && !s.empty()) {
// Append a space and the specified suffix.
snprintf(&buf[len], sizeof(buf)-len, " %s", suffix);
s += ' ';
s += suffix;
}
return addField_string(name, buf, flags);
return addField_string(name, s, flags);
}
/**

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase) *
* TextOut.hpp: Text output for RomData. (User-readable text) *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* Copyright (c) 2016-2018 by Egor. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -889,7 +889,7 @@ public:
if (name) {
os << name;
} else {
os << rp_sprintf(C_("TextOut", "(tab %d)"), tabIdx);
os << fmt::format(C_("TextOut", "(tab {:d})"), tabIdx);
}
os << " -----" << '\n';
}
@ -948,7 +948,7 @@ std::ostream& operator<<(std::ostream& os, const ROMOutput& fo) {
// NOTE: RomDataView context is used for the "unknown" strings.
{
// tr: "[System] [FileType] detected."
const string detectMsg = rp_sprintf_p(C_("TextOut", "%1$s %2$s detected"),
const string detectMsg = fmt::format(C_("TextOut", "{0:s} {1:s} detected"),
(systemName ? systemName : C_("RomDataView", "(unknown system)")),
(fileType ? fileType : C_("RomDataView", "(unknown filetype)")));
@ -974,7 +974,7 @@ std::ostream& operator<<(std::ostream& os, const ROMOutput& fo) {
auto image = romdata->image(static_cast<RomData::ImageType>(i));
if (image && image->isValid()) {
// tr: Image Type name, followed by Image Type ID
os << "-- " << rp_sprintf_p(C_("TextOut", "%1$s is present (use -x%2$d to extract)"),
os << "-- " << fmt::format(C_("TextOut", "{0:s} is present (use -x{1:d} to extract)"),
RomData::getImageTypeName(static_cast<RomData::ImageType>(i)), i) << '\n';
// TODO: After localizing, add enough spaces for alignment.
os << " Format : " << rp_image::getFormatName(image->format()) << '\n';

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase) *
* RpJpeg.cpp: JPEG image handler. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -101,7 +101,7 @@ static void JPEGCALL my_output_message(j_common_ptr cinfo)
OutputDebugStringA(txtbuf);
#else /* !_WIN32 */
// Print to stderr.
fprintf(stderr, "libjpeg error: %s\n", buffer);
fmt::print(stderr, "libjpeg error: {:s}\n", buffer);
#endif /* _WIN32 */
}
@ -285,14 +285,14 @@ rp_image_ptr load(IRpFile *file)
if (try_ext_bgra && tried_ext_bgra) {
// Tried using JCS_EXT_BGRA and it didn't work.
// Try again with JCS_RGB.
fputs("JCS_EXT_BGRA FAILED, trying JCS_RGB\n", stderr);
fmt::print(stderr, "JCS_EXT_BGRA FAILED, trying JCS_RGB\n");
try_ext_bgra = false;
direct_copy = false;
file->rewind();
jmperr = setjmp(jerr.setjmp_buffer);
}
if (jmperr) {
fputs("JPEG decoding FAILED\n", stderr);
fmt::print(stderr, "JPEG decoding FAILED\n");
// An error occurred while decoding the JPEG.
// NOTE: buffer is allocated using JPEG allocation functions,
// so it's automatically freed when we destroy cinfo.

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase) *
* stdafx.h: Common definitions and includes. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -37,6 +37,11 @@
#include <utility>
#include <vector>
// libfmt
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
#else /* !__cplusplus */
/** C **/

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* AesCipherTest.cpp: AesCipher class test. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -27,6 +27,13 @@ using std::ostringstream;
using std::string;
using std::vector;
// libfmt
// FIXME: libfmt has its own "PACKED" definition.
#undef PACKED
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
namespace LibRpBase { namespace Tests {
typedef IAesCipher* (*PFNCREATEIAESCIPHER)(void);
@ -170,11 +177,13 @@ void AesCipherTest::CompareByteArrays(
// Output format: (assume ~64 bytes per line)
// 0000: 01 23 45 67 89 AB CD EF 01 23 45 67 89 AB CD EF
const size_t bufSize = ((size / 16) + !!(size % 16)) * 64;
char printf_buf[16];
string s_expected, s_actual;
s_expected.reserve(bufSize);
s_actual.reserve(bufSize);
string s_tmp;
s_tmp.reserve(14);
const uint8_t *pE = expected, *pA = actual;
for (size_t i = 0; i < size; i++, pE++, pA++) {
if (i % 16 == 0) {
@ -185,16 +194,14 @@ void AesCipherTest::CompareByteArrays(
s_actual += '\n';
}
snprintf(printf_buf, sizeof(printf_buf), "%04X: ", static_cast<unsigned int>(i));
s_expected += printf_buf;
s_actual += printf_buf;
s_tmp = fmt::format(FSTR("{:0>4X}: "), static_cast<unsigned int>(i));
s_expected += s_tmp;
s_actual += s_tmp;
}
// Print the byte.
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pE);
s_expected += printf_buf;
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pA);
s_actual += printf_buf;
s_expected += fmt::format(FSTR("{:0>2X}"), *pE);
s_actual += fmt::format(FSTR("{:0>2X}"), *pA);
if (i % 16 == 7) {
s_expected += " ";
@ -227,11 +234,11 @@ void AesCipherTest::SetUp(void)
// Print the AesCipher implementation name.
const char *name = m_cipher->name();
ASSERT_TRUE(name != nullptr);
printf("AesCipher implementation: %s\n", name);
fmt::print(FSTR("AesCipher implementation: {:s}\n"), name);
pfnLastCreateIAesCipher = mode.pfnCreateIAesCipher;
if (!mode.isRequired && !m_cipher->isInit()) {
fputs("This implementation is not supported on this system; skipping tests.\n", stdout);
fmt::print(FSTR("This implementation is not supported on this system; skipping tests.\n"));
}
}
@ -708,7 +715,7 @@ AesDecryptTestSet(Nettle, true)
*/
extern "C" int gtest_main(int argc, TCHAR *argv[])
{
fputs("LibRpBase test suite: Crypto tests.\n\n", stderr);
fmt::print(stderr, FSTR("LibRpBase test suite: Crypto tests.\n\n"));
fflush(nullptr);
// coverity[fun_call_w_exception]: uncaught exceptions cause nonzero exit anyway, so don't warn.

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* CBCReaderTest.cpp: CBCReader class test. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -31,6 +31,13 @@ using std::array;
using std::ostringstream;
using std::string;
// libfmt
// FIXME: libfmt has its own "PACKED" definition.
#undef PACKED
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
namespace LibRpBase { namespace Tests {
enum class CryptoMode {
@ -195,11 +202,13 @@ void CBCReaderTest::CompareByteArrays(
// Output format: (assume ~64 bytes per line)
// 0000: 01 23 45 67 89 AB CD EF 01 23 45 67 89 AB CD EF
const size_t bufSize = ((size / 16) + !!(size % 16)) * 64;
char printf_buf[16];
string s_expected, s_actual;
s_expected.reserve(bufSize);
s_actual.reserve(bufSize);
string s_tmp;
s_tmp.reserve(14);
const uint8_t *pE = expected, *pA = actual;
for (size_t i = 0; i < size; i++, pE++, pA++) {
if (i % 16 == 0) {
@ -210,16 +219,14 @@ void CBCReaderTest::CompareByteArrays(
s_actual += '\n';
}
snprintf(printf_buf, sizeof(printf_buf), "%04X: ", static_cast<unsigned int>(i));
s_expected += printf_buf;
s_actual += printf_buf;
s_tmp = fmt::format(FSTR("{:0>4X}: "), static_cast<unsigned int>(i));
s_expected += s_tmp;
s_actual += s_tmp;
}
// Print the byte.
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pE);
s_expected += printf_buf;
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pA);
s_actual += printf_buf;
s_expected += fmt::format(FSTR("{:0>2X}"), *pE);
s_actual += fmt::format(FSTR("{:0>2X}"), *pA);
if (i % 16 == 7) {
s_expected += " ";
@ -389,7 +396,7 @@ INSTANTIATE_TEST_SUITE_P(CBCReaderTest, CBCReaderTest,
*/
extern "C" int gtest_main(int argc, TCHAR *argv[])
{
fputs("LibRpBase test suite: CBCReader tests.\n\n", stderr);
fmt::print(stderr, FSTR("LibRpBase test suite: CBCReader tests.\n\n"));
fflush(nullptr);
// coverity[fun_call_w_exception]: uncaught exceptions cause nonzero exit anyway, so don't warn.

View File

@ -36,6 +36,9 @@ TARGET_COMPILE_DEFINITIONS(RpPngFormatTest PRIVATE RP_BUILDING_FOR_DLL=1)
TARGET_LINK_LIBRARIES(RpPngFormatTest PRIVATE ${ZLIB_LIBRARIES} ${PNG_LIBRARY})
TARGET_INCLUDE_DIRECTORIES(RpPngFormatTest PRIVATE ${ZLIB_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS})
TARGET_COMPILE_DEFINITIONS(RpPngFormatTest PRIVATE ${ZLIB_DEFINITIONS} ${PNG_DEFINITIONS})
IF(Fmt_FOUND)
TARGET_LINK_LIBRARIES(RpPngFormatTest PRIVATE ${Fmt_LIBRARY})
ENDIF(Fmt_FOUND)
DO_SPLIT_DEBUG(RpPngFormatTest)
SET_WINDOWS_SUBSYSTEM(RpPngFormatTest CONSOLE)
SET_WINDOWS_ENTRYPOINT(RpPngFormatTest wmain OFF)
@ -60,6 +63,9 @@ IF(ENABLE_DECRYPTION)
TARGET_LINK_LIBRARIES(CryptoTests PRIVATE ${NETTLE_LIBRARY})
TARGET_INCLUDE_DIRECTORIES(CryptoTests PRIVATE ${NETTLE_INCLUDE_DIRS})
ENDIF(NETTLE_LIBRARY)
IF(Fmt_FOUND)
TARGET_LINK_LIBRARIES(CryptoTests PRIVATE ${Fmt_LIBRARY})
ENDIF(Fmt_FOUND)
DO_SPLIT_DEBUG(CryptoTests)
SET_WINDOWS_SUBSYSTEM(CryptoTests CONSOLE)
SET_WINDOWS_ENTRYPOINT(CryptoTests wmain OFF)
@ -78,6 +84,9 @@ IF(NETTLE_LIBRARY)
TARGET_LINK_LIBRARIES(CBCReaderTests PRIVATE ${NETTLE_LIBRARY})
TARGET_INCLUDE_DIRECTORIES(CBCReaderTests PRIVATE ${NETTLE_INCLUDE_DIRS})
ENDIF(NETTLE_LIBRARY)
IF(Fmt_FOUND)
TARGET_LINK_LIBRARIES(CBCReaderTests PRIVATE ${Fmt_LIBRARY})
ENDIF(Fmt_FOUND)
DO_SPLIT_DEBUG(CBCReaderTests)
SET_WINDOWS_SUBSYSTEM(CBCReaderTests CONSOLE)
SET_WINDOWS_ENTRYPOINT(CBCReaderTests wmain OFF)
@ -91,3 +100,6 @@ DO_SPLIT_DEBUG(TimegmTest)
SET_WINDOWS_SUBSYSTEM(TimegmTest CONSOLE)
SET_WINDOWS_ENTRYPOINT(TimegmTest wmain OFF)
ADD_TEST(NAME TimegmTest COMMAND TimegmTest --gtest_brief)
IF(Fmt_FOUND)
TARGET_LINK_LIBRARIES(TimegmTest PRIVATE ${Fmt_LIBRARY})
ENDIF(Fmt_FOUND)

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* HashTest.cpp: Hash class test. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -15,16 +15,23 @@
// Hash
#include "../crypto/Hash.hpp"
// C includes. (C++ namespace)
// C includes (C++ namespace)
#include <cstdio>
// C++ includes.
// C++ includes
#include <iostream>
#include <sstream>
#include <string>
using std::ostringstream;
using std::string;
// libfmt
// FIXME: libfmt has its own "PACKED" definition.
#undef PACKED
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
namespace LibRpBase { namespace Tests {
struct HashTest_mode
@ -79,11 +86,13 @@ void HashTest::CompareByteArrays(
// Output format: (assume ~64 bytes per line)
// 0000: 01 23 45 67 89 AB CD EF 01 23 45 67 89 AB CD EF
const size_t bufSize = ((size / 16) + !!(size % 16)) * 64;
char printf_buf[16];
string s_expected, s_actual;
s_expected.reserve(bufSize);
s_actual.reserve(bufSize);
string s_tmp;
s_tmp.reserve(14);
const uint8_t *pE = expected, *pA = actual;
for (size_t i = 0; i < size; i++, pE++, pA++) {
if (i % 16 == 0) {
@ -94,16 +103,14 @@ void HashTest::CompareByteArrays(
s_actual += '\n';
}
snprintf(printf_buf, sizeof(printf_buf), "%04X: ", static_cast<unsigned int>(i));
s_expected += printf_buf;
s_actual += printf_buf;
s_tmp = fmt::format(FSTR("{:0>4X}: "), static_cast<unsigned int>(i));
s_expected += s_tmp;
s_actual += s_tmp;
}
// Print the byte.
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pE);
s_expected += printf_buf;
snprintf(printf_buf, sizeof(printf_buf), "%02X", *pA);
s_actual += printf_buf;
s_expected += fmt::format(FSTR("{:0>2X}"), *pE);
s_actual += fmt::format(FSTR("{:0>2X}"), *pA);
if (i % 16 == 7) {
s_expected += " ";

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* TimegmTest.cpp: timegm() test. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -13,6 +13,11 @@
// timegm() and/or replacement function.
#include "time_r.h"
// libfmt
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
// NOTE: MSVCRT's documentation for _mkgmtime64() says it has a limited range:
// - Documented: [1970/01/01, 3000/12/31]
// - Actual: [1969/01/01, 3001/01/01] ??? (may need more testing)
@ -237,13 +242,14 @@ extern "C" int gtest_main(int argc, TCHAR *argv[])
static constexpr char func_name[] = "timegm() (internal)";
#endif
fputs("LibRpBase test suite: timegm() tests.\n", stderr);
fprintf(stderr, "Time conversion function in use: %s\n", func_name);
fmt::print(stderr, FSTR("LibRpBase test suite: timegm() tests.\n"));
fmt::print(stderr, FSTR("Time conversion function in use: {:s}\n"), func_name);
if (sizeof(time_t) < 8) {
fputs("*** WARNING: 32-bit time_t is in use.\n"
"*** Disabling tests known to fail with 32-bit time_t.\n", stderr);
fmt::print(stderr,
FSTR("*** WARNING: 32-bit time_t is in use.\n"
"*** Disabling tests known to fail with 32-bit time_t.\n"));
}
fputc('\n', stderr);
fmt::print(stderr, FSTR("\n"));
fflush(nullptr);

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* gtest_init.cpp: Google Test initialization. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -30,6 +30,11 @@ namespace Gdiplus {
# include <gdiplus.h>
#endif /* _WIN32 */
// libfmt
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
extern "C" int gtest_main(int argc, TCHAR *argv[]);
int RP_C_API _tmain(int argc, TCHAR *argv[])
@ -126,7 +131,7 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
ULONG_PTR gdipToken;
Gdiplus::Status status = GdiplusStartup(&gdipToken, &gdipSI, nullptr);
if (status != Gdiplus::Status::Ok) {
fputs("*** ERROR: GDI+ initialization failed.\n", stderr);
fmt::print(stderr, FSTR("*** ERROR: GDI+ initialization failed.\n"));
return EXIT_FAILURE;
}

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (librpbase/tests) *
* RpPngFormatTest.cpp: RpPng format test. *
* *
* Copyright (c) 2016-2024 by David Korth. *
* Copyright (c) 2016-2025 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -61,6 +61,13 @@ using std::array;
using std::shared_ptr;
using std::string;
// libfmt
// FIXME: libfmt has its own "PACKED" definition.
#undef PACKED
#include <fmt/core.h>
#include <fmt/format.h>
#define FSTR FMT_STRING
namespace LibRpBase { namespace Tests {
// tRNS chunk for CI8 paletted images.
@ -1317,7 +1324,7 @@ INSTANTIATE_TEST_SUITE_P(happy_mac_mono_png, RpPngFormatTest,
*/
extern "C" int gtest_main(int argc, TCHAR *argv[])
{
fputs("LibRpBase test suite: RpPng format test.\n\n", stderr);
fmt::print(stderr, FSTR("LibRpBase test suite: RpPng format test.\n\n"));
fflush(nullptr);
// Make sure the CRC32 table is initialized.
@ -1363,7 +1370,7 @@ extern "C" int gtest_main(int argc, TCHAR *argv[])
}
if (!is_found) {
fputs("*** ERROR: Cannot find the png_data test images directory.\n", stderr);
fmt::print(stderr, FSTR("*** ERROR: Cannot find the png_data test images directory.\n"));
return EXIT_FAILURE;
}