Merge branch 'PCSX2:master' into master

This commit is contained in:
GregoireLD 2025-01-22 17:14:33 +01:00 committed by GitHub
commit 828ab10f00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1352 changed files with 239108 additions and 138441 deletions

View File

@ -43,7 +43,7 @@ jobs:
compiler: clang
cmakeflags: ""
publish: true
branch: stable
fetchTags: true
stableBuild: false
secrets: inherit

View File

@ -20,7 +20,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
title: "Qt: Update Base Translation"
commit-message: "Qt: Update Base Translation"
commit-message: "[ci skip] Qt: Update Base Translation."
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
body: "Daily update of base translation sources."

View File

@ -22,7 +22,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
title: "PAD: Update to latest controller database"
commit-message: "PAD: Update to latest controller database."
commit-message: "[ci skip] PAD: Update to latest controller database."
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
body: "Weekly automatic update of SDL Controller DB."

View File

@ -23,10 +23,6 @@ on:
cmakeflags:
required: true
type: string
branch:
required: false
type: string
default: "stable"
publish:
required: false
type: boolean
@ -98,7 +94,8 @@ jobs:
run: |
flatpak-builder-lint manifest .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
- name: Build Flatpak
- name: Build Flatpak (beta)
if: ${{ inputs.stableBuild == false || inputs.stableBuild == 'false' }}
uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
@ -108,17 +105,33 @@ jobs:
build-bundle: true
verbose: true
mirror-screenshots-url: https://dl.flathub.org/media
branch: ${{ inputs.branch }}
branch: beta
cache: true
restore-cache: true
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json', '.github/workflows/scripts/common/*.patch') }}
- name: Build Flatpak (stable)
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
manifest-path: .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
arch: x86_64
build-bundle: true
verbose: true
mirror-screenshots-url: https://dl.flathub.org/media
branch: stable
cache: true
restore-cache: true
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json') }}
#- name: Validate build
# run: |
# flatpak-builder-lint repo repo
- name: Push to Flathub beta
if: inputs.publish == true && inputs.branch == 'beta'
- name: Push to Flathub (beta)
if: ${{ inputs.publish == true && (inputs.stableBuild == false || inputs.stableBuild == 'false') }}
uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with:
flat-manager-url: https://hub.flathub.org/
@ -126,8 +139,8 @@ jobs:
token: ${{ secrets.FLATHUB_BETA_TOKEN }}
build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Push to Flathub stable
if: inputs.publish == true && inputs.branch == 'stable'
- name: Push to Flathub (stable)
if: ${{ inputs.publish == true && (inputs.stableBuild == true || inputs.stableBuild == 'true') }}
uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
with:
flat-manager-url: https://hub.flathub.org/

View File

@ -117,7 +117,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/deps
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh') }}
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'

View File

@ -85,7 +85,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/deps
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh') }}
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'

View File

@ -114,7 +114,6 @@ jobs:
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak"
compiler: clang
cmakeflags: ""
branch: "stable"
publish: false
fetchTags: true
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}

View File

@ -114,6 +114,15 @@ diff --git a/libshaderc/include/shaderc/shaderc.h b/libshaderc/include/shaderc/s
index 3a3e97d..65d5b77 100644
--- a/libshaderc/include/shaderc/shaderc.h
+++ b/libshaderc/include/shaderc/shaderc.h
@@ -15,6 +15,8 @@
#ifndef SHADERC_SHADERC_H_
#define SHADERC_SHADERC_H_
+#define SHADERC_PCSX2_CUSTOM 1
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -317,7 +317,7 @@ SHADERC_EXPORT void shaderc_compile_options_set_source_language(
// Sets the compiler mode to generate debug information in the output.
@ -123,69 +132,6 @@ index 3a3e97d..65d5b77 100644
// Sets the compiler optimization level to the given level. Only the last one
// takes effect if multiple calls of this function exist.
@@ -506,6 +506,10 @@ SHADERC_EXPORT void shaderc_compile_options_set_invert_y(
SHADERC_EXPORT void shaderc_compile_options_set_nan_clamp(
shaderc_compile_options_t options, bool enable);
+// Returns a string representation of the specified compilation status.
+SHADERC_EXPORT const char* shaderc_compilation_status_to_string(
+ shaderc_compilation_status status);
+
// An opaque handle to the results of a call to any shaderc_compile_into_*()
// function.
typedef struct shaderc_compilation_result* shaderc_compilation_result_t;
@@ -529,28 +533,31 @@ typedef struct shaderc_compilation_result* shaderc_compilation_result_t;
// present. May be safely called from multiple threads without explicit
// synchronization. If there was failure in allocating the compiler object,
// null will be returned.
-SHADERC_EXPORT shaderc_compilation_result_t shaderc_compile_into_spv(
+SHADERC_EXPORT shaderc_compilation_status shaderc_compile_into_spv(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options);
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result);
// Like shaderc_compile_into_spv, but the result contains SPIR-V assembly text
// instead of a SPIR-V binary module. The SPIR-V assembly syntax is as defined
// by the SPIRV-Tools open source project.
-SHADERC_EXPORT shaderc_compilation_result_t shaderc_compile_into_spv_assembly(
+SHADERC_EXPORT shaderc_compilation_status shaderc_compile_into_spv_assembly(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options);
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result);
// Like shaderc_compile_into_spv, but the result contains preprocessed source
// code instead of a SPIR-V binary module
-SHADERC_EXPORT shaderc_compilation_result_t shaderc_compile_into_preprocessed_text(
+SHADERC_EXPORT shaderc_compilation_status shaderc_compile_into_preprocessed_text(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options);
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result);
// Takes an assembly string of the format defined in the SPIRV-Tools project
// (https://github.com/KhronosGroup/SPIRV-Tools/blob/master/syntax.md),
@@ -561,10 +568,11 @@ SHADERC_EXPORT shaderc_compilation_result_t shaderc_compile_into_preprocessed_te
// May be safely called from multiple threads without explicit synchronization.
// If there was failure in allocating the compiler object, null will be
// returned.
-SHADERC_EXPORT shaderc_compilation_result_t shaderc_assemble_into_spv(
+SHADERC_EXPORT shaderc_compilation_status shaderc_assemble_into_spv(
const shaderc_compiler_t compiler, const char* source_assembly,
size_t source_assembly_size,
- const shaderc_compile_options_t additional_options);
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result);
// The following functions, operating on shaderc_compilation_result_t objects,
// offer only the basic thread-safety guarantee.
diff --git a/libshaderc/include/shaderc/shaderc.hpp b/libshaderc/include/shaderc/shaderc.hpp
index 3817af8..5592b49 100644
--- a/libshaderc/include/shaderc/shaderc.hpp
@ -202,119 +148,6 @@ index 3817af8..5592b49 100644
}
// Sets the compiler optimization level to the given level. Only the last one
@@ -425,9 +426,10 @@ class Compiler {
const char* input_file_name,
const char* entry_point_name,
const CompileOptions& options) const {
- shaderc_compilation_result_t compilation_result = shaderc_compile_into_spv(
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_compile_into_spv(
compiler_, source_text, source_text_size, shader_kind, input_file_name,
- entry_point_name, options.options_);
+ entry_point_name, options.options_, &compilation_result);
return SpvCompilationResult(compilation_result);
}
@@ -451,9 +453,10 @@ class Compiler {
size_t source_text_size,
shaderc_shader_kind shader_kind,
const char* input_file_name) const {
- shaderc_compilation_result_t compilation_result =
- shaderc_compile_into_spv(compiler_, source_text, source_text_size,
- shader_kind, input_file_name, "main", nullptr);
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_compile_into_spv(compiler_, source_text, source_text_size,
+ shader_kind, input_file_name, "main", nullptr,
+ &compilation_result);
return SpvCompilationResult(compilation_result);
}
@@ -504,8 +507,11 @@ class Compiler {
SpvCompilationResult AssembleToSpv(const char* source_assembly,
size_t source_assembly_size,
const CompileOptions& options) const {
- return SpvCompilationResult(shaderc_assemble_into_spv(
- compiler_, source_assembly, source_assembly_size, options.options_));
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_assemble_into_spv(
+ compiler_, source_assembly, source_assembly_size, options.options_,
+ &compilation_result);
+ return SpvCompilationResult(compilation_result);
}
// Assembles the given SPIR-V assembly and returns a SPIR-V binary module
@@ -513,8 +519,11 @@ class Compiler {
// Like the first AssembleToSpv method but uses the default compiler options.
SpvCompilationResult AssembleToSpv(const char* source_assembly,
size_t source_assembly_size) const {
- return SpvCompilationResult(shaderc_assemble_into_spv(
- compiler_, source_assembly, source_assembly_size, nullptr));
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_assemble_into_spv(
+ compiler_, source_assembly, source_assembly_size, nullptr,
+ &compilation_result);
+ return SpvCompilationResult(compilation_result);
}
// Assembles the given SPIR-V assembly and returns a SPIR-V binary module
@@ -523,9 +532,11 @@ class Compiler {
// std::string.
SpvCompilationResult AssembleToSpv(const std::string& source_assembly,
const CompileOptions& options) const {
- return SpvCompilationResult(
- shaderc_assemble_into_spv(compiler_, source_assembly.data(),
- source_assembly.size(), options.options_));
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_assemble_into_spv(
+ compiler_, source_assembly.data(), source_assembly.size(),
+ options.options_, &compilation_result);
+ return SpvCompilationResult(compilation_result);
}
// Assembles the given SPIR-V assembly and returns a SPIR-V binary module
@@ -533,8 +544,10 @@ class Compiler {
// Like the first AssembleToSpv method but the source is provided as a
// std::string and also uses default compiler options.
SpvCompilationResult AssembleToSpv(const std::string& source_assembly) const {
- return SpvCompilationResult(shaderc_assemble_into_spv(
- compiler_, source_assembly.data(), source_assembly.size(), nullptr));
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_assemble_into_spv(compiler_, source_assembly.data(),
+ source_assembly.size(), nullptr, &compilation_result);
+ return SpvCompilationResult(compilation_result);
}
// Compiles the given source GLSL and returns the SPIR-V assembly text
@@ -544,10 +557,11 @@ class Compiler {
const char* source_text, size_t source_text_size,
shaderc_shader_kind shader_kind, const char* input_file_name,
const char* entry_point_name, const CompileOptions& options) const {
- shaderc_compilation_result_t compilation_result =
- shaderc_compile_into_spv_assembly(
- compiler_, source_text, source_text_size, shader_kind,
- input_file_name, entry_point_name, options.options_);
+ shaderc_compilation_result_t compilation_result = nullptr;
+ shaderc_compile_into_spv_assembly(
+ compiler_, source_text, source_text_size, shader_kind,
+ input_file_name, entry_point_name, options.options_,
+ &compilation_result);
return AssemblyCompilationResult(compilation_result);
}
@@ -592,10 +606,10 @@ class Compiler {
const char* source_text, size_t source_text_size,
shaderc_shader_kind shader_kind, const char* input_file_name,
const CompileOptions& options) const {
- shaderc_compilation_result_t compilation_result =
- shaderc_compile_into_preprocessed_text(
+ shaderc_compilation_result_t compilation_result;
+ shaderc_compile_into_preprocessed_text(
compiler_, source_text, source_text_size, shader_kind,
- input_file_name, "main", options.options_);
+ input_file_name, "main", options.options_, &compilation_result);
return PreprocessedSourceCompilationResult(compilation_result);
}
diff --git a/libshaderc/src/shaderc.cc b/libshaderc/src/shaderc.cc
index 63f1bbc..c1a9b12 100644
--- a/libshaderc/src/shaderc.cc
@ -334,146 +167,6 @@ index 63f1bbc..c1a9b12 100644
}
void shaderc_compile_options_set_optimization_level(
@@ -591,8 +595,31 @@ void shaderc_compiler_release(shaderc_compiler_t compiler) {
delete compiler;
}
+const char* shaderc_compilation_status_to_string(shaderc_compilation_status status)
+{
+ static constexpr const std::pair<shaderc_compilation_status, const char*> status_names[] = {
+ {shaderc_compilation_status_success, "shaderc_compilation_status_success"},
+ {shaderc_compilation_status_invalid_stage, "shaderc_compilation_status_invalid_stage"},
+ {shaderc_compilation_status_compilation_error, "shaderc_compilation_status_compilation_error"},
+ {shaderc_compilation_status_internal_error, "shaderc_compilation_status_internal_error"},
+ {shaderc_compilation_status_null_result_object, "shaderc_compilation_status_null_result_object"},
+ {shaderc_compilation_status_invalid_assembly, "shaderc_compilation_status_invalid_assembly"},
+ {shaderc_compilation_status_validation_error, "shaderc_compilation_status_validation_error"},
+ {shaderc_compilation_status_transformation_error, "shaderc_compilation_status_transformation_error"},
+ {shaderc_compilation_status_configuration_error, "shaderc_compilation_status_configuration_error"},
+ };
+
+ for (const auto& it : status_names)
+ {
+ if (status == it.first)
+ return it.second;
+ }
+
+ return "shaderc_compilation_status_unknown";
+}
+
namespace {
-shaderc_compilation_result_t CompileToSpecifiedOutputType(
+shaderc_compilation_result_vector* CompileToSpecifiedOutputType(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
@@ -669,48 +696,59 @@ shaderc_compilation_result_t CompileToSpecifiedOutputType(
}
} // anonymous namespace
-shaderc_compilation_result_t shaderc_compile_into_spv(
+shaderc_compilation_status shaderc_compile_into_spv(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options) {
- return CompileToSpecifiedOutputType(
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result) {
+ shaderc_compilation_result_vector* resultv = CompileToSpecifiedOutputType(
compiler, source_text, source_text_size, shader_kind, input_file_name,
entry_point_name, additional_options,
shaderc_util::Compiler::OutputType::SpirvBinary);
+ *result = resultv;
+ return resultv ? resultv->compilation_status : shaderc_compilation_status_internal_error;
}
-shaderc_compilation_result_t shaderc_compile_into_spv_assembly(
+shaderc_compilation_status shaderc_compile_into_spv_assembly(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options) {
- return CompileToSpecifiedOutputType(
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result) {
+ shaderc_compilation_result_vector* resultv = CompileToSpecifiedOutputType(
compiler, source_text, source_text_size, shader_kind, input_file_name,
entry_point_name, additional_options,
shaderc_util::Compiler::OutputType::SpirvAssemblyText);
+ *result = resultv;
+ return resultv ? resultv->compilation_status : shaderc_compilation_status_internal_error;
}
-shaderc_compilation_result_t shaderc_compile_into_preprocessed_text(
+shaderc_compilation_status shaderc_compile_into_preprocessed_text(
const shaderc_compiler_t compiler, const char* source_text,
size_t source_text_size, shaderc_shader_kind shader_kind,
const char* input_file_name, const char* entry_point_name,
- const shaderc_compile_options_t additional_options) {
- return CompileToSpecifiedOutputType(
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result) {
+ shaderc_compilation_result_vector* resultv = CompileToSpecifiedOutputType(
compiler, source_text, source_text_size, shader_kind, input_file_name,
entry_point_name, additional_options,
shaderc_util::Compiler::OutputType::PreprocessedText);
+ *result = resultv;
+ return resultv ? resultv->compilation_status : shaderc_compilation_status_internal_error;
}
-shaderc_compilation_result_t shaderc_assemble_into_spv(
+shaderc_compilation_status shaderc_assemble_into_spv(
const shaderc_compiler_t compiler, const char* source_assembly,
size_t source_assembly_size,
- const shaderc_compile_options_t additional_options) {
- auto* result = new (std::nothrow) shaderc_compilation_result_spv_binary;
- if (!result) return nullptr;
- result->compilation_status = shaderc_compilation_status_invalid_assembly;
- if (!compiler->initializer) return result;
- if (source_assembly == nullptr) return result;
+ const shaderc_compile_options_t additional_options,
+ shaderc_compilation_result_t* result) {
+ auto* bresult = new (std::nothrow) shaderc_compilation_result_spv_binary;
+ if (!bresult) return shaderc_compilation_status_internal_error;
+ bresult->compilation_status = shaderc_compilation_status_invalid_assembly;
+ *result = bresult;
+ if (!compiler->initializer) return bresult->compilation_status;
+ if (source_assembly == nullptr) return bresult->compilation_status;
TRY_IF_EXCEPTIONS_ENABLED {
spv_binary assembling_output_data = nullptr;
@@ -724,22 +762,22 @@ shaderc_compilation_result_t shaderc_assemble_into_spv(
GetCompilerTargetEnvVersion(target_env_version),
{source_assembly, source_assembly + source_assembly_size},
&assembling_output_data, &errors);
- result->num_errors = !assembling_succeeded;
+ bresult->num_errors = !assembling_succeeded;
if (assembling_succeeded) {
- result->SetOutputData(assembling_output_data);
- result->output_data_size =
+ bresult->SetOutputData(assembling_output_data);
+ bresult->output_data_size =
assembling_output_data->wordCount * sizeof(uint32_t);
- result->compilation_status = shaderc_compilation_status_success;
+ bresult->compilation_status = shaderc_compilation_status_success;
} else {
- result->messages = std::move(errors);
- result->compilation_status = shaderc_compilation_status_invalid_assembly;
+ bresult->messages = std::move(errors);
+ bresult->compilation_status = shaderc_compilation_status_invalid_assembly;
}
}
CATCH_IF_EXCEPTIONS_ENABLED(...) {
- result->compilation_status = shaderc_compilation_status_internal_error;
+ bresult->compilation_status = shaderc_compilation_status_internal_error;
}
- return result;
+ return bresult->compilation_status;
}
size_t shaderc_result_get_length(const shaderc_compilation_result_t result) {
diff --git a/libshaderc_util/include/libshaderc_util/compiler.h b/libshaderc_util/include/libshaderc_util/compiler.h
index d9d02b9..b076ec8 100644
--- a/libshaderc_util/include/libshaderc_util/compiler.h

View File

@ -6,6 +6,9 @@ set -e
# While we use custom Qt builds for our releases, the Qt6 package will be good enough
# for just updating translations. Saves building it for this action alone.
"$SCRIPTDIR/../../../../tools/retry.sh" sudo apt-get -y install qt6-l10n-tools
"$SCRIPTDIR/../../../../tools/retry.sh" sudo apt-get -y install qt6-l10n-tools python3
"$SCRIPTDIR/../../../../tools/generate_fullscreen_ui_translation_strings.py"
"$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_glyph_ranges.py"
"$SCRIPTDIR/../../../../tools/generate_update_fa_glyph_ranges.py"
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_base_translation.sh"

View File

@ -206,7 +206,7 @@ GIT_VERSION=$(git tag --points-at HEAD)
if [[ "${GIT_VERSION}" == "" ]]; then
# In the odd event that we run this script before the release gets tagged.
GIT_VERSION=$(git describe --tags)
GIT_VERSION=$(git describe --tags || true)
if [[ "${GIT_VERSION}" == "" ]]; then
GIT_VERSION=$(git rev-parse HEAD)
fi

View File

@ -16,11 +16,11 @@ fi
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEG=9f
LIBPNG=1.6.43
LIBWEBP=1.4.0
LIBPNG=1.6.45
LIBWEBP=1.5.0
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
SDL=SDL2-2.30.8
QT=6.7.2
SDL=SDL2-2.30.11
QT=6.8.1
ZSTD=1.5.6
SHADERC=2024.1
@ -34,17 +34,17 @@ cd deps-build
cat > SHASUMS <<EOF
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-$LIBPNG.tar.xz
61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
380c295ea76b9bd72d90075793971c8bcb232ba0a69a9b14da4ae8f603350058 $SDL.tar.gz
8b8d4aef2038533da814965220f88f77d60dfa0f32685f80ead65e501337da7f $SDL.tar.gz
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
c5f22a5e10fb162895ded7de0963328e7307611c688487b5d152c9ee64767599 qtbase-everywhere-src-$QT.tar.xz
e1a1d8785fae67d16ad0a443b01d5f32663a6b68d275f1806ebab257485ce5d6 qtimageformats-everywhere-src-$QT.tar.xz
fb0d1286a35be3583fee34aeb5843c94719e07193bdf1d4d8b0dc14009caef01 qtsvg-everywhere-src-$QT.tar.xz
58e855ad1b2533094726c8a425766b63a04a0eede2ed85086860e54593aa4b2a qttools-everywhere-src-$QT.tar.xz
9845780b5dc1b7279d57836db51aeaf2e4a1160c42be09750616f39157582ca9 qttranslations-everywhere-src-$QT.tar.xz
a2a057e1dd644bd44abb9990fecc194b2e25c2e0f39e81aa9fee4c1e5e2a8a5b qtwayland-everywhere-src-$QT.tar.xz
40b14562ef3bd779bc0e0418ea2ae08fa28235f8ea6e8c0cb3bce1d6ad58dcaf qtbase-everywhere-src-$QT.tar.xz
138cc2909aa98f5ff7283e36eb3936eb5e625d3ca3b4febae2ca21d8903dd237 qtimageformats-everywhere-src-$QT.tar.xz
3d0de73596e36b2daa7c48d77c4426bb091752856912fba720215f756c560dd0 qtsvg-everywhere-src-$QT.tar.xz
9d43d409be08b8681a0155a9c65114b69c9a3fc11aef6487bb7fdc5b283c432d qttools-everywhere-src-$QT.tar.xz
635a6093e99152243b807de51077485ceadd4786d4acb135b9340b2303035a4a qttranslations-everywhere-src-$QT.tar.xz
2226fbde4e2ddd12f8bf4b239c8f38fd706a54e789e63467dfddc77129eca203 qtwayland-everywhere-src-$QT.tar.xz
eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADERC.tar.gz
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
@ -150,7 +150,7 @@ tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
mkdir build
cd build
../configure -prefix "$INSTALLDIR" -release -dbus-linked -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -gtk -- -DFEATURE_dbus=ON -DFEATURE_icu=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
../configure -prefix "$INSTALLDIR" -release -dbus-linked -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -gtk -- -DFEATURE_dbus=ON -DFEATURE_icu=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
cmake --build . --parallel
ninja install
cd ../../

View File

@ -8,8 +8,8 @@
{
"type": "git",
"url": "https://github.com/the-tcpdump-group/libpcap.git",
"tag": "libpcap-1.10.4",
"commit": "104271ba4a14de6743e43bcf87536786d8fddea4"
"tag": "libpcap-1.10.5",
"commit": "bbcbc9174df3298a854daee2b3e666a4b6e5383a"
}
],
"cleanup": [

View File

@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL2-2.30.8.tar.gz",
"sha256": "380c295ea76b9bd72d90075793971c8bcb232ba0a69a9b14da4ae8f603350058"
"url": "https://libsdl.org/release/SDL2-2.30.11.tar.gz",
"sha256": "8b8d4aef2038533da814965220f88f77d60dfa0f32685f80ead65e501337da7f"
}
],
"cleanup": [

View File

@ -1,15 +1,15 @@
{
"app-id": "net.pcsx2.PCSX2",
"runtime": "org.kde.Platform",
"runtime-version": "6.7",
"runtime-version": "6.8",
"sdk": "org.kde.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.llvm17"
"org.freedesktop.Sdk.Extension.llvm18"
],
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"directory": "lib/ffmpeg",
"version": "23.08",
"version": "24.08",
"add-ld-path": ".",
"autodownload": true
}
@ -44,8 +44,8 @@
"config-opts": [
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm17/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm17/bin/clang++",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm18/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm18/bin/clang++",
"-DCMAKE_EXE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_MODULE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",

View File

@ -38,17 +38,17 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.2
HARFBUZZ=8.3.1
SDL=SDL2-2.30.3
ZSTD=1.5.5
FREETYPE=2.13.3
HARFBUZZ=10.0.1
SDL=SDL2-2.30.11
ZSTD=1.5.6
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
LIBPNG=1.6.43
LIBPNG=1.6.45
LIBJPEG=9f
LIBWEBP=1.3.2
LIBWEBP=1.5.0
FFMPEG=6.0
MOLTENVK=1.2.8
QT=6.7.1
MOLTENVK=1.2.9
QT=6.7.2
SHADERC=2024.1
SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
@ -74,21 +74,21 @@ CMAKE_ARCH_ARM64=-DCMAKE_OSX_ARCHITECTURES="arm64"
CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d freetype-$FREETYPE.tar.xz
19a54fe9596f7a47c502549fce8e8a10978c697203774008cc173f8360b19a9a harfbuzz-$HARFBUZZ.tar.gz
820440072f8f5b50188c1dae104f2ad25984de268785be40c41a099a510f0aec $SDL.tar.gz
9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4 zstd-$ZSTD.tar.gz
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
e7358ea86fe10fb9261931af6f010d4358dac64f7074420ca9bc94aae2bdd542 harfbuzz-$HARFBUZZ.tar.gz
8b8d4aef2038533da814965220f88f77d60dfa0f32685f80ead65e501337da7f $SDL.tar.gz
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-$LIBPNG.tar.xz
2a499607df669e40258e53d0ade8035ba4ec0175244869d1025d460562aa09b4 libwebp-$LIBWEBP.tar.gz
926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
85beaf8abfcc54d9da0ff0257ae311abd9e7aa96e53da37e1c37d6bc04ac83cd v$MOLTENVK.tar.gz
b7338da1bdccb4d861e714efffaa83f174dfe37e194916bfd7ec82279a6ace19 qtbase-everywhere-src-$QT.tar.xz
a733b98f771064d000476b8861f822143982749448ba8abf9f1813edb8dfe79f qtimageformats-everywhere-src-$QT.tar.xz
3ed5b80f7228c41dd463b7a57284ed273d224d1c323c0dd78c5209635807cbce qtsvg-everywhere-src-$QT.tar.xz
0953cddf6248f3959279a10904892e8a98eb3e463d729a174b6fc47febd99824 qttools-everywhere-src-$QT.tar.xz
03d71565872b0e0e7303349071df031ab0f922f6dbdd3a5ec1ade9e188e4fbf4 qttranslations-everywhere-src-$QT.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
c5f22a5e10fb162895ded7de0963328e7307611c688487b5d152c9ee64767599 qtbase-everywhere-src-$QT.tar.xz
e1a1d8785fae67d16ad0a443b01d5f32663a6b68d275f1806ebab257485ce5d6 qtimageformats-everywhere-src-$QT.tar.xz
fb0d1286a35be3583fee34aeb5843c94719e07193bdf1d4d8b0dc14009caef01 qtsvg-everywhere-src-$QT.tar.xz
58e855ad1b2533094726c8a425766b63a04a0eede2ed85086860e54593aa4b2a qttools-everywhere-src-$QT.tar.xz
9845780b5dc1b7279d57836db51aeaf2e4a1160c42be09750616f39157582ca9 qttranslations-everywhere-src-$QT.tar.xz
eb3b5f0c16313d34f208d90c2fa1e588a23283eed63b101edd5422be6165d528 shaderc-$SHADERC.tar.gz
aa27e4454ce631c5a17924ce0624eac736da19fc6f5a2ab15a6c58da7b36950f shaderc-glslang-$SHADERC_GLSLANG.tar.gz
5d866ce34a4b6908e262e5ebfffc0a5e11dd411640b5f24c85a80ad44c0d4697 shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
@ -330,20 +330,6 @@ echo "Installing Qt Tools..."
rm -fr "qttools-everywhere-src-$QT"
tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
# Linguist relies on a library in the Designer target, which takes 5-7 minutes to build on the CI
# Avoid it by not building Linguist, since we only need the tools that come with it
patch -u src/linguist/CMakeLists.txt <<EOF
--- src/linguist/CMakeLists.txt
+++ src/linguist/CMakeLists.txt
@@ -14,7 +14,7 @@
add_subdirectory(lrelease-pro)
add_subdirectory(lupdate)
add_subdirectory(lupdate-pro)
-if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND NOT no-png)
+if(QT_FEATURE_process AND QT_FEATURE_pushbutton AND QT_FEATURE_toolbutton AND TARGET Qt::Widgets AND TARGET Qt::PrintSupport AND NOT no-png)
add_subdirectory(linguist)
endif()
EOF
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF

View File

@ -20,14 +20,14 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.13.2
HARFBUZZ=8.3.1
SDL=SDL2-2.30.8
FREETYPE=2.13.3
HARFBUZZ=10.0.1
SDL=SDL2-2.30.11
ZSTD=1.5.6
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
LIBPNG=1.6.43
LIBPNG=1.6.45
LIBJPEG=9f
LIBWEBP=1.4.0
LIBWEBP=1.5.0
FFMPEG=6.0
MOLTENVK=1.2.9
QT=6.7.2
@ -54,13 +54,13 @@ CMAKE_COMMON=(
)
cat > SHASUMS <<EOF
12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d freetype-$FREETYPE.tar.xz
19a54fe9596f7a47c502549fce8e8a10978c697203774008cc173f8360b19a9a harfbuzz-$HARFBUZZ.tar.gz
380c295ea76b9bd72d90075793971c8bcb232ba0a69a9b14da4ae8f603350058 $SDL.tar.gz
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
e7358ea86fe10fb9261931af6f010d4358dac64f7074420ca9bc94aae2bdd542 harfbuzz-$HARFBUZZ.tar.gz
8b8d4aef2038533da814965220f88f77d60dfa0f32685f80ead65e501337da7f $SDL.tar.gz
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-$LIBPNG.tar.xz
61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
926485350139ffb51ef69760db35f78846c805fef3d59bfdcb2fba704663f370 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b jpegsrc.v$LIBJPEG.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz

View File

@ -42,15 +42,15 @@ echo INSTALLDIR=%INSTALLDIR%
cd "%BUILDDIR%"
set FREETYPE=2.13.2
set HARFBUZZ=8.3.1
set FREETYPE=2.13.3
set HARFBUZZ=10.0.1
set LIBJPEG=9f
set LIBPNG=1643
set LIBPNG=1645
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
set QT=6.7.2
set QTMINOR=6.7
set SDL=SDL2-2.30.8
set WEBP=1.4.0
set QT=6.8.1
set QTMINOR=6.8
set SDL=SDL2-2.30.11
set WEBP=1.5.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.6
@ -60,21 +60,21 @@ set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
set SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
set SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 1ac27e16c134a7f2ccea177faba19801131116fd682efc1f5737037c5db224b5 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip b2bc56184ae37324bc4829fde7d3f9e6916866ad711ee85792e457547c9fd127 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1643.zip fc466a1e638e635d6c66363bdf3f38555b81b0141d0b06ba45b49ccca327436d || goto error
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 8adf9f5a4b6022aa2744f45c89ce347df46fea8403e99f01d650b11c417d0aa8 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1645.zip a66c4b1350b67776e90263e2550933067cd9ccbd318db489f84dcc0d2b033249 || goto error
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" abe2921dffcb25d39d270454810b211a9f47be3e5e802bc45e7d058f286a325e || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 488119aad60719a085a1e45c31641ac2406ef86fc088a3c99885c18e9d6b4bb9 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8e736b02db7dd67dbe834d56503b242344ce85d3532da692f1812b30ccf80997 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 85a22142270a92be0dd0ab5d27cc53617b2a2f1a45fc0a3890024164032f8475 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 9e15f1fdbd83e4123e733bff20aff1b45921c09056c3790fa42eb71d0a5cd01f || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" d1f25e0f68a1282feffdd5fe795a027ee5f16ad19e3b1fa2e04a51cea19110ec || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" a0b3e7ac5f708042683ff0f22e069bdf75563540c615f9854ecc9bc8913e2488 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" e22d997bd15b795a176c8da62c8c1da0a674eb534e02f7c01ca507bf11bce0c3 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 247a0a58039275a5a4fb499a600a90f66dc6e00321bb6f86a9b8d8020344d853 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 57bd332e5550ff70a852560c591b786b6ba587c5e41cb5ef91038d82db137ab9 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c65a89140f5d68137ffec67d631ec97002fb37077d9b4eb4ee45cbec39b1c38a || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 30a8e7773e1f274557e049a97f158b808f344247da03ae5240e4956c81d51cd5 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 675f144b11f8ab2424b64bed8ccdca5d3f35b9326046fa7a883925dd180f0651 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error

View File

@ -40,15 +40,15 @@ set "PATH=%PATH%;%INSTALLDIR%\bin"
cd "%BUILDDIR%"
set FREETYPE=2.13.2
set HARFBUZZ=8.3.1
set FREETYPE=2.13.3
set HARFBUZZ=10.0.1
set LIBJPEG=9f
set LIBPNG=1643
set LIBPNG=1645
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
set QT=6.7.2
set QTMINOR=6.7
set SDL=SDL2-2.30.8
set WEBP=1.4.0
set QT=6.8.1
set QTMINOR=6.8
set SDL=SDL2-2.30.11
set WEBP=1.5.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.6
@ -58,21 +58,21 @@ set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
set SHADERC_SPIRVHEADERS=5e3ad389ee56fca27c9705d093ae5387ce404df4
set SHADERC_SPIRVTOOLS=dd4b663e13c07fea4fbb3f70c1c91c86731099f7
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 1ac27e16c134a7f2ccea177faba19801131116fd682efc1f5737037c5db224b5 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip b2bc56184ae37324bc4829fde7d3f9e6916866ad711ee85792e457547c9fd127 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1643.zip fc466a1e638e635d6c66363bdf3f38555b81b0141d0b06ba45b49ccca327436d || goto error
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 8adf9f5a4b6022aa2744f45c89ce347df46fea8403e99f01d650b11c417d0aa8 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1645.zip a66c4b1350b67776e90263e2550933067cd9ccbd318db489f84dcc0d2b033249 || goto error
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" abe2921dffcb25d39d270454810b211a9f47be3e5e802bc45e7d058f286a325e || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 488119aad60719a085a1e45c31641ac2406ef86fc088a3c99885c18e9d6b4bb9 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8e736b02db7dd67dbe834d56503b242344ce85d3532da692f1812b30ccf80997 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 85a22142270a92be0dd0ab5d27cc53617b2a2f1a45fc0a3890024164032f8475 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 9e15f1fdbd83e4123e733bff20aff1b45921c09056c3790fa42eb71d0a5cd01f || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" d1f25e0f68a1282feffdd5fe795a027ee5f16ad19e3b1fa2e04a51cea19110ec || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" a0b3e7ac5f708042683ff0f22e069bdf75563540c615f9854ecc9bc8913e2488 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" e22d997bd15b795a176c8da62c8c1da0a674eb534e02f7c01ca507bf11bce0c3 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 247a0a58039275a5a4fb499a600a90f66dc6e00321bb6f86a9b8d8020344d853 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 57bd332e5550ff70a852560c591b786b6ba587c5e41cb5ef91038d82db137ab9 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c65a89140f5d68137ffec67d631ec97002fb37077d9b4eb4ee45cbec39b1c38a || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 30a8e7773e1f274557e049a97f158b808f344247da03ae5240e4956c81d51cd5 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 675f144b11f8ab2424b64bed8ccdca5d3f35b9326046fa7a883925dd180f0651 || goto error
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 8df152f4969b308546306c074628de761f0b80265de7de534e3822fab22d7535 || goto error
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
@ -195,6 +195,9 @@ cd "qtbase-everywhere-src-%QT%" || goto error
rem Disable the PCRE2 JIT, it doesn't properly verify AVX2 support.
%PATCH% -p1 < "%SCRIPTDIR%\qtbase-disable-pcre2-jit.patch" || goto error
rem Hackfix settings icon stretching
%PATCH% -p1 < "%SCRIPTDIR%\qtbase-fix-icon-stretch.patch" || goto error
cmake -B build -DFEATURE_sql=OFF -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" %FORCEPDB% -DINPUT_gui=yes -DINPUT_widgets=yes -DINPUT_ssl=yes -DINPUT_openssl=no -DINPUT_schannel=yes -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON %QTBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

View File

@ -0,0 +1,13 @@
diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
index 208420d7e8..26ef6f31ef 100644
--- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
@@ -4232,8 +4232,6 @@ QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption
case SE_ItemViewItemDecoration:
rect = QWindowsStyle::subElementRect(element, option, widget);
- if (qstyleoption_cast<const QStyleOptionViewItem *>(option))
- rect.adjust(-2, 0, 2, 0);
break;
case SE_ItemViewItemFocusRect:

View File

@ -13,7 +13,7 @@ jobs:
lint_vs_proj_files:
name: Lint VS Project Files
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
runs-on: windows-2019
runs-on: windows-2025
steps:
- name: Checkout Repository
uses: actions/checkout@v4

View File

@ -12,7 +12,7 @@ on:
os:
required: false
type: string
default: windows-2022
default: windows-2025
platform:
required: false
type: string
@ -55,13 +55,31 @@ jobs:
POWERSHELL_TELEMETRY_OPTOUT: 1
steps:
- name: Tempfix Clang
if: inputs.configuration == 'CMake'
run: choco uninstall llvm
- name: Checkout Repository
uses: actions/checkout@v4
- name: Configure MSBuild Clang Version
if: inputs.configuration != 'CMake'
shell: pwsh
run: |
[string[]] $clang_cl = &clang-cl.exe --version
$version = [Regex]::Match($clang_cl[0], "(\d+\.\d+\.\d+)")
$path = $clang_cl[3].TrimStart("InstalledDir: ").TrimEnd("\bin")
$output = @"
<Project>
<PropertyGroup>
<LLVMInstallDir>$path</LLVMInstallDir>
<LLVMToolsVersion>$version</LLVMToolsVersion>
</PropertyGroup>
</Project>
"@
Write-Host $output
$output | Out-File Directory.build.props
# actions/checkout elides tags, fetch them primarily for releases
- name: Fetch Tags
if: ${{ inputs.fetchTags }}
@ -100,7 +118,7 @@ jobs:
uses: actions/cache@v4
with:
path: deps
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat') }}
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'

View File

@ -10,34 +10,35 @@ Result<ElfFile> ElfFile::parse(std::vector<u8> image)
ElfFile elf;
elf.image = std::move(image);
const ElfIdentHeader* ident = get_packed<ElfIdentHeader>(elf.image, 0);
const ElfIdentHeader* ident = get_unaligned<ElfIdentHeader>(elf.image, 0);
CCC_CHECK(ident, "ELF ident header out of range.");
CCC_CHECK(ident->magic == CCC_FOURCC("\x7f\x45\x4c\x46"), "Not an ELF file.");
CCC_CHECK(ident->e_class == ElfIdentClass::B32, "Wrong ELF class (not 32 bit).");
const ElfFileHeader* header = get_packed<ElfFileHeader>(elf.image, sizeof(ElfIdentHeader));
const ElfFileHeader* header = get_unaligned<ElfFileHeader>(elf.image, sizeof(ElfIdentHeader));
CCC_CHECK(header, "ELF file header out of range.");
elf.file_header = *header;
const ElfSectionHeader* shstr_section_header = get_packed<ElfSectionHeader>(elf.image, header->shoff + header->shstrndx * sizeof(ElfSectionHeader));
const ElfSectionHeader* shstr_section_header =
get_unaligned<ElfSectionHeader>(elf.image, header->shoff + header->shstrndx * sizeof(ElfSectionHeader));
CCC_CHECK(shstr_section_header, "ELF section name header out of range.");
for(u32 i = 0; i < header->shnum; i++) {
u64 header_offset = header->shoff + i * sizeof(ElfSectionHeader);
const ElfSectionHeader* section_header = get_packed<ElfSectionHeader>(elf.image, header_offset);
const ElfSectionHeader* section_header = get_unaligned<ElfSectionHeader>(elf.image, header_offset);
CCC_CHECK(section_header, "ELF section header out of range.");
const char* name = get_string(elf.image, shstr_section_header->offset + section_header->name);
CCC_CHECK(section_header, "ELF section name out of range.");
std::optional<std::string_view> name = get_string(elf.image, shstr_section_header->offset + section_header->name);
CCC_CHECK(name.has_value(), "ELF section name out of range.");
ElfSection& section = elf.sections.emplace_back();
section.name = name;
section.name = *name;
section.header = *section_header;
}
for(u32 i = 0; i < header->phnum; i++) {
u64 header_offset = header->phoff + i * sizeof(ElfProgramHeader);
const ElfProgramHeader* program_header = get_packed<ElfProgramHeader>(elf.image, header_offset);
const ElfProgramHeader* program_header = get_unaligned<ElfProgramHeader>(elf.image, header_offset);
CCC_CHECK(program_header, "ELF program header out of range.");
elf.segments.emplace_back(*program_header);
@ -93,7 +94,7 @@ const ElfProgramHeader* ElfFile::entry_point_segment() const
return entry_segment;
}
Result<std::span<const u8>> ElfFile::get_virtual(u32 address, u32 size) const
std::optional<std::span<const u8>> ElfFile::get_virtual(u32 address, u32 size) const
{
u32 end_address = address + size;
@ -109,17 +110,19 @@ Result<std::span<const u8>> ElfFile::get_virtual(u32 address, u32 size) const
}
}
return CCC_FAILURE("No ELF segment for address range 0x%x to 0x%x.", address, end_address);
return std::nullopt;
}
Result<void> ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const
bool ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const
{
Result<std::span<const u8>> block = get_virtual(address, size);
CCC_RETURN_IF_ERROR(block);
std::optional<std::span<const u8>> block = get_virtual(address, size);
if(!block.has_value()) {
return false;
}
memcpy(dest, block->data(), size);
return Result<void>();
return true;
}
}

View File

@ -125,18 +125,20 @@ struct ElfFile {
const ElfProgramHeader* entry_point_segment() const;
// Retrieve a block of data in an ELF file given its address and size.
Result<std::span<const u8>> get_virtual(u32 address, u32 size) const;
std::optional<std::span<const u8>> get_virtual(u32 address, u32 size) const;
// Copy a block of data in an ELF file to the destination buffer given its
// address and size.
Result<void> copy_virtual(u8* dest, u32 address, u32 size) const;
bool copy_virtual(u8* dest, u32 address, u32 size) const;
// Retrieve an object of type T from an ELF file given its address.
template <typename T>
Result<T> get_object_virtual(u32 address) const
std::optional<T> get_object_virtual(u32 address) const
{
Result<std::span<const u8>> result = get_virtual(address, sizeof(T));
CCC_RETURN_IF_ERROR(result);
std::optional<std::span<const u8>> result = get_virtual(address, sizeof(T));
if(!result.has_value()) {
return std::nullopt;
}
return *(T*) result->data();
}
@ -144,10 +146,12 @@ struct ElfFile {
// Retrieve an array of objects of type T from an ELF file given its
// address and element count.
template <typename T>
Result<std::span<const T>> get_array_virtual(u32 address, u32 element_count) const
std::optional<std::span<const T>> get_array_virtual(u32 address, u32 element_count) const
{
Result<std::span<const u8>> result = get_virtual(address, element_count * sizeof(T));
CCC_RETURN_IF_ERROR(result);
std::optional<std::span<const u8>> result = get_virtual(address, element_count * sizeof(T));
if(!result.has_value()) {
return std::nullopt;
}
return std::span<const T>((T*) result->data(), (T*) (result->data() + result->size()));
}

View File

@ -60,7 +60,7 @@ Result<void> import_symbols(
DemanglerFunctions demangler)
{
for(u32 i = 0; i < symtab.size() / sizeof(Symbol); i++) {
const Symbol* symbol = get_packed<Symbol>(symtab, i * sizeof(Symbol));
const Symbol* symbol = get_unaligned<Symbol>(symtab, i * sizeof(Symbol));
CCC_ASSERT(symbol);
Address address;
@ -86,13 +86,14 @@ Result<void> import_symbols(
}
}
const char* string = get_string(strtab, symbol->name);
CCC_CHECK(string, "Symbol string out of range.");
std::optional<std::string_view> string_view = get_string(strtab, symbol->name);
CCC_CHECK(string_view.has_value(), "Symbol string out of range.");
std::string string(*string_view);
switch(symbol->type()) {
case SymbolType::NOTYPE: {
Result<Label*> label = database.labels.create_symbol(
string, group.source, group.module_symbol, address, importer_flags, demangler);
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(label);
// These symbols get emitted at the same addresses as functions
@ -108,7 +109,7 @@ Result<void> import_symbols(
case SymbolType::OBJECT: {
if(symbol->size != 0) {
Result<GlobalVariable*> global_variable = database.global_variables.create_symbol(
string, group.source, group.module_symbol, address, importer_flags, demangler);
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(global_variable);
if(*global_variable) {
@ -116,7 +117,7 @@ Result<void> import_symbols(
}
} else {
Result<Label*> label = database.labels.create_symbol(
string, group.source, group.module_symbol, address, importer_flags, demangler);
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(label);
}
@ -124,7 +125,7 @@ Result<void> import_symbols(
}
case SymbolType::FUNC: {
Result<Function*> function = database.functions.create_symbol(
string, group.source, group.module_symbol, address, importer_flags, demangler);
std::move(string), group.source, group.module_symbol, address, importer_flags, demangler);
CCC_RETURN_IF_ERROR(function);
if(*function) {
@ -135,7 +136,7 @@ Result<void> import_symbols(
}
case SymbolType::FILE: {
Result<SourceFile*> source_file = database.source_files.create_symbol(
string, group.source, group.module_symbol);
std::move(string), group.source, group.module_symbol);
CCC_RETURN_IF_ERROR(source_file);
break;
@ -153,18 +154,18 @@ Result<void> print_symbol_table(FILE* out, std::span<const u8> symtab, std::span
fprintf(out, " Num: Value Size Type Bind Vis Ndx Name\n");
for(u32 i = 0; i < symtab.size() / sizeof(Symbol); i++) {
const Symbol* symbol = get_packed<Symbol>(symtab, i * sizeof(Symbol));
const Symbol* symbol = get_unaligned<Symbol>(symtab, i * sizeof(Symbol));
CCC_ASSERT(symbol);
const char* type = symbol_type_to_string(symbol->type());
const char* bind = symbol_bind_to_string(symbol->bind());
const char* visibility = symbol_visibility_to_string(symbol->visibility());
const char* string = get_string(strtab, symbol->name);
CCC_CHECK(string, "Symbol string out of range.");
std::optional<std::string_view> string = get_string(strtab, symbol->name);
CCC_CHECK(string.has_value(), "Symbol string out of range.");
fprintf(out, "%6u: %08x %5u %-7s %-7s %-7s %3u %s\n",
i, symbol->value, symbol->size, type, bind, visibility, symbol->shndx, string);
i, symbol->value, symbol->size, type, bind, visibility, symbol->shndx, string->data());
}

View File

@ -90,7 +90,7 @@ Result<void> SymbolTableReader::init(std::span<const u8> elf, s32 section_offset
m_elf = elf;
m_section_offset = section_offset;
m_hdrr = get_packed<SymbolicHeader>(m_elf, m_section_offset);
m_hdrr = get_unaligned<SymbolicHeader>(m_elf, m_section_offset);
CCC_CHECK(m_hdrr != nullptr, "MIPS debug section header out of bounds.");
CCC_CHECK(m_hdrr->magic == 0x7009, "Invalid symbolic header.");
@ -116,7 +116,7 @@ Result<File> SymbolTableReader::parse_file(s32 index) const
File file;
u64 fd_offset = m_hdrr->file_descriptors_offset + index * sizeof(FileDescriptor);
const FileDescriptor* fd_header = get_packed<FileDescriptor>(m_elf, fd_offset + m_fudge_offset);
const FileDescriptor* fd_header = get_unaligned<FileDescriptor>(m_elf, fd_offset + m_fudge_offset);
CCC_CHECK(fd_header != nullptr, "MIPS debug file descriptor out of bounds.");
CCC_CHECK(fd_header->f_big_endian == 0, "Not little endian or bad file descriptor table.");
@ -124,16 +124,16 @@ Result<File> SymbolTableReader::parse_file(s32 index) const
s32 rel_raw_path_offset = fd_header->strings_offset + fd_header->file_path_string_offset;
s32 raw_path_offset = m_hdrr->local_strings_offset + rel_raw_path_offset + m_fudge_offset;
const char* command_line_path = get_string(m_elf, raw_path_offset);
if(command_line_path) {
file.command_line_path = command_line_path;
std::optional<std::string_view> command_line_path = get_string(m_elf, raw_path_offset);
if(command_line_path.has_value()) {
file.command_line_path = *command_line_path;
}
// Parse local symbols.
for(s64 j = 0; j < fd_header->symbol_count; j++) {
u64 rel_symbol_offset = (fd_header->isym_base + j) * sizeof(SymbolHeader);
u64 symbol_offset = m_hdrr->local_symbols_offset + rel_symbol_offset + m_fudge_offset;
const SymbolHeader* symbol_header = get_packed<SymbolHeader>(m_elf, symbol_offset);
const SymbolHeader* symbol_header = get_unaligned<SymbolHeader>(m_elf, symbol_offset);
CCC_CHECK(symbol_header != nullptr, "Symbol header out of bounds.");
s32 strings_offset = m_hdrr->local_strings_offset + fd_header->strings_offset + m_fudge_offset;
@ -155,7 +155,7 @@ Result<File> SymbolTableReader::parse_file(s32 index) const
for(s64 i = 0; i < fd_header->procedure_descriptor_count; i++) {
u64 rel_procedure_offset = (fd_header->ipd_first + i) * sizeof(ProcedureDescriptor);
u64 procedure_offset = m_hdrr->procedure_descriptors_offset + rel_procedure_offset + m_fudge_offset;
const ProcedureDescriptor* procedure_descriptor = get_packed<ProcedureDescriptor>(m_elf, procedure_offset);
const ProcedureDescriptor* procedure_descriptor = get_unaligned<ProcedureDescriptor>(m_elf, procedure_offset);
CCC_CHECK(procedure_descriptor != nullptr, "Procedure descriptor out of bounds.");
CCC_CHECK(procedure_descriptor->symbol_index < file.symbols.size(), "Symbol index out of bounds.");
@ -175,7 +175,7 @@ Result<std::vector<Symbol>> SymbolTableReader::parse_external_symbols() const
std::vector<Symbol> external_symbols;
for(s64 i = 0; i < m_hdrr->external_symbols_count; i++) {
u64 sym_offset = m_hdrr->external_symbols_offset + i * sizeof(ExternalSymbolHeader);
const ExternalSymbolHeader* external_header = get_packed<ExternalSymbolHeader>(m_elf, sym_offset + m_fudge_offset);
const ExternalSymbolHeader* external_header = get_unaligned<ExternalSymbolHeader>(m_elf, sym_offset + m_fudge_offset);
CCC_CHECK(external_header != nullptr, "External header out of bounds.");
Result<Symbol> sym = get_symbol(external_header->symbol, m_elf, m_hdrr->external_strings_offset + m_fudge_offset);
@ -351,9 +351,9 @@ static Result<Symbol> get_symbol(const SymbolHeader& header, std::span<const u8>
{
Symbol symbol;
const char* string = get_string(elf, strings_offset + header.iss);
CCC_CHECK(string, "Symbol has invalid string.");
symbol.string = string;
std::optional<std::string_view> string = get_string(elf, strings_offset + header.iss);
CCC_CHECK(string.has_value(), "Symbol has invalid string.");
symbol.string = string->data();
symbol.value = header.value;
symbol.symbol_type = (SymbolType) header.st;

View File

@ -54,18 +54,19 @@ static const char* sndll_symbol_type_to_string(SNDLLSymbolType type);
Result<SNDLLFile> parse_sndll_file(std::span<const u8> image, Address address, SNDLLType type)
{
const u32* magic = get_packed<u32>(image, 0);
std::optional<u32> magic = copy_unaligned<u32>(image, 0);
CCC_CHECK(magic.has_value(), "Failed to read SNDLL header.");
CCC_CHECK((*magic & 0xffffff) == CCC_FOURCC("SNR\00"), "Not a SNDLL %s.", address.valid() ? "section" : "file");
char version = *magic >> 24;
switch(version) {
case '1': {
const SNDLLHeaderV1* header = get_packed<SNDLLHeaderV1>(image, 0);
const SNDLLHeaderV1* header = get_unaligned<SNDLLHeaderV1>(image, 0);
CCC_CHECK(header, "File too small to contain SNDLL V1 header.");
return parse_sndll_common(image, address, type, header->common, SNDLL_V1);
}
case '2': {
const SNDLLHeaderV2* header = get_packed<SNDLLHeaderV2>(image, 0);
const SNDLLHeaderV2* header = get_unaligned<SNDLLHeaderV2>(image, 0);
CCC_CHECK(header, "File too small to contain SNDLL V2 header.");
return parse_sndll_common(image, address, type, header->common, SNDLL_V2);
}
@ -84,10 +85,9 @@ static Result<SNDLLFile> parse_sndll_common(
sndll.version = version;
if(common.elf_path) {
const char* elf_path = get_string(image, common.elf_path);
if(elf_path) {
sndll.elf_path = elf_path;
}
std::optional<std::string_view> elf_path = get_string(image, common.elf_path);
CCC_CHECK(elf_path.has_value(), "SNDLL header has invalid ELF path field.");
sndll.elf_path = *elf_path;
}
CCC_CHECK(common.symbol_count < (32 * 1024 * 1024) / sizeof(SNDLLSymbol), "SNDLL symbol count is too high.");
@ -95,10 +95,10 @@ static Result<SNDLLFile> parse_sndll_common(
for(u32 i = 0; i < common.symbol_count; i++) {
u32 symbol_offset = common.symbols - address.get_or_zero() + i * sizeof(SNDLLSymbolHeader);
const SNDLLSymbolHeader* symbol_header = get_packed<SNDLLSymbolHeader>(image, symbol_offset);
const SNDLLSymbolHeader* symbol_header = get_unaligned<SNDLLSymbolHeader>(image, symbol_offset);
CCC_CHECK(symbol_header, "SNDLL symbol out of range.");
const char* string = nullptr;
std::optional<std::string_view> string;
if(symbol_header->string) {
string = get_string(image, symbol_header->string - address.get_or_zero());
}
@ -106,7 +106,9 @@ static Result<SNDLLFile> parse_sndll_common(
SNDLLSymbol& symbol = sndll.symbols.emplace_back();
symbol.type = symbol_header->type;
symbol.value = symbol_header->value;
symbol.string = string;
if(string.has_value()) {
symbol.string = *string;
}
}
return sndll;

View File

@ -753,7 +753,7 @@ void SourceFile::check_functions_match(const SymbolDatabase& database)
u32 modified = 0;
for(FunctionHandle function_handle : functions()) {
const ccc::Function* function = database.functions.symbol_from_handle(function_handle);
if(!function || function->original_hash() == 0) {
if(!function || function->current_hash() == 0 || function->original_hash() == 0) {
continue;
}

View File

@ -435,7 +435,7 @@ class Label : public Symbol {
public:
static constexpr const SymbolDescriptor DESCRIPTOR = LABEL;
static constexpr const char* NAME = "Label";
static constexpr u32 FLAGS = WITH_ADDRESS_MAP;
static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP;
LabelHandle handle() const { return m_handle; }

View File

@ -7,8 +7,8 @@ namespace ccc {
Result<std::unique_ptr<SymbolFile>> parse_symbol_file(std::vector<u8> image, std::string file_name)
{
const u32* magic = get_packed<u32>(image, 0);
CCC_CHECK(magic, "File too small.");
const std::optional<u32> magic = copy_unaligned<u32>(image, 0);
CCC_CHECK(magic.has_value(), "File too small.");
std::unique_ptr<SymbolFile> symbol_file;

View File

@ -100,8 +100,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
std::string module_name,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt)
@ -109,7 +110,8 @@ Result<ModuleHandle> import_symbol_tables(
Result<SymbolSourceHandle> module_source = database.get_symbol_source("Symbol Table Importer");
CCC_RETURN_IF_ERROR(module_source);
Result<Module*> module_symbol = database.modules.create_symbol(std::move(module_name), *module_source, nullptr);
Result<Module*> module_symbol = database.modules.create_symbol(
std::move(module_name), base_address, *module_source, nullptr);
CCC_RETURN_IF_ERROR(module_symbol);
ModuleHandle module_handle = (*module_symbol)->handle();

View File

@ -71,8 +71,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
// and to generate a module handle.
Result<ModuleHandle> import_symbol_tables(
SymbolDatabase& database,
std::string module_name,
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
std::string module_name,
Address base_address,
u32 importer_flags,
DemanglerFunctions demangler,
const std::atomic_bool* interrupt);

View File

@ -51,14 +51,17 @@ void set_custom_error_callback(CustomErrorCallback callback)
custom_error_callback = callback;
}
const char* get_string(std::span<const u8> bytes, u64 offset)
std::optional<std::string_view> get_string(std::span<const u8> bytes, u64 offset)
{
for(const unsigned char* c = bytes.data() + offset; c < bytes.data() + bytes.size(); c++) {
if(*c == '\0') {
return (const char*) &bytes[offset];
for(u64 i = offset; i < bytes.size(); i++) {
if(bytes[i] == '\0') {
return std::string_view(
reinterpret_cast<const char*>(&bytes[offset]),
reinterpret_cast<const char*>(&bytes[i]));
}
}
return nullptr;
return std::nullopt;
}
std::string merge_paths(const std::string& base, const std::string& path)

View File

@ -71,8 +71,15 @@ void set_custom_error_callback(CustomErrorCallback callback);
exit(1); \
}
#define CCC_ABORT_IF_FALSE(condition, ...) \
if(!(condition)) { \
ccc::Error error = ccc::format_error(__FILE__, __LINE__, __VA_ARGS__); \
ccc::report_error(error); \
abort(); \
}
#define CCC_ASSERT(condition) \
CCC_CHECK_FATAL(condition, #condition)
CCC_ABORT_IF_FALSE(condition, #condition)
// The main error handling construct in CCC. This class is used to bundle
// together a return value and a pointer to error information, so that errors
@ -201,16 +208,38 @@ void warn_impl(const char* source_file, int source_line, const char* format, Arg
#endif
template <typename T>
const T* get_packed(std::span<const u8> bytes, u64 offset)
const T* get_aligned(std::span<const u8> bytes, u64 offset)
{
if(offset + sizeof(T) <= bytes.size()) {
return reinterpret_cast<const T*>(&bytes[offset]);
} else {
if(offset > bytes.size() || bytes.size() - offset < sizeof(T) || offset % alignof(T) != 0) {
return nullptr;
}
return reinterpret_cast<const T*>(&bytes[offset]);
}
const char* get_string(std::span<const u8> bytes, u64 offset);
template <typename T>
const T* get_unaligned(std::span<const u8> bytes, u64 offset)
{
if(offset > bytes.size() || bytes.size() - offset < sizeof(T)) {
return nullptr;
}
return reinterpret_cast<const T*>(&bytes[offset]);
}
template <typename T>
std::optional<T> copy_unaligned(std::span<const u8> bytes, u64 offset)
{
if(offset > bytes.size() || bytes.size() - offset < sizeof(T)) {
return std::nullopt;
}
T value;
memcpy(&value, &bytes[offset], sizeof(T));
return value;
}
std::optional<std::string_view> get_string(std::span<const u8> bytes, u64 offset);
#define CCC_BEGIN_END(x) (x).begin(), (x).end()
#define CCC_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

View File

@ -60,7 +60,7 @@ ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity);
void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap);
#endif
#if CPUINFO_ARCH_ARM
void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2);
void CPUINFO_ABI cpuinfo_set_hwcap2(uint64_t hwcap2);
#endif
#endif

View File

@ -419,6 +419,8 @@ enum cpuinfo_uarch {
cpuinfo_uarch_zen3 = 0x0020010B,
/** AMD Zen 4 microarchitecture. */
cpuinfo_uarch_zen4 = 0x0020010C,
/** AMD Zen 5 microarchitecture. */
cpuinfo_uarch_zen5 = 0x0020010D,
/** NSC Geode and AMD Geode GX and LX. */
cpuinfo_uarch_geode = 0x00200200,
@ -496,13 +498,19 @@ enum cpuinfo_uarch {
cpuinfo_uarch_cortex_x2 = 0x00300502,
/** ARM Cortex-X3. */
cpuinfo_uarch_cortex_x3 = 0x00300503,
/** ARM Cortex-X4. */
cpuinfo_uarch_cortex_x4 = 0x00300504,
/** ARM Cortex-A510. */
cpuinfo_uarch_cortex_a510 = 0x00300551,
/** ARM Cortex-A520. */
cpuinfo_uarch_cortex_a520 = 0x00300552,
/** ARM Cortex-A710. */
cpuinfo_uarch_cortex_a710 = 0x00300571,
/** ARM Cortex-A715. */
cpuinfo_uarch_cortex_a715 = 0x00300572,
/** ARM Cortex-A720. */
cpuinfo_uarch_cortex_a720 = 0x00300573,
/** Qualcomm Scorpion. */
cpuinfo_uarch_scorpion = 0x00400100,
@ -812,6 +820,7 @@ struct cpuinfo_x86_isa {
bool avx512vp2intersect;
bool avx512_4vnniw;
bool avx512_4fmaps;
bool avx10_1;
bool amx_bf16;
bool amx_tile;
bool amx_int8;
@ -1427,6 +1436,14 @@ static inline bool cpuinfo_has_x86_avx_ne_convert(void) {
#endif
}
static inline bool cpuinfo_has_x86_avx10_1(void) {
#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64
return cpuinfo_isa.avx10_1;
#else
return false;
#endif
}
static inline bool cpuinfo_has_x86_hle(void) {
#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64
return cpuinfo_isa.hle;
@ -1664,6 +1681,14 @@ struct cpuinfo_arm_isa {
bool sve;
bool sve2;
bool i8mm;
bool sme;
bool sme2;
bool sme2p1;
bool sme_i16i32;
bool sme_bi32i32;
bool sme_b16b16;
bool sme_f16f16;
uint32_t svelen;
#endif
bool rdm;
bool fp16arith;
@ -2036,6 +2061,71 @@ static inline bool cpuinfo_has_arm_sve2(void) {
#endif
}
// Function to get the max SVE vector length on ARM CPU's which support SVE.
static inline uint32_t cpuinfo_get_max_arm_sve_length(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.svelen * 8; // bytes * 8 = bit length(vector length)
#else
return 0;
#endif
}
static inline bool cpuinfo_has_arm_sme(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme2(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme2;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme2p1(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme2p1;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme_i16i32(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme_i16i32;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme_bi32i32(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme_bi32i32;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme_b16b16(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme_b16b16;
#else
return false;
#endif
}
static inline bool cpuinfo_has_arm_sme_f16f16(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme_f16f16;
#else
return false;
#endif
}
#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
/* This structure is not a part of stable API. Use cpuinfo_has_riscv_* functions
* instead. */

View File

@ -64,6 +64,7 @@ enum cpuinfo_arm_chipset_series {
cpuinfo_arm_chipset_series_telechips_tcc,
cpuinfo_arm_chipset_series_texas_instruments_omap,
cpuinfo_arm_chipset_series_unisoc_t,
cpuinfo_arm_chipset_series_unisoc_ums,
cpuinfo_arm_chipset_series_wondermedia_wm,
cpuinfo_arm_chipset_series_max,
};

View File

@ -1341,7 +1341,8 @@ void cpuinfo_arm_decode_cache(
* information, please refer to the technical manuals
* linked above
*/
const uint32_t min_l2_size_KB = uarch == cpuinfo_uarch_neoverse_v2 ? 1024 : 256;
const uint32_t min_l2_size_KB =
(uarch == cpuinfo_uarch_neoverse_v2 || midr_is_ampere_altra(midr)) ? 1024 : 256;
const uint32_t min_l3_size_KB = 0;
*l1i = (struct cpuinfo_cache){

View File

@ -24,7 +24,7 @@ void cpuinfo_set_wcid(uint32_t wcid) {
void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t features,
uint32_t features2,
uint64_t features2,
uint32_t midr,
uint32_t architecture_version,
uint32_t architecture_flags,
@ -147,6 +147,8 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
"VDOT instructions disabled: cause occasional SIGILL on Spreadtrum SC9863A");
} else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_t && chipset->model == 310) {
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc T310");
} else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_ums && chipset->model == 312) {
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc UMS312");
} else {
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D0B0): /* Cortex-A76 */

View File

@ -3,9 +3,11 @@
#include <arm/linux/api.h>
#include <cpuinfo/log.h>
#include <sys/prctl.h>
void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
uint32_t features,
uint32_t features2,
uint64_t features2,
uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]) {
@ -142,6 +144,27 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) {
isa->sve2 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME) {
isa->sme = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME2) {
isa->sme2 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME2P1) {
isa->sme2p1 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_I16I32) {
isa->sme_i16i32 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_BI32I32) {
isa->sme_bi32i32 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_B16B16) {
isa->sme_b16b16 = true;
}
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SME_F16F16) {
isa->sme_f16f16 = true;
}
// SVEBF16 is set iff SVE and BF16 are both supported, but the SVEBF16
// feature flag was added in Linux kernel before the BF16 feature flag,
// so we check for either.
@ -151,4 +174,21 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM) {
isa->fhm = true;
}
#ifndef PR_SVE_GET_VL
#define PR_SVE_GET_VL 51
#endif
#ifndef PR_SVE_VL_LEN_MASK
#define PR_SVE_VL_LEN_MASK 0xffff
#endif
int ret = prctl(PR_SVE_GET_VL);
if (ret < 0) {
cpuinfo_log_warning("No SVE support on this machine");
isa->svelen = 0; // Assume no SVE support if the call fails
} else {
// Mask out the SVE vector length bits
isa->svelen = ret & PR_SVE_VL_LEN_MASK;
}
}

View File

@ -137,6 +137,13 @@ struct cpuinfo_arm_linux_proc_cpuinfo_cache {
#define CPUINFO_ARM_LINUX_FEATURE2_DGH UINT32_C(0x00008000)
#define CPUINFO_ARM_LINUX_FEATURE2_RNG UINT32_C(0x00010000)
#define CPUINFO_ARM_LINUX_FEATURE2_BTI UINT32_C(0x00020000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME UINT32_C(0x00800000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME2 UINT64_C(0x0000002000000000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME2P1 UINT64_C(0x0000004000000000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME_I16I32 UINT64_C(0x0000008000000000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME_BI32I32 UINT64_C(0x0000010000000000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME_B16B16 UINT64_C(0x0000020000000000)
#define CPUINFO_ARM_LINUX_FEATURE2_SME_F16F16 UINT64_C(0x0000040000000000)
#endif
#define CPUINFO_ARM_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000)
@ -172,7 +179,7 @@ struct cpuinfo_arm_linux_processor {
struct cpuinfo_arm_linux_proc_cpuinfo_cache proc_cpuinfo_cache;
#endif
uint32_t features;
uint32_t features2;
uint64_t features2;
/**
* Main ID Register value.
*/
@ -295,14 +302,14 @@ CPUINFO_INTERNAL bool cpuinfo_arm_linux_parse_proc_cpuinfo(
#if CPUINFO_ARCH_ARM
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_getauxval(
uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]);
uint64_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_procfs(
uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]);
uint64_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t features,
uint32_t features2,
uint64_t features2,
uint32_t midr,
uint32_t architecture_version,
uint32_t architecture_flags,
@ -311,11 +318,11 @@ CPUINFO_INTERNAL void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
#elif CPUINFO_ARCH_ARM64
CPUINFO_INTERNAL void cpuinfo_arm_linux_hwcap_from_getauxval(
uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]);
uint64_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
uint32_t features,
uint32_t features2,
uint64_t features2,
uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]);

View File

@ -85,6 +85,7 @@ static enum cpuinfo_arm_chipset_vendor chipset_series_vendor[cpuinfo_arm_chipset
[cpuinfo_arm_chipset_series_telechips_tcc] = cpuinfo_arm_chipset_vendor_telechips,
[cpuinfo_arm_chipset_series_texas_instruments_omap] = cpuinfo_arm_chipset_vendor_texas_instruments,
[cpuinfo_arm_chipset_series_unisoc_t] = cpuinfo_arm_chipset_vendor_unisoc,
[cpuinfo_arm_chipset_series_unisoc_ums] = cpuinfo_arm_chipset_vendor_unisoc,
[cpuinfo_arm_chipset_series_wondermedia_wm] = cpuinfo_arm_chipset_vendor_wondermedia,
};
@ -959,6 +960,70 @@ static bool match_t(const char* start, const char* end, struct cpuinfo_arm_chips
return true;
}
/**
* Tries to match, case-sentitively, /Unisoc UMS\d{3,4}/ signature for Unisoc UMS
* chipset. If match successful, extracts model information into \p chipset
* argument.
*
* @param start - start of the platform identifier (/proc/cpuinfo Hardware
* string, ro.product.board, ro.board.platform, or ro.chipname) to match.
* @param end - end of the platform identifier (/proc/cpuinfo Hardware string,
* ro.product.board, ro.board.platform, or ro.chipname) to match.
* @param[out] chipset - location where chipset information will be stored upon
* a successful match.
*
* @returns true if signature matched, false otherwise.
*/
static bool match_ums(const char* start, const char* end, struct cpuinfo_arm_chipset chipset[restrict static 1]) {
/* Expect 13-14 symbols: "Unisoc UMS" (10 symbols) + 3-4-digit model number
*/
const size_t length = end - start;
switch (length) {
case 13:
case 14:
break;
default:
return false;
}
/* Check that string starts with "Unisoc UMS". The first four characters
* are loaded as 32-bit little endian word */
const uint32_t expected_unis = load_u32le(start);
if (expected_unis != UINT32_C(0x73696E55) /* "sinU" = reverse("Unis") */) {
return false;
}
/* The next four characters are loaded as 32-bit little endian word */
const uint32_t expected_oc_u = load_u32le(start + 4);
if (expected_oc_u != UINT32_C(0x5520636F) /* "U co" = reverse("oc U") */) {
return false;
}
/* The next four characters are loaded as 16-bit little endian word */
const uint16_t expected_ms = load_u16le(start + 8);
if (expected_ms != UINT16_C(0x534D) /* "SM" = reverse("MS") */) {
return false;
}
/* Validate and parse 3-4 digit model number */
uint32_t model = 0;
for (uint32_t i = 10; i < length; i++) {
const uint32_t digit = (uint32_t)(uint8_t)start[i] - '0';
if (digit >= 10) {
/* Not really a digit */
return false;
}
model = model * 10 + digit;
}
*chipset = (struct cpuinfo_arm_chipset){
.vendor = cpuinfo_arm_chipset_vendor_unisoc,
.series = cpuinfo_arm_chipset_series_unisoc_ums,
.model = model,
};
return true;
}
/**
* Tries to match /lc\d{4}[a-z]?$/ signature for Leadcore LC chipsets.
* If match successful, extracts model information into \p chipset argument.
@ -2508,6 +2573,16 @@ struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_ha
return chipset;
}
/* Check Unisoc UMS signature */
if (match_ums(hardware, hardware_end, &chipset)) {
cpuinfo_log_debug(
"matched Unisoc UMS signature in /proc/cpuinfo Hardware string \"%.*s\"",
(int)hardware_length,
hardware);
return chipset;
}
#if CPUINFO_ARCH_ARM
/* Check Marvell PXA signature */
if (match_pxa(hardware, hardware_end, &chipset)) {
@ -3726,6 +3801,7 @@ static const char* chipset_series_string[cpuinfo_arm_chipset_series_max] = {
[cpuinfo_arm_chipset_series_telechips_tcc] = "TCC",
[cpuinfo_arm_chipset_series_texas_instruments_omap] = "OMAP",
[cpuinfo_arm_chipset_series_unisoc_t] = "T",
[cpuinfo_arm_chipset_series_unisoc_ums] = "UMS",
[cpuinfo_arm_chipset_series_wondermedia_wm] = "WM",
};

View File

@ -31,8 +31,8 @@ void cpuinfo_set_hwcap(uint32_t hwcap) {
mock_hwcap = hwcap;
}
static uint32_t mock_hwcap2 = 0;
void cpuinfo_set_hwcap2(uint32_t hwcap2) {
static uint64_t mock_hwcap2 = 0;
void cpuinfo_set_hwcap2(uint64_t hwcap2) {
mock_hwcap2 = hwcap2;
}
#endif
@ -40,7 +40,7 @@ void cpuinfo_set_hwcap2(uint32_t hwcap2) {
#if CPUINFO_ARCH_ARM
typedef unsigned long (*getauxval_function_t)(unsigned long);
bool cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
bool cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
@ -83,13 +83,13 @@ cleanup:
}
#ifdef __ANDROID__
bool cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
bool cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
return true;
#else
uint32_t hwcaps[2] = {0, 0};
uint64_t hwcaps[2] = {0, 0};
bool result = false;
int file = -1;
@ -113,7 +113,7 @@ bool cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static 1], uint
hwcaps[0] = (uint32_t)elf_auxv.a_un.a_val;
break;
case AT_HWCAP2:
hwcaps[1] = (uint32_t)elf_auxv.a_un.a_val;
hwcaps[1] = (uint64_t)elf_auxv.a_un.a_val;
break;
}
} else {
@ -141,13 +141,13 @@ cleanup:
}
#endif /* __ANDROID__ */
#elif CPUINFO_ARCH_ARM64
void cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
void cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint64_t hwcap2[restrict static 1]) {
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
#else
*hwcap = (uint32_t)getauxval(AT_HWCAP);
*hwcap2 = (uint32_t)getauxval(AT_HWCAP2);
*hwcap2 = (uint64_t)getauxval(AT_HWCAP2);
return;
#endif
}

View File

@ -247,7 +247,8 @@ void cpuinfo_arm_linux_init(void) {
#endif
#if CPUINFO_ARCH_ARM
uint32_t isa_features = 0, isa_features2 = 0;
uint32_t isa_features = 0;
uint64_t isa_features2 = 0;
#ifdef __ANDROID__
/*
* On Android before API 20, libc.so does not provide getauxval
@ -299,7 +300,8 @@ void cpuinfo_arm_linux_init(void) {
&chipset,
&cpuinfo_isa);
#elif CPUINFO_ARCH_ARM64
uint32_t isa_features = 0, isa_features2 = 0;
uint32_t isa_features = 0;
uint64_t isa_features2 = 0;
/* getauxval is always available on ARM64 Android */
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2);
cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
@ -333,18 +335,52 @@ void cpuinfo_arm_linux_init(void) {
}
/* Propagate topology group IDs among siblings */
bool detected_core_siblings_list_node = false;
bool detected_cluster_cpus_list_node = false;
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (!bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
continue;
}
if (arm_linux_processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_ID) {
if (!bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_PACKAGE_ID)) {
continue;
}
/* Use the cluster_cpus_list topology node if available. If not
* found, cache the result to avoid repeatedly attempting to
* read the non-existent paths.
* */
if (!detected_core_siblings_list_node && !detected_cluster_cpus_list_node) {
if (cpuinfo_linux_detect_cluster_cpus(
arm_linux_processors_count,
i,
(cpuinfo_siblings_callback)cluster_siblings_parser,
arm_linux_processors)) {
detected_cluster_cpus_list_node = true;
continue;
} else {
detected_core_siblings_list_node = true;
}
}
/* The cached result above will guarantee only one of the blocks
* below will execute, with a bias towards cluster_cpus_list.
**/
if (detected_core_siblings_list_node) {
cpuinfo_linux_detect_core_siblings(
arm_linux_processors_count,
i,
(cpuinfo_siblings_callback)cluster_siblings_parser,
arm_linux_processors);
}
if (detected_cluster_cpus_list_node) {
cpuinfo_linux_detect_cluster_cpus(
arm_linux_processors_count,
i,
(cpuinfo_siblings_callback)cluster_siblings_parser,
arm_linux_processors);
}
}
/* Propagate all cluster IDs */

View File

@ -399,6 +399,16 @@ void cpuinfo_arm_mach_init(void) {
cpuinfo_isa.i8mm = true;
}
const uint32_t has_feat_sme = get_sys_info_by_name("hw.optional.arm.FEAT_SME");
if (has_feat_sme != 0) {
cpuinfo_isa.sme = true;
}
const uint32_t has_feat_sme2 = get_sys_info_by_name("hw.optional.arm.FEAT_SME2");
if (has_feat_sme2 != 0) {
cpuinfo_isa.sme2 = true;
}
uint32_t num_clusters = 1;
for (uint32_t i = 0; i < mach_topology.cores; i++) {
cores[i] = (struct cpuinfo_core){

View File

@ -34,6 +34,7 @@
#define CPUINFO_ARM_MIDR_KRYO_SILVER_820 UINT32_C(0x510F2110)
#define CPUINFO_ARM_MIDR_EXYNOS_M1_M2 UINT32_C(0x530F0010)
#define CPUINFO_ARM_MIDR_DENVER2 UINT32_C(0x4E0F0030)
#define CPUINFO_ARM_MIDR_AMPERE_ALTRA UINT32_C(0x413fd0c1)
inline static uint32_t midr_set_implementer(uint32_t midr, uint32_t implementer) {
return (midr & ~CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) |
@ -167,6 +168,11 @@ inline static bool midr_is_kryo_gold(uint32_t midr) {
return (midr & uarch_mask) == (CPUINFO_ARM_MIDR_KRYO_GOLD & uarch_mask);
}
inline static bool midr_is_ampere_altra(uint32_t midr) {
const uint32_t uarch_mask = CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK;
return (midr & uarch_mask) == (CPUINFO_ARM_MIDR_AMPERE_ALTRA & uarch_mask);
}
inline static uint32_t midr_score_core(uint32_t midr) {
const uint32_t core_mask = CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK;
switch (midr & core_mask) {

View File

@ -24,8 +24,10 @@ static char* sysctl_str(const char* name) {
size_t value_size = 0;
if (sysctlbyname(name, NULL, &value_size, NULL, 0) != 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
return NULL;
} else if (value_size <= 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value size %zu", name, value_size);
return NULL;
}
value_size += 1;
char* value = calloc(value_size, 1);
@ -52,29 +54,22 @@ struct cpuinfo_freebsd_topology cpuinfo_freebsd_detect_topology(void) {
if (!topology_spec) {
return topology;
}
const char* group_tag = "<group level=\"1\" cache-level=\"0\">";
char* p = strstr(topology_spec, group_tag);
while (p) {
const char* cpu_tag = "cpu count=\"";
char* q = strstr(p, cpu_tag);
if (q) {
p = q + strlen(cpu_tag);
topology.packages += atoi(p);
} else {
break;
}
}
if (topology.packages == 0) {
const char* group_tag = "<group level=\"1\"";
const char* group_tags[] = {"<group level=\"2\" cache-level=\"0\">", "<group level=\"1\" "};
for (size_t i = 0; i < sizeof(group_tags) / sizeof(group_tags[0]); i++) {
const char* group_tag = group_tags[i];
char* p = strstr(topology_spec, group_tag);
while (p) {
topology.packages += 1;
p++;
p = strstr(p, group_tag);
}
if (topology.packages > 0) {
break;
}
}
if (topology.packages == 0) {
cpuinfo_log_error("failed to parse topology_spec:%s", topology_spec);
cpuinfo_log_error("failed to parse topology_spec: %s", topology_spec);
free(topology_spec);
goto fail;
}
@ -84,6 +79,7 @@ struct cpuinfo_freebsd_topology cpuinfo_freebsd_detect_topology(void) {
goto fail;
}
if (topology.cores < topology.packages) {
cpuinfo_log_error("invalid numbers of package and core: %d %d", topology.packages, topology.cores);
goto fail;
}
topology.threads_per_core = sysctl_int("kern.smp.threads_per_core");

View File

@ -135,6 +135,10 @@ void cpuinfo_x86_freebsd_init(void) {
if (x86_processor.cache.l1i.size != 0 || x86_processor.cache.l1d.size != 0) {
/* Assume that threads on the same core share L1 */
threads_per_l1 = freebsd_topology.threads / freebsd_topology.cores;
if (threads_per_l1 == 0) {
cpuinfo_log_error("failed to detect threads_per_l1");
goto cleanup;
}
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L1 cache; assume %" PRIu32,
@ -154,6 +158,10 @@ void cpuinfo_x86_freebsd_init(void) {
* the same package share L2 */
threads_per_l2 = freebsd_topology.threads / freebsd_topology.packages;
}
if (threads_per_l2 == 0) {
cpuinfo_log_error("failed to detect threads_per_l1");
goto cleanup;
}
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L2 cache; assume %" PRIu32,
@ -170,6 +178,10 @@ void cpuinfo_x86_freebsd_init(void) {
* may be L4 cache as well)
*/
threads_per_l3 = freebsd_topology.threads / freebsd_topology.packages;
if (threads_per_l3 == 0) {
cpuinfo_log_error("failed to detect threads_per_l3");
goto cleanup;
}
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L3 cache; assume %" PRIu32,
@ -187,6 +199,10 @@ void cpuinfo_x86_freebsd_init(void) {
* shared L4 (like on IBM POWER8).
*/
threads_per_l4 = freebsd_topology.threads;
if (threads_per_l4 == 0) {
cpuinfo_log_error("failed to detect threads_per_l4");
goto cleanup;
}
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L4 cache; assume %" PRIu32,
@ -203,7 +219,7 @@ void cpuinfo_x86_freebsd_init(void) {
"%" PRIu32 " L1I caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return;
goto cleanup;
}
for (uint32_t c = 0; c < l1_count; c++) {
l1i[c] = (struct cpuinfo_cache){
@ -230,7 +246,7 @@ void cpuinfo_x86_freebsd_init(void) {
"%" PRIu32 " L1D caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return;
goto cleanup;
}
for (uint32_t c = 0; c < l1_count; c++) {
l1d[c] = (struct cpuinfo_cache){
@ -257,7 +273,7 @@ void cpuinfo_x86_freebsd_init(void) {
"%" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
return;
goto cleanup;
}
for (uint32_t c = 0; c < l2_count; c++) {
l2[c] = (struct cpuinfo_cache){
@ -284,7 +300,7 @@ void cpuinfo_x86_freebsd_init(void) {
"%" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
return;
goto cleanup;
}
for (uint32_t c = 0; c < l3_count; c++) {
l3[c] = (struct cpuinfo_cache){
@ -311,7 +327,7 @@ void cpuinfo_x86_freebsd_init(void) {
"%" PRIu32 " L4 caches",
l4_count * sizeof(struct cpuinfo_cache),
l4_count);
return;
goto cleanup;
}
for (uint32_t c = 0; c < l4_count; c++) {
l4[c] = (struct cpuinfo_cache){

View File

@ -429,6 +429,11 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
*/
isa.avx512f = avx512_regs && !!(structured_feature_info0.ebx & UINT32_C(0x00010000));
/*
* AVX 10.1 instructions:
*/
isa.avx10_1 = avx512_regs && !!(structured_feature_info1.edx & UINT32_C(0x00080000));
/*
* AVX512PF instructions:
* - Intel: ebx[bit 26] in structured feature info (ecx = 0).

View File

@ -387,6 +387,8 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
return cpuinfo_uarch_zen4;
}
break;
case 0x1a:
return cpuinfo_uarch_zen5;
}
break;
case cpuinfo_vendor_hygon:

View File

@ -1,4 +1,4 @@
Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved.
Copyright (c) 2019-2024 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -2,7 +2,7 @@
Easy to integrate memory allocation library for Direct3D 12.
**Documentation:** Browse online: [D3D12 Memory Allocator](https://gpuopen-librariesandsdks.github.io/D3D12MemoryAllocator/html/) (generated from Doxygen-style comments in [src/D3D12MemAlloc.h](src/D3D12MemAlloc.h))
**Documentation:** Browse online: [D3D12 Memory Allocator](https://gpuopen-librariesandsdks.github.io/D3D12MemoryAllocator/html/) (generated from Doxygen-style comments in [include/D3D12MemAlloc.h](include/D3D12MemAlloc.h))
**License:** MIT. See [LICENSE.txt](LICENSE.txt)
@ -36,6 +36,7 @@ Additional features:
- Customization and integration with custom engines: Predefine appropriate macros to provide your own implementation of external facilities used by the library, like assert, mutex, and atomic.
- Support for resource aliasing (overlap).
- Custom memory pools: Create a pool with desired parameters (e.g. fixed or limited maximum size, custom `D3D12_HEAP_PROPERTIES` and `D3D12_HEAP_FLAGS`) and allocate memory out of it.
- Support for GPU Upload Heaps from preview Agility SDK (needs compilation with `D3D12MA_OPTIONS16_SUPPORTED` macro).
- Linear allocator: Create a pool with linear algorithm and use it for much faster allocations and deallocations in free-at-once, stack, double stack, or ring buffer fashion.
- Defragmentation: Let the library move data around to free some memory blocks and make your allocations better compacted.
- Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated heaps, number of allocations etc. - globally and per memory heap type. Current memory usage and budget as reported by the system can also be queried.
@ -101,15 +102,18 @@ This software package uses third party software:
For more information see [NOTICES.txt](NOTICES.txt).
# Software using this library
- **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0.
- **[Wicked Engine<img src="https://github.com/turanszkij/WickedEngine/blob/master/Content/logo_small.png" width="28px" align="center"/>](https://github.com/turanszkij/WickedEngine)** - 3D engine with modern graphics
[Some other projects on GitHub](https://github.com/search?q=D3D12MemAlloc.h&type=Code) and some game development studios that use DX12 in their games.
# See also
- **[Vcpkg](https://github.com/Microsoft/vcpkg)** dependency manager from Microsoft offers a port of this library that is easy to install.
- **[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/)** - equivalent library for Vulkan. License: MIT.
- **[d3d12ma.c](https://github.com/milliewalky/d3d12ma.c)** - C bindings for this library. Author: Mateusz Maciejewski (Matt Walky). License: MIT.
- **[TerraFX.Interop.D3D12MemoryAllocator](https://github.com/terrafx/terrafx.interop.d3d12memoryallocator)** - interop bindings for this library for C#, as used by [TerraFX](https://github.com/terrafx/terrafx). License: MIT.
- **[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/)** - equivalent library for Vulkan. License: MIT.
# Software using this library
- **[Qt Project](https://github.com/qt)**
- **[Ghost of Tsushima: Director's Cut PC](https://www.youtube.com/watch?v=cPKBDbCYctc&t=698s)** - Information avaliable in 11:38 of credits
- **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0.
- **[Wicked Engine<img src="https://github.com/turanszkij/WickedEngine/blob/master/Content/logo_small.png" width="28px" align="center"/>](https://github.com/turanszkij/WickedEngine)** - 3D engine with modern graphics
[Some other projects on GitHub](https://github.com/search?q=D3D12MemAlloc.h&type=Code) and some game development studios that use DX12 in their games.

View File

@ -24,9 +24,9 @@
/** \mainpage D3D12 Memory Allocator
<b>Version 2.1.0-development</b> (2023-07-05)
<b>Version 2.1.0-development</b> (2024-07-05)
Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. \n
Copyright (c) 2019-2024 Advanced Micro Devices, Inc. All rights reserved. \n
License: MIT
Documentation of all members: D3D12MemAlloc.h
@ -160,9 +160,9 @@ class D3D12MA_API IUnknownImpl : public IUnknown
{
public:
virtual ~IUnknownImpl() = default;
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
protected:
virtual void ReleaseThis() { delete this; }
private:
@ -265,18 +265,18 @@ enum ALLOCATION_FLAGS
*/
ALLOCATION_FLAG_CAN_ALIAS = 0x10,
/** Allocation strategy that chooses smallest possible free range for the allocation
/** %Allocation strategy that chooses smallest possible free range for the allocation
to minimize memory usage and fragmentation, possibly at the expense of allocation time.
*/
ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = 0x00010000,
/** Allocation strategy that chooses first suitable free range for the allocation -
/** %Allocation strategy that chooses first suitable free range for the allocation -
not necessarily in terms of the smallest offset but the one that is easiest and fastest to find
to minimize allocation time, possibly at the expense of allocation quality.
*/
ALLOCATION_FLAG_STRATEGY_MIN_TIME = 0x00020000,
/** Allocation strategy that chooses always the lowest offset in available space.
/** %Allocation strategy that chooses always the lowest offset in available space.
This is not the most efficient strategy but achieves highly packed data.
Used internally by defragmentation, not recomended in typical usage.
*/
@ -402,8 +402,9 @@ struct TotalStatistics
- 1 = `D3D12_HEAP_TYPE_UPLOAD`
- 2 = `D3D12_HEAP_TYPE_READBACK`
- 3 = `D3D12_HEAP_TYPE_CUSTOM`
- 4 = `D3D12_HEAP_TYPE_GPU_UPLOAD`
*/
DetailedStatistics HeapType[4];
DetailedStatistics HeapType[5];
/** \brief One element for each memory segment group located at the following indices:
- 0 = `DXGI_MEMORY_SEGMENT_GROUP_LOCAL`
@ -413,9 +414,9 @@ struct TotalStatistics
- When `IsUMA() == FALSE` (discrete graphics card):
- `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` (index 0) represents GPU memory
(resources allocated in `D3D12_HEAP_TYPE_DEFAULT` or `D3D12_MEMORY_POOL_L1`).
(resources allocated in `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_GPU_UPLOAD` or `D3D12_MEMORY_POOL_L1`).
- `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` (index 1) represents system memory
(resources allocated in `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`, or `D3D12_MEMORY_POOL_L0`).
(resources allocated in `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`, or `D3D12_MEMORY_POOL_L0`).
- When `IsUMA() == TRUE` (integrated graphics chip):
- `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` = (index 0) represents memory shared for all the resources.
- `DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL` = (index 1) is unused and always 0.
@ -542,26 +543,6 @@ public:
*/
LPCWSTR GetName() const { return m_Name; }
/** \brief Returns `TRUE` if the memory of the allocation was filled with zeros when the allocation was created.
Returns `TRUE` only if the allocator is sure that the entire memory where the
allocation was created was filled with zeros at the moment the allocation was made.
Returns `FALSE` if the memory could potentially contain garbage data.
If it's a render-target or depth-stencil texture, it then needs proper
initialization with `ClearRenderTargetView`, `ClearDepthStencilView`, `DiscardResource`,
or a copy operation, as described on page
"ID3D12Device::CreatePlacedResource method - Notes on the required resource initialization" in Microsoft documentation.
Please note that rendering a fullscreen triangle or quad to the texture as
a render target is not a proper way of initialization!
See also articles:
- "Coming to DirectX 12: More control over memory allocation" on DirectX Developer Blog
- ["Initializing DX12 Textures After Allocation and Aliasing"](https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing).
*/
BOOL WasZeroInitialized() const { return m_PackedData.WasZeroInitialized(); }
protected:
void ReleaseThis() override;
@ -620,29 +601,26 @@ private:
{
public:
PackedData() :
m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0), m_WasZeroInitialized(0) { }
m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0) { }
Type GetType() const { return (Type)m_Type; }
D3D12_RESOURCE_DIMENSION GetResourceDimension() const { return (D3D12_RESOURCE_DIMENSION)m_ResourceDimension; }
D3D12_RESOURCE_FLAGS GetResourceFlags() const { return (D3D12_RESOURCE_FLAGS)m_ResourceFlags; }
D3D12_TEXTURE_LAYOUT GetTextureLayout() const { return (D3D12_TEXTURE_LAYOUT)m_TextureLayout; }
BOOL WasZeroInitialized() const { return (BOOL)m_WasZeroInitialized; }
void SetType(Type type);
void SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension);
void SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags);
void SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout);
void SetWasZeroInitialized(BOOL wasZeroInitialized) { m_WasZeroInitialized = wasZeroInitialized ? 1 : 0; }
private:
UINT m_Type : 2; // enum Type
UINT m_ResourceDimension : 3; // enum D3D12_RESOURCE_DIMENSION
UINT m_ResourceFlags : 24; // flags D3D12_RESOURCE_FLAGS
UINT m_TextureLayout : 9; // enum D3D12_TEXTURE_LAYOUT
UINT m_WasZeroInitialized : 1; // BOOL
} m_PackedData;
Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized);
Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment);
// Nothing here, everything already done in Release.
virtual ~Allocation() = default;
@ -1065,6 +1043,16 @@ enum ALLOCATOR_FLAGS
to create its heaps on smaller alignment not suitable for MSAA textures.
*/
ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x8,
/** \brief Disable optimization that prefers creating small buffers as committed to avoid 64 KB alignment.
By default, the library prefers creating small buffers <= 32 KB as committed,
because drivers tend to pack them better, while placed buffers require 64 KB alignment.
This, however, may decrease performance, as creating committed resources involves allocation of implicit heaps,
which may take longer than creating placed resources in existing heaps.
Passing this flag will disable this committed preference globally for the allocator.
It can also be disabled for a single allocation by using #ALLOCATION_FLAG_STRATEGY_MIN_TIME.
*/
ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED = 0x10,
};
/// \brief Parameters of created Allocator object. To be used with CreateAllocator().
@ -1130,6 +1118,15 @@ public:
- "ID3D12Device::GetCustomHeapProperties method (d3d12.h)"
*/
BOOL IsCacheCoherentUMA() const;
/** \brief Returns true if GPU Upload Heaps are supported on the current system.
When true, you can use `D3D12_HEAP_TYPE_GPU_UPLOAD`.
This flag is fetched from `D3D12_FEATURE_D3D12_OPTIONS16::GPUUploadHeapSupported`.
`#define D3D12MA_OPTIONS16_SUPPORTED 1` is needed for the compilation of this library. Otherwise the flag is always false.
*/
BOOL IsGPUUploadHeapSupported() const;
/** \brief Returns total amount of memory of specific segment group, in bytes.
\param memorySegmentGroup use `DXGI_MEMORY_SEGMENT_GROUP_LOCAL` or DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL`.
@ -1447,11 +1444,11 @@ enum VIRTUAL_ALLOCATION_FLAGS
*/
VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS = ALLOCATION_FLAG_UPPER_ADDRESS,
/// Allocation strategy that tries to minimize memory usage.
/// %Allocation strategy that tries to minimize memory usage.
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_MEMORY = ALLOCATION_FLAG_STRATEGY_MIN_MEMORY,
/// Allocation strategy that tries to minimize allocation time.
/// %Allocation strategy that tries to minimize allocation time.
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_TIME = ALLOCATION_FLAG_STRATEGY_MIN_TIME,
/** \brief Allocation strategy that chooses always the lowest offset in available space.
/** %Allocation strategy that chooses always the lowest offset in available space.
This is not the most efficient strategy but achieves highly packed data.
*/
VIRTUAL_ALLOCATION_FLAG_STRATEGY_MIN_OFFSET = ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,
@ -1640,6 +1637,9 @@ ID3D12Device* device = (...)
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
allocatorDesc.pDevice = device;
allocatorDesc.pAdapter = adapter;
// These flags are optional but recommended.
allocatorDesc.Flags = D3D12MA::ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED |
D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED;
D3D12MA::Allocator* allocator;
HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
@ -1864,6 +1864,9 @@ to obtain object D3D12MA::Pool. Example:
\code
POOL_DESC poolDesc = {};
poolDesc.HeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
// These flags are optional but recommended.
poolDesc.Flags = D3D12MA::POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED;
poolDesc.HeapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
Pool* pool;
HRESULT hr = allocator->CreatePool(&poolDesc, &pool);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.8...3.26)
cmake_minimum_required(VERSION 3.8...3.28)
# Fallback for using newer policies on CMake <3.12.
if (${CMAKE_VERSION} VERSION_LESS 3.12)
@ -36,6 +36,12 @@ function(enable_module target)
endif ()
endfunction()
set(FMT_USE_CMAKE_MODULES FALSE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND
CMAKE_GENERATOR STREQUAL "Ninja")
set(FMT_USE_CMAKE_MODULES TRUE)
endif ()
# Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a
@ -53,6 +59,7 @@ function(add_module_library name)
if (NOT ${${AML_IF}})
# Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK})
set_target_properties(${name} PROPERTIES CXX_SCAN_FOR_MODULES OFF)
return()
endif ()
@ -62,48 +69,55 @@ function(add_module_library name)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
target_compile_definitions(${name} PRIVATE FMT_MODULE)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
if (FMT_USE_CMAKE_MODULES)
target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
FILES ${sources})
else()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND ${CMAKE_CXX_COMPILER}
-std=c++${std} -x c++-module --precompile -c
-o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
# Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND ${CMAKE_CXX_COMPILER}
-std=c++${std} -x c++-module --precompile -c
-o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endif()
endfunction()
include(CMakeParseArguments)
@ -145,21 +159,33 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ON)
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include OS-specific APIs." ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
option(FMT_UNICODE "Enable Unicode support." ON)
if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.")
endif ()
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif ()
# Get version from core.h
file(READ include/fmt/core.h core_h)
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
# Get version from base.h
file(READ include/fmt/base.h base_h)
if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from base.h.")
endif ()
# Use math to skip leading zeros if any.
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
@ -167,7 +193,7 @@ math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
${CPACK_PACKAGE_VERSION_PATCH})
message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "{fmt} version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@ -214,7 +240,13 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wnull-dereference -Wduplicated-cond)
-Wduplicated-cond)
# Workaround for GCC regression
# [12/13/14/15 regression] New (since gcc 12) false positive null-dereference in vector.resize
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference)
endif ()
endif ()
set(WERROR_FLAG -Werror)
endif ()
@ -263,13 +295,10 @@ function(add_headers VAR)
endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
format-inl.h os.h ostream.h printf.h ranges.h std.h
xchar.h)
set(FMT_SOURCES src/format.cc)
if (FMT_OS)
set(FMT_SOURCES ${FMT_SOURCES} src/os.cc)
endif ()
add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
@ -277,6 +306,10 @@ add_module_library(fmt src/fmt.cc FALLBACK
add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
enable_module(fmt)
elseif (FMT_OS)
target_sources(fmt PRIVATE src/os.cc)
else()
target_compile_definitions(fmt PRIVATE FMT_OS=0)
endif ()
if (FMT_WERROR)
@ -292,7 +325,7 @@ else ()
message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
endif ()
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -328,11 +361,21 @@ endif ()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
if (NOT MSVC)
# Unicode is always supported on compilers other than MSVC.
elseif (FMT_UNICODE)
# Unicode support requires compiling with /utf-8.
target_compile_options(fmt PUBLIC $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
target_compile_options(fmt-header-only INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
else ()
target_compile_definitions(fmt PUBLIC FMT_UNICODE=0)
endif ()
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only
${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -377,12 +420,20 @@ if (FMT_INSTALL)
set(INSTALL_TARGETS fmt fmt-header-only)
set(INSTALL_FILE_SET)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION "${FMT_INC_DIR}/fmt")
endif()
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
install(TARGETS ${INSTALL_TARGETS}
COMPONENT fmt-core
EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
${INSTALL_FILE_SET})
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
@ -390,13 +441,61 @@ if (FMT_INSTALL)
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR})
install(FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR}
COMPONENT fmt-core)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::)
NAMESPACE fmt::
COMPONENT fmt-core)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
COMPONENT fmt-core)
endif ()
function(add_doc_target)
find_program(DOXYGEN doxygen
PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
if (NOT DOXYGEN)
message(STATUS "Target 'doc' disabled because doxygen not found")
return ()
endif ()
find_program(MKDOCS mkdocs)
if (NOT MKDOCS)
message(STATUS "Target 'doc' disabled because mkdocs not found")
return ()
endif ()
set(sources )
foreach (source api.md index.md syntax.md get-started.md fmt.css fmt.js)
set(sources ${sources} doc/${source})
endforeach()
add_custom_target(
doc
COMMAND
${CMAKE_COMMAND}
-E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python
${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml
# MkDocs requires the site dir to be outside of the doc dir.
--site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html
--no-directory-urls
SOURCES ${sources})
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
COMPONENT fmt-doc OPTIONAL)
endfunction()
if (FMT_DOC)
add_doc_target()
endif ()
if (FMT_TEST)
enable_testing()
add_subdirectory(test)
endif ()
# Control fuzzing independent of the unit tests.
@ -421,8 +520,7 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files}
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant)
set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})

File diff suppressed because it is too large Load Diff

View File

@ -20,16 +20,16 @@ that help victims of the war in Ukraine: <https://www.stopputin.net/>.
Q&A: ask questions on [StackOverflow with the tag
fmt](https://stackoverflow.com/questions/tagged/fmt).
Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
# Features
- Simple [format API](https://fmt.dev/latest/api.html) with positional
- Simple [format API](https://fmt.dev/latest/api/) with positional
arguments for localization
- Implementation of [C++20
std::format](https://en.cppreference.com/w/cpp/utility/format) and
[C++23 std::print](https://en.cppreference.com/w/cpp/io/print)
- [Format string syntax](https://fmt.dev/latest/syntax.html) similar
- [Format string syntax](https://fmt.dev/latest/syntax/) similar
to Python\'s
[format](https://docs.python.org/3/library/stdtypes.html#str.format)
- Fast IEEE 754 floating-point formatter with correct rounding,
@ -37,10 +37,10 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
[Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm
- Portable Unicode support
- Safe [printf
implementation](https://fmt.dev/latest/api.html#printf-formatting)
implementation](https://fmt.dev/latest/api/#printf-formatting)
including the POSIX extension for positional arguments
- Extensibility: [support for user-defined
types](https://fmt.dev/latest/api.html#formatting-user-defined-types)
types](https://fmt.dev/latest/api/#formatting-user-defined-types)
- High performance: faster than common standard library
implementations of `(s)printf`, iostreams, `to_string` and
`to_chars`, see [Speed tests](#speed-tests) and [Converting a
@ -58,8 +58,8 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
buffer overflow errors
- Ease of use: small self-contained code base, no external
dependencies, permissive MIT
[license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst)
- [Portability](https://fmt.dev/latest/index.html#portability) with
[license](https://github.com/fmtlib/fmt/blob/master/LICENSE)
- [Portability](https://fmt.dev/latest/#portability) with
consistent output across platforms and support for older compilers
- Clean warning-free codebase even on high warning levels such as
`-Wall -Wextra -pedantic`
@ -203,43 +203,38 @@ and [ryu](https://github.com/ulfjack/ryu):
## Compile time and code bloat
The script
[bloat-test.py](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py)
from [format-benchmark](https://github.com/fmtlib/format-benchmark)
tests compile time and code bloat for nontrivial projects. It generates
100 translation units and uses `printf()` or its alternative five times
in each to simulate a medium-sized project. The resulting executable
size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), macOS
Sierra, best of three) is shown in the following tables.
The script [bloat-test.py][test] from [format-benchmark][bench] tests compile
time and code bloat for nontrivial projects. It generates 100 translation units
and uses `printf()` or its alternative five times in each to simulate a
medium-sized project. The resulting executable size and compile time (Apple
clang version 15.0.0 (clang-1500.1.0.2.5), macOS Sonoma, best of three) is shown
in the following tables.
[test]: https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py
[bench]: https://github.com/fmtlib/format-benchmark
**Optimized build (-O3)**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
| printf | 2.6 | 29 | 26 |
| printf+string | 16.4 | 29 | 26 |
| iostreams | 31.1 | 59 | 55 |
| {fmt} | 19.0 | 37 | 34 |
| Boost Format | 91.9 | 226 | 203 |
| Folly Format | 115.7 | 101 | 88 |
| printf | 1.6 | 54 | 50 |
| IOStreams | 25.9 | 98 | 84 |
| fmt 83652df | 4.8 | 54 | 50 |
| tinyformat | 29.1 | 161 | 136 |
| Boost Format | 55.0 | 530 | 317 |
As you can see, {fmt} has 60% less overhead in terms of resulting binary
code size compared to iostreams and comes pretty close to `printf`.
Boost Format and Folly Format have the largest overheads.
`printf+string` is the same as `printf` but with an extra `<string>`
include to measure the overhead of the latter.
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
binary size (within a rounding error on this system).
**Non-optimized build**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
| printf | 2.2 | 33 | 30 |
| printf+string | 16.0 | 33 | 30 |
| iostreams | 28.3 | 56 | 52 |
| {fmt} | 18.2 | 59 | 50 |
| Boost Format | 54.1 | 365 | 303 |
| Folly Format | 79.9 | 445 | 430 |
| printf | 1.4 | 54 | 50 |
| IOStreams | 23.4 | 92 | 68 |
| {fmt} 83652df | 4.4 | 89 | 85 |
| tinyformat | 24.5 | 204 | 161 |
| Boost Format | 36.4 | 831 | 462 |
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
to compare formatting function overhead only. Boost Format is a
@ -248,7 +243,7 @@ header-only library so it doesn\'t provide any linkage options.
## Running the tests
Please refer to [Building the
library](https://fmt.dev/latest/usage.html#building-the-library) for
library](https://fmt.dev/latest/get-started/#building-from-source) for
instructions on how to build the library and run the unit tests.
Benchmarks reside in a separate repository,
@ -270,8 +265,7 @@ or the bloat test:
# Migrating code
[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v17 (not yet
released) provides the
[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v18 provides the
[modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html)
check that is capable of converting occurrences of `printf` and
`fprintf` to `fmt::print` if configured to do so. (By default it
@ -297,13 +291,14 @@ converts to `std::print`.)
- [ccache](https://ccache.dev/): a compiler cache
- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
analytical database management system
- [ContextVision](https://www.contextvision.com/): medical imaging software
- [Contour](https://github.com/contour-terminal/contour/): a modern
terminal emulator
- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous
underwater vehicle
- [Drake](https://drake.mit.edu/): a planning, control, and analysis
toolbox for nonlinear dynamical systems (MIT)
- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and
- [Envoy](https://github.com/envoyproxy/envoy): C++ L7 proxy and
communication bus (Lyft)
- [FiveM](https://fivem.net/): a modification framework for GTA V
- [fmtlog](https://github.com/MengRao/fmtlog): a performant
@ -343,7 +338,7 @@ converts to `std::print`.)
- [Quill](https://github.com/odygrd/quill): asynchronous low-latency
logging library
- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to
simplify navigation, and executing complex multi-line terminal
simplify navigation, and execute complex multi-line terminal
command sequences
- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis
cluster proxy
@ -432,7 +427,7 @@ code bloat issues (see [Benchmarks](#benchmarks)).
## FastFormat
This is an interesting library that is fast, safe, and has positional
This is an interesting library that is fast, safe and has positional
arguments. However, it has significant limitations, citing its author:
> Three features that have no hope of being accommodated within the
@ -442,8 +437,8 @@ arguments. However, it has significant limitations, citing its author:
> - Octal/hexadecimal encoding
> - Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
It is also quite big and has a heavy dependency, on STLSoft, which might be
too restrictive for use in some projects.
## Boost Spirit.Karma
@ -462,7 +457,7 @@ second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html).
# Documentation License
The [Format String Syntax](https://fmt.dev/latest/syntax.html) section
The [Format String Syntax](https://fmt.dev/latest/syntax/) section
in the documentation is based on the one from Python [string module
documentation](https://docs.python.org/3/library/string.html#module-string).
For this reason, the documentation is distributed under the Python
@ -486,5 +481,5 @@ To report a security issue, please disclose it at [security
advisory](https://github.com/fmtlib/fmt/security/advisories/new).
This project is maintained by a team of volunteers on a
reasonable-effort basis. As such, please give us at least 90 days to
reasonable-effort basis. As such, please give us at least *90* days to
work on a fix before public exposure.

View File

@ -8,14 +8,15 @@
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h"
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
@ -28,15 +29,18 @@ auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
@ -62,28 +66,18 @@ class dynamic_arg_list {
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
* A dynamic list of formatting arguments with storage.
*
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;
detail::mapped_type_constant<T, char_type>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
@ -96,7 +90,7 @@ class dynamic_format_arg_store
};
template <typename T>
using stored_type = conditional_t<
using stored_t = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
@ -111,80 +105,72 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>;
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
if (named_info_.empty())
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.emplace_back(detail::unwrap(arg.value));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
data_[0] = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
operator basic_format_args<Context>() const {
return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
!named_info_.empty());
}
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*
* Note that custom types and string types (but not string views) are copied
* into the store dynamically allocating memory if necessary.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
* Adds a reference to the argument into the dynamic store for later passing
* to a formatting function.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* char band[] = "Rolling Stones";
* store.push_back(std::cref(band));
* band[9] = 'c'; // Changing str affects the output.
* std::string result = fmt::vformat("{}", store);
* // result == "Rolling Scones"
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
@ -193,41 +179,40 @@ class dynamic_format_arg_store
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
* Adds named argument into the dynamic store for later passing to a
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/** Erase all elements from the store */
/// Erase all elements from the store.
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
dynamic_args_ = {};
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");
"set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
};
FMT_END_NAMESPACE

2958
3rdparty/fmt/include/fmt/base.h vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -227,7 +227,7 @@ struct color_type {
};
} // namespace detail
/** A text style consisting of foreground and background colors and emphasis. */
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
@ -239,7 +239,7 @@ class text_style {
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@ -248,7 +248,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@ -310,13 +310,13 @@ class text_style {
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
@ -330,7 +330,7 @@ FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
FMT_CONSTEXPR ansi_color_escape(color_type text_color,
const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code
// sequence.
@ -390,8 +390,8 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer);
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
}
private:
@ -412,13 +412,13 @@ template <typename Char> struct ansi_color_escape {
};
template <typename Char>
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
@ -434,7 +434,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg : detail::view {
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
@ -442,145 +442,115 @@ template <typename T> struct styled_arg : detail::view {
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background = detail::make_background_color<Char>(ts.get_background());
auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color<Char>(buf);
vformat_to(buf, fmt, args);
if (has_style) reset_color<Char>(buf);
}
} // namespace detail
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
// Legacy wide streams are not supported.
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) {
detail::print(f, string_view(buf.begin(), buf.size()));
return;
}
buf.push_back('\0');
int result = std::fputs(buf.data(), f);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to the specified file stream using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
template <typename... T>
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to stdout using ANSI escape sequences to
* specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
template <typename S, typename Char = char_t<S>>
inline auto vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
inline auto vformat(const text_style& ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline auto format(const text_style& ts, const S& format_str,
const Args&... args) -> std::basic_string<Char> {
return fmt::vformat(ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename... T>
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
auto vformat_to(OutputIt out, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <
typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value &&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
* Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, const text_style& ts,
format_string<T...> fmt, T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
}
template <typename T, typename Char>
@ -589,47 +559,44 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
const auto& value = arg.value;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out);
out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out);
out = detail::copy<Char>(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(value, ctx);
out = formatter<T, Char>::format(arg.value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = std::copy(reset_color.begin(), reset_color.end(), out);
out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
\rst
Returns an argument that will be formatted using ANSI escape sequences,
to be used in a formatting function.
**Example**::
fmt::print("Elapsed time: {0:.2f} seconds",
fmt::styled(1.23, fmt::fg(fmt::color::green) |
fmt::bg(fmt::color::blue)));
\endrst
* Returns an argument that will be formatted using ANSI escape sequences,
* to be used in a formatting function.
*
* **Example**:
*
* fmt::print("Elapsed time: {0:.2f} seconds",
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
* fmt::bg(fmt::color::blue)));
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)

View File

@ -8,49 +8,44 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
counting_iterator it) -> counting_iterator {
return it + (end - begin);
}
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
FMT_EXPORT class compiled_string {};
namespace detail {
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
* `constexpr if` compiler support.
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
explicit constexpr operator basic_string_view<char_type>() const {
constexpr explicit operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
@ -75,6 +70,29 @@ constexpr const auto& get([[maybe_unused]] const T& first,
return detail::get<N - 1>(rest...);
}
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (is_static_named_arg<T>()) {
if (name == T::name) return N;
}
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter.
return -1;
}
# endif
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
# endif
(void)name;
return -1;
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
@ -144,11 +162,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy_str<Char>(s.begin(), s.end(), out);
return copy<Char>(s.begin(), s.end(), out);
} else {
return write<Char>(out, arg);
}
return write<Char>(out, arg);
}
};
@ -236,13 +255,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@ -274,6 +292,7 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
}
template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
@ -281,25 +300,28 @@ template <typename Char> struct arg_id_handler {
return 0;
}
constexpr int on_index(int id) {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
}
};
template <typename Char> struct parse_arg_id_result {
arg_id_kind kind;
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
@ -313,14 +335,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
@ -333,7 +354,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
fmt);
}
}
}
@ -341,22 +362,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -364,28 +384,27 @@ constexpr auto compile_format_string(S format_str) {
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
fmt);
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
@ -394,29 +413,26 @@ constexpr auto compile_format_string(S format_str) {
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
@ -488,40 +504,40 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args)
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), format_str,
std::forward<Args>(args)...);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args)
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
return buf.count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()});
void print(std::FILE* f, const S& fmt, const Args&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
Str>();

File diff suppressed because it is too large Load Diff

View File

@ -8,36 +8,36 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#include <algorithm>
#include <cerrno> // errno
#include <climits>
#include <cmath>
#include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#ifndef FMT_MODULE
# include <algorithm>
# include <cerrno> // errno
# include <climits>
# include <cmath>
# include <exception>
#endif
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <io.h> // _isatty
#endif
#include "format.h"
#if FMT_USE_LOCALE
# include <locale>
#endif
#ifndef FMT_FUNC
# define FMT_FUNC
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device
// code pass.
std::terminate();
}
FMT_FUNC void throw_format_error(const char* message) {
FMT_THROW(format_error(message));
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
@ -56,89 +56,105 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
++error_code_size;
}
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out);
auto it = appender(out);
if (message.size() <= inline_buffer_size - error_code_size)
fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
FMT_ASSERT(out.size() <= inline_buffer_size, "");
}
FMT_FUNC void report_error(format_func func, int error_code,
const char* message) noexcept {
FMT_FUNC void do_report_error(format_func func, int error_code,
const char* message) noexcept {
memory_buffer full_message;
func(full_message, error_code, message);
// Don't use fwrite_fully because the latter may throw.
// Don't use fwrite_all because the latter may throw.
if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
std::fputc('\n', stderr);
}
// A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
auto grouping = facet.grouping();
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep};
}
template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point();
}
#else
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
}
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.';
}
#endif
#if FMT_USE_LOCALE
FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs<>& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
const format_specs& specs, locale_ref loc) -> bool {
auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs);
return use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs);
#endif
return false;
}
#endif
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#endif
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
grouping_ = numpunct.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
auto& np = detail::use_facet<detail::numpunct<char>>(loc);
grouping_ = np.grouping();
if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep());
}
#if FMT_USE_LOCALE
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs<>& specs) const -> bool {
appender out, loc_value val, const format_specs& specs) const -> bool {
return val.visit(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
}
@ -1411,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
const char* message) noexcept {
FMT_TRY {
auto ec = std::error_code(error_code, std::generic_category());
write(std::back_inserter(out), std::system_error(ec, message).what());
detail::write(appender(out), std::system_error(ec, message).what());
return;
}
FMT_CATCH(...) {}
@ -1420,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
FMT_FUNC void report_system_error(int error_code,
const char* message) noexcept {
report_error(format_system_error, error_code, message);
do_report_error(format_system_error, error_code, message);
}
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
@ -1432,9 +1448,252 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
}
namespace detail {
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
locale_ref loc) {
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
}
template <typename T> struct span {
T* data;
size_t size;
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
}
#ifndef getc_unlocked
template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
return _fgetc_nolock(f);
}
#endif
template <typename F = FILE, typename Enable = void>
struct has_flockfile : std::false_type {};
template <typename F>
struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
: std::true_type {};
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
template <typename F> class file_base {
public:
F* file_;
public:
file_base(F* file) : file_(file) {}
operator F*() const { return file_; }
// Reads a code unit from the stream.
auto get() -> int {
int result = getc_unlocked(file_);
if (result == EOF && ferror(file_) != 0)
FMT_THROW(system_error(errno, FMT_STRING("getc failed")));
return result;
}
// Puts the code unit back into the stream buffer.
void unget(char c) {
if (ungetc(c, file_) == EOF)
FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
}
void flush() { fflush(this->file_); }
};
// A FILE wrapper for glibc.
template <typename F> class glibc_file : public file_base<F> {
private:
enum {
line_buffered = 0x200, // _IO_LINE_BUF
unbuffered = 2 // _IO_UNBUFFERED
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_IO_write_ptr) return;
// Force buffer initialization by placing and removing a char in a buffer.
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
}
// Returns the file's read buffer.
auto get_read_buffer() const -> span<const char> {
auto ptr = this->file_->_IO_read_ptr;
return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)};
}
// Returns the file's write buffer.
auto get_write_buffer() const -> span<char> {
auto ptr = this->file_->_IO_write_ptr;
return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)};
}
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
}
void flush() { fflush_unlocked(this->file_); }
};
// A FILE wrapper for Apple's libc.
template <typename F> class apple_file : public file_base<F> {
private:
enum {
line_buffered = 1, // __SNBF
unbuffered = 2 // __SLBF
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
auto get_read_buffer() const -> span<const char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_r)};
}
auto get_write_buffer() const -> span<char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_bf._base + this->file_->_bf._size -
this->file_->_p)};
}
void advance_write_buffer(size_t size) {
this->file_->_p += size;
this->file_->_w -= size;
}
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));
}
};
// A fallback FILE wrapper.
template <typename F> class fallback_file : public file_base<F> {
private:
char next_; // The next unconsumed character in the buffer.
bool has_next_ = false;
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool { return false; }
auto needs_flush() const -> bool { return false; }
void init_buffer() {}
auto get_read_buffer() const -> span<const char> {
return {&next_, has_next_ ? 1u : 0u};
}
auto get_write_buffer() const -> span<char> { return {nullptr, 0}; }
void advance_write_buffer(size_t) {}
auto get() -> int {
has_next_ = false;
return file_base<F>::get();
}
void unget(char c) {
file_base<F>::unget(c);
next_ = c;
has_next_ = true;
}
};
#ifndef FMT_USE_FALLBACK_FILE
# define FMT_USE_FALLBACK_FILE 0
#endif
template <typename F,
FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
auto get_file(F* f, int) -> apple_file<F> {
return f;
}
template <typename F,
FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)>
inline auto get_file(F* f, int) -> glibc_file<F> {
return f;
}
inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
template <typename F = FILE, typename Enable = void>
class file_print_buffer : public buffer<char> {
public:
explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
};
template <typename F>
class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
: public buffer<char> {
private:
file_ref file_;
static void grow(buffer<char>& base, size_t) {
auto& self = static_cast<file_print_buffer&>(base);
self.file_.advance_write_buffer(self.size());
if (self.file_.get_write_buffer().size == 0) self.file_.flush();
auto buf = self.file_.get_write_buffer();
FMT_ASSERT(buf.size > 0, "");
self.set(buf.data, buf.size);
self.clear();
}
public:
explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
file_.init_buffer();
auto buf = file_.get_write_buffer();
set(buf.data, buf.size);
}
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
F* f = file_; // Make funlockfile depend on the template parameter F
funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}
};
#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE)
FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
@ -1445,39 +1704,51 @@ FMT_FUNC bool write_console(int fd, string_view text) {
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
}
FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool {
return write_console(_fileno(f), text);
}
#endif
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args,
bool newline) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
fwrite_fully(buffer.data(), buffer.size(), f);
if (newline) buffer.push_back('\n');
fwrite_all(buffer.data(), buffer.size(), f);
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
if (write_console(fd, text)) return;
}
#endif
fwrite_fully(text.data(), text.size(), f);
fwrite_all(text.data(), text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>())
return vprint_buffered(f, fmt, args);
auto&& buffer = detail::file_print_buffer<>(f);
return detail::vformat_to(buffer, fmt, args);
}
FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
buffer.push_back('\n');
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(string_view fmt, format_args args) {
vprint(stdout, fmt, args);
}

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,18 @@
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#include "format.h"
#if defined __APPLE__ || defined(__FreeBSD__)
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
@ -77,46 +77,33 @@ FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
@ -131,41 +118,38 @@ FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
}
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
return vwindows_error(error_code, message, fmt::make_format_args(args...));
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... T>
auto windows_error(int error_code, string_view message, const T&... args)
-> std::system_error {
return vwindows_error(error_code, message, vargs<T...>{{args...}});
}
// Reports a Windows error without throwing an exception.
@ -180,8 +164,8 @@ inline auto system_category() noexcept -> const std::error_category& {
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
void say(const S& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
@ -192,24 +176,24 @@ class buffered_file {
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() noexcept : file_(nullptr) {}
inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept;
public:
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
auto operator=(buffered_file&& other) -> buffered_file& {
inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@ -223,21 +207,20 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
auto get() const noexcept -> FILE* { return file_; }
inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
template <typename... T>
inline void print(string_view fmt, const T&... args) {
fmt::vargs<T...> vargs = {{args...}};
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
@ -251,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
@ -263,7 +248,7 @@ class FMT_API file {
};
// Constructs a file object which doesn't represent any file.
file() noexcept : fd_(-1) {}
inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag);
@ -272,10 +257,10 @@ class FMT_API file {
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
auto operator=(file&& other) -> file& {
inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -286,7 +271,7 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
auto descriptor() const noexcept -> int { return fd_; }
inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
@ -313,11 +298,6 @@ class FMT_API file {
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
// DEPRECATED! Taking files as out parameters is deprecated.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
auto fdopen(const char* mode) -> buffered_file;
@ -329,15 +309,24 @@ class FMT_API file {
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
auto getpagesize() -> long;
namespace detail {
struct buffer_size {
buffer_size() = default;
constexpr buffer_size() = default;
size_t value = 0;
auto operator=(size_t val) const -> buffer_size {
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@ -348,7 +337,7 @@ struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
@ -369,79 +358,62 @@ struct ostream_params {
# endif
};
class file_buffer final : public buffer<char> {
} // namespace detail
FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
/// A fast buffered output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream : private detail::buffer<char> {
private:
file file_;
FMT_API void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params);
static void grow(buffer<char>& buf, size_t);
public:
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other);
FMT_API ~file_buffer();
ostream(ostream&& other) noexcept;
~ostream();
void flush() {
operator writer() {
detail::buffer<char>& buf = *this;
return buf;
}
inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
void close() {
flush();
file_.close();
}
};
} // namespace detail
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
inline void close() {
flush();
file_.close();
}
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(std::back_inserter(buffer_), fmt,
fmt::make_format_args(args...));
vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline auto output_file(cstring_view path, T... params) -> ostream {

View File

@ -8,7 +8,9 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <fstream> // std::filebuf
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32
# ifdef __GLIBCXX__
@ -18,42 +20,19 @@
# include <io.h>
#endif
#include "format.h"
#include "chrono.h" // formatbuf
#ifdef _MSVC_STL_UPDATE
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
# define FMT_MSVC_STL_UPDATE 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
@ -64,53 +43,18 @@ class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
#if FMT_MSC_VERSION
#if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && false
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
// Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
using unsigned_streamsize = make_unsigned_t<std::streamsize>;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
@ -121,21 +65,9 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
@ -143,11 +75,14 @@ template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete;
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value);
auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
auto&& output = std::basic_ostream<Char>(&formatbuf);
output.imbue(std::locale::classic()); // The default is always unlocalized.
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
@ -158,73 +93,67 @@ using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
template <typename OutputIt>
auto format(detail::streamed_view<T> view,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
/**
\rst
Returns a view that formats `value` via an ostream ``operator<<``.
**Example**::
fmt::print("Current thread id: {}\n",
fmt::streamed(std::this_thread::get_id()));
\endrst
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
inline void vprint(std::ostream& os, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
} // namespace detail
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args);
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
detail::vformat_to(buffer, fmt, args);
FILE* f = nullptr;
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
}
}
#endif
detail::ignore_unused(f);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
fmt::vargs<T...> vargs = {{args...}};
if (detail::use_utf8) return vprint(os, fmt.str, vargs);
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt.str, vargs);
detail::write_buffer(os, buffer);
}
FMT_EXPORT template <typename... T>
@ -232,14 +161,6 @@ void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@ -8,8 +8,10 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
@ -22,7 +24,7 @@ template <typename T> struct printf_formatter {
template <typename Char> class basic_printf_context {
private:
detail::buffer_appender<Char> out_;
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
@ -31,43 +33,53 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = basic_format_parse_context<Char>;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(detail::buffer_appender<Char> out,
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
auto out() -> detail::buffer_appender<Char> { return out_; }
void advance_to(detail::buffer_appender<Char>) {}
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
namespace detail {
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
template <>
inline auto find<false, char>(const char* first, const char* last, char value,
const char*& out) -> bool {
out =
static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
return out != nullptr;
}
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>();
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static auto fits_in_int(bool) -> bool { return true; }
inline static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
@ -75,20 +87,20 @@ template <> struct int_checker<true> {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static auto fits_in_int(int) -> bool { return true; }
inline static auto fits_in_int(int) -> bool { return true; }
};
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big");
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int {
throw_format_error("precision is not integer");
report_error("precision is not integer");
return 0;
}
};
@ -133,25 +145,19 @@ template <typename T, typename Context> class arg_converter {
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
auto n = static_cast<int>(static_cast<target_type>(value));
arg_ = detail::make_arg<Context>(n);
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
if (is_signed)
arg_ = static_cast<int>(static_cast<target_type>(value));
else
arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else {
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
if (is_signed)
arg_ = static_cast<long long>(value);
else
arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
}
}
@ -165,7 +171,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
@ -178,8 +184,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
auto c = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
arg_ = static_cast<typename Context::char_type>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -195,28 +200,28 @@ template <typename Char> struct get_cstring {
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
class printf_width_handler {
private:
format_specs<Char>& specs_;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
specs_.set_align(align::left);
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) throw_format_error("number is too big");
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned {
throw_format_error("width is not integer");
report_error("width is not integer");
return 0;
}
};
@ -224,12 +229,12 @@ template <typename Char> class printf_width_handler {
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The ``printf`` argument formatter.
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
@ -240,105 +245,96 @@ class printf_arg_formatter : public arg_formatter<Char> {
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
template <typename T> void write(T value) {
detail::write<Char>(this->out, value, this->specs, this->locale);
}
public:
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { base::operator()(value); }
void operator()(monostate value) { write(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (!std::is_same<T, Char>::value) {
base::operator()(value);
write(value);
return;
}
format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
format_specs s = this->specs;
if (s.type() != presentation_type::none &&
s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
s.set_sign(sign::none);
s.clear_alt();
s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
if (s.align() == align::none || s.align() == align::numeric)
s.set_align(align::right);
detail::write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) {
base::operator()(value);
write(value);
}
/** Formats a null-terminated C string. */
void operator()(const char* value) {
if (value)
base::operator()(value);
write(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
/** Formats a null-terminated wide C string. */
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
write(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { base::operator()(value); }
void operator()(basic_string_view<Char> value) { write(value); }
/** Formats a pointer. */
void operator()(const void* value) {
if (value)
base::operator()(value);
write(value);
else
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({});
auto parse_ctx = parse_context<Char>({});
handle.format(parse_ctx, context_);
}
};
template <typename Char>
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case '-': specs.set_align(align::left); break;
case '+': specs.set_sign(sign::plus); break;
case '0': specs.set_fill('0'); break;
case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space;
if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break;
case '#':
specs.alt = true;
break;
default:
return;
case '#': specs.set_alt(); break;
default: return;
}
}
}
template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
@ -350,11 +346,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill[0] = '0';
if (c == '0') specs.set_fill('0');
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) throw_format_error("number is too big");
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
@ -365,63 +361,47 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) throw_format_error("number is too big");
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<Char>(specs), get_arg(-1)));
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t)
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'x':
return in(t, integral_set) ? pt::hex_lower : pt::none;
case 'X':
return in(t, integral_set) ? pt::hex_upper : pt::none;
case 'a':
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
case 'A':
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
case 'e':
return in(t, float_set) ? pt::exp_lower : pt::none;
case 'E':
return in(t, float_set) ? pt::exp_upper : pt::none;
case 'f':
return in(t, float_set) ? pt::fixed_lower : pt::none;
case 'F':
return in(t, float_set) ? pt::fixed_upper : pt::none;
case 'g':
return in(t, float_set) ? pt::general_lower : pt::none;
case 'G':
return in(t, float_set) ? pt::general_upper : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
case 'd': return in(t, integral_set) ? pt::dec : pt::none;
case 'o': return in(t, integral_set) ? pt::oct : pt::none;
case 'X': upper = true; FMT_FALLTHROUGH;
case 'x': return in(t, integral_set) ? pt::hex : pt::none;
case 'E': upper = true; FMT_FALLTHROUGH;
case 'e': return in(t, float_set) ? pt::exp : pt::none;
case 'F': upper = true; FMT_FALLTHROUGH;
case 'f': return in(t, float_set) ? pt::fixed : pt::none;
case 'G': upper = true; FMT_FALLTHROUGH;
case 'g': return in(t, float_set) ? pt::general : pt::none;
case 'A': upper = true; FMT_FALLTHROUGH;
case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c': return in(t, integral_set) ? pt::chr : pt::none;
case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default: return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using iterator = buffer_appender<Char>;
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format);
auto parse_ctx = parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
@ -449,12 +429,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>();
specs.align = align::right;
auto specs = format_specs();
specs.set_align(align::right);
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) throw_format_error("argument not found");
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
@ -464,8 +444,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(printf_precision_handler(), get_arg(-1)));
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
@ -474,25 +454,26 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) {
if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill[0] = ' ';
specs.set_fill(' ');
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg);
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
arg = sv;
}
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
if (specs.fill_unit<Char>() == '0') {
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
specs.set_align(align::numeric);
} else {
// Ignore '0' flag for non-numeric types or if '-' flag is also present.
specs.set_fill(' ');
}
}
// Parse length and convert the argument to the required type.
@ -517,47 +498,39 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'j': convert_arg<intmax_t>(arg, t); break;
case 'z': convert_arg<size_t>(arg, t); break;
case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
default: --it; convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) throw_format_error("invalid format string");
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
if (is_integral_type(arg.type())) {
// Normalize type.
switch (type) {
case 'i':
case 'u':
type = 'd';
break;
case 'u': type = 'd'; break;
case 'c':
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
specs.type = parse_printf_presentation_type(type, arg.type());
if (specs.type == presentation_type::none)
throw_format_error("invalid format specifier");
bool upper = false;
specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
if (upper) specs.set_upper();
start = it;
// Format argument.
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
@ -569,56 +542,44 @@ using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
// DEPRECATED!
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename Char>
inline auto vsprintf(
basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return to_string(buf);
return {buf.data(), buf.size()};
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename Char>
inline auto vfprintf(
std::FILE* f, basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int {
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
@ -628,36 +589,33 @@ inline auto vfprintf(
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = char_t<S>>
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
make_printf_args<Char>(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(
basic_string_view<Char> fmt,
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
@ -666,7 +624,7 @@ inline auto printf(string_view fmt, const T&... args) -> int {
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...));
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT

View File

@ -8,67 +8,31 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <tuple>
#include <type_traits>
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename Range, typename OutputIt>
auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIt>
auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static constexpr const bool value =
is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
#endif
};
template <typename T> class is_set {
@ -76,26 +40,10 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
@ -110,17 +58,21 @@ template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overload
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
// ADL overload. Only participates in overload resolution if member functions
// ADL overloads. Only participate in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
@ -141,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T,
void_t<
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
T, void_t<decltype(*detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_end(std::declval<T>())),
T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
# undef FMT_DECLTYPE_RETURN
#endif
// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename U, typename V = typename std::remove_cv<U>::type>
static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
template <typename> static void check(...);
public:
@ -206,12 +157,13 @@ class is_tuple_formattable_ {
static constexpr const bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is>
static auto check2(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>) -> std::true_type;
static auto check2(...) -> std::false_type;
template <std::size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(check2(
template <size_t... Is>
static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto all_true(...) -> std::false_type;
template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{},
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
@ -292,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11.
template <typename ParseContext> struct parse_empty_specs {
template <typename Char> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
ParseContext& ctx;
parse_context<Char>& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0)
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx));
++i;
}
@ -355,66 +318,48 @@ struct formatter<Tuple, Char,
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
if (it != ctx.end() && *it != '}')
FMT_THROW(format_error("invalid format specifier"));
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
auto end = ctx.end();
if (it != end && detail::to_ascii(*it) == 'n') {
++it;
set_brackets({}, {});
set_separator({});
}
if (it != end && *it != '}') report_error("invalid format specifier");
ctx.advance_to(it);
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy_str<Char>(closing_bracket_, ctx.out());
return detail::copy<Char>(closing_bracket_, ctx.out());
}
};
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_convertible<T, detail::std_string_view<Char>>::value;
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element>
using range_formatter_type =
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>;
using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
@ -438,6 +383,24 @@ struct range_formatter<
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public:
FMT_CONSTEXPR range_formatter() {}
@ -456,21 +419,40 @@ struct range_formatter<
closing_bracket_ = close;
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
if (it != end && *it == 'n') {
switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {});
++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
}
if (it != end && *it != '}') {
if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
}
ctx.advance_to(it);
@ -479,80 +461,26 @@ struct range_formatter<
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out();
out = detail::copy_str<Char>(opening_bracket_, out);
int i = 0;
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out);
if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out);
auto&& item = *it;
out = underlying_.format(mapper.map(item), ctx);
auto&& item = *it; // Need an lvalue
out = underlying_.format(item, ctx);
++i;
}
out = detail::copy_str<Char>(closing_bracket_, out);
out = detail::copy<Char>(closing_bracket_, out);
return out;
}
};
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail {
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter;
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
template <range_format K, typename R, typename Char>
struct range_default_formatter<
K, R, Char,
enable_if_t<(K == range_format::sequence || K == range_format::map ||
K == range_format::set)>> {
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
underlying_.underlying().set_brackets({}, {});
underlying_.underlying().set_separator(
detail::string_literal<Char, ':', ' '>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return underlying_.format(range, ctx);
}
};
} // namespace detail
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
@ -562,23 +490,189 @@ struct range_format_kind
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
range_format::disabled>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
detail::is_formattable_delayed<R, Char>
#endif
>::value>>
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
Char> {
enable_if_t<conjunction<
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
};
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
// A map formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
@ -589,65 +683,64 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value,
auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
template <typename ParseContext>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
-> decltype(ctx.begin()) {
-> const Char* {
return ctx.begin();
}
template <typename ParseContext, size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
template <size_t N>
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) {
-> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements"));
report_error("incompatible format specs for tuple elements");
}
#endif
return end;
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
if (N > 1) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
return out;
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
};
@ -691,40 +784,57 @@ struct formatter<
FMT_BEGIN_EXPORT
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{1, 2, 3};
* fmt::print("{}", fmt::join(v, ", "));
* // Output: 1, 2, 3
*
* `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
return {tuple, sep};
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
/**
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
* auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
* Returns an object that formats `std::initializer_list` with elements
* separated by `sep`.
*
* **Example**:
*
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)

View File

@ -8,39 +8,49 @@
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>) && \
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
#if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
#endif
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
@ -52,17 +62,6 @@
# endif
#endif
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
@ -117,7 +116,7 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs<Char> specs_;
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
@ -125,33 +124,33 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
Char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = *it++;
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
# ifdef _WIN32
auto path_string = !path_type_ ? p.native() : p.generic_wstring();
# else
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
@ -163,13 +162,30 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
specs);
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
@ -189,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
return this->write_padded(ctx, writer{bs});
}
};
@ -222,7 +238,7 @@ struct formatter<std::optional<T>, Char,
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
@ -242,13 +258,62 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
is_formattable<T, Char>::value) &&
is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
if constexpr (!std::is_void<T>::value)
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
@ -291,16 +356,6 @@ template <typename T, typename C> class is_variant_formattable_ {
decltype(check(variant_index_sequence<T>{}))::value;
};
template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);
else
return write<Char>(out, v);
}
} // namespace detail
template <typename T> struct is_variant_like {
@ -314,8 +369,7 @@ template <typename T, typename C> struct is_variant_formattable {
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@ -332,8 +386,7 @@ struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@ -346,7 +399,7 @@ struct formatter<
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v);
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
@ -362,23 +415,128 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
detail::arg_ref<char> width_ref_;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
return it;
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
memory_buffer buf;
buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
specs);
}
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
@ -388,81 +546,29 @@ struct formatter<
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_TYPEID != 0;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
format_specs<Char> spec;
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex);
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
out = detail::write_bytes(out, demangled_name_view, spec);
# elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec);
# else
out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
}
};
@ -509,6 +615,14 @@ struct formatter<BitRef, Char,
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
@ -533,5 +647,80 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.set_sign(sign::plus);
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.dynamic()) {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.copy_fill_from(specs);
outer_specs.set_align(specs.align());
specs.width = 0;
specs.set_fill({});
specs.set_align(align::none);
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@ -8,12 +8,16 @@
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include <cwchar>
#include "color.h"
#include "format.h"
#include "ostream.h"
#include "ranges.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#ifndef FMT_MODULE
# include <cwchar>
# if FMT_USE_LOCALE
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
@ -22,10 +26,26 @@ namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<
S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#if FMT_USE_LOCALE
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
@ -40,42 +60,79 @@ inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_parse_context = parse_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
template <typename Char, typename... T> struct basic_fstring {
private:
basic_string_view<Char> str_;
static constexpr int num_static_named_args =
detail::count_static_named_args<T...>();
using checker = detail::format_string_checker<
Char, static_cast<int>(sizeof...(T)), num_static_named_args,
num_static_named_args != detail::count_named_args<T...>()>;
using arg_pack = detail::arg_pack<T...>;
public:
using t = basic_fstring;
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<Char>(s, checker(s, arg_pack()));
}
template <typename S,
FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
std::is_same<typename S::char_type, Char>::value)>
FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
FMT_CONSTEXPR int ignore =
(parse_format_string(sv, checker(sv, arg_pack())), 0);
detail::ignore_unused(ignore);
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
template <typename Char, typename... T>
using basic_format_string = basic_fstring<Char, T...>;
template <typename... T>
using wformat_string = typename basic_format_string<wchar_t, T...>::t;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(const T&... args)
-> format_arg_store<wformat_context, T...> {
return {args...};
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
return fmt::make_format_args<wformat_context>(args...);
}
#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
return {s};
}
#endif
} // namespace literals
#endif
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
@ -83,9 +140,9 @@ auto join(It begin, Sentinel end, wstring_view sep)
return {begin, end, sep};
}
template <typename Range>
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
@ -96,13 +153,19 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep);
}
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args);
return to_string(buf);
detail::vformat_to(buf, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
@ -110,110 +173,122 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T, typename Char = char_t<S>,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename Char = char_t<S>,
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), args);
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, T&&... args)
inline auto format(const Locale& loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc));
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename Locale, typename S, typename... T,
typename Char = char_t<S>,
template <typename Locale, typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args);
detail::vformat_to(buf, fmt, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... T, typename Char = char_t<S>,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
@ -247,9 +322,48 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);
detail::write_buffer(os, buffer);
}
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>
void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}

View File

@ -1,38 +1,57 @@
module;
#ifdef _MSVC_LANG
# define FMT_CPLUSPLUS _MSVC_LANG
#else
# define FMT_CPLUSPLUS __cplusplus
#endif
// Put all implementation-provided headers into the global module fragment
// to prevent attachment to this module.
#include <algorithm>
#ifndef FMT_IMPORT_STD
# include <algorithm>
# include <bitset>
# include <chrono>
# include <cmath>
# include <complex>
# include <cstddef>
# include <cstdint>
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <ctime>
# include <exception>
# if FMT_CPLUSPLUS > 202002L
# include <expected>
# endif
# include <filesystem>
# include <fstream>
# include <functional>
# include <iterator>
# include <limits>
# include <locale>
# include <memory>
# include <optional>
# include <ostream>
# include <source_location>
# include <stdexcept>
# include <string>
# include <string_view>
# include <system_error>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <variant>
# include <vector>
#else
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <time.h>
#endif
#include <cerrno>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <exception>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <variant>
#include <vector>
#include <version>
#if __has_include(<cxxabi.h>)
@ -70,6 +89,10 @@ module;
export module fmt;
#ifdef FMT_IMPORT_STD
import std;
#endif
#define FMT_EXPORT export
#define FMT_BEGIN_EXPORT export {
#define FMT_END_EXPORT }
@ -83,6 +106,10 @@ export module fmt;
extern "C++" {
#endif
#ifndef FMT_OS
# define FMT_OS 1
#endif
// All library-provided declarations and definitions must be in the module
// purview to be exported.
#include "fmt/args.h"
@ -90,8 +117,12 @@ extern "C++" {
#include "fmt/color.h"
#include "fmt/compile.h"
#include "fmt/format.h"
#include "fmt/os.h"
#if FMT_OS
# include "fmt/os.h"
#endif
#include "fmt/ostream.h"
#include "fmt/printf.h"
#include "fmt/ranges.h"
#include "fmt/std.h"
#include "fmt/xchar.h"
@ -104,5 +135,17 @@ extern "C++" {
module :private;
#endif
#include "format.cc"
#include "os.cc"
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#endif
#if FMT_HAS_INCLUDE("format.cc")
# include "format.cc"
#endif
#if FMT_OS && FMT_HAS_INCLUDE("os.cc")
# include "os.cc"
#endif
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
}
#endif

View File

@ -15,7 +15,8 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
#if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
@ -26,8 +27,10 @@ template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);

152
3rdparty/fmt/src/os.cc vendored
View File

@ -12,47 +12,51 @@
#include "fmt/os.h"
#include <climits>
#ifndef FMT_MODULE
# include <climits>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# endif // _WIN32
# endif // FMT_USE_FCNTL
# ifdef _WIN32
# include <windows.h>
# endif
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifndef S_IRGRP
# define S_IRGRP 0
# endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH
# define S_IROTH 0
# endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#endif
#ifdef _WIN32
# include <windows.h>
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifndef S_IRGRP
# define S_IRGRP 0
# endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH
# define S_IROTH 0
# endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
#endif
namespace {
@ -156,7 +160,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
}
void report_windows_error(int error_code, const char* message) noexcept {
report_error(detail::format_windows_error, error_code, message);
do_report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
@ -182,12 +186,14 @@ void buffered_file::close() {
}
int buffered_file::descriptor() const {
#if !defined(fileno)
#ifdef FMT_HAS_SYSTEM
// fileno is a macro on OpenBSD.
# ifdef fileno
# undef fileno
# endif
int fd = FMT_POSIX_CALL(fileno(file_));
#elif defined(FMT_HAS_SYSTEM)
// fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
# define FMT_DISABLE_MACRO
int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_));
#elif defined(_WIN32)
int fd = _fileno(file_);
#else
int fd = fileno(file_);
#endif
@ -200,6 +206,7 @@ int buffered_file::descriptor() const {
# ifdef _WIN32
using mode_t = int;
# endif
constexpr mode_t default_open_mode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
@ -301,29 +308,6 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
if (result == -1) ec = std::error_code(errno, std::generic_category());
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
@ -352,6 +336,24 @@ file file::open_windows_file(wcstring_view path, int oflag) {
}
# endif
pipe::pipe() {
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
// The following assignments don't throw.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
# if !defined(__MSDOS__)
long getpagesize() {
# ifdef _WIN32
@ -372,31 +374,25 @@ long getpagesize() {
}
# endif
namespace detail {
void file_buffer::grow(size_t) {
if (this->size() == this->capacity()) flush();
void ostream::grow(buffer<char>& buf, size_t) {
if (buf.size() == buf.capacity()) static_cast<ostream&>(buf).flush();
}
file_buffer::file_buffer(cstring_view path,
const detail::ostream_params& params)
: file_(path, params.oflag) {
ostream::ostream(cstring_view path, const detail::ostream_params& params)
: buffer<char>(grow), file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
file_buffer::file_buffer(file_buffer&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
ostream::ostream(ostream&& other) noexcept
: buffer<char>(grow, other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
file_buffer::~file_buffer() {
ostream::~ostream() {
flush();
delete[] data();
}
} // namespace detail
ostream::~ostream() = default;
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@ -9,7 +9,7 @@ GoogleTest now follows the
We recommend
[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it).
We do publish occasional semantic versions, tagged with
`v${major}.${minor}.${patch}` (e.g. `v1.13.0`).
`v${major}.${minor}.${patch}` (e.g. `v1.15.0`).
#### Documentation Updates
@ -17,25 +17,21 @@ Our documentation is now live on GitHub Pages at
https://google.github.io/googletest/. We recommend browsing the documentation on
GitHub Pages rather than directly in the repository.
#### Release 1.13.0
#### Release 1.15.0
[Release 1.13.0](https://github.com/google/googletest/releases/tag/v1.13.0) is
[Release 1.15.0](https://github.com/google/googletest/releases/tag/v1.15.0) is
now available.
The 1.13.x branch requires at least C++14.
The 1.15.x branch requires at least C++14.
#### Continuous Integration
We use Google's internal systems for continuous integration. \
GitHub Actions were added for the convenience of open-source contributors. They
are exclusively maintained by the open-source community and not used by the
GoogleTest team.
We use Google's internal systems for continuous integration.
#### Coming Soon
* We are planning to take a dependency on
[Abseil](https://github.com/abseil/abseil-cpp).
* More documentation improvements are planned.
## Welcome to **GoogleTest**, Google's C++ test framework!
@ -100,12 +96,12 @@ tools.
In addition to many internal projects at Google, GoogleTest is also used by the
following notable projects:
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
and Chrome OS).
* The [LLVM](http://llvm.org/) compiler.
* The [Chromium projects](https://www.chromium.org/) (behind the Chrome
browser and Chrome OS).
* The [LLVM](https://llvm.org/) compiler.
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
interchange format.
* The [OpenCV](http://opencv.org/) computer vision library.
* The [OpenCV](https://opencv.org/) computer vision library.
## Related Open Source Projects

View File

@ -8,8 +8,8 @@ derive better designs of your system and write better tests.
It is inspired by:
* [jMock](http://www.jmock.org/)
* [EasyMock](http://www.easymock.org/)
* [Hamcrest](http://code.google.com/p/hamcrest/)
* [EasyMock](https://easymock.org/)
* [Hamcrest](https://code.google.com/p/hamcrest/)
It is designed with C++'s specifics in mind.
@ -36,5 +36,5 @@ Details and examples can be found here:
* [gMock Cheat Sheet](https://google.github.io/googletest/gmock_cheat_sheet.html)
GoogleMock is a part of
[GoogleTest C++ testing framework](http://github.com/google/googletest/) and a
[GoogleTest C++ testing framework](https://github.com/google/googletest/) and a
subject to the same requirements.

View File

@ -135,6 +135,7 @@
#endif
#include <algorithm>
#include <exception>
#include <functional>
#include <memory>
#include <string>
@ -175,9 +176,15 @@ struct BuiltInDefaultValueGetter<T, false> {
static T Get() {
Assert(false, __FILE__, __LINE__,
"Default action undefined for the function return type.");
return internal::Invalid<T>();
#if defined(__GNUC__) || defined(__clang__)
__builtin_unreachable();
#elif defined(_MSC_VER)
__assume(0);
#else
return Invalid<T>();
// The above statement will never be reached, but is required in
// order for this function to compile.
#endif
}
};
@ -1740,6 +1747,13 @@ struct ThrowAction {
return [copy](Args...) -> R { throw copy; };
}
};
struct RethrowAction {
std::exception_ptr exception;
template <typename R, typename... Args>
operator Action<R(Args...)>() const { // NOLINT
return [ex = exception](Args...) -> R { std::rethrow_exception(ex); };
}
};
#endif // GTEST_HAS_EXCEPTIONS
} // namespace internal
@ -2056,13 +2070,23 @@ internal::ReturnPointeeAction<Ptr> ReturnPointee(Ptr pointer) {
return {pointer};
}
// Action Throw(exception) can be used in a mock function of any type
// to throw the given exception. Any copyable value can be thrown.
#if GTEST_HAS_EXCEPTIONS
// Action Throw(exception) can be used in a mock function of any type
// to throw the given exception. Any copyable value can be thrown,
// except for std::exception_ptr, which is likely a mistake if
// thrown directly.
template <typename T>
internal::ThrowAction<typename std::decay<T>::type> Throw(T&& exception) {
typename std::enable_if<
!std::is_base_of<std::exception_ptr, typename std::decay<T>::type>::value,
internal::ThrowAction<typename std::decay<T>::type>>::type
Throw(T&& exception) {
return {std::forward<T>(exception)};
}
// Action Rethrow(exception_ptr) can be used in a mock function of any type
// to rethrow any exception_ptr. Note that the same object is thrown each time.
inline internal::RethrowAction Rethrow(std::exception_ptr exception) {
return {std::move(exception)};
}
#endif // GTEST_HAS_EXCEPTIONS
namespace internal {
@ -2111,13 +2135,13 @@ struct ActionImpl<R(Args...), Impl> : ImplBase<Impl>::type {
R operator()(Args&&... arg) const {
static constexpr size_t kMaxArgs =
sizeof...(Args) <= 10 ? sizeof...(Args) : 10;
return Apply(MakeIndexSequence<kMaxArgs>{},
MakeIndexSequence<10 - kMaxArgs>{},
return Apply(std::make_index_sequence<kMaxArgs>{},
std::make_index_sequence<10 - kMaxArgs>{},
args_type{std::forward<Args>(arg)...});
}
template <std::size_t... arg_id, std::size_t... excess_id>
R Apply(IndexSequence<arg_id...>, IndexSequence<excess_id...>,
R Apply(std::index_sequence<arg_id...>, std::index_sequence<excess_id...>,
const args_type& args) const {
// Impl need not be specific to the signature of action being implemented;
// only the implementing function body needs to have all of the specific
@ -2150,9 +2174,9 @@ template <typename F, typename Impl>
}
#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \
, const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \
const args_type& args GTEST_ATTRIBUTE_UNUSED_ GMOCK_PP_REPEAT( \
, GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const arg##i##_type& arg##i
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const args_type& args GMOCK_PP_REPEAT( \
GMOCK_INTERNAL_ARG_UNUSED, , 10)
#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i

View File

@ -37,6 +37,7 @@
#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_
#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_
#include <cstddef>
#include <type_traits> // IWYU pragma: keep
#include <utility> // IWYU pragma: keep
@ -69,22 +70,22 @@ constexpr bool PrefixOf(const char* a, const char* b) {
return *a == 0 || (*a == *b && internal::PrefixOf(a + 1, b + 1));
}
template <int N, int M>
template <size_t N, size_t M>
constexpr bool StartsWith(const char (&prefix)[N], const char (&str)[M]) {
return N <= M && internal::PrefixOf(prefix, str);
}
template <int N, int M>
template <size_t N, size_t M>
constexpr bool EndsWith(const char (&suffix)[N], const char (&str)[M]) {
return N <= M && internal::PrefixOf(suffix, str + M - N);
}
template <int N, int M>
template <size_t N, size_t M>
constexpr bool Equals(const char (&a)[N], const char (&b)[M]) {
return N == M && internal::PrefixOf(a, b);
}
template <int N>
template <size_t N>
constexpr bool ValidateSpec(const char (&spec)[N]) {
return internal::Equals("const", spec) ||
internal::Equals("override", spec) ||

View File

@ -490,12 +490,12 @@ class MatcherBaseImpl<Derived<Ts...>> {
template <typename F>
operator ::testing::Matcher<F>() const { // NOLINT(runtime/explicit)
return Apply<F>(MakeIndexSequence<sizeof...(Ts)>{});
return Apply<F>(std::make_index_sequence<sizeof...(Ts)>{});
}
private:
template <typename F, std::size_t... tuple_ids>
::testing::Matcher<F> Apply(IndexSequence<tuple_ids...>) const {
::testing::Matcher<F> Apply(std::index_sequence<tuple_ids...>) const {
return ::testing::Matcher<F>(
new typename Derived<Ts...>::template gmock_Impl<F>(
std::get<tuple_ids>(params_)...));
@ -1048,7 +1048,7 @@ class StartsWithMatcher {
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
const StringType& s2(s);
const StringType s2(s);
return s2.length() >= prefix_.length() &&
s2.substr(0, prefix_.length()) == prefix_;
}
@ -1102,7 +1102,7 @@ class EndsWithMatcher {
template <typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
const StringType& s2(s);
const StringType s2(s);
return s2.length() >= suffix_.length() &&
s2.substr(s2.length() - suffix_.length()) == suffix_;
}
@ -2920,26 +2920,27 @@ class EachMatcher {
const M inner_matcher_;
};
struct Rank1 {};
struct Rank0 : Rank1 {};
// Use go/ranked-overloads for dispatching.
struct Rank0 {};
struct Rank1 : Rank0 {};
namespace pair_getters {
using std::get;
template <typename T>
auto First(T& x, Rank1) -> decltype(get<0>(x)) { // NOLINT
auto First(T& x, Rank0) -> decltype(get<0>(x)) { // NOLINT
return get<0>(x);
}
template <typename T>
auto First(T& x, Rank0) -> decltype((x.first)) { // NOLINT
auto First(T& x, Rank1) -> decltype((x.first)) { // NOLINT
return x.first;
}
template <typename T>
auto Second(T& x, Rank1) -> decltype(get<1>(x)) { // NOLINT
auto Second(T& x, Rank0) -> decltype(get<1>(x)) { // NOLINT
return get<1>(x);
}
template <typename T>
auto Second(T& x, Rank0) -> decltype((x.second)) { // NOLINT
auto Second(T& x, Rank1) -> decltype((x.second)) { // NOLINT
return x.second;
}
} // namespace pair_getters
@ -2965,7 +2966,7 @@ class KeyMatcherImpl : public MatcherInterface<PairType> {
MatchResultListener* listener) const override {
StringMatchResultListener inner_listener;
const bool match = inner_matcher_.MatchAndExplain(
pair_getters::First(key_value, Rank0()), &inner_listener);
pair_getters::First(key_value, Rank1()), &inner_listener);
const std::string explanation = inner_listener.str();
if (!explanation.empty()) {
*listener << "whose first field is a value " << explanation;
@ -3087,18 +3088,18 @@ class PairMatcherImpl : public MatcherInterface<PairType> {
if (!listener->IsInterested()) {
// If the listener is not interested, we don't need to construct the
// explanation.
return first_matcher_.Matches(pair_getters::First(a_pair, Rank0())) &&
second_matcher_.Matches(pair_getters::Second(a_pair, Rank0()));
return first_matcher_.Matches(pair_getters::First(a_pair, Rank1())) &&
second_matcher_.Matches(pair_getters::Second(a_pair, Rank1()));
}
StringMatchResultListener first_inner_listener;
if (!first_matcher_.MatchAndExplain(pair_getters::First(a_pair, Rank0()),
if (!first_matcher_.MatchAndExplain(pair_getters::First(a_pair, Rank1()),
&first_inner_listener)) {
*listener << "whose first field does not match";
PrintIfNotEmpty(first_inner_listener.str(), listener->stream());
return false;
}
StringMatchResultListener second_inner_listener;
if (!second_matcher_.MatchAndExplain(pair_getters::Second(a_pair, Rank0()),
if (!second_matcher_.MatchAndExplain(pair_getters::Second(a_pair, Rank1()),
&second_inner_listener)) {
*listener << "whose second field does not match";
PrintIfNotEmpty(second_inner_listener.str(), listener->stream());
@ -3151,8 +3152,8 @@ class PairMatcher {
};
template <typename T, size_t... I>
auto UnpackStructImpl(const T& t, IndexSequence<I...>, int)
-> decltype(std::tie(get<I>(t)...)) {
auto UnpackStructImpl(const T& t, std::index_sequence<I...>,
int) -> decltype(std::tie(get<I>(t)...)) {
static_assert(std::tuple_size<T>::value == sizeof...(I),
"Number of arguments doesn't match the number of fields.");
return std::tie(get<I>(t)...);
@ -3160,97 +3161,97 @@ auto UnpackStructImpl(const T& t, IndexSequence<I...>, int)
#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<1>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<1>, char) {
const auto& [a] = t;
return std::tie(a);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<2>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<2>, char) {
const auto& [a, b] = t;
return std::tie(a, b);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<3>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<3>, char) {
const auto& [a, b, c] = t;
return std::tie(a, b, c);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<4>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<4>, char) {
const auto& [a, b, c, d] = t;
return std::tie(a, b, c, d);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<5>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<5>, char) {
const auto& [a, b, c, d, e] = t;
return std::tie(a, b, c, d, e);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<6>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<6>, char) {
const auto& [a, b, c, d, e, f] = t;
return std::tie(a, b, c, d, e, f);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<7>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<7>, char) {
const auto& [a, b, c, d, e, f, g] = t;
return std::tie(a, b, c, d, e, f, g);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<8>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<8>, char) {
const auto& [a, b, c, d, e, f, g, h] = t;
return std::tie(a, b, c, d, e, f, g, h);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<9>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<9>, char) {
const auto& [a, b, c, d, e, f, g, h, i] = t;
return std::tie(a, b, c, d, e, f, g, h, i);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<10>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<10>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<11>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<11>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<12>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<12>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<13>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<13>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<14>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<14>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<15>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<15>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<16>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<16>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<17>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<17>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<18>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<18>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r);
}
template <typename T>
auto UnpackStructImpl(const T& t, MakeIndexSequence<19>, char) {
auto UnpackStructImpl(const T& t, std::make_index_sequence<19>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s] = t;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s);
}
@ -3258,8 +3259,8 @@ auto UnpackStructImpl(const T& t, MakeIndexSequence<19>, char) {
template <size_t I, typename T>
auto UnpackStruct(const T& t)
-> decltype((UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0)) {
return (UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0);
-> decltype((UnpackStructImpl)(t, std::make_index_sequence<I>{}, 0)) {
return (UnpackStructImpl)(t, std::make_index_sequence<I>{}, 0);
}
// Helper function to do comma folding in C++11.
@ -3272,7 +3273,7 @@ template <typename Struct, typename StructSize>
class FieldsAreMatcherImpl;
template <typename Struct, size_t... I>
class FieldsAreMatcherImpl<Struct, IndexSequence<I...>>
class FieldsAreMatcherImpl<Struct, std::index_sequence<I...>>
: public MatcherInterface<Struct> {
using UnpackedType =
decltype(UnpackStruct<sizeof...(I)>(std::declval<const Struct&>()));
@ -3354,8 +3355,8 @@ class FieldsAreMatcher {
template <typename Struct>
operator Matcher<Struct>() const { // NOLINT
return Matcher<Struct>(
new FieldsAreMatcherImpl<const Struct&, IndexSequenceFor<Inner...>>(
matchers_));
new FieldsAreMatcherImpl<const Struct&,
std::index_sequence_for<Inner...>>(matchers_));
}
private:
@ -5444,47 +5445,47 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
// MATCHER* macros itself are listed below.
#define MATCHER(name, description) \
class name##Matcher \
: public ::testing::internal::MatcherBaseImpl<name##Matcher> { \
public: \
template <typename arg_type> \
class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> { \
public: \
gmock_Impl() {} \
bool MatchAndExplain( \
const arg_type& arg, \
::testing::MatchResultListener* result_listener) const override; \
void DescribeTo(::std::ostream* gmock_os) const override { \
*gmock_os << FormatDescription(false); \
} \
void DescribeNegationTo(::std::ostream* gmock_os) const override { \
*gmock_os << FormatDescription(true); \
} \
\
private: \
::std::string FormatDescription(bool negation) const { \
/* NOLINTNEXTLINE readability-redundant-string-init */ \
::std::string gmock_description = (description); \
if (!gmock_description.empty()) { \
return gmock_description; \
} \
return ::testing::internal::FormatMatcherDescription(negation, #name, \
{}, {}); \
} \
}; \
}; \
inline name##Matcher GMOCK_INTERNAL_WARNING_PUSH() \
GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-function") \
GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-member-function") \
name GMOCK_INTERNAL_WARNING_POP()() { \
return {}; \
} \
template <typename arg_type> \
bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_) \
const
#define MATCHER(name, description) \
class name##Matcher \
: public ::testing::internal::MatcherBaseImpl<name##Matcher> { \
public: \
template <typename arg_type> \
class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> { \
public: \
gmock_Impl() {} \
bool MatchAndExplain( \
const arg_type& arg, \
::testing::MatchResultListener* result_listener) const override; \
void DescribeTo(::std::ostream* gmock_os) const override { \
*gmock_os << FormatDescription(false); \
} \
void DescribeNegationTo(::std::ostream* gmock_os) const override { \
*gmock_os << FormatDescription(true); \
} \
\
private: \
::std::string FormatDescription(bool negation) const { \
/* NOLINTNEXTLINE readability-redundant-string-init */ \
::std::string gmock_description = (description); \
if (!gmock_description.empty()) { \
return gmock_description; \
} \
return ::testing::internal::FormatMatcherDescription(negation, #name, \
{}, {}); \
} \
}; \
}; \
inline name##Matcher GMOCK_INTERNAL_WARNING_PUSH() \
GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-function") \
GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-member-function") \
name GMOCK_INTERNAL_WARNING_POP()() { \
return {}; \
} \
template <typename arg_type> \
bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing::MatchResultListener* \
result_listener) const
#define MATCHER_P(name, p0, description) \
GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (#p0), (p0))
@ -5566,11 +5567,11 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
} \
template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)> \
template <typename arg_type> \
bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>::gmock_Impl< \
arg_type>::MatchAndExplain(const arg_type& arg, \
::testing::MatchResultListener* \
result_listener GTEST_ATTRIBUTE_UNUSED_) \
const
bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>:: \
gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing:: \
MatchResultListener* result_listener) const
#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \
GMOCK_PP_TAIL( \
@ -5605,8 +5606,8 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
#define GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args) \
GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_ARG_USAGE, , args))
#define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg_unused) \
, gmock_p##i
#define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg) \
, ::std::forward<arg##_type>(gmock_p##i)
// To prevent ADL on certain functions we put them on a separate namespace.
using namespace no_adl; // NOLINT

View File

@ -592,8 +592,9 @@ namespace internal {
// Overloads for other custom-callables are provided in the
// internal/custom/gmock-generated-actions.h header.
template <typename F, typename... Args>
auto InvokeArgument(F f, Args... args) -> decltype(f(args...)) {
return f(args...);
auto InvokeArgument(F &&f,
Args... args) -> decltype(std::forward<F>(f)(args...)) {
return std::forward<F>(f)(args...);
}
template <std::size_t index, typename... Params>
@ -606,7 +607,7 @@ struct InvokeArgumentAction {
internal::FlatTuple<Args &&...> args_tuple(FlatTupleConstructTag{},
std::forward<Args>(args)...);
return params.Apply([&](const Params &...unpacked_params) {
auto &&callable = args_tuple.template Get<index>();
auto &&callable = std::move(args_tuple.template Get<index>());
return internal::InvokeArgument(
std::forward<decltype(callable)>(callable), unpacked_params...);
});

View File

@ -53,13 +53,14 @@
//
// where all clauses are optional and WillOnce() can be repeated.
#include "gmock/gmock-actions.h"
#include "gmock/gmock-cardinalities.h"
#include "gmock/gmock-function-mocker.h"
#include "gmock/gmock-matchers.h"
#include "gmock/gmock-more-actions.h"
#include "gmock/gmock-more-matchers.h"
#include "gmock/gmock-nice-strict.h"
#include "gmock/gmock-actions.h" // IWYU pragma: export
#include "gmock/gmock-cardinalities.h" // IWYU pragma: export
#include "gmock/gmock-function-mocker.h" // IWYU pragma: export
#include "gmock/gmock-matchers.h" // IWYU pragma: export
#include "gmock/gmock-more-actions.h" // IWYU pragma: export
#include "gmock/gmock-more-matchers.h" // IWYU pragma: export
#include "gmock/gmock-nice-strict.h" // IWYU pragma: export
#include "gmock/gmock-spec-builders.h" // IWYU pragma: export
#include "gmock/internal/gmock-internal-utils.h"
#include "gmock/internal/gmock-port.h"

View File

@ -44,6 +44,7 @@
#include <ostream> // NOLINT
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "gmock/internal/gmock-port.h"
@ -420,7 +421,7 @@ struct RemoveConstFromKey<std::pair<const K, V> > {
GTEST_API_ void IllegalDoDefault(const char* file, int line);
template <typename F, typename Tuple, size_t... Idx>
auto ApplyImpl(F&& f, Tuple&& args, IndexSequence<Idx...>)
auto ApplyImpl(F&& f, Tuple&& args, std::index_sequence<Idx...>)
-> decltype(std::forward<F>(f)(
std::get<Idx>(std::forward<Tuple>(args))...)) {
return std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...);
@ -428,12 +429,13 @@ auto ApplyImpl(F&& f, Tuple&& args, IndexSequence<Idx...>)
// Apply the function to a tuple of arguments.
template <typename F, typename Tuple>
auto Apply(F&& f, Tuple&& args) -> decltype(ApplyImpl(
std::forward<F>(f), std::forward<Tuple>(args),
MakeIndexSequence<std::tuple_size<
typename std::remove_reference<Tuple>::type>::value>())) {
auto Apply(F&& f, Tuple&& args)
-> decltype(ApplyImpl(
std::forward<F>(f), std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<
typename std::remove_reference<Tuple>::type>::value>())) {
return ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
MakeIndexSequence<std::tuple_size<
std::make_index_sequence<std::tuple_size<
typename std::remove_reference<Tuple>::type>::value>());
}

View File

@ -56,7 +56,7 @@
#include "gmock/internal/custom/gmock-port.h"
#include "gtest/internal/gtest-port.h"
#ifdef GTEST_HAS_ABSL
#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS)
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#endif
@ -73,7 +73,7 @@
#define GMOCK_FLAG(name) FLAGS_gmock_##name
// Pick a command line flags implementation.
#ifdef GTEST_HAS_ABSL
#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS)
// Macros for defining flags.
#define GMOCK_DEFINE_bool_(name, default_val, doc) \
@ -95,7 +95,7 @@
#define GMOCK_FLAG_SET(name, value) \
(void)(::absl::SetFlag(&GMOCK_FLAG(name), value))
#else // GTEST_HAS_ABSL
#else // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS)
// Macros for defining flags.
#define GMOCK_DEFINE_bool_(name, default_val, doc) \
@ -134,6 +134,6 @@
#define GMOCK_FLAG_GET(name) ::testing::GMOCK_FLAG(name)
#define GMOCK_FLAG_SET(name, value) (void)(::testing::GMOCK_FLAG(name) = value)
#endif // GTEST_HAS_ABSL
#endif // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS)
#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_

View File

@ -44,6 +44,7 @@
#include <iostream>
#include <ostream> // NOLINT
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
@ -211,14 +212,14 @@ constexpr char UnBase64Impl(char c, const char* const base64, char carry) {
}
template <size_t... I>
constexpr std::array<char, 256> UnBase64Impl(IndexSequence<I...>,
constexpr std::array<char, 256> UnBase64Impl(std::index_sequence<I...>,
const char* const base64) {
return {
{UnBase64Impl(UndoWebSafeEncoding(static_cast<char>(I)), base64, 0)...}};
}
constexpr std::array<char, 256> UnBase64(const char* const base64) {
return UnBase64Impl(MakeIndexSequence<256>{}, base64);
return UnBase64Impl(std::make_index_sequence<256>{}, base64);
}
static constexpr char kBase64[] =

View File

@ -120,7 +120,7 @@ GTEST_API_ std::string FormatMatcherDescription(
// [1] Cormen, et al (2001). "Section 26.2: The Ford-Fulkerson method".
// "Introduction to Algorithms (Second ed.)", pp. 651-664.
// [2] "Ford-Fulkerson algorithm", Wikipedia,
// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'
// 'https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'
class MaxBipartiteMatchState {
public:
explicit MaxBipartiteMatchState(const MatchMatrix& graph)
@ -236,9 +236,8 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
os << "{";
const char* sep = "";
for (Iter it = pairs.begin(); it != pairs.end(); ++it) {
os << sep << "\n ("
<< "element #" << it->first << ", "
<< "matcher #" << it->second << ")";
os << sep << "\n (" << "element #" << it->first << ", " << "matcher #"
<< it->second << ")";
sep = ",";
}
os << "\n}";
@ -374,20 +373,20 @@ bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix(
return true;
}
if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
if (matrix.LhsSize() != matrix.RhsSize()) {
// The element count doesn't match. If the container is empty,
// there's no need to explain anything as Google Mock already
// prints the empty container. Otherwise we just need to show
// how many elements there actually are.
if (matrix.LhsSize() != 0 && listener->IsInterested()) {
*listener << "which has " << Elements(matrix.LhsSize());
}
return false;
const bool is_exact_match_with_size_discrepency =
match_flags() == UnorderedMatcherRequire::ExactMatch &&
matrix.LhsSize() != matrix.RhsSize();
if (is_exact_match_with_size_discrepency) {
// The element count doesn't match. If the container is empty,
// there's no need to explain anything as Google Mock already
// prints the empty container. Otherwise we just need to show
// how many elements there actually are.
if (matrix.LhsSize() != 0 && listener->IsInterested()) {
*listener << "which has " << Elements(matrix.LhsSize()) << "\n";
}
}
bool result = true;
bool result = !is_exact_match_with_size_discrepency;
::std::vector<char> element_matched(matrix.LhsSize(), 0);
::std::vector<char> matcher_matched(matrix.RhsSize(), 0);

View File

@ -490,6 +490,7 @@ class MockObjectRegistry {
// failure, unless the user explicitly asked us to ignore it.
~MockObjectRegistry() {
if (!GMOCK_FLAG_GET(catch_leaked_mocks)) return;
internal::MutexLock l(&internal::g_gmock_mutex);
int leaked_count = 0;
for (StateMap::const_iterator it = states_.begin(); it != states_.end();
@ -530,7 +531,7 @@ class MockObjectRegistry {
#ifdef GTEST_OS_QURT
qurt_exception_raise_fatal();
#else
_exit(1); // We cannot call exit() as it is not reentrant and
_Exit(1); // We cannot call exit() as it is not reentrant and
// may already have been called.
#endif
}

View File

@ -12,7 +12,7 @@ GoogleTest comes with a CMake build script
([CMakeLists.txt](https://github.com/google/googletest/blob/main/CMakeLists.txt))
that can be used on a wide range of platforms ("C" stands for cross-platform.).
If you don't have CMake installed already, you can download it for free from
<http://www.cmake.org/>.
<https://cmake.org/>.
CMake works by generating native makefiles or build projects that can be used in
the compiler environment of your choice. You can either build GoogleTest as a
@ -25,7 +25,7 @@ When building GoogleTest as a standalone project, the typical workflow starts
with
```
git clone https://github.com/google/googletest.git -b v1.13.0
git clone https://github.com/google/googletest.git -b v1.15.0
cd googletest # Main directory of the cloned repository.
mkdir build # Create a directory to hold the build output.
cd build

View File

@ -129,7 +129,7 @@ namespace testing {
//
// Expected: Foo() is even
// Actual: it's 5
//
class GTEST_API_ AssertionResult {
public:
// Copy constructor.

View File

@ -293,8 +293,8 @@ class GTEST_API_ KilledBySignal {
// statement is compiled but not executed, to ensure that
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
// parameter if and only if EXPECT_DEATH compiles with it.
// regex - A regex that a macro such as EXPECT_DEATH would use to test
// the output of statement. This parameter has to be
// regex_or_matcher - A regex that a macro such as EXPECT_DEATH would use
// to test the output of statement. This parameter has to be
// compiled but not evaluated by this macro, to ensure that
// this macro only accepts expressions that a macro such as
// EXPECT_DEATH would accept.
@ -311,13 +311,13 @@ class GTEST_API_ KilledBySignal {
// statement unconditionally returns or throws. The Message constructor at
// the end allows the syntax of streaming additional messages into the
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex_or_matcher, terminator) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \
<< "Statement '" #statement "' cannot be verified."; \
} else if (::testing::internal::AlwaysFalse()) { \
::testing::internal::RE::PartialMatch(".*", (regex)); \
::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
terminator; \
} else \

View File

@ -59,7 +59,7 @@
#ifdef GTEST_HAS_ABSL
#include <type_traits>
#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/str_cat.h"
#endif // GTEST_HAS_ABSL
@ -121,14 +121,14 @@ class GTEST_API_ Message {
// Streams a non-pointer value to this object. If building a version of
// GoogleTest with ABSL, this overload is only enabled if the value does not
// have an AbslStringify definition.
template <typename T
template <
typename T
#ifdef GTEST_HAS_ABSL
,
typename std::enable_if<
!absl::strings_internal::HasAbslStringify<T>::value, // NOLINT
int>::type = 0
,
typename std::enable_if<!absl::HasAbslStringify<T>::value, // NOLINT
int>::type = 0
#endif // GTEST_HAS_ABSL
>
>
inline Message& operator<<(const T& val) {
// Some libraries overload << for STL containers. These
// overloads are defined in the global namespace instead of ::std.
@ -153,9 +153,8 @@ class GTEST_API_ Message {
// Streams a non-pointer value with an AbslStringify definition to this
// object.
template <typename T,
typename std::enable_if<
absl::strings_internal::HasAbslStringify<T>::value, // NOLINT
int>::type = 0>
typename std::enable_if<absl::HasAbslStringify<T>::value, // NOLINT
int>::type = 0>
inline Message& operator<<(const T& val) {
// ::operator<< is needed here for a similar reason as with the non-Abseil
// version above

View File

@ -178,7 +178,7 @@ TEST_P(DerivedTest, DoesBlah) {
#include <utility>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-param-util.h"
#include "gtest/internal/gtest-param-util.h" // IWYU pragma: export
#include "gtest/internal/gtest-port.h"
namespace testing {
@ -469,7 +469,7 @@ internal::ParamConverterGenerator<T> ConvertGenerator(
::testing::internal::CodeLocation(__FILE__, __LINE__)); \
return 0; \
} \
static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int gtest_registering_dummy_; \
}; \
int GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::gtest_registering_dummy_ = \
@ -514,8 +514,8 @@ internal::ParamConverterGenerator<T> ConvertGenerator(
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))))(info); \
} \
static int gtest_##prefix##test_suite_name##_dummy_ \
GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int \
gtest_##prefix##test_suite_name##_dummy_ = \
::testing::UnitTest::GetInstance() \
->parameterized_test_registry() \
.GetTestSuitePatternHolder<test_suite_name>( \

View File

@ -116,12 +116,16 @@
#include <vector>
#ifdef GTEST_HAS_ABSL
#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/str_cat.h"
#endif // GTEST_HAS_ABSL
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
#if GTEST_INTERNAL_HAS_STD_SPAN
#include <span> // NOLINT
#endif // GTEST_INTERNAL_HAS_STD_SPAN
namespace testing {
// Definitions in the internal* namespaces are subject to change without notice.
@ -131,13 +135,32 @@ namespace internal {
template <typename T>
void UniversalPrint(const T& value, ::std::ostream* os);
template <typename T>
struct IsStdSpan {
static constexpr bool value = false;
};
#if GTEST_INTERNAL_HAS_STD_SPAN
template <typename E>
struct IsStdSpan<std::span<E>> {
static constexpr bool value = true;
};
#endif // GTEST_INTERNAL_HAS_STD_SPAN
// Used to print an STL-style container when the user doesn't define
// a PrintTo() for it.
//
// NOTE: Since std::span does not have const_iterator until C++23, it would
// fail IsContainerTest before C++23. However, IsContainerTest only uses
// the presence of const_iterator to avoid treating iterators as containers
// because of iterator::iterator. Which means std::span satisfies the *intended*
// condition of IsContainerTest.
struct ContainerPrinter {
template <typename T,
typename = typename std::enable_if<
(sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
!IsRecursiveContainer<T>::value>::type>
((sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
!IsRecursiveContainer<T>::value) ||
IsStdSpan<T>::value>::type>
static void PrintValue(const T& container, std::ostream* os) {
const size_t kMaxCount = 32; // The maximum number of elements to print.
*os << '{';
@ -218,8 +241,8 @@ struct StreamPrinter {
// ADL (possibly involving implicit conversions).
// (Use SFINAE via return type, because it seems GCC < 12 doesn't handle name
// lookup properly when we do it in the template parameter list.)
static auto PrintValue(const T& value, ::std::ostream* os)
-> decltype((void)(*os << value)) {
static auto PrintValue(const T& value,
::std::ostream* os) -> decltype((void)(*os << value)) {
// Call streaming operator found by ADL, possibly with implicit conversions
// of the arguments.
*os << value;
@ -269,10 +292,9 @@ struct ConvertibleToStringViewPrinter {
#ifdef GTEST_HAS_ABSL
struct ConvertibleToAbslStringifyPrinter {
template <
typename T,
typename = typename std::enable_if<
absl::strings_internal::HasAbslStringify<T>::value>::type> // NOLINT
template <typename T,
typename = typename std::enable_if<
absl::HasAbslStringify<T>::value>::type> // NOLINT
static void PrintValue(const T& value, ::std::ostream* os) {
*os << absl::StrCat(value);
}
@ -530,49 +552,63 @@ int AppropriateResolution(FloatType val) {
int full = std::numeric_limits<FloatType>::max_digits10;
if (val < 0) val = -val;
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
if (val < 1000000) {
FloatType mulfor6 = 1e10;
if (val >= 100000.0) { // 100,000 to 999,999
// Without these static casts, the template instantiation for float would
// fail to compile when -Wdouble-promotion is enabled, as the arithmetic and
// comparison logic would promote floats to doubles.
if (val >= static_cast<FloatType>(100000.0)) { // 100,000 to 999,999
mulfor6 = 1.0;
} else if (val >= 10000.0) {
} else if (val >= static_cast<FloatType>(10000.0)) {
mulfor6 = 1e1;
} else if (val >= 1000.0) {
} else if (val >= static_cast<FloatType>(1000.0)) {
mulfor6 = 1e2;
} else if (val >= 100.0) {
} else if (val >= static_cast<FloatType>(100.0)) {
mulfor6 = 1e3;
} else if (val >= 10.0) {
} else if (val >= static_cast<FloatType>(10.0)) {
mulfor6 = 1e4;
} else if (val >= 1.0) {
} else if (val >= static_cast<FloatType>(1.0)) {
mulfor6 = 1e5;
} else if (val >= 0.1) {
} else if (val >= static_cast<FloatType>(0.1)) {
mulfor6 = 1e6;
} else if (val >= 0.01) {
} else if (val >= static_cast<FloatType>(0.01)) {
mulfor6 = 1e7;
} else if (val >= 0.001) {
} else if (val >= static_cast<FloatType>(0.001)) {
mulfor6 = 1e8;
} else if (val >= 0.0001) {
} else if (val >= static_cast<FloatType>(0.0001)) {
mulfor6 = 1e9;
}
if (static_cast<FloatType>(static_cast<int32_t>(val * mulfor6 + 0.5)) /
if (static_cast<FloatType>(static_cast<int32_t>(
val * mulfor6 + (static_cast<FloatType>(0.5)))) /
mulfor6 ==
val)
return 6;
} else if (val < 1e10) {
FloatType divfor6 = 1.0;
if (val >= 1e9) { // 1,000,000,000 to 9,999,999,999
} else if (val < static_cast<FloatType>(1e10)) {
FloatType divfor6 = static_cast<FloatType>(1.0);
if (val >= static_cast<FloatType>(1e9)) { // 1,000,000,000 to 9,999,999,999
divfor6 = 10000;
} else if (val >= 1e8) { // 100,000,000 to 999,999,999
} else if (val >=
static_cast<FloatType>(1e8)) { // 100,000,000 to 999,999,999
divfor6 = 1000;
} else if (val >= 1e7) { // 10,000,000 to 99,999,999
} else if (val >=
static_cast<FloatType>(1e7)) { // 10,000,000 to 99,999,999
divfor6 = 100;
} else if (val >= 1e6) { // 1,000,000 to 9,999,999
} else if (val >= static_cast<FloatType>(1e6)) { // 1,000,000 to 9,999,999
divfor6 = 10;
}
if (static_cast<FloatType>(static_cast<int32_t>(val / divfor6 + 0.5)) *
if (static_cast<FloatType>(static_cast<int32_t>(
val / divfor6 + (static_cast<FloatType>(0.5)))) *
divfor6 ==
val)
return 6;
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
return full;
}

View File

@ -194,33 +194,34 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
GTEST_NAME_GENERATOR_(CaseName)
#define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##CaseName##_##TestName##_registered_ \
GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
#define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##CaseName##_##TestName##_registered_ = \
::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_( \
CaseName, TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)<gtest_TypeParam_>::TestBody()
// Legacy API is deprecated but still available
@ -267,22 +268,23 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
template <typename gtest_TypeParam_> \
class TestName : public SuiteName<gtest_TypeParam_> { \
private: \
typedef SuiteName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
#define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
template <typename gtest_TypeParam_> \
class TestName : public SuiteName<gtest_TypeParam_> { \
private: \
typedef SuiteName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##TestName##_defined_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
SuiteName)::TestName<gtest_TypeParam_>::TestBody()
// Note: this won't work correctly if the trailing arguments are macros.
@ -290,8 +292,8 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \
} \
static const char* const GTEST_REGISTERED_TEST_NAMES_( \
SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static const char* const \
GTEST_REGISTERED_TEST_NAMES_(SuiteName) = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
@ -303,22 +305,24 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
REGISTER_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register(GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##Prefix##_##SuiteName = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register( \
GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_

View File

@ -51,7 +51,6 @@
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <limits>
#include <memory>
#include <ostream>
@ -61,16 +60,16 @@
#include <type_traits>
#include <vector>
#include "gtest/gtest-assertion-result.h"
#include "gtest/gtest-death-test.h"
#include "gtest/gtest-matchers.h"
#include "gtest/gtest-message.h"
#include "gtest/gtest-param-test.h"
#include "gtest/gtest-printers.h"
#include "gtest/gtest-test-part.h"
#include "gtest/gtest-typed-test.h"
#include "gtest/gtest_pred_impl.h"
#include "gtest/gtest_prod.h"
#include "gtest/gtest-assertion-result.h" // IWYU pragma: export
#include "gtest/gtest-death-test.h" // IWYU pragma: export
#include "gtest/gtest-matchers.h" // IWYU pragma: export
#include "gtest/gtest-message.h" // IWYU pragma: export
#include "gtest/gtest-param-test.h" // IWYU pragma: export
#include "gtest/gtest-printers.h" // IWYU pragma: export
#include "gtest/gtest-test-part.h" // IWYU pragma: export
#include "gtest/gtest-typed-test.h" // IWYU pragma: export
#include "gtest/gtest_pred_impl.h" // IWYU pragma: export
#include "gtest/gtest_prod.h" // IWYU pragma: export
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
@ -608,7 +607,7 @@ class GTEST_API_ TestInfo {
friend class internal::UnitTestImpl;
friend class internal::StreamingListenerTest;
friend TestInfo* internal::MakeAndRegisterTestInfo(
const char* test_suite_name, const char* name, const char* type_param,
std::string test_suite_name, const char* name, const char* type_param,
const char* value_param, internal::CodeLocation code_location,
internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc,
@ -616,7 +615,7 @@ class GTEST_API_ TestInfo {
// Constructs a TestInfo object. The newly constructed instance assumes
// ownership of the factory object.
TestInfo(const std::string& test_suite_name, const std::string& name,
TestInfo(std::string test_suite_name, std::string name,
const char* a_type_param, // NULL if not a type-parameterized test
const char* a_value_param, // NULL if not a value-parameterized test
internal::CodeLocation a_code_location,
@ -684,7 +683,7 @@ class GTEST_API_ TestSuite {
// this is not a type-parameterized test.
// set_up_tc: pointer to the function that sets up the test suite
// tear_down_tc: pointer to the function that tears down the test suite
TestSuite(const char* name, const char* a_type_param,
TestSuite(const std::string& name, const char* a_type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc);
@ -1263,6 +1262,20 @@ class GTEST_API_ UnitTest {
// total_test_suite_count() - 1. If i is not in that range, returns NULL.
TestSuite* GetMutableTestSuite(int i);
// Invokes OsStackTrackGetterInterface::UponLeavingGTest. UponLeavingGTest()
// should be called immediately before Google Test calls user code. It saves
// some information about the current stack that CurrentStackTrace() will use
// to find and hide Google Test stack frames.
void UponLeavingGTest();
// Sets the TestSuite object for the test that's currently running.
void set_current_test_suite(TestSuite* a_current_test_suite)
GTEST_LOCK_EXCLUDED_(mutex_);
// Sets the TestInfo object for the test that's currently running.
void set_current_test_info(TestInfo* a_current_test_info)
GTEST_LOCK_EXCLUDED_(mutex_);
// Accessors for the implementation object.
internal::UnitTestImpl* impl() { return impl_; }
const internal::UnitTestImpl* impl() const { return impl_; }
@ -1271,6 +1284,8 @@ class GTEST_API_ UnitTest {
// members of UnitTest.
friend class ScopedTrace;
friend class Test;
friend class TestInfo;
friend class TestSuite;
friend class internal::AssertHelper;
friend class internal::StreamingListenerTest;
friend class internal::UnitTestRecordPropertyTestHelper;
@ -1574,12 +1589,12 @@ AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression,
}
::std::stringstream lhs_ss;
lhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
<< lhs_value;
lhs_ss.precision(std::numeric_limits<RawType>::digits10 + 2);
lhs_ss << lhs_value;
::std::stringstream rhs_ss;
rhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
<< rhs_value;
rhs_ss.precision(std::numeric_limits<RawType>::digits10 + 2);
rhs_ss << rhs_value;
return EqFailure(lhs_expression, rhs_expression,
StringStreamToString(&lhs_ss), StringStreamToString(&rhs_ss),
@ -1752,6 +1767,7 @@ class TestWithParam : public Test, public WithParamInterface<T> {};
// generic name and clashes with some other libraries.
#if !(defined(GTEST_DONT_DEFINE_FAIL) && GTEST_DONT_DEFINE_FAIL)
#define FAIL() GTEST_FAIL()
#define FAIL_AT(file, line) GTEST_FAIL_AT(file, line)
#endif
// Generates a success with a generic message.
@ -2308,7 +2324,8 @@ TestInfo* RegisterTest(const char* test_suite_name, const char* test_name,
// tests are successful, or 1 otherwise.
//
// RUN_ALL_TESTS() should be invoked after the command line has been
// parsed by InitGoogleTest().
// parsed by InitGoogleTest(). RUN_ALL_TESTS will tear down and delete any
// installed environments and should only be called once per binary.
//
// This function was formerly a macro; thus, it is in the global
// namespace and has an all-caps name.

View File

@ -46,17 +46,38 @@
#include "gtest/gtest-matchers.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
GTEST_DECLARE_string_(internal_run_death_test);
namespace testing {
namespace internal {
// Names of the flags (needed for parsing Google Test flags).
const char kDeathTestStyleFlag[] = "death_test_style";
const char kDeathTestUseFork[] = "death_test_use_fork";
// Name of the flag (needed for parsing Google Test flag).
const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
// and interpreted as a regex (rather than an Eq matcher) for legacy
// compatibility.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
::testing::internal::RE regex) {
return ContainsRegex(regex.pattern());
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
return ContainsRegex(regex);
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
const ::std::string& regex) {
return ContainsRegex(regex);
}
// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
// used directly.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
Matcher<const ::std::string&> matcher) {
return matcher;
}
#ifdef GTEST_HAS_DEATH_TEST
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
@ -73,7 +94,7 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
//
// exit status: The integer exit information in the format specified
// by wait(2)
// exit code: The integer code passed to exit(3), _exit(2), or
// exit code: The integer code passed to exit(3), _Exit(2), or
// returned from main()
class GTEST_API_ DeathTest {
public:
@ -170,28 +191,6 @@ class DefaultDeathTestFactory : public DeathTestFactory {
// by a signal, or exited normally with a nonzero exit code.
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
// and interpreted as a regex (rather than an Eq matcher) for legacy
// compatibility.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
::testing::internal::RE regex) {
return ContainsRegex(regex.pattern());
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
return ContainsRegex(regex);
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
const ::std::string& regex) {
return ContainsRegex(regex);
}
// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
// used directly.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
Matcher<const ::std::string&> matcher) {
return matcher;
}
// Traps C++ exceptions escaping statement and reports them as test
// failures. Note that trapping SEH exceptions is not implemented here.
#if GTEST_HAS_EXCEPTIONS

View File

@ -43,6 +43,7 @@
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#include <string>
#include <utility>
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-string.h"
@ -70,8 +71,9 @@ class GTEST_API_ FilePath {
public:
FilePath() : pathname_("") {}
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {}
FilePath(FilePath&& rhs) noexcept : pathname_(std::move(rhs.pathname_)) {}
explicit FilePath(const std::string& pathname) : pathname_(pathname) {
explicit FilePath(std::string pathname) : pathname_(std::move(pathname)) {
Normalize();
}
@ -79,6 +81,10 @@ class GTEST_API_ FilePath {
Set(rhs);
return *this;
}
FilePath& operator=(FilePath&& rhs) noexcept {
pathname_ = std::move(rhs.pathname_);
return *this;
}
void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; }

View File

@ -58,7 +58,6 @@
#include <cstdint>
#include <functional>
#include <iomanip>
#include <limits>
#include <map>
#include <set>
@ -79,7 +78,7 @@
//
// will result in the token foo__LINE__, instead of foo followed by
// the current line number. For more details, see
// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
// https://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar
@ -170,7 +169,7 @@ namespace edit_distance {
// All edits cost the same, with replace having lower priority than
// add/remove.
// Simple implementation of the Wagner-Fischer algorithm.
// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
// See https://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
enum EditType { kMatch, kAdd, kRemove, kReplace };
GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
const std::vector<size_t>& left, const std::vector<size_t>& right);
@ -237,7 +236,7 @@ GTEST_API_ std::string GetBoolAssertionFailureMessage(
// For double, there are 11 exponent bits and 52 fraction bits.
//
// More details can be found at
// http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
// https://en.wikipedia.org/wiki/IEEE_floating-point_standard.
//
// Template parameter:
//
@ -282,7 +281,7 @@ class FloatingPoint {
// bits. Therefore, 4 should be enough for ordinary use.
//
// See the following article for more details on ULP:
// http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
static const uint32_t kMaxUlps = 4;
// Constructs a FloatingPoint from a raw floating-point number.
@ -363,7 +362,7 @@ class FloatingPoint {
// N - 1 (the biggest number representable using
// sign-and-magnitude) is represented by 2N - 1.
//
// Read http://en.wikipedia.org/wiki/Signed_number_representations
// Read https://en.wikipedia.org/wiki/Signed_number_representations
// for more details on signed number representations.
static Bits SignAndMagnitudeToBiased(const Bits& sam) {
if (kSignBitMask & sam) {
@ -475,8 +474,8 @@ using SetUpTestSuiteFunc = void (*)();
using TearDownTestSuiteFunc = void (*)();
struct CodeLocation {
CodeLocation(const std::string& a_file, int a_line)
: file(a_file), line(a_line) {}
CodeLocation(std::string a_file, int a_line)
: file(std::move(a_file)), line(a_line) {}
std::string file;
int line;
@ -556,7 +555,7 @@ struct SuiteApiResolver : T {
// type_param: the name of the test's type parameter, or NULL if
// this is not a typed or a type-parameterized test.
// value_param: text representation of the test's value parameter,
// or NULL if this is not a type-parameterized test.
// or NULL if this is not a value-parameterized test.
// code_location: code location where the test is defined
// fixture_class_id: ID of the test fixture class
// set_up_tc: pointer to the function that sets up the test suite
@ -565,7 +564,7 @@ struct SuiteApiResolver : T {
// The newly created TestInfo instance will assume
// ownership of the factory object.
GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
const char* test_suite_name, const char* name, const char* type_param,
std::string test_suite_name, const char* name, const char* type_param,
const char* value_param, CodeLocation code_location,
TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory);
@ -596,8 +595,7 @@ class GTEST_API_ TypedTestSuitePState {
fflush(stderr);
posix::Abort();
}
registered_tests_.insert(
::std::make_pair(test_name, CodeLocation(file, line)));
registered_tests_.emplace(test_name, CodeLocation(file, line));
return true;
}
@ -701,7 +699,7 @@ class TypeParameterizedTest {
// specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite,
// Types). Valid values for 'index' are [0, N - 1] where N is the
// length of Types.
static bool Register(const char* prefix, const CodeLocation& code_location,
static bool Register(const char* prefix, CodeLocation code_location,
const char* case_name, const char* test_names, int index,
const std::vector<std::string>& type_names =
GenerateNames<DefaultNameGenerator, Types>()) {
@ -713,8 +711,7 @@ class TypeParameterizedTest {
// list.
MakeAndRegisterTestInfo(
(std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name +
"/" + type_names[static_cast<size_t>(index)])
.c_str(),
"/" + type_names[static_cast<size_t>(index)]),
StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
GetTypeName<Type>().c_str(),
nullptr, // No value parameter.
@ -726,13 +723,9 @@ class TypeParameterizedTest {
new TestFactoryImpl<TestClass>);
// Next, recurses (at compile time) with the tail of the type list.
return TypeParameterizedTest<Fixture, TestSel,
typename Types::Tail>::Register(prefix,
code_location,
case_name,
test_names,
index + 1,
type_names);
return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>::
Register(prefix, std::move(code_location), case_name, test_names,
index + 1, type_names);
}
};
@ -740,7 +733,7 @@ class TypeParameterizedTest {
template <GTEST_TEMPLATE_ Fixture, class TestSel>
class TypeParameterizedTest<Fixture, TestSel, internal::None> {
public:
static bool Register(const char* /*prefix*/, const CodeLocation&,
static bool Register(const char* /*prefix*/, CodeLocation,
const char* /*case_name*/, const char* /*test_names*/,
int /*index*/,
const std::vector<std::string>& =
@ -787,7 +780,8 @@ class TypeParameterizedTestSuite {
// Next, recurses (at compile time) with the tail of the test list.
return TypeParameterizedTestSuite<Fixture, typename Tests::Tail,
Types>::Register(prefix, code_location,
Types>::Register(prefix,
std::move(code_location),
state, case_name,
SkipComma(test_names),
type_names);
@ -1143,40 +1137,6 @@ class NativeArray {
void (NativeArray::*clone_)(const Element*, size_t);
};
// Backport of std::index_sequence.
template <size_t... Is>
struct IndexSequence {
using type = IndexSequence;
};
// Double the IndexSequence, and one if plus_one is true.
template <bool plus_one, typename T, size_t sizeofT>
struct DoubleSequence;
template <size_t... I, size_t sizeofT>
struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
};
template <size_t... I, size_t sizeofT>
struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)...>;
};
// Backport of std::make_index_sequence.
// It uses O(ln(N)) instantiation depth.
template <size_t N>
struct MakeIndexSequenceImpl
: DoubleSequence<N % 2 == 1, typename MakeIndexSequenceImpl<N / 2>::type,
N / 2>::type {};
template <>
struct MakeIndexSequenceImpl<0> : IndexSequence<> {};
template <size_t N>
using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::type;
template <typename... T>
using IndexSequenceFor = typename MakeIndexSequence<sizeof...(T)>::type;
template <size_t>
struct Ignore {
Ignore(...); // NOLINT
@ -1185,7 +1145,7 @@ struct Ignore {
template <typename>
struct ElemFromListImpl;
template <size_t... I>
struct ElemFromListImpl<IndexSequence<I...>> {
struct ElemFromListImpl<std::index_sequence<I...>> {
// We make Ignore a template to solve a problem with MSVC.
// A non-template Ignore would work fine with `decltype(Ignore(I))...`, but
// MSVC doesn't understand how to deal with that pack expansion.
@ -1196,9 +1156,8 @@ struct ElemFromListImpl<IndexSequence<I...>> {
template <size_t N, typename... T>
struct ElemFromList {
using type =
decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply(
static_cast<T (*)()>(nullptr)...));
using type = decltype(ElemFromListImpl<std::make_index_sequence<N>>::Apply(
static_cast<T (*)()>(nullptr)...));
};
struct FlatTupleConstructTag {};
@ -1223,9 +1182,9 @@ template <typename Derived, typename Idx>
struct FlatTupleBase;
template <size_t... Idx, typename... T>
struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
struct FlatTupleBase<FlatTuple<T...>, std::index_sequence<Idx...>>
: FlatTupleElemBase<FlatTuple<T...>, Idx>... {
using Indices = IndexSequence<Idx...>;
using Indices = std::index_sequence<Idx...>;
FlatTupleBase() = default;
template <typename... Args>
explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args)
@ -1260,14 +1219,15 @@ struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
// implementations.
// FlatTuple and ElemFromList are not recursive and have a fixed depth
// regardless of T...
// MakeIndexSequence, on the other hand, it is recursive but with an
// std::make_index_sequence, on the other hand, it is recursive but with an
// instantiation depth of O(ln(N)).
template <typename... T>
class FlatTuple
: private FlatTupleBase<FlatTuple<T...>,
typename MakeIndexSequence<sizeof...(T)>::type> {
using Indices = typename FlatTupleBase<
FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>::Indices;
std::make_index_sequence<sizeof...(T)>> {
using Indices =
typename FlatTupleBase<FlatTuple<T...>,
std::make_index_sequence<sizeof...(T)>>::Indices;
public:
FlatTuple() = default;
@ -1541,7 +1501,8 @@ class NeverThrown {
\
private: \
void TestBody() override; \
static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static ::testing::TestInfo* const \
test_info_; \
}; \
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \

View File

@ -47,6 +47,7 @@
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
@ -85,7 +86,7 @@ namespace internal {
// TEST_P macro is used to define two tests with the same name
// but in different namespaces.
GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
CodeLocation code_location);
const CodeLocation& code_location);
template <typename>
class ParamGeneratorInterface;
@ -379,9 +380,7 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// integer test parameter index.
template <class ParamType>
std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
Message name_stream;
name_stream << info.index;
return name_stream.GetString();
return std::to_string(info.index);
}
template <typename T = int>
@ -513,9 +512,10 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
explicit ParameterizedTestSuiteInfo(const char* name,
explicit ParameterizedTestSuiteInfo(std::string name,
CodeLocation code_location)
: test_suite_name_(name), code_location_(code_location) {}
: test_suite_name_(std::move(name)),
code_location_(std::move(code_location)) {}
// Test suite base name for display purposes.
const std::string& GetTestSuiteName() const override {
@ -529,20 +529,21 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
// prefix). test_base_name is the name of an individual test without
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
// test suite base name and DoBar is test base name.
void AddTestPattern(const char* test_suite_name, const char* test_base_name,
void AddTestPattern(const char*,
const char* test_base_name,
TestMetaFactoryBase<ParamType>* meta_factory,
CodeLocation code_location) {
tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
test_suite_name, test_base_name, meta_factory, code_location)));
tests_.emplace_back(
new TestInfo(test_base_name, meta_factory, std::move(code_location)));
}
// INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
// about a generator.
int AddTestSuiteInstantiation(const std::string& instantiation_name,
int AddTestSuiteInstantiation(std::string instantiation_name,
GeneratorCreationFunc* func,
ParamNameGeneratorFunc* name_func,
const char* file, int line) {
instantiations_.push_back(
InstantiationInfo(instantiation_name, func, name_func, file, line));
instantiations_.emplace_back(std::move(instantiation_name), func, name_func,
file, line);
return 0; // Return value used only to run this method in namespace scope.
}
// UnitTest class invokes this method to register tests in this test suite
@ -553,60 +554,61 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
void RegisterTests() override {
bool generated_instantiations = false;
for (typename TestInfoContainer::iterator test_it = tests_.begin();
test_it != tests_.end(); ++test_it) {
std::shared_ptr<TestInfo> test_info = *test_it;
for (typename InstantiationContainer::iterator gen_it =
instantiations_.begin();
gen_it != instantiations_.end(); ++gen_it) {
const std::string& instantiation_name = gen_it->name;
ParamGenerator<ParamType> generator((*gen_it->generator)());
ParamNameGeneratorFunc* name_func = gen_it->name_func;
const char* file = gen_it->file;
int line = gen_it->line;
std::string test_suite_name;
std::string test_name;
for (const std::shared_ptr<TestInfo>& test_info : tests_) {
for (const InstantiationInfo& instantiation : instantiations_) {
const std::string& instantiation_name = instantiation.name;
ParamGenerator<ParamType> generator((*instantiation.generator)());
ParamNameGeneratorFunc* name_func = instantiation.name_func;
const char* file = instantiation.file;
int line = instantiation.line;
std::string test_suite_name;
if (!instantiation_name.empty())
test_suite_name = instantiation_name + "/";
test_suite_name += test_info->test_suite_base_name;
else
test_suite_name.clear();
test_suite_name += test_suite_name_;
size_t i = 0;
std::set<std::string> test_param_names;
for (typename ParamGenerator<ParamType>::iterator param_it =
generator.begin();
param_it != generator.end(); ++param_it, ++i) {
for (const auto& param : generator) {
generated_instantiations = true;
Message test_name_stream;
test_name.clear();
std::string param_name =
name_func(TestParamInfo<ParamType>(*param_it, i));
name_func(TestParamInfo<ParamType>(param, i));
GTEST_CHECK_(IsValidParamName(param_name))
<< "Parameterized test name '" << param_name
<< "' is invalid, in " << file << " line " << line << std::endl;
<< "' is invalid (contains spaces, dashes, or any "
"non-alphanumeric characters other than underscores), in "
<< file << " line " << line << "" << std::endl;
GTEST_CHECK_(test_param_names.count(param_name) == 0)
<< "Duplicate parameterized test name '" << param_name << "', in "
<< file << " line " << line << std::endl;
test_param_names.insert(param_name);
if (!test_info->test_base_name.empty()) {
test_name_stream << test_info->test_base_name << "/";
test_name.append(test_info->test_base_name).append("/");
}
test_name_stream << param_name;
test_name += param_name;
test_param_names.insert(std::move(param_name));
MakeAndRegisterTestInfo(
test_suite_name.c_str(), test_name_stream.GetString().c_str(),
test_suite_name, test_name.c_str(),
nullptr, // No type parameter.
PrintToString(*param_it).c_str(), test_info->code_location,
PrintToString(param).c_str(), test_info->code_location,
GetTestSuiteTypeId(),
SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
test_info->test_meta_factory->CreateTestFactory(*param_it));
} // for param_it
} // for gen_it
} // for test_it
test_info->test_meta_factory->CreateTestFactory(param));
++i;
} // for param
} // for instantiation
} // for test_info
if (!generated_instantiations) {
// There are no generaotrs, or they all generate nothing ...
@ -619,15 +621,13 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
// LocalTestInfo structure keeps information about a single test registered
// with TEST_P macro.
struct TestInfo {
TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
TestInfo(const char* a_test_base_name,
TestMetaFactoryBase<ParamType>* a_test_meta_factory,
CodeLocation a_code_location)
: test_suite_base_name(a_test_suite_base_name),
test_base_name(a_test_base_name),
: test_base_name(a_test_base_name),
test_meta_factory(a_test_meta_factory),
code_location(a_code_location) {}
code_location(std::move(a_code_location)) {}
const std::string test_suite_base_name;
const std::string test_base_name;
const std::unique_ptr<TestMetaFactoryBase<ParamType>> test_meta_factory;
const CodeLocation code_location;
@ -637,11 +637,10 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
// <Instantiation name, Sequence generator creation function,
// Name generator function, Source file, Source line>
struct InstantiationInfo {
InstantiationInfo(const std::string& name_in,
GeneratorCreationFunc* generator_in,
InstantiationInfo(std::string name_in, GeneratorCreationFunc* generator_in,
ParamNameGeneratorFunc* name_func_in, const char* file_in,
int line_in)
: name(name_in),
: name(std::move(name_in)),
generator(generator_in),
name_func(name_func_in),
file(file_in),
@ -702,29 +701,32 @@ class ParameterizedTestSuiteRegistry {
// tests and instantiations of a particular test suite.
template <class TestSuite>
ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
const char* test_suite_name, CodeLocation code_location) {
std::string test_suite_name, CodeLocation code_location) {
ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
for (auto& test_suite_info : test_suite_infos_) {
if (test_suite_info->GetTestSuiteName() == test_suite_name) {
if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
// Complain about incorrect usage of Google Test facilities
// and terminate the program since we cannot guaranty correct
// test suite setup and tear-down in this case.
ReportInvalidTestSuiteType(test_suite_name, code_location);
posix::Abort();
} else {
// At this point we are sure that the object we found is of the same
// type we are looking for, so we downcast it to that type
// without further checks.
typed_test_info = CheckedDowncastToActualType<
ParameterizedTestSuiteInfo<TestSuite>>(test_suite_info);
}
break;
auto item_it = suite_name_to_info_index_.find(test_suite_name);
if (item_it != suite_name_to_info_index_.end()) {
auto* test_suite_info = test_suite_infos_[item_it->second];
if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
// Complain about incorrect usage of Google Test facilities
// and terminate the program since we cannot guaranty correct
// test suite setup and tear-down in this case.
ReportInvalidTestSuiteType(test_suite_name.c_str(), code_location);
posix::Abort();
} else {
// At this point we are sure that the object we found is of the same
// type we are looking for, so we downcast it to that type
// without further checks.
typed_test_info =
CheckedDowncastToActualType<ParameterizedTestSuiteInfo<TestSuite>>(
test_suite_info);
}
}
if (typed_test_info == nullptr) {
typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
test_suite_name, code_location);
test_suite_name, std::move(code_location));
suite_name_to_info_index_.emplace(std::move(test_suite_name),
test_suite_infos_.size());
test_suite_infos_.push_back(typed_test_info);
}
return typed_test_info;
@ -738,8 +740,9 @@ class ParameterizedTestSuiteRegistry {
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
template <class TestCase>
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
const char* test_case_name, CodeLocation code_location) {
return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location);
std::string test_case_name, CodeLocation code_location) {
return GetTestSuitePatternHolder<TestCase>(std::move(test_case_name),
std::move(code_location));
}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
@ -748,6 +751,7 @@ class ParameterizedTestSuiteRegistry {
using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>;
TestSuiteInfoContainer test_suite_infos_;
::std::unordered_map<std::string, size_t> suite_name_to_info_index_;
ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) =
delete;
@ -774,7 +778,7 @@ class TypeParameterizedTestSuiteRegistry {
private:
struct TypeParameterizedTestSuiteInfo {
explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
: code_location(c), instantiated(false) {}
: code_location(std::move(c)), instantiated(false) {}
CodeLocation code_location;
bool instantiated;
@ -803,12 +807,12 @@ class ValueArray {
template <typename T>
operator ParamGenerator<T>() const { // NOLINT
return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
return ValuesIn(MakeVector<T>(std::make_index_sequence<sizeof...(Ts)>()));
}
private:
template <typename T, size_t... I>
std::vector<T> MakeVector(IndexSequence<I...>) const {
std::vector<T> MakeVector(std::index_sequence<I...>) const {
return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
}
@ -838,7 +842,7 @@ class CartesianProductGenerator
template <class I>
class IteratorImpl;
template <size_t... I>
class IteratorImpl<IndexSequence<I...>>
class IteratorImpl<std::index_sequence<I...>>
: public ParamIteratorInterface<ParamType> {
public:
IteratorImpl(const ParamGeneratorInterface<ParamType>* base,
@ -929,7 +933,7 @@ class CartesianProductGenerator
std::shared_ptr<ParamType> current_value_;
};
using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>;
using Iterator = IteratorImpl<std::make_index_sequence<sizeof...(T)>>;
std::tuple<ParamGenerator<T>...> generators_;
};

View File

@ -56,6 +56,8 @@
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
#define GTEST_OS_WINDOWS_PHONE 1
#define GTEST_OS_WINDOWS_TV_TITLE 1
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_GAMES)
#define GTEST_OS_WINDOWS_GAMES 1
#else
// WINAPI_FAMILY defined but no known partition matched.
// Default to desktop.

View File

@ -194,8 +194,6 @@
//
// Macros for basic C++ coding:
// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a
// variable don't have to be used.
// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is
// suppressed (constant conditional).
@ -208,6 +206,8 @@
// or
// UniversalPrinter<absl::optional>
// specializations. Always defined to 0 or 1.
// GTEST_INTERNAL_HAS_STD_SPAN - for enabling UniversalPrinter<std::span>
// specializations. Always defined to 0 or 1
// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or
// Matcher<absl::string_view>
// specializations. Always defined to 0 or 1.
@ -279,6 +279,22 @@
#error C++ versions less than C++14 are not supported.
#endif
// MSVC >= 19.11 (VS 2017 Update 3) supports __has_include.
#ifdef __has_include
#define GTEST_INTERNAL_HAS_INCLUDE __has_include
#else
#define GTEST_INTERNAL_HAS_INCLUDE(...) 0
#endif
// Detect C++ feature test macros as gracefully as possible.
// MSVC >= 19.15, Clang >= 3.4.1, and GCC >= 4.1.2 support feature test macros.
#if GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L && \
(!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE(<version>))
#include <version> // C++20 and later
#elif (!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE(<ciso646>))
#include <ciso646> // Pre-C++20
#endif
#include <ctype.h> // for isspace, etc
#include <stddef.h> // for ptrdiff_t
#include <stdio.h>
@ -320,7 +336,8 @@
#define GTEST_HAS_NOTIFICATION_ 0
#endif
#ifdef GTEST_HAS_ABSL
#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS)
#define GTEST_INTERNAL_HAS_ABSL_FLAGS // Used only in this file.
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/reflection.h"
@ -590,7 +607,8 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \
defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \
defined(GTEST_OS_OPENBSD) || defined(GTEST_OS_HAIKU) || \
defined(GTEST_OS_GNU_HURD))
defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_SOLARIS) || \
defined(GTEST_OS_AIX) || defined(GTEST_OS_ZOS))
#define GTEST_HAS_PTHREAD 1
#else
#define GTEST_HAS_PTHREAD 0
@ -609,7 +627,7 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
// Determines whether clone(2) is supported.
// Usually it will only be available on Linux, excluding
// Linux on the Itanium architecture.
// Also see http://linux.die.net/man/2/clone.
// Also see https://linux.die.net/man/2/clone.
#ifndef GTEST_HAS_CLONE
// The user didn't tell us, so we need to figure it out.
@ -640,9 +658,9 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
// platforms except known mobile / embedded ones. Also, if the port doesn't have
// a file system, stream redirection is not supported.
#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \
defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_ESP8266) || \
defined(GTEST_OS_XTENSA) || defined(GTEST_OS_QURT) || \
!GTEST_HAS_FILE_SYSTEM
defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_WINDOWS_GAMES) || \
defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \
defined(GTEST_OS_QURT) || !GTEST_HAS_FILE_SYSTEM
#define GTEST_HAS_STREAM_REDIRECTION 0
#else
#define GTEST_HAS_STREAM_REDIRECTION 1
@ -652,7 +670,7 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
// Determines whether to support death tests.
// pops up a dialog window that cannot be suppressed programmatically.
#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_CYGWIN) || \
defined(GTEST_OS_SOLARIS) || \
defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_ZOS) || \
(defined(GTEST_OS_MAC) && !defined(GTEST_OS_IOS)) || \
(defined(GTEST_OS_WINDOWS_DESKTOP) && _MSC_VER) || \
defined(GTEST_OS_WINDOWS_MINGW) || defined(GTEST_OS_AIX) || \
@ -730,6 +748,20 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#define GTEST_HAVE_ATTRIBUTE_(x) 0
#endif
// GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE
//
// A function-like feature checking macro that accepts C++11 style attributes.
// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
// find `__has_cpp_attribute`, will evaluate to 0.
#if defined(__has_cpp_attribute)
// NOTE: requiring __cplusplus above should not be necessary, but
// works around https://bugs.llvm.org/show_bug.cgi?id=23435.
#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) 0
#endif
// GTEST_HAVE_FEATURE_
//
// A function-like feature checking macro that is a wrapper around
@ -741,14 +773,22 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#endif
// Use this annotation after a variable or parameter declaration to tell the
// compiler the variable/parameter does not have to be used.
// compiler the variable/parameter may be used.
// Example:
//
// GTEST_ATTRIBUTE_UNUSED_ int foo = bar();
#if GTEST_HAVE_ATTRIBUTE_(unused)
#define GTEST_ATTRIBUTE_UNUSED_ __attribute__((unused))
// GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED int foo = bar();
//
// This can be removed once we only support only C++17 or newer and
// [[maybe_unused]] is available on all supported platforms.
#if GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(maybe_unused)
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
#elif GTEST_HAVE_ATTRIBUTE_(unused)
// This is inferior to [[maybe_unused]] as it can produce a
// -Wused-but-marked-unused warning on optionally used symbols, but it is all we
// have.
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED __attribute__((__unused__))
#else
#define GTEST_ATTRIBUTE_UNUSED_
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED
#endif
// Use this annotation before a function that takes a printf format string.
@ -827,9 +867,9 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#ifndef GTEST_API_
#ifdef _MSC_VER
#if GTEST_LINKED_AS_SHARED_LIBRARY
#if defined(GTEST_LINKED_AS_SHARED_LIBRARY) && GTEST_LINKED_AS_SHARED_LIBRARY
#define GTEST_API_ __declspec(dllimport)
#elif GTEST_CREATE_SHARED_LIBRARY
#elif defined(GTEST_CREATE_SHARED_LIBRARY) && GTEST_CREATE_SHARED_LIBRARY
#define GTEST_API_ __declspec(dllexport)
#endif
#elif GTEST_HAVE_ATTRIBUTE_(visibility)
@ -1987,7 +2027,9 @@ inline std::string StripTrailingSpaces(std::string str) {
namespace posix {
// File system porting.
#if GTEST_HAS_FILE_SYSTEM
// Note: Not every I/O-related function is related to file systems, so don't
// just disable all of them here. For example, fileno() and isatty(), etc. must
// always be available in order to detect if a pipe points to a terminal.
#ifdef GTEST_OS_WINDOWS
typedef struct _stat StatStruct;
@ -1998,27 +2040,32 @@ inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); }
// time and thus not defined there.
#else
inline int FileNo(FILE* file) { return _fileno(file); }
#if GTEST_HAS_FILE_SYSTEM
inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); }
inline int RmDir(const char* dir) { return _rmdir(dir); }
inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; }
#endif
#endif // GTEST_OS_WINDOWS_MOBILE
#elif defined(GTEST_OS_ESP8266)
typedef struct stat StatStruct;
inline int FileNo(FILE* file) { return fileno(file); }
#if GTEST_HAS_FILE_SYSTEM
inline int Stat(const char* path, StatStruct* buf) {
// stat function not implemented on ESP8266
return 0;
}
inline int RmDir(const char* dir) { return rmdir(dir); }
inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
#endif
#else
typedef struct stat StatStruct;
inline int FileNo(FILE* file) { return fileno(file); }
#if GTEST_HAS_FILE_SYSTEM
inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
#ifdef GTEST_OS_QURT
// QuRT doesn't support any directory functions, including rmdir
@ -2027,9 +2074,9 @@ inline int RmDir(const char*) { return 0; }
inline int RmDir(const char* dir) { return rmdir(dir); }
#endif
inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
#endif
#endif // GTEST_OS_WINDOWS
#endif // GTEST_HAS_FILE_SYSTEM
// Other functions with a different name on Windows.
@ -2082,8 +2129,9 @@ GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
// defined there.
#if GTEST_HAS_FILE_SYSTEM
#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_WINDOWS_PHONE) && \
!defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_ESP8266) && \
!defined(GTEST_OS_XTENSA) && !defined(GTEST_OS_QURT)
!defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_GAMES) && \
!defined(GTEST_OS_ESP8266) && !defined(GTEST_OS_XTENSA) && \
!defined(GTEST_OS_QURT)
inline int ChDir(const char* dir) { return chdir(dir); }
#endif
inline FILE* FOpen(const char* path, const char* mode) {
@ -2227,7 +2275,7 @@ using TimeInMillis = int64_t; // Represents time in milliseconds.
#endif // !defined(GTEST_FLAG)
// Pick a command line flags implementation.
#ifdef GTEST_HAS_ABSL
#ifdef GTEST_INTERNAL_HAS_ABSL_FLAGS
// Macros for defining flags.
#define GTEST_DEFINE_bool_(name, default_val, doc) \
@ -2252,7 +2300,8 @@ using TimeInMillis = int64_t; // Represents time in milliseconds.
(void)(::absl::SetFlag(&GTEST_FLAG(name), value))
#define GTEST_USE_OWN_FLAGFILE_FLAG_ 0
#else // GTEST_HAS_ABSL
#undef GTEST_INTERNAL_HAS_ABSL_FLAGS
#else // ndef GTEST_INTERNAL_HAS_ABSL_FLAGS
// Macros for defining flags.
#define GTEST_DEFINE_bool_(name, default_val, doc) \
@ -2294,7 +2343,7 @@ using TimeInMillis = int64_t; // Represents time in milliseconds.
#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value)
#define GTEST_USE_OWN_FLAGFILE_FLAG_ 1
#endif // GTEST_HAS_ABSL
#endif // GTEST_INTERNAL_HAS_ABSL_FLAGS
// Thread annotations
#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
@ -2349,9 +2398,9 @@ using Any = ::absl::any;
} // namespace internal
} // namespace testing
#else
#ifdef __has_include
#if __has_include(<any>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
(!defined(_MSC_VER) || GTEST_HAS_RTTI)
#if defined(__cpp_lib_any) || (GTEST_INTERNAL_HAS_INCLUDE(<any>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
(!defined(_MSC_VER) || GTEST_HAS_RTTI))
// Otherwise for C++17 and higher use std::any for UniversalPrinter<>
// specializations.
#define GTEST_INTERNAL_HAS_ANY 1
@ -2363,8 +2412,7 @@ using Any = ::std::any;
} // namespace testing
// The case where absl is configured NOT to alias std::any is not
// supported.
#endif // __has_include(<any>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#endif // __has_include
#endif // __cpp_lib_any
#endif // GTEST_HAS_ABSL
#ifndef GTEST_INTERNAL_HAS_ANY
@ -2384,8 +2432,8 @@ inline ::absl::nullopt_t Nullopt() { return ::absl::nullopt; }
} // namespace internal
} // namespace testing
#else
#ifdef __has_include
#if __has_include(<optional>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#if defined(__cpp_lib_optional) || (GTEST_INTERNAL_HAS_INCLUDE(<optional>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L)
// Otherwise for C++17 and higher use std::optional for UniversalPrinter<>
// specializations.
#define GTEST_INTERNAL_HAS_OPTIONAL 1
@ -2399,14 +2447,22 @@ inline ::std::nullopt_t Nullopt() { return ::std::nullopt; }
} // namespace testing
// The case where absl is configured NOT to alias std::optional is not
// supported.
#endif // __has_include(<optional>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#endif // __has_include
#endif // __cpp_lib_optional
#endif // GTEST_HAS_ABSL
#ifndef GTEST_INTERNAL_HAS_OPTIONAL
#define GTEST_INTERNAL_HAS_OPTIONAL 0
#endif
#if defined(__cpp_lib_span) || (GTEST_INTERNAL_HAS_INCLUDE(<span>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L)
#define GTEST_INTERNAL_HAS_STD_SPAN 1
#endif // __cpp_lib_span
#ifndef GTEST_INTERNAL_HAS_STD_SPAN
#define GTEST_INTERNAL_HAS_STD_SPAN 0
#endif
#ifdef GTEST_HAS_ABSL
// Always use absl::string_view for Matcher<> specializations if googletest
// is built with absl support.
@ -2418,8 +2474,9 @@ using StringView = ::absl::string_view;
} // namespace internal
} // namespace testing
#else
#ifdef __has_include
#if __has_include(<string_view>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#if defined(__cpp_lib_string_view) || \
(GTEST_INTERNAL_HAS_INCLUDE(<string_view>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L)
// Otherwise for C++17 and higher use std::string_view for Matcher<>
// specializations.
#define GTEST_INTERNAL_HAS_STRING_VIEW 1
@ -2431,9 +2488,7 @@ using StringView = ::std::string_view;
} // namespace testing
// The case where absl is configured NOT to alias std::string_view is not
// supported.
#endif // __has_include(<string_view>) && GTEST_INTERNAL_CPLUSPLUS_LANG >=
// 201703L
#endif // __has_include
#endif // __cpp_lib_string_view
#endif // GTEST_HAS_ABSL
#ifndef GTEST_INTERNAL_HAS_STRING_VIEW
@ -2452,8 +2507,8 @@ using Variant = ::absl::variant<T...>;
} // namespace internal
} // namespace testing
#else
#ifdef __has_include
#if __has_include(<variant>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#if defined(__cpp_lib_variant) || (GTEST_INTERNAL_HAS_INCLUDE(<variant>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L)
// Otherwise for C++17 and higher use std::variant for UniversalPrinter<>
// specializations.
#define GTEST_INTERNAL_HAS_VARIANT 1
@ -2465,16 +2520,16 @@ using Variant = ::std::variant<T...>;
} // namespace internal
} // namespace testing
// The case where absl is configured NOT to alias std::variant is not supported.
#endif // __has_include(<variant>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L
#endif // __has_include
#endif // __cpp_lib_variant
#endif // GTEST_HAS_ABSL
#ifndef GTEST_INTERNAL_HAS_VARIANT
#define GTEST_INTERNAL_HAS_VARIANT 0
#endif
#if defined(GTEST_INTERNAL_CPLUSPLUS_LANG) && \
GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L
#if (defined(__cpp_constexpr) && !defined(__cpp_inline_variables)) || \
(defined(GTEST_INTERNAL_CPLUSPLUS_LANG) && \
GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L)
#define GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1
#endif

View File

@ -71,7 +71,7 @@ inline std::string CanonicalizeForStdLibVersioning(std::string s) {
// Strip redundant spaces in typename to match MSVC
// For example, std::pair<int, bool> -> std::pair<int,bool>
static const char to_search[] = ", ";
static const char replace_str[] = ",";
const char replace_char = ',';
size_t pos = 0;
while (true) {
// Get the next occurrence from the current position
@ -80,8 +80,8 @@ inline std::string CanonicalizeForStdLibVersioning(std::string s) {
break;
}
// Replace this occurrence of substring
s.replace(pos, strlen(to_search), replace_str);
pos += strlen(replace_str);
s.replace(pos, strlen(to_search), 1, replace_char);
++pos;
}
return s;
}

Some files were not shown because too many files have changed in this diff Show More