Skip to content

Commit

Permalink
Initial C++23 support
Browse files Browse the repository at this point in the history
The required features of the fmt library have been incorporated into the C++20 and C++23 standards via the <format> and <print> headers. Currently, those headers have been implemented in MSVC's standard library, but not yet GCC or Clang.

The source code has been changed so that the program can format/print using either the fmt library OR the C++ standard library. The fmt dependency will eventually be dropped in favor of the standard library when the headers become more widely available.
  • Loading branch information
complexlogic committed Sep 14, 2023
1 parent 5ea1ec7 commit 18425fc
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 126 deletions.
46 changes: 41 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ on:

workflow_dispatch:

defaults:
run:
shell: bash

permissions:
actions: none
checks: none
Expand All @@ -28,6 +24,9 @@ permissions:
repository-projects: none
security-events: none
statuses: read
defaults:
run:
shell: bash

env:
VCPKG_COMMITTISH: 962e5e39f8a25f42522f51fffc574e05a3efd26b
Expand All @@ -45,10 +44,42 @@ jobs:
VCPKG_TRIPLET: custom-triplet

steps:
- name: Remove Old VS version (Hack)
shell: pwsh
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$componentsToRemove= @(
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ARM"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ARM64"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ARM64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.x86.x64"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.x86.x64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL.ARM"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL.ARM64"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.ATL.ARM64.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC.ARM"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC.ARM.Spectre"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC.ARM64"
"Microsoft.VisualStudio.Component.VC.14.35.17.5.MFC.ARM64.Spectre"
)
[string]$workloadArgs = $componentsToRemove | ForEach-Object {" --remove " + $_}
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
# should be run twice
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
- name: Checkout Git repository
uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0

- name: Setup vcpkg
uses: friendlyanon/setup-vcpkg@v1
Expand All @@ -64,7 +95,7 @@ jobs:

- name: Configure
run: cmake -S . -B build
-G "${{env.CMAKE_GENERATOR}}"
-G "${{env.CMAKE_GENERATOR}}"
-DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
-DVCPKG_OVERLAY_PORTS=build/overlays/ports
-DVCPKG_OVERLAY_TRIPLETS=config/vcpkg_triplets
Expand Down Expand Up @@ -120,6 +151,8 @@ jobs:
steps:
- name: Checkout Git repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: "Install dependencies"
run: |
Expand All @@ -141,6 +174,7 @@ jobs:
run: cmake -S . -B build
-DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
-DVCPKG_TARGET_TRIPLET=${{env.VCPKG_TRIPLET}}
-DVCPKG_MANIFEST_FEATURES="external-fmt;"
-DCMAKE_BUILD_TYPE=${{env.CMAKE_BUILD_TYPE}}
-DCMAKE_INSTALL_PREFIX=/usr
-DPACKAGE=${{matrix.config.package_type}}
Expand Down Expand Up @@ -179,6 +213,8 @@ jobs:
steps:
- name: Checkout Git repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: "Install dependencies"
run: |
Expand Down
23 changes: 20 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ set(CMAKE_CXX_STANDARD 20)
set(EXECUTABLE_TITLE "rsgain")
include_directories(${PROJECT_BINARY_DIR})
add_compile_definitions("$<$<CONFIG:DEBUG>:DEBUG>")
if (WIN32)
set (USE_STD_FORMAT true)
if (MSVC_VERSION AND MSVC_VERSION VERSION_LESS 1937)
message(FATAL_ERROR "Visual Studio 17.7 and later supported only")
endif ()
endif ()
if (USE_STD_FORMAT)
if (UNIX)
include(CheckIncludeFiles)
CHECK_INCLUDE_FILES("format;print" SUPPORT_STD_FORMAT LANGUAGE CXX)
if (NOT SUPPORT_STD_FORMAT)
message(FATAL_ERROR "You do not have the required system headers for std::format and/or std::print")
endif ()
endif ()
add_compile_definitions(USE_STD_FORMAT)
set(CMAKE_CXX_STANDARD 23)
endif ()

# GCC 9 and earlier are not supported due to C++20 features
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
Expand Down Expand Up @@ -75,8 +92,6 @@ if (WIN32)
find_library(TAGLIB tag REQUIRED)
find_path(LIBEBUR128_INCLUDE_DIR "ebur128.h" REQUIRED)
find_library(LIBEBUR128 ebur128 REQUIRED)
find_path(FMT_INCLUDE_DIR "fmt/core.h" REQUIRED)
find_library(FMT fmt REQUIRED)
find_path(GETOPT_INCLUDE_DIR "getopt.h" REQUIRED)
find_library(GETOPT getopt REQUIRED)
find_path(INIH_INCLUDE_DIR "ini.h" REQUIRED)
Expand All @@ -99,7 +114,9 @@ elseif (UNIX)
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib>=1.11.1)
pkg_check_modules(LIBEBUR128 REQUIRED IMPORTED_TARGET libebur128>=1.2.4)
pkg_check_modules(INIH REQUIRED IMPORTED_TARGET inih)
pkg_check_modules(FMT REQUIRED IMPORTED_TARGET fmt)
if (NOT USE_STD_FORMAT)
pkg_check_modules(FMT REQUIRED IMPORTED_TARGET fmt)
endif ()
endif()

# Generate Windows application manifest and resource file
Expand Down
14 changes: 10 additions & 4 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@
rsgain builds natively on Unix and Windows, and features a cross-platform CMake build system. The following external dependencies are required:

- [libebur128](https://github.com/jiixyj/libebur128) or [ebur128](https://github.com/sdroege/ebur128)
- taglib
- TagLib
- FFmpeg, specifically the libraries:
+ libavformat
+ libavcodec
+ libswresample
+ libavutil
- fmt
- inih
- Unix only: fmt (see [below](#fmt-library))

The source code is written in C++20, and as such requires a relatively modern compiler to build:
The Windows version uses C++23, and the Unix versions use either C++20 or C++23 depending on the compiler. As such, it requires a relatively modern compiler to build:

- On Windows, use Visual Studio 2022
- On Windows, Visual Studio 2022 17.7 or later is required
- On Linux, use GCC 10 or later
- On macOS, the latest available Xcode for your machine should work

### fmt library

The features that rsgain uses from the fmt library have been integrated into the C++20 and C++23 standards, specifically the `<format>` and `<print>` headers. Currently, neither GCC nor Clang have full support those features, so the fmt libary is required. When they gain support, the fmt dependency will be dropped in favor of the C++ standard library.

The features have been incorporated into Microsoft's C++ standard library implementation, so the fmt library is no longer required for the Windows version.

## Unix

Before starting, make sure you have the development tools Git, CMake and pkg-config installed.
Expand Down
5 changes: 3 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ if (WIN32)
${LIBSWRESAMPLE}
${TAGLIB}
${LIBEBUR128}
${FMT}
${GETOPT}
${INIH}
FDK-AAC::fdk-aac
Expand All @@ -51,9 +50,11 @@ elseif (UNIX)
PkgConfig::TAGLIB
PkgConfig::LIBEBUR128
PkgConfig::INIH
PkgConfig::FMT
Threads::Threads
)
if (NOT USE_STD_FORMAT)
target_link_libraries(${EXECUTABLE_TITLE} PkgConfig::FMT)
endif ()
endif()
set (EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}")
string(TIMESTAMP BUILD_DATE "%Y-%m-%d")
Expand Down
46 changes: 22 additions & 24 deletions src/easymode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
#endif
#include <ini.h>
#include <getopt.h>
#include <fmt/core.h>
#include <fmt/chrono.h>

#include <config.h>
#include "rsgain.hpp"
Expand All @@ -29,7 +27,7 @@
#include "scan.hpp"

#define MAX_THREAD_SLEEP 30
#define HELP_STATS(title, format, ...) fmt::print(COLOR_YELLOW "{:<18} " COLOR_OFF format "\n", title ":" __VA_OPT__(,) __VA_ARGS__)
#define HELP_STATS(title, format, ...) print(COLOR_YELLOW "{:<18} " COLOR_OFF format "\n", title ":" __VA_OPT__(,) __VA_ARGS__)

extern "C" {
int format_handler(void *user, const char *section, const char *name, const char *value);
Expand Down Expand Up @@ -767,7 +765,7 @@ void scan_easy(const std::filesystem::path &path, const std::filesystem::path &p
break;
cv.wait_for(lock, std::chrono::milliseconds(200));
}
fmt::print("\33[2K\n");
print("\33[2K\n");
}

// Single threaded scanning
Expand All @@ -778,69 +776,69 @@ void scan_easy(const std::filesystem::path &path, const std::filesystem::path &p
job->update_data(data);
jobs.pop();
}
fmt::print("\n");
print("\n");
}

// Output statistics at the end
auto duration = std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now() - start_time);
if (!data.files) {
if (data.skipped)
fmt::print("Skipped {:L} file{} with existing ReplayGain information\n",
print("Skipped {:L} file{} with existing ReplayGain information\n",
data.skipped,
data.skipped > 1 ? "s" : ""
);
fmt::print("No files were scanned\n");
print("No files were scanned\n");
return;
}

fmt::print(COLOR_GREEN "Scanning Complete" COLOR_OFF "\n");
print(COLOR_GREEN "Scanning Complete" COLOR_OFF "\n");
HELP_STATS("Time Elapsed", "{:%H:%M:%S}", duration);
HELP_STATS("Files Scanned", "{:L}", data.files);
if (data.skipped)
HELP_STATS("Files Skipped", "{:L}", data.skipped);
HELP_STATS("Clip Adjustments", "{:L} ({:.1f}% of files)", data.clipping_adjustments, 100.f * (float) data.clipping_adjustments / (float) data.files);
HELP_STATS("Average Gain", "{:.2f} dB", data.total_gain / (double) data.files);
double average_peak = data.total_peak / (double) data.files;
HELP_STATS("Average Peak", "{:.6f}{}", average_peak, average_peak != 0.0 ? fmt::format(" ({:.2f} dB)", 20.0 * log10(average_peak)) : "");
HELP_STATS("Average Peak", "{:.6f}{}", average_peak, average_peak != 0.0 ? format(" ({:.2f} dB)", 20.0 * log10(average_peak)) : "");
HELP_STATS("Negative Gains", "{:L} ({:.1f}% of files)", data.total_negative, 100.f * (float) data.total_negative / (float) data.files);
HELP_STATS("Positive Gains", "{:L} ({:.1f}% of files)", data.total_positive, 100.f * (float) data.total_positive / (float) data.files);
fmt::print("\n");
print("\n");

// Inform user of errors
if (!data.error_directories.empty()) {
fmt::print(COLOR_RED "There were errors while scanning the following directories:" COLOR_OFF "\n");
print(COLOR_RED "There were errors while scanning the following directories:" COLOR_OFF "\n");
for (const std::string &s : data.error_directories)
fmt::print("{}\n", s);
fmt::print("\n");
print("{}\n", s);
print("\n");
}
}

static inline void help_easy() {
fmt::print(COLOR_RED "Usage: " COLOR_OFF "{}{}{} easy [OPTIONS] DIRECTORY\n", COLOR_GREEN, EXECUTABLE_TITLE, COLOR_OFF);
print(COLOR_RED "Usage: " COLOR_OFF "{}{}{} easy [OPTIONS] DIRECTORY\n", COLOR_GREEN, EXECUTABLE_TITLE, COLOR_OFF);

fmt::print(" Easy Mode recursively scans a directory using the recommended settings for each\n");
fmt::print(" file type. Easy Mode assumes that you have your music library organized with each album\n");
fmt::print(" in its own folder.\n");
print(" Easy Mode recursively scans a directory using the recommended settings for each\n");
print(" file type. Easy Mode assumes that you have your music library organized with each album\n");
print(" in its own folder.\n");

fmt::print("\n");
fmt::print(COLOR_RED "Options:\n" COLOR_OFF);
print("\n");
print(COLOR_RED "Options:\n" COLOR_OFF);

CMD_HELP("--help", "-h", "Show this help");
CMD_HELP("--quiet", "-q", "Don't print scanning status messages");
fmt::print("\n");
print("\n");

CMD_HELP("--skip-existing", "-S", "Don't scan files with existing ReplayGain information");
CMD_HELP("--multithread=n", "-m n", "Scan files with n parallel threads");
CMD_HELP("--preset=s", "-p s", "Load scan preset s");

fmt::print("\n");
print("\n");

CMD_HELP("--output", "-O", "Output tab-delimited scan data to CSV file per directory");
CMD_HELP("--output=s", "-O s", "Output with sep header (needed for Microsoft Excel compatibility)");
CMD_HELP("--output=a", "-O a", "Output with files sorted in alphanumeric order");

fmt::print("\n");
print("\n");

fmt::print("Please report any issues to " PROJECT_URL "/issues\n");
fmt::print("\n");
print("Please report any issues to " PROJECT_URL "/issues\n");
print("\n");
}
1 change: 0 additions & 1 deletion src/easymode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <mutex>
#include <filesystem>
#include <condition_variable>
#include <fmt/core.h>
#include "scan.hpp"

class WorkerThread {
Expand Down
7 changes: 3 additions & 4 deletions src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include <sys/ioctl.h>
#endif

#include <fmt/core.h>
#include "output.hpp"
#include "config.h"

Expand Down Expand Up @@ -92,7 +91,7 @@ void ProgressBar::update(int pos)

// Only output if we've actually made progress since last the call, or the console width changed
if (c != c_prev || w != w_prev) {
fmt::print(" {:3.0f}% [", percent * 100.f);
print(" {:3.0f}% [", percent * 100.f);
memset(buffer, '=', (size_t) c);
memset(buffer + c, ' ', (size_t) (w - c));
buffer[w] = ']';
Expand All @@ -118,7 +117,7 @@ void ProgressBar::complete()

delete buffer;
buffer = nullptr;
fmt::print("\n");
print("\n");
}

inline int ProgressBar::get_console_width()
Expand All @@ -145,7 +144,7 @@ void MTProgress::update(const std::string &path)
if (w_path + w_message >= w_console)
w_path = w_console - w_message;

fmt::print("\33[2K " COLOR_GREEN "{:5.1f}%" COLOR_OFF MT_MESSAGE "{:.{}}\r",
print("\33[2K " COLOR_GREEN "{:5.1f}%" COLOR_OFF MT_MESSAGE "{:.{}}\r",
100.f * ((float) (cur) / (float) (total)),
path,
w_path < 0 ? 0 : w_path
Expand Down
19 changes: 15 additions & 4 deletions src/output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,18 @@

#include <string>
#include <string_view>

#ifdef USE_STD_FORMAT
#include <format>
#include <print>
#define format std::format
#define print std::print
#else
#include <fmt/core.h>
#include <fmt/chrono.h>
#define format fmt::format
#define print fmt::print
#endif

#define COLOR_GREEN ""
#define COLOR_YELLOW ""
Expand All @@ -76,10 +87,10 @@
#define FAIL_PREFIX "[" COLOR_RED FAIL_CHAR COLOR_OFF "] "

extern int quiet;
#define output_ok(format, ...) if (!quiet) fmt::print(OK_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_warn(format, ...) if (!quiet) fmt::print(WARN_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_error(format, ...) fmt::print(stderr, ERROR_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_fail(format, ...) fmt::print(stderr, FAIL_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_ok(format, ...) if (!quiet) print(OK_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_warn(format, ...) if (!quiet) print(WARN_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_error(format, ...) print(stderr, ERROR_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)
#define output_fail(format, ...) print(stderr, FAIL_PREFIX format "\n" __VA_OPT__(,) __VA_ARGS__)

class ProgressBar {
private:
Expand Down
Loading

0 comments on commit 18425fc

Please sign in to comment.