diff --git a/.github/workflows/bvt-appleclang.yml b/.github/workflows/bvt-appleclang.yml index 32f84f1..ace5880 100644 --- a/.github/workflows/bvt-appleclang.yml +++ b/.github/workflows/bvt-appleclang.yml @@ -7,26 +7,33 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: check compiler versions run: | - clang --version + cc --version xcodebuild -version - - name: build and run test with AppleClang + - name: build and run test with AppleClang on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++ build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh c++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with AppleClang on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-appleclang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-clang.yml b/.github/workflows/bvt-clang.yml index 3ead78b..1706930 100644 --- a/.github/workflows/bvt-clang.yml +++ b/.github/workflows/bvt-clang.yml @@ -7,35 +7,45 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install clang run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 20 - sudo apt install -y libc++-20-dev clang-format-20 + curl https://apt.llvm.org/llvm.sh | sudo bash -s -- 21 + sudo apt install -y clang-21 libc++-21-dev clang-format-21 + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-21 + CXX=clang++-21 + CXXFLAGS=-stdlib=libc++ + EOF - name: check compiler version run: | - clang++-20 --version + "$CXX" --version - - name: build and run test with clang 20 + - name: build and run test with clang 21 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + mapfile -t FILES < <(find include tests benchmarks build-cmake/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) echo "Running clang-format on ${#FILES[@]} files: ${FILES[*]}" - clang-format-20 --dry-run --Werror "${FILES[@]}" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++-20 build/drop/env-info.json + clang-format-21 --dry-run --Werror "${FILES[@]}" + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with clang 21 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-clang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-compatibility.yml b/.github/workflows/bvt-compatibility.yml index 7164c83..6dda774 100644 --- a/.github/workflows/bvt-compatibility.yml +++ b/.github/workflows/bvt-compatibility.yml @@ -4,54 +4,46 @@ on: jobs: bvt-compatibility: runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + compiler: + - {family: gcc, version: 13, modules: false} + - {family: gcc, version: 14, modules: true} + - {family: clang, version: 16, modules: false} + - {family: clang, version: 17, modules: false} + - {family: clang, version: 18, modules: false} + - {family: clang, version: 19, modules: false} + - {family: clang, version: 20, modules: true} + steps: - uses: actions/checkout@v4 - - name: install compilers - run: | - sudo apt install -y gcc-13 g++-13 gcc-14 g++-14 clang-16 clang-17 clang-18 clang-19 libc++-17-dev - - - name: check compiler versions - run: | - g++-13 --version - g++-14 --version - clang++-16 --version - clang++-17 --version - clang++-18 --version - clang++-19 --version - - - name: build and run test with gcc 14 - run: | - cmake -B build-gcc-14 -GNinja -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build-gcc-14 -j - ctest --test-dir build-gcc-14 -j - - - name: build and run test with gcc 13 - run: | - cmake -B build-gcc-13 -DCMAKE_C_COMPILER=gcc-13 -DCMAKE_CXX_COMPILER=g++-13 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-gcc-13 -j - ctest --test-dir build-gcc-13 -j - - - name: build and run test with clang 19 + - name: install gcc + if: ${{ matrix.compiler.family == 'gcc' }} run: | - cmake -B build-clang-19 -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-19 -j - ctest --test-dir build-clang-19 -j + sudo apt install -y 'g++-${{ matrix.compiler.version }}' + cat <<'EOF' >> "$GITHUB_ENV" + CC=gcc-${{ matrix.compiler.version }} + CXX=g++-${{ matrix.compiler.version }} + EOF - - name: build and run test with clang 18 + - name: install clang + if: ${{ matrix.compiler.family == 'clang' }} run: | - cmake -B build-clang-18 -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-18 -j - ctest --test-dir build-clang-18 -j + sudo apt install -y 'clang-${{ matrix.compiler.version }}' 'clang-tools-${{ matrix.compiler.version }}' 'libc++-${{ matrix.compiler.version }}-dev' 'libc++abi-${{ matrix.compiler.version }}-dev' + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-${{ matrix.compiler.version }} + CXX=clang++-${{ matrix.compiler.version }} + CXXFLAGS=-stdlib=libc++ + EOF - - name: build and run test with clang 17 + - name: check compiler version run: | - cmake -B build-clang-17 -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-17 -j - ctest --test-dir build-clang-17 -j + "$CXX" --version - - name: build and run test with clang 16 + - name: build and run test with cmake run: | - cmake -B build-clang-16 -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-16 -j - ctest --test-dir build-clang-16 -j + cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release '-DPROXY_BUILD_MODULES=${{ matrix.compiler.modules }}' + cmake --build build -j + ctest --test-dir build -j diff --git a/.github/workflows/bvt-gcc.yml b/.github/workflows/bvt-gcc.yml index 81679dd..4a5e613 100644 --- a/.github/workflows/bvt-gcc.yml +++ b/.github/workflows/bvt-gcc.yml @@ -4,32 +4,39 @@ on: jobs: bvt-gcc: runs-on: ubuntu-24.04 + container: gcc:15 steps: - uses: actions/checkout@v4 - - name: install gcc + - name: install cmake and meson run: | - sudo apt install -y gcc-14 g++-14 + apt-get update + apt-get install -y cmake meson ninja-build - name: check compiler version run: | - g++-14 --version + g++ --version - - name: build and run test with gcc 14 + - name: build and run test with gcc 15 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh g++-14 build/drop/env-info.json + ./tools/dump_build_env.sh g++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with gcc 15 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-gcc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-msvc.yml b/.github/workflows/bvt-msvc.yml index bf38ae2..fe06cca 100644 --- a/.github/workflows/bvt-msvc.yml +++ b/.github/workflows/bvt-msvc.yml @@ -7,23 +7,31 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: add cl.exe to PATH uses: ilammy/msvc-dev-cmd@v1 - - name: build and run test with MSVC + - name: build and run test with MSVC on cmake run: | - cmake -B build -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` - && cmake --build build --config Release -j ` - && ctest --test-dir build -j ` - && mkdir build\drop > $null ` - && .\tools\dump_build_env_msvc.ps1 -OutputPath build\drop\env-info.json + cmake -B build-cmake -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` + && cmake --build build-cmake --config Release -j ` + && ctest --test-dir build-cmake -j ` + && mkdir build-cmake\drop > $null ` + && .\tools\dump_build_env_msvc.ps1 -OutputPath build-cmake\drop\env-info.json - - name: run benchmarks + - name: build and run test with MSVC on meson run: | - build\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build\drop\benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt --vsenv + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake\drop\benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-msvc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-nvhpc.yml b/.github/workflows/bvt-nvhpc.yml index 78696e4..e9c1eea 100644 --- a/.github/workflows/bvt-nvhpc.yml +++ b/.github/workflows/bvt-nvhpc.yml @@ -10,29 +10,41 @@ jobs: - name: free disk space uses: jlumbroso/free-disk-space@v1.3.1 - - name: install NVHPC 25.7 + # - name: install meson + # # FIXME: install from upstream once https://github.com/mesonbuild/meson/pull/15353 is released + # run: pipx install git+https://github.com/mesonbuild/meson@02b85a846629090a0c7f18e860bab0a10ea4349b + + - name: install NVHPC 25.11 run: | curl https://developer.download.nvidia.com/hpc-sdk/ubuntu/DEB-GPG-KEY-NVIDIA-HPC-SDK | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg echo 'deb [signed-by=/usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | sudo tee /etc/apt/sources.list.d/nvhpc.list sudo apt-get update -y - sudo apt-get install -y nvhpc-25-7 + sudo apt-get install -y nvhpc-25-11 + cat<<'EOF' >> "$GITHUB_ENV" + CC=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc + CXX=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc++ + EOF - - name: build and run test with NVHPC 25.7 + - name: build and run test with NVHPC 25.11 on cmake run: | - PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/25.7/compilers/bin:$PATH; export PATH - cmake -B build -GNinja -DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++ -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh nvc++ build/drop/env-info.json + ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json + + # - name: build and run test with NVHPC 25.11 on meson + # run: | + # meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + # meson test -C build-meson + # meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true - name: run benchmarks - run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-nvhpc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-oneapi.yml b/.github/workflows/bvt-oneapi.yml index 3bc1d43..86f1914 100644 --- a/.github/workflows/bvt-oneapi.yml +++ b/.github/workflows/bvt-oneapi.yml @@ -7,9 +7,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install libc++ run: | sudo apt-get install -y libc++-19-dev libc++abi-19-dev + cat <<'EOF' >> "$GITHUB_ENV" + CXXFLAGS=-stdlib=libc++ + EOF - name: install intel oneAPI run: | @@ -18,28 +24,36 @@ jobs: sudo apt update sudo apt install -y intel-oneapi-compiler-dpcpp-cpp source /opt/intel/oneapi/setvars.sh - echo "PATH=$PATH" >> $GITHUB_ENV - echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV + cat<<-EOF >> "$GITHUB_ENV" + PATH=$PATH + LD_LIBRARY_PATH=$LD_LIBRARY_PATH + CC=$(which icx) + CXX=$(which icpx) + EOF - name: check compiler version run: | - icpx --version + "$CXX" --version - - name: build and run test with oneapi + - name: build and run test with oneapi on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_COMPILER=icpx -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-stdlib=libc++" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh icpx build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with oneapi on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-oneapi - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-report.yml b/.github/workflows/bvt-report.yml index 2b3a104..087a6b9 100644 --- a/.github/workflows/bvt-report.yml +++ b/.github/workflows/bvt-report.yml @@ -23,8 +23,8 @@ jobs: cat < benchmarking-report.md # Benchmarking Report - - Generated for: [Microsoft "Proxy" library](https://github.com/microsoft/proxy) - - Commit ID: [${{ github.sha }}](https://github.com/microsoft/proxy/commit/${{ github.sha }}) + - Generated for: [C++ Proxy library](https://github.com/ngcpp/proxy) + - Commit ID: [${{ github.sha }}](https://github.com/ngcpp/proxy/commit/${{ github.sha }}) - Generated at: $(date -u +"%Y-%m-%dT%H:%M:%SZ") EOF diff --git a/.github/workflows/pipeline-release.yml b/.github/workflows/pipeline-release.yml index 6777874..77a9d2c 100644 --- a/.github/workflows/pipeline-release.yml +++ b/.github/workflows/pipeline-release.yml @@ -17,8 +17,8 @@ jobs: version=$(grep -oP 'msft_proxy\d+\s+VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt) git tag "$version" git push origin "$version" - tar -czf "proxy-$version.tgz" $(git ls-files 'include/**.h' 'include/**.ixx') - echo "PRO_VER=$version" >> $GITHUB_OUTPUT + git ls-files 'include/**.h' 'include/**.ixx' | xargs tar -czf "proxy-$version.tgz" + echo "PRO_VER=$version" >> "$GITHUB_OUTPUT" shell: bash - name: create release draft diff --git a/.gitignore b/.gitignore index 631c244..d2e81b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ # Ignore build directories build/ Testing/ + +# Python bytecode cache +__pycache__/ + +# Meson subprojects +/subprojects/* +!/subprojects/*.wrap diff --git a/CMakeLists.txt b/CMakeLists.txt index ee8d16c..60e796c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.28) -project(msft_proxy4 VERSION 4.0.1 LANGUAGES CXX) +project(msft_proxy4 VERSION 4.0.2 LANGUAGES CXX) add_library(msft_proxy4 INTERFACE) set_target_properties(msft_proxy4 PROPERTIES EXPORT_NAME proxy) add_library(msft_proxy4::proxy ALIAS msft_proxy4) @@ -94,6 +94,22 @@ install( # build tests if BUILD_TESTING is ON if (BUILD_TESTING) include(CTest) + + include(FetchContent) + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects URL changes. + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + endif() + + FetchContent_Declare( + fmt + URL https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz + URL_HASH SHA256=ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea + SYSTEM + ) + FetchContent_MakeAvailable(fmt) + add_subdirectory(tests) add_subdirectory(benchmarks) add_subdirectory(docs) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f9ba8cf..191fcda 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,9 +1,9 @@ -# Microsoft Open Source Code of Conduct +# Next Gen C++ Foundation Code of Conduct -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). Resources: -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) +- [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) +- Contact [ngcpp@outlook.com](mailto:ngcpp@outlook.com) with questions or concerns diff --git a/LICENSE b/LICENSE index 9e841e7..6f07338 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License - Copyright (c) Microsoft Corporation. + Copyright (c) 2022-2026 Microsoft Corporation. + Copyright (c) 2026-Present Next Gen C++ Foundation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9d55153..2b34a01 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Proxy: Next Generation Polymorphism in C++ -[![Proxy-CI](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml/badge.svg)](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml) +[![Proxy-CI](https://github.com/ngcpp/proxy/actions/workflows/pipeline-ci.yml/badge.svg)](https://github.com/ngcpp/proxy/actions/workflows/pipeline-ci.yml) Are you looking to simplify the lifetime management and maintenance of polymorphic objects in C++? @@ -16,7 +16,7 @@ If so, this library is for you. "Proxy" is a modern C++ library that helps you use polymorphism (a way to use different types of objects interchangeably) without needing inheritance. -"Proxy" was created by Microsoft engineers and has been used in the Windows operating system since 2022. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: +"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, and has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). This repository was ported from [microsoft/proxy](https://github.com/microsoft/proxy), where more historical releases can be found. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: - **Portable**: "Proxy" was implemented as a header-only library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded engineering or kernel design of an operating system. - **Non-intrusive**: An implementation type is no longer required to inherit from an abstract binding. @@ -25,11 +25,11 @@ If so, this library is for you. - **Accessible**: Learned from user feedback, accessibility has been significantly improved with intuitive syntax, good IDE compatibility, and accurate diagnostics. - **Flexible**: Not only member functions, the "abstraction" of "Proxy" allows *any* expression to be polymorphic, including free functions, operators, conversions, etc. Different abstractions can be freely composed on demand. Performance tuning is supported for experts to balance between extensibility and performance. -Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.github.io/proxy/faq) for more background, and refer to the [specifications](https://microsoft.github.io/proxy/spec) for more technical details. +Please refer to the [Proxy's Frequently Asked Questions](https://ngcpp.github.io/proxy/faq) for more background, and refer to the [specifications](https://ngcpp.github.io/proxy/spec) for more technical details. ## Quick Start -"Proxy" is a header-only C++20 library. To use the library, make sure your compiler meets the [minimum requirements](#compiler-req) and just put the [proxy](https://github.com/microsoft/proxy/tree/main/include/proxy) directory in your project's include directory. Alternatively, you can install the library via: +"Proxy" is a header-only C++20 library. To use the library, make sure your compiler meets the [minimum requirements](#compiler-req) and just put the [proxy](https://github.com/ngcpp/proxy/tree/main/include/proxy) directory in your project's include directory. Alternatively, you can install the library via: - [vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/overview): [proxy port on vcpkg.io](https://vcpkg.io/en/package/proxy) - [conan](https://conan.io/): [proxy recipe on conan.io](https://conan.io/center/recipes/proxy) @@ -39,7 +39,7 @@ Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.githu CPMAddPackage( NAME msft_proxy4 GIT_TAG 4.0.0 # or above - GIT_REPOSITORY https://github.com/microsoft/proxy.git + GIT_REPOSITORY https://github.com/ngcpp/proxy.git ) target_link_libraries(main PRIVATE msft_proxy4::proxy) @@ -79,22 +79,22 @@ Here is a step-by-step explanation: - `#include `: For [`std::cout`](https://en.cppreference.com/w/cpp/io/cout). - `#include `: For [`std::string`](https://en.cppreference.com/w/cpp/string/basic_string). - `#include `: For the "Proxy" library. Most of the facilities of the library are defined in namespace `pro`. -- `struct Formattable : pro::facade_builder ... ::build {}`: Defines a facade type `Formattable`. The term "facade", formally defined as the [*ProFacade* requirements](https://microsoft.github.io/proxy/spec/ProFacade), is how the "Proxy" library models runtime abstraction. Specifically, - - [`pro::facade_builder`](https://microsoft.github.io/proxy/spec/basic_facade_builder): Provides capability to build a facade type at compile-time. - - [`add_skill`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_skill)`<`[`pro::skills::format`](https://microsoft.github.io/proxy/spec/skills_format)`>`: Specifies the capability of formatting (via [standard formatting functions](https://en.cppreference.com/w/cpp/utility/format)). - - [`build`](https://microsoft.github.io/proxy/docs/basic_facade_builder/build.html): Builds the context into a facade type. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. `p1` behaves like a raw pointer, and does not have ownership of the underlying `std::string`. If the lifetime of `str` ends before `p1`, `p1` becomes dangling. +- `struct Formattable : pro::facade_builder ... ::build {}`: Defines a facade type `Formattable`. The term "facade", formally defined as the [*ProFacade* requirements](https://ngcpp.github.io/proxy/spec/ProFacade), is how the "Proxy" library models runtime abstraction. Specifically, + - [`pro::facade_builder`](https://ngcpp.github.io/proxy/spec/basic_facade_builder): Provides capability to build a facade type at compile-time. + - [`add_skill`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_skill)`<`[`pro::skills::format`](https://ngcpp.github.io/proxy/spec/skills_format)`>`: Specifies the capability of formatting (via [standard formatting functions](https://en.cppreference.com/w/cpp/utility/format)). + - [`build`](https://ngcpp.github.io/proxy/docs/basic_facade_builder/build.html): Builds the context into a facade type. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. `p1` behaves like a raw pointer, and does not have ownership of the underlying `std::string`. If the lifetime of `str` ends before `p1`, `p1` becomes dangling. - `std::format("*p1 = {}\n", *p1)`: This is how it works. `*p1` is formatted as "Hello World" because the capability was defined in the facade `Formattable`, so it works as if by calling `std::format("*p1 = {}\n", str)`. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. Different from `p1`, `p2` has ownership of the underlying `int` because it is instantiated from a value of `std::unique_ptr`, and will call the destructor of `std::unique_ptr` when `p2` is destroyed, while `p1` does not have ownership of the underlying `int` because it is instantiated from a raw pointer. `p1` and `p2` are of the same type `pro::proxy`, which means you can have a function that returns `pro::proxy` without exposing any information about the implementation details to its caller. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. Different from `p1`, `p2` has ownership of the underlying `int` because it is instantiated from a value of `std::unique_ptr`, and will call the destructor of `std::unique_ptr` when `p2` is destroyed, while `p1` does not have ownership of the underlying `int` because it is instantiated from a raw pointer. `p1` and `p2` are of the same type `pro::proxy`, which means you can have a function that returns `pro::proxy` without exposing any information about the implementation details to its caller. - `std::format("*p2 = {}\n", *p2)`: Formats `*p2` as "123" with no surprises. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/spec/make_proxy)`(3.14159)`: Creates a `proxy` from a `double` without specifying the underlying pointer type. Specifically, +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://ngcpp.github.io/proxy/spec/make_proxy)`(3.14159)`: Creates a `proxy` from a `double` without specifying the underlying pointer type. Specifically, - Similar with `p2`, `p3` also has ownership of the underlying `double` value, but can effectively avoid heap allocation. - - Since the size of the underlying type (`double`) is known to be small (on major 32- or 64-bit platforms), [`pro::make_proxy`](https://microsoft.github.io/proxy/spec/make_proxy) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://microsoft.github.io/proxy/spec/make_proxy_inplace), which guarantees no heap allocation. + - Since the size of the underlying type (`double`) is known to be small (on major 32- or 64-bit platforms), [`pro::make_proxy`](https://ngcpp.github.io/proxy/spec/make_proxy) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://ngcpp.github.io/proxy/spec/make_proxy_inplace), which guarantees no heap allocation. - The "Proxy" library explicitly defines when heap allocation occurs or not to avoid users falling into performance hell, which is different from [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and other existing polymorphic wrappers in the standard. - `std::format("*p3 = {:.2f}\n", *p3)`: Formats `*p3` as "3.14" as per the [standard format specification](https://en.cppreference.com/w/cpp/utility/format/spec) with no surprises. - When `main` returns, `p2` and `p3` will destroy the underlying objects, while `p1` does nothing because it holds a raw pointer that does not have ownership of the underlying `std::string`. -Note: If you prefer the library to be consumed as a (C++20) module, refer to [C++20 Modules support](https://microsoft.github.io/proxy/modules_support). +Note: If you prefer the library to be consumed as a (C++20) module, refer to [C++20 Modules support](https://ngcpp.github.io/proxy/modules_support). ### Hello World (Stream Version) @@ -131,28 +131,28 @@ Here is a step-by-step explanation: - `#include `: For [`std::string`](https://en.cppreference.com/w/cpp/string/basic_string). - `#include `: For the "Proxy" library. - `struct Streamable : pro::facade_builder ... ::build {}`: Defines a facade type `Streamable`. Specifically, - - [`pro::facade_builder`](https://microsoft.github.io/proxy/spec/basic_facade_builder): Gets prepared to build another facade. - - [`add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention): Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context. - - [`pro::operator_dispatch`](https://microsoft.github.io/proxy/spec/operator_dispatch)`<"<<", true>`: Specifies a dispatch for operator `<<` expressions where the primary operand (`proxy`) is on the right-hand side (specified by the second template parameter `true`). Note that polymorphism in the "Proxy" library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages. + - [`pro::facade_builder`](https://ngcpp.github.io/proxy/spec/basic_facade_builder): Gets prepared to build another facade. + - [`add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention): Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context. + - [`pro::operator_dispatch`](https://ngcpp.github.io/proxy/spec/operator_dispatch)`<"<<", true>`: Specifies a dispatch for operator `<<` expressions where the primary operand (`proxy`) is on the right-hand side (specified by the second template parameter `true`). Note that polymorphism in the "Proxy" library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages. - `std::ostream&(std::ostream& out) const`: The signature of the calling convention, similar with [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function). `const` specifies that the primary operand is `const`. - - [`build`](https://microsoft.github.io/proxy/spec/basic_facade_builder/build): Builds the context into a facade type. + - [`build`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/build): Builds the context into a facade type. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. - `std::cout << *p1`: It prints "Hello World" because the calling convention is defined in the facade `Streamable`, so it works as if by calling `std::cout << str`. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. - `std::cout << *p2`: Prints "123" with no surprises. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/spec/make_proxy)`(3.14)`: Creates a `proxy` from a `double`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://ngcpp.github.io/proxy/spec/make_proxy)`(3.14)`: Creates a `proxy` from a `double`. - `std::cout << std::fixed << std::setprecision(2) << *p3;`: Prints "3.14" with no surprises. ### More Expressions In addition to the operator expressions demonstrated in the previous examples, the library supports almost all forms of expressions in C++ and can make them polymorphic. Specifically, -- [macro `PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH): Defines a dispatch type for member function call expressions, providing accessibility as member functions. -- [macro `PRO_DEF_FREE_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_FREE_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as free functions. -- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_FREE_AS_MEM_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as member functions. -- [class template `pro::operator_dispatch`](https://microsoft.github.io/proxy/spec/operator_dispatch): Dispatch type for operator expressions. -- [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](https://microsoft.github.io/proxy/spec/explicit_conversion_dispatch) and [class `implicit_conversion_dispatch`](https://microsoft.github.io/proxy/spec/implicit_conversion_dispatch): Dispatch type for conversion expressions. +- [macro `PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH): Defines a dispatch type for member function call expressions, providing accessibility as member functions. +- [macro `PRO_DEF_FREE_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_FREE_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as free functions. +- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_FREE_AS_MEM_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as member functions. +- [class template `pro::operator_dispatch`](https://ngcpp.github.io/proxy/spec/operator_dispatch): Dispatch type for operator expressions. +- [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](https://ngcpp.github.io/proxy/spec/explicit_conversion_dispatch) and [class `implicit_conversion_dispatch`](https://ngcpp.github.io/proxy/spec/implicit_conversion_dispatch): Dispatch type for conversion expressions. Note that some facilities are provided as macro, because C++ templates today do not support generating a function with an arbitrary name. Here is another example that makes member function call expressions polymorphic ([run](https://godbolt.org/z/E95nY7PYq)): @@ -206,11 +206,11 @@ Here is a step-by-step explanation: - `#include `: For [`std::cout`](https://en.cppreference.com/w/cpp/io/cout). - `#include `: For [`std::stringstream`](https://en.cppreference.com/w/cpp/io/basic_stringstream). - `#include `: For the "Proxy" library. -- [`PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemDraw, Draw)`: Defines a dispatch type `MemDraw` for expressions of calling member function `Draw`. -- [`PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemArea, Area)`: Defines a dispatch type `MemArea` for expressions of calling member function `Area`. +- [`PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemDraw, Draw)`: Defines a dispatch type `MemDraw` for expressions of calling member function `Draw`. +- [`PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemArea, Area)`: Defines a dispatch type `MemArea` for expressions of calling member function `Area`. - `struct Drawable : pro::facade_builder ... ::build {}`: Defines a facade type `Drawable`. Specifically, - - [`add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention): Adds calling conventions to the build context. - - [`support_copy`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_copy)`<`[`pro::constraint_level`](https://microsoft.github.io/proxy/spec/constraint_level)`::nontrivial>`: Specifies the underlying pointer type shall be copyable, which also makes the resulting `proxy` type copyable. + - [`add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention): Adds calling conventions to the build context. + - [`support_copy`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_copy)`<`[`pro::constraint_level`](https://ngcpp.github.io/proxy/spec/constraint_level)`::nontrivial>`: Specifies the underlying pointer type shall be copyable, which also makes the resulting `proxy` type copyable. - `class Rectangle`: An implementation of `Drawable`. - Function `PrintDrawableToString`: Converts a `Drawable` into a `std::string`. Note that this is a function rather than a function template, which means it can generate [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) in a larger build system. - `pro::proxy p = pro::make_proxy(3, 5)`: Creates a `proxy` object containing a `Rectangle`. @@ -219,18 +219,18 @@ Here is a step-by-step explanation: ### Other Useful Features -The "Proxy" library is a self-contained solution for runtime polymorphism in C++. There are many other capabilities documented in the [specifications](https://microsoft.github.io/proxy/spec). In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback: +The "Proxy" library is a self-contained solution for runtime polymorphism in C++. There are many other capabilities documented in the [specifications](https://ngcpp.github.io/proxy/spec). In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback: -- **Overloading**: [`facade_builder::add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention) is more powerful than demonstrated above. It can take any number of overload types (formally, any type meeting the [*ProOverload* requirements](https://microsoft.github.io/proxy/spec/ProOverload)) and perform standard overload resolution when invoking a `proxy`. -- **Facade composition**: [`facade_builder::add_facade`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_facade) allows flexible composition of different abstractions. -- **Concepts**: To facilitate template programming with "Proxy", 3 concepts are exported from a facade type. Namely, [`proxiable`](https://microsoft.github.io/proxy/spec/proxiable), [`proxiable_target`](https://microsoft.github.io/proxy/spec/proxiable_target) and [`inplace_proxiable_target`](https://microsoft.github.io/proxy/spec/inplace_proxiable_target). -- **Allocator awareness**: [function template `allocate_proxy`](https://microsoft.github.io/proxy/spec/allocate_proxy) is able to create a `proxy` from a value with any custom allocator. In C++11, [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and [`std::packaged_task`](https://en.cppreference.com/w/cpp/thread/packaged_task) had constructors that accepted custom allocators for performance tuning, but these were [removed in C++17](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r1.html) because "the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment". These issues do not apply to `allocate_proxy`. -- **Configurable constraints**: [`facade_builder`](https://microsoft.github.io/proxy/spec/basic_facade_builder) provides full support for constraints configuration, including memory layout (by [`restrict_layout`](https://microsoft.github.io/proxy/spec/basic_facade_builder/restrict_layout)), copyability (by [`support_copy`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_copy)), relocatability (by [`support_relocation`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_relocation)), and destructibility (by [`support_destruction`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_destruction)). -- **Reflection**: `proxy` supports type-based compile-time reflection for runtime queries. Please refer to [`facade_builder::add_reflection`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_reflection) and [function template `proxy_reflect`](https://microsoft.github.io/proxy/spec/proxy_reflect) for more details. -- **Non-owning proxy**: Although `proxy` can manage the lifetime of an object effectively, similar to a smart pointer, we sometimes want to dereference it before passing to a non-owning context. This has been implemented as an extension since 3.2.0. Please refer to [function template `make_proxy_view`](https://microsoft.github.io/proxy/spec/make_proxy_view), [alias template `proxy_view`, class template `observer_facade`](https://microsoft.github.io/proxy/spec/proxy_view) and [`skills::as_view`](https://microsoft.github.io/proxy/spec/skills_as_view) for more details. -- **RTTI**: [RTTI (run-time type information)](https://en.wikipedia.org/wiki/Run-time_type_information) provides "weak" reflection capability in C++ since the last century. Although it is not as powerful as reflection in some other languages (like `Object.GetType()` in C# or `Object.getClass()` in Java), it offers the basic infrastructure for type-safe casting at runtime. Since 4.0.0, "RTTI for `proxy`" has been implemented as an extension and allows users to opt-in for each facade definition. Please refer to [`skills::rtti`](https://microsoft.github.io/proxy/spec/skills_rtti) for more details. -- **Shared and weak ownership**: Although `proxy` can be created from a [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr), extensions are available to create `proxy` objects with shared and weak ownership in a more efficient way since 3.3.0. Please refer to [function template `make_proxy_shared`](https://microsoft.github.io/proxy/spec/make_proxy_shared), [`allocate_proxy_shared`](https://microsoft.github.io/proxy/spec/allocate_proxy_shared), [alias template `weak_proxy`, class template `weak_facade`](https://microsoft.github.io/proxy/spec/weak_proxy) and [`skills::as_weak`](https://microsoft.github.io/proxy/spec/skills_as_weak) for more details. -- **Weak dispatch**: When an object does not implement a convention, and we do not want it to trigger a hard compile error, it is allowed to specify a [`weak_dispatch`](https://microsoft.github.io/proxy/spec/weak_dispatch) that throws when invoked. +- **Overloading**: [`facade_builder::add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention) is more powerful than demonstrated above. It can take any number of overload types (formally, any type meeting the [*ProOverload* requirements](https://ngcpp.github.io/proxy/spec/ProOverload)) and perform standard overload resolution when invoking a `proxy`. +- **Facade composition**: [`facade_builder::add_facade`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_facade) allows flexible composition of different abstractions. +- **Concepts**: To facilitate template programming with "Proxy", 3 concepts are exported from a facade type. Namely, [`proxiable`](https://ngcpp.github.io/proxy/spec/proxiable), [`proxiable_target`](https://ngcpp.github.io/proxy/spec/proxiable_target) and [`inplace_proxiable_target`](https://ngcpp.github.io/proxy/spec/inplace_proxiable_target). +- **Allocator awareness**: [function template `allocate_proxy`](https://ngcpp.github.io/proxy/spec/allocate_proxy) is able to create a `proxy` from a value with any custom allocator. In C++11, [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and [`std::packaged_task`](https://en.cppreference.com/w/cpp/thread/packaged_task) had constructors that accepted custom allocators for performance tuning, but these were [removed in C++17](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r1.html) because "the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment". These issues do not apply to `allocate_proxy`. +- **Configurable constraints**: [`facade_builder`](https://ngcpp.github.io/proxy/spec/basic_facade_builder) provides full support for constraints configuration, including memory layout (by [`restrict_layout`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/restrict_layout)), copyability (by [`support_copy`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_copy)), relocatability (by [`support_relocation`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_relocation)), and destructibility (by [`support_destruction`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_destruction)). +- **Reflection**: `proxy` supports type-based compile-time reflection for runtime queries. Please refer to [`facade_builder::add_reflection`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_reflection) and [function template `proxy_reflect`](https://ngcpp.github.io/proxy/spec/proxy_reflect) for more details. +- **Non-owning proxy**: Although `proxy` can manage the lifetime of an object effectively, similar to a smart pointer, we sometimes want to dereference it before passing to a non-owning context. This has been implemented as an extension since 3.2.0. Please refer to [function template `make_proxy_view`](https://ngcpp.github.io/proxy/spec/make_proxy_view), [alias template `proxy_view`, class template `observer_facade`](https://ngcpp.github.io/proxy/spec/proxy_view) and [`skills::as_view`](https://ngcpp.github.io/proxy/spec/skills_as_view) for more details. +- **RTTI**: [RTTI (run-time type information)](https://en.wikipedia.org/wiki/Run-time_type_information) provides "weak" reflection capability in C++ since the last century. Although it is not as powerful as reflection in some other languages (like `Object.GetType()` in C# or `Object.getClass()` in Java), it offers the basic infrastructure for type-safe casting at runtime. Since 4.0.0, "RTTI for `proxy`" has been implemented as an extension and allows users to opt-in for each facade definition. Please refer to [`skills::rtti`](https://ngcpp.github.io/proxy/spec/skills_rtti) for more details. +- **Shared and weak ownership**: Although `proxy` can be created from a [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr), extensions are available to create `proxy` objects with shared and weak ownership in a more efficient way since 3.3.0. Please refer to [function template `make_proxy_shared`](https://ngcpp.github.io/proxy/spec/make_proxy_shared), [`allocate_proxy_shared`](https://ngcpp.github.io/proxy/spec/allocate_proxy_shared), [alias template `weak_proxy`, class template `weak_facade`](https://ngcpp.github.io/proxy/spec/weak_proxy) and [`skills::as_weak`](https://ngcpp.github.io/proxy/spec/skills_as_weak) for more details. +- **Weak dispatch**: When an object does not implement a convention, and we do not want it to trigger a hard compile error, it is allowed to specify a [`weak_dispatch`](https://ngcpp.github.io/proxy/spec/weak_dispatch) that throws when invoked. ## Minimum Requirements for Compilers @@ -245,7 +245,7 @@ The "Proxy" library is a self-contained solution for runtime polymorphism in C++ ## Build and Run Tests with CMake ``` -git clone https://github.com/microsoft/proxy.git +git clone https://github.com/ngcpp/proxy.git cd proxy cmake -B build cmake --build build -j @@ -264,22 +264,10 @@ ctest --test-dir build -j ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a +This project welcomes contributions and suggestions. Some contributions may require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +the rights to use your contribution. If a CLA is required, the PR bot will provide instructions. -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). +For more information see the [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) or +contact [ngcpp@outlook.com](mailto:ngcpp@outlook.com) with any additional questions or comments. diff --git a/SECURITY.md b/SECURITY.md index 869fdfe..bc82d67 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,41 +1,25 @@ - - ## Security -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. +The Next Gen C++ Foundation (ngcpp) takes security seriously. If you believe you have found a security vulnerability in this repository, please report it responsibly. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. +Instead, please create a private security advisory at: -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. +- https://github.com/ngcpp/proxy/security/advisories/new -## Preferred Languages +If you need to share additional details that do not fit in the advisory, you may email: -We prefer all communications to be in English. +- ngcpp@outlook.com -## Policy +Please include as much of the following as possible to help us triage quickly: -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). +- Type of issue (e.g., buffer overflow, RCE, etc.) +- Affected file paths and versions/commits +- Reproduction steps and any required configuration +- Proof-of-concept (if available) +- Impact assessment - +We aim to acknowledge reports within 72 hours. diff --git a/SUPPORT.md b/SUPPORT.md index 291d4d4..50dfa90 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,25 +1,16 @@ -# TODO: The maintainer of this repo has not yet edited this file - -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. -- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. - -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* - # Support ## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new ones to avoid duplicates. For new issues, file your bug or feature request +as a new issue. + +For help and questions about using this project, please use GitHub Discussions: -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. +- https://github.com/ngcpp/proxy/discussions -## Microsoft Support Policy +## Support Policy -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. +Support for this project is provided on a best-effort basis by the community and maintainers via +the resources listed above. diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index d39821c..018a984 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,25 +1,18 @@ project(msft_proxy_benchmarks) -include(FetchContent) -# The policy uses the download time for timestamp, instead of the timestamp in the archive. This -# allows for proper rebuilds when a projects URL changes. -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - FetchContent_Declare( benchmark - URL https://github.com/google/benchmark/archive/refs/tags/v1.9.0.tar.gz - URL_HASH SHA256=35a77f46cc782b16fac8d3b107fbfbb37dcd645f7c28eee19f3b8e0758b48994 + URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.tar.gz + URL_HASH SHA256=b334658edd35efcf06a99d9be21e4e93e092bd5f95074c1673d5c8705d95c104 ) set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable tests for google benchmark") set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "Disable google benchmark unit tests") FetchContent_MakeAvailable(benchmark) add_executable(msft_proxy_benchmarks - proxy_invocation_benchmark_context.cpp - proxy_invocation_benchmark.cpp - proxy_management_benchmark.cpp + proxy_creation_benchmark.cpp + proxy_operation_benchmark_context.cpp + proxy_operation_benchmark.cpp ) target_include_directories(msft_proxy_benchmarks PRIVATE .) target_link_libraries(msft_proxy_benchmarks PRIVATE msft_proxy4::proxy benchmark::benchmark benchmark::benchmark_main) diff --git a/benchmarks/meson.build b/benchmarks/meson.build new file mode 100644 index 0000000..f878c36 --- /dev/null +++ b/benchmarks/meson.build @@ -0,0 +1,13 @@ +benchmark( + 'ProxyBenchmarks', + executable( + 'msft_proxy_benchmarks', + files( + 'proxy_creation_benchmark.cpp', + 'proxy_operation_benchmark.cpp', + 'proxy_operation_benchmark_context.cpp', + ), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, benchmark_dep], + ), +) diff --git a/benchmarks/proxy_management_benchmark.cpp b/benchmarks/proxy_creation_benchmark.cpp similarity index 81% rename from benchmarks/proxy_management_benchmark.cpp rename to benchmarks/proxy_creation_benchmark.cpp index 25dbb44..dc7ccda 100644 --- a/benchmarks/proxy_management_benchmark.cpp +++ b/benchmarks/proxy_creation_benchmark.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include @@ -30,7 +31,7 @@ constexpr int TestManagedObjectCount = 600000; constexpr int TypeSeriesCount = 3; using SmallObject1 = int; -using SmallObject2 = std::shared_ptr; +using SmallObject2 = double; struct SmallObject3 { SmallObject3() noexcept = default; SmallObject3(SmallObject3&&) noexcept = default; @@ -38,7 +39,7 @@ struct SmallObject3 { throw std::runtime_error{"Not implemented"}; } - std::unique_lock Field1; + std::unique_ptr Field1; }; using LargeObject1 = std::array; @@ -58,9 +59,10 @@ struct PolymorphicObject : PolymorphicObjectBase { struct DefaultFacade : pro::facade_builder // ::support_copy // + ::add_skill // ::build {}; -void BM_SmallObjectManagementWithProxy(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -73,7 +75,7 @@ void BM_SmallObjectManagementWithProxy(benchmark::State& state) { } } -void BM_SmallObjectManagementWithProxy_Shared(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy_Shared(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -86,7 +88,7 @@ void BM_SmallObjectManagementWithProxy_Shared(benchmark::State& state) { } } -void BM_SmallObjectManagementWithProxy_SharedPooled(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy_SharedPooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -104,7 +106,7 @@ void BM_SmallObjectManagementWithProxy_SharedPooled(benchmark::State& state) { } } -void BM_SmallObjectManagementWithUniquePtr(benchmark::State& state) { +void BM_SmallObjectCreationWithUniquePtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -120,7 +122,7 @@ void BM_SmallObjectManagementWithUniquePtr(benchmark::State& state) { } } -void BM_SmallObjectManagementWithSharedPtr(benchmark::State& state) { +void BM_SmallObjectCreationWithSharedPtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -133,7 +135,7 @@ void BM_SmallObjectManagementWithSharedPtr(benchmark::State& state) { } } -void BM_SmallObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { +void BM_SmallObjectCreationWithSharedPtr_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -148,7 +150,7 @@ void BM_SmallObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { } } -void BM_SmallObjectManagementWithAny(benchmark::State& state) { +void BM_SmallObjectCreationWithAny(benchmark::State& state) { for (auto _ : state) { std::vector data; data.reserve(TestManagedObjectCount); @@ -161,7 +163,7 @@ void BM_SmallObjectManagementWithAny(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -174,7 +176,7 @@ void BM_LargeObjectManagementWithProxy(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_Pooled(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -189,7 +191,7 @@ void BM_LargeObjectManagementWithProxy_Pooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_Shared(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_Shared(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -202,7 +204,7 @@ void BM_LargeObjectManagementWithProxy_Shared(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_SharedPooled(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_SharedPooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -220,7 +222,7 @@ void BM_LargeObjectManagementWithProxy_SharedPooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithUniquePtr(benchmark::State& state) { +void BM_LargeObjectCreationWithUniquePtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -236,7 +238,7 @@ void BM_LargeObjectManagementWithUniquePtr(benchmark::State& state) { } } -void BM_LargeObjectManagementWithSharedPtr(benchmark::State& state) { +void BM_LargeObjectCreationWithSharedPtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -249,7 +251,7 @@ void BM_LargeObjectManagementWithSharedPtr(benchmark::State& state) { } } -void BM_LargeObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { +void BM_LargeObjectCreationWithSharedPtr_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -264,7 +266,7 @@ void BM_LargeObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithAny(benchmark::State& state) { +void BM_LargeObjectCreationWithAny(benchmark::State& state) { for (auto _ : state) { std::vector data; data.reserve(TestManagedObjectCount); @@ -277,20 +279,20 @@ void BM_LargeObjectManagementWithAny(benchmark::State& state) { } } -BENCHMARK(BM_SmallObjectManagementWithProxy); -BENCHMARK(BM_SmallObjectManagementWithProxy_Shared); -BENCHMARK(BM_SmallObjectManagementWithProxy_SharedPooled); -BENCHMARK(BM_SmallObjectManagementWithUniquePtr); -BENCHMARK(BM_SmallObjectManagementWithSharedPtr); -BENCHMARK(BM_SmallObjectManagementWithSharedPtr_Pooled); -BENCHMARK(BM_SmallObjectManagementWithAny); -BENCHMARK(BM_LargeObjectManagementWithProxy); -BENCHMARK(BM_LargeObjectManagementWithProxy_Pooled); -BENCHMARK(BM_LargeObjectManagementWithProxy_Shared); -BENCHMARK(BM_LargeObjectManagementWithProxy_SharedPooled); -BENCHMARK(BM_LargeObjectManagementWithUniquePtr); -BENCHMARK(BM_LargeObjectManagementWithSharedPtr); -BENCHMARK(BM_LargeObjectManagementWithSharedPtr_Pooled); -BENCHMARK(BM_LargeObjectManagementWithAny); +BENCHMARK(BM_SmallObjectCreationWithProxy); +BENCHMARK(BM_SmallObjectCreationWithProxy_Shared); +BENCHMARK(BM_SmallObjectCreationWithProxy_SharedPooled); +BENCHMARK(BM_SmallObjectCreationWithUniquePtr); +BENCHMARK(BM_SmallObjectCreationWithSharedPtr); +BENCHMARK(BM_SmallObjectCreationWithSharedPtr_Pooled); +BENCHMARK(BM_SmallObjectCreationWithAny); +BENCHMARK(BM_LargeObjectCreationWithProxy); +BENCHMARK(BM_LargeObjectCreationWithProxy_Pooled); +BENCHMARK(BM_LargeObjectCreationWithProxy_Shared); +BENCHMARK(BM_LargeObjectCreationWithProxy_SharedPooled); +BENCHMARK(BM_LargeObjectCreationWithUniquePtr); +BENCHMARK(BM_LargeObjectCreationWithSharedPtr); +BENCHMARK(BM_LargeObjectCreationWithSharedPtr_Pooled); +BENCHMARK(BM_LargeObjectCreationWithAny); } // namespace diff --git a/benchmarks/proxy_invocation_benchmark.cpp b/benchmarks/proxy_invocation_benchmark.cpp deleted file mode 100644 index b19aff3..0000000 --- a/benchmarks/proxy_invocation_benchmark.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include "proxy_invocation_benchmark_context.h" - -namespace { - -void BM_SmallObjectInvocationViaProxy(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaProxy_Shared(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData_Shared(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaProxyView(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaVirtualFunction(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaVirtualFunction_Shared( - benchmark::State& state) { - auto data = GenerateSmallObjectInvocationVirtualFunctionTestData_Shared(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxy(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxy_Shared(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData_Shared(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxyView(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaVirtualFunction_Shared( - benchmark::State& state) { - auto data = GenerateLargeObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -BENCHMARK(BM_SmallObjectInvocationViaProxy); -BENCHMARK(BM_SmallObjectInvocationViaProxy_Shared); -BENCHMARK(BM_SmallObjectInvocationViaProxyView); -BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction); -BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_Shared); -BENCHMARK(BM_LargeObjectInvocationViaProxy); -BENCHMARK(BM_LargeObjectInvocationViaProxy_Shared); -BENCHMARK(BM_LargeObjectInvocationViaProxyView); -BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction); -BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_Shared); - -} // namespace diff --git a/benchmarks/proxy_invocation_benchmark_context.h b/benchmarks/proxy_invocation_benchmark_context.h deleted file mode 100644 index 5404330..0000000 --- a/benchmarks/proxy_invocation_benchmark_context.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include - -PRO_DEF_MEM_DISPATCH(MemFun, Fun); - -struct InvocationTestFacade : pro::facade_builder // - ::add_convention // - ::add_skill // - ::build {}; - -struct InvocationTestBase { - virtual int Fun() const = 0; - virtual ~InvocationTestBase() = default; -}; - -std::vector> - GenerateSmallObjectInvocationProxyTestData(); -std::vector> - GenerateSmallObjectInvocationProxyTestData_Shared(); -std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData(); -std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData_Shared(); -std::vector> - GenerateLargeObjectInvocationProxyTestData(); -std::vector> - GenerateLargeObjectInvocationProxyTestData_Shared(); -std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData(); -std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData_Shared(); diff --git a/benchmarks/proxy_operation_benchmark.cpp b/benchmarks/proxy_operation_benchmark.cpp new file mode 100644 index 0000000..9c21576 --- /dev/null +++ b/benchmarks/proxy_operation_benchmark.cpp @@ -0,0 +1,268 @@ +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. +// Licensed under the MIT License. + +#include + +#include "proxy_operation_benchmark_context.h" + +namespace { + +void BM_SmallObjectInvocationViaProxy(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaProxy_Shared(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaProxyView(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + std::vector> views(data.begin(), + data.end()); + for (auto _ : state) { + for (auto& p : views) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction(benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction_Shared( + benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction_RawPtr( + benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + std::vector ptrs; + ptrs.reserve(data.size()); + for (auto& p : data) { + ptrs.push_back(p.get()); + } + for (auto _ : state) { + for (auto& p : ptrs) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxy(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxy_Shared(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxyView(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + std::vector> views(data.begin(), + data.end()); + for (auto _ : state) { + for (auto& p : views) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction_Shared( + benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction_RawPtr( + benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + std::vector ptrs; + ptrs.reserve(data.size()); + for (auto& p : data) { + ptrs.push_back(p.get()); + } + for (auto _ : state) { + for (auto& p : ptrs) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectRelocationViaProxy(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaProxy_NothrowRelocatable( + benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData_NothrowRelocatable(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaUniquePtr(benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaAny(benchmark::State& state) { + auto data = GenerateSmallObjectAnyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaProxy(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaProxy_NothrowRelocatable( + benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData_NothrowRelocatable(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaUniquePtr(benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaAny(benchmark::State& state) { + auto data = GenerateLargeObjectAnyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +BENCHMARK(BM_SmallObjectInvocationViaProxy); +BENCHMARK(BM_SmallObjectInvocationViaProxy_Shared); +BENCHMARK(BM_SmallObjectInvocationViaProxyView); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_Shared); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_RawPtr); +BENCHMARK(BM_LargeObjectInvocationViaProxy); +BENCHMARK(BM_LargeObjectInvocationViaProxy_Shared); +BENCHMARK(BM_LargeObjectInvocationViaProxyView); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_Shared); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_RawPtr); +BENCHMARK(BM_SmallObjectRelocationViaProxy); +BENCHMARK(BM_SmallObjectRelocationViaProxy_NothrowRelocatable); +BENCHMARK(BM_SmallObjectRelocationViaUniquePtr); +BENCHMARK(BM_SmallObjectRelocationViaAny); +BENCHMARK(BM_LargeObjectRelocationViaProxy); +BENCHMARK(BM_LargeObjectRelocationViaProxy_NothrowRelocatable); +BENCHMARK(BM_LargeObjectRelocationViaUniquePtr); +BENCHMARK(BM_LargeObjectRelocationViaAny); + +} // namespace diff --git a/benchmarks/proxy_invocation_benchmark_context.cpp b/benchmarks/proxy_operation_benchmark_context.cpp similarity index 70% rename from benchmarks/proxy_invocation_benchmark_context.cpp rename to benchmarks/proxy_operation_benchmark_context.cpp index 13395af..7ab293d 100644 --- a/benchmarks/proxy_invocation_benchmark_context.cpp +++ b/benchmarks/proxy_operation_benchmark_context.cpp @@ -1,7 +1,8 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. -#include "proxy_invocation_benchmark_context.h" +#include "proxy_operation_benchmark_context.h" namespace { @@ -77,15 +78,23 @@ auto GenerateTestData(const F& generator) { } // namespace std::vector> - GenerateSmallObjectInvocationProxyTestData() { + GenerateSmallObjectProxyTestData() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy>(seed); }); } +std::vector> + GenerateSmallObjectProxyTestData_NothrowRelocatable() { + return GenerateTestData( + [](IntConstant, int seed) { + return pro::make_proxy>(seed); + }); +} std::vector> - GenerateSmallObjectInvocationProxyTestData_Shared() { + GenerateSmallObjectProxyTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy_shared> }); } std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData() { + GenerateSmallObjectVirtualFunctionTestData() { return GenerateTestData( [](IntConstant, int seed) { return std::unique_ptr{ @@ -101,23 +110,38 @@ std::vector> }); } std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData_Shared() { + GenerateSmallObjectVirtualFunctionTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return std::shared_ptr{ std::make_shared>(seed)}; }); } +std::vector GenerateSmallObjectAnyTestData() { + return GenerateTestData( + [](IntConstant, int seed) { + return std::make_any>(seed); + }); +} + std::vector> - GenerateLargeObjectInvocationProxyTestData() { + GenerateLargeObjectProxyTestData() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy>(seed); }); } +std::vector> + GenerateLargeObjectProxyTestData_NothrowRelocatable() { + return GenerateTestData( + [](IntConstant, int seed) { + return pro::make_proxy>(seed); + }); +} std::vector> - GenerateLargeObjectInvocationProxyTestData_Shared() { + GenerateLargeObjectProxyTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy_shared> }); } std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData() { + GenerateLargeObjectVirtualFunctionTestData() { return GenerateTestData( [](IntConstant, int seed) { return std::unique_ptr{ @@ -133,10 +157,16 @@ std::vector> }); } std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData_Shared() { + GenerateLargeObjectVirtualFunctionTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return std::shared_ptr{ std::make_shared>(seed)}; }); } +std::vector GenerateLargeObjectAnyTestData() { + return GenerateTestData( + [](IntConstant, int seed) { + return std::make_any>(seed); + }); +} diff --git a/benchmarks/proxy_operation_benchmark_context.h b/benchmarks/proxy_operation_benchmark_context.h new file mode 100644 index 0000000..d0566c7 --- /dev/null +++ b/benchmarks/proxy_operation_benchmark_context.h @@ -0,0 +1,50 @@ +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. +// Licensed under the MIT License. + +#include +#include +#include + +#include + +PRO_DEF_MEM_DISPATCH(MemFun, Fun); + +struct InvocationTestFacade : pro::facade_builder // + ::add_convention // + ::add_skill // + ::add_skill // + ::build {}; + +struct NothrowRelocatableInvocationTestFacade : InvocationTestFacade { + static constexpr auto relocatability = pro::constraint_level::nothrow; +}; + +struct InvocationTestBase { + virtual int Fun() const = 0; + virtual ~InvocationTestBase() = default; +}; + +std::vector> + GenerateSmallObjectProxyTestData(); +std::vector> + GenerateSmallObjectProxyTestData_NothrowRelocatable(); +std::vector> + GenerateSmallObjectProxyTestData_Shared(); +std::vector> + GenerateSmallObjectVirtualFunctionTestData(); +std::vector> + GenerateSmallObjectVirtualFunctionTestData_Shared(); +std::vector GenerateSmallObjectAnyTestData(); + +std::vector> + GenerateLargeObjectProxyTestData(); +std::vector> + GenerateLargeObjectProxyTestData_NothrowRelocatable(); +std::vector> + GenerateLargeObjectProxyTestData_Shared(); +std::vector> + GenerateLargeObjectVirtualFunctionTestData(); +std::vector> + GenerateLargeObjectVirtualFunctionTestData_Shared(); +std::vector GenerateLargeObjectAnyTestData(); diff --git a/cmake/msft_proxy4ModuleTargets.cmake b/cmake/msft_proxy4ModuleTargets.cmake index a4c2d1b..ee13002 100644 --- a/cmake/msft_proxy4ModuleTargets.cmake +++ b/cmake/msft_proxy4ModuleTargets.cmake @@ -22,5 +22,6 @@ target_sources(msft_proxy4_module PUBLIC target_compile_features(msft_proxy4_module PUBLIC cxx_std_20) target_compile_options(msft_proxy4_module PRIVATE $<$:/utf-8> + $<$:-Wno-c++2b-extensions> ) target_link_libraries(msft_proxy4_module PUBLIC msft_proxy4::proxy) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 4ed3bb1..ff9dc5b 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -1,12 +1,5 @@ project(msft_proxy_docs) -FetchContent_Declare( - fmt - URL https://github.com/fmtlib/fmt/archive/refs/tags/11.2.0.tar.gz - URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e -) -FetchContent_MakeAvailable(fmt) - find_package(Python3 REQUIRED COMPONENTS Interpreter) file(GLOB_RECURSE DOC_FILES "*.md") diff --git a/docs/faq.md b/docs/faq.md index e08ec82..5d48888 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -21,7 +21,7 @@ ### Why is "Proxy" so popular? -"Proxy" is built by engineers at Microsoft and initially deployed in the Windows operating system. For 40 years, the inheritance-based polymorphism paradigm has been the only scalable solution for runtime polymorphism in C++. However, a "virtual function" is no longer the optimal choice for runtime polymorphism today, and new languages with better paradigms, like [traits in Rust](https://doc.rust-lang.org/book/ch10-02-traits.html), are emerging. "Proxy" is our latest and greatest solution for generic runtime polymorphism in C++. It is easy to integrate and makes C++ feel like a brand new language when dealing with runtime abstractions. +"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). For 40 years, the inheritance-based polymorphism paradigm has been the only scalable solution for runtime polymorphism in C++. However, a "virtual function" is no longer the optimal choice for runtime polymorphism today, and new languages with better paradigms, like [traits in Rust](https://doc.rust-lang.org/book/ch10-02-traits.html), are emerging. "Proxy" is our latest and greatest solution for generic runtime polymorphism in C++. It is easy to integrate and makes C++ feel like a brand new language when dealing with runtime abstractions. ### Who is "Proxy" for? @@ -37,7 +37,7 @@ The fundamental abstraction of "Proxy" is called "facade". It is recommended for ### How to integrate "Proxy" into my project? -Since "Proxy" is a header-only library, you can simply navigate to the [latest release](https://github.com/microsoft/proxy/releases), download the source code, and include "proxy.h" in your project. Make sure your compiler version meets the [minimum requirements for compilers](README.md#minimum-requirements-for-compilers). If your project has already integrated with [vcpkg](https://vcpkg.io/) or [conan](https://conan.io/), just search for the keyword "proxy" and install it. Thanks to the community that helped port "Proxy" to these platforms! +Since "Proxy" is a header-only library, you can simply navigate to the [latest release](https://github.com/ngcpp/proxy/releases), download the source code, and include "proxy.h" in your project. Make sure your compiler version meets the [minimum requirements for compilers](README.md#minimum-requirements-for-compilers). If your project has already integrated with [vcpkg](https://vcpkg.io/) or [conan](https://conan.io/), just search for the keyword "proxy" and install it. Thanks to the community that helped port "Proxy" to these platforms! ### My existing project uses virtual functions. How should I migrate to "Proxy"? @@ -92,4 +92,4 @@ These rules let old and new code coexist during the transition while keeping ODR ### What should I do if I found this library deficient in my scenario? -Please search for your scenario in the existing issues first, and feel free to file an a new one on demand, following the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +Please search for your scenario in the existing issues first, and feel free to file an a new one on demand, following the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). diff --git a/docs/meson.build b/docs/meson.build new file mode 100644 index 0000000..1b18b77 --- /dev/null +++ b/docs/meson.build @@ -0,0 +1,39 @@ +extract_doctest_exe = find_program('../tools/meson_extract_doctest.py') + +docs = run_command( + extract_doctest_exe, + meson.current_source_dir(), + capture: true, + check: true, +).stdout().strip('\0').split('\0') + +examples = [] + +foreach doc : docs + deps = [msft_proxy4_dep] + if doc.contains('fmt') + deps += fmt_dep + endif + + name = doc.replace('/', '_').substring(0, -3) + example_exe = executable( + f'example_@name@', + custom_target( + command: [extract_doctest_exe, '@INPUT@', '@OUTPUT@'], + input: doc, + output: f'@name@.cpp', + ), + extra_files: doc, + implicit_include_directories: false, + dependencies: deps, + build_by_default: false, + ) + examples += example_exe + test( + name, + example_exe, + suite: 'ProxyExamples', + ) +endforeach + +alias_target('examples', examples) diff --git a/docs/resources/icon.png b/docs/resources/icon.png index dbfd434..bc87464 100644 Binary files a/docs/resources/icon.png and b/docs/resources/icon.png differ diff --git a/docs/spec/ProBasicConvention.md b/docs/spec/ProBasicConvention.md index 06a749f..1b4dc24 100644 --- a/docs/spec/ProBasicConvention.md +++ b/docs/spec/ProBasicConvention.md @@ -5,7 +5,7 @@ A type `C` meets the *ProBasicConvention* requirements if the following expressi | Expressions | Semantics | | ---------------------------- | ------------------------------------------------------------ | | `C::is_direct` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `bool`, specifying whether the convention applies to a pointer type itself (`true`), or the element type of a pointer type (`false`). | -| `typename C::dispatch_type` | A [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType) that defines how the calls are forwarded to the concrete types. | +| `typename C::dispatch_type` | A type that defines how the calls are forwarded to the concrete types. Shall be *nothrow-default-constructible* and *nothrow-destructible*. | | `typename C::overload_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of one or more distinct types `Os`. Each type `O` in `Os` shall meet the [*ProOverload* requirements](ProOverload.md). | ## See Also diff --git a/docs/spec/ProBasicFacade.md b/docs/spec/ProBasicFacade.md index 85fd877..f454c9c 100644 --- a/docs/spec/ProBasicFacade.md +++ b/docs/spec/ProBasicFacade.md @@ -6,12 +6,14 @@ A type `F` meets the *ProBasicFacade* requirements if the following expressions | ------------------------------ | ------------------------------------------------------------ | | `typename F::convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Cs`. Each type `C` in `Cs` shall meet the [*ProBasicConvention* requirements](ProBasicConvention.md). | | `typename F::reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Rs`. Each type `R` in `Rs` shall define reflection data structure. | -| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum size of a pointer type. | -| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum alignment of a pointer type. | +| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum size of a pointer type. Shall be greater than `0` and a multiple of `F::max_align`. | +| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum alignment of a pointer type. Shall be a power of `2`. | | `F::copyability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required copyability of a pointer type. | | `F::relocatability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of a pointer type. | | `F::destructibility` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of a pointer type. | +Each of `F::copyability`, `F::relocatability`, and `F::destructibility` shall be exactly one of the four enumerators of `constraint_level` (`none`, `nontrivial`, `nothrow`, `trivial`). + ## See Also - [concept `facade`](facade.md) diff --git a/docs/spec/ProBasicReflection.md b/docs/spec/ProBasicReflection.md index 771885b..32296b5 100644 --- a/docs/spec/ProBasicReflection.md +++ b/docs/spec/ProBasicReflection.md @@ -7,7 +7,7 @@ A type `R` meets the *ProBasicReflection* requirements if the following expressi | Expressions | Semantics | | ---------------------------- | ------------------------------------------------------------ | | `R::is_direct` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `bool`, specifying whether the reflection applies to a pointer type itself (`true`), or the element type of a pointer type (`false`). | -| `typename R::reflector_type` | A [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType) that defines the data structure reflected from the type. | +| `typename R::reflector_type` | A type that defines the data structure reflected from the type. Shall be *nothrow-default-constructible* and *nothrow-destructible*. | ## See Also diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md index 34582fa..1ca2515 100644 --- a/docs/spec/ProFacade.md +++ b/docs/spec/ProFacade.md @@ -12,6 +12,15 @@ A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*P | `F::relocatability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of `P`. | | `F::destructibility` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of `P`. | +*Since 4.0.2*: `P` shall be a pointer-like type eligible for `proxy`. A type `P` is eligible if `P` is not a specialization of `proxy` and the following condition is satisfied: + +```cpp +(requires { *std::declval(); } || requires { typename P::element_type; }) && +requires { typename std::pointer_traits

::element_type; } +``` + +In other words, `P` either supports dereferencing or provides an `element_type`, and `std::pointer_traits

` yields a valid `element_type`. + ## Notes Relocatability is defined as *move-construct an object and then destroy the original instance*. Specifically, the value of `F::relocatability` maps to the following requirements on `P`: diff --git a/docs/spec/basic_facade_builder/README.md b/docs/spec/basic_facade_builder/README.md index 0e1defc..2ee5ea2 100644 --- a/docs/spec/basic_facade_builder/README.md +++ b/docs/spec/basic_facade_builder/README.md @@ -12,7 +12,7 @@ constexpr constraint_level default-cl = static_cast( std::numeric_limits>::min()); // exposition only ``` -Given a [facade](../facade.md ) type `F`, any meaningful value of `F::max_size` and `F::max_align` is less than *default-size*; any meaningful value of `F::copyability`, `F::relocatability`, and `F::destructibility` is greater than *default-cl*. +Given a [facade](../facade.md) type `F`, any meaningful value of `F::max_size` and `F::max_align` is less than *default-size*; any meaningful value of `F::copyability`, `F::relocatability`, and `F::destructibility` is greater than *default-cl*. ```cpp template - requires(std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) + requires(PtrSize > 0u && std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) using restrict_layout = basic_facade_builder; ``` -The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t`). After applying the restriction, `MaxSize` becomes `std::min(MaxSize, PtrSize)`, and `MaxAlign` becomes `std::min(C::max_align, MaxAlign)`. +The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t)`. After applying the restriction, `MaxSize` becomes `std::min(MaxSize, PtrSize)`, and `MaxAlign` becomes `std::min(C::max_align, MaxAlign)`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_copy.md b/docs/spec/basic_facade_builder/support_copy.md index 0223306..6fac5f0 100644 --- a/docs/spec/basic_facade_builder/support_copy.md +++ b/docs/spec/basic_facade_builder/support_copy.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_copy = basic_facade_builder; ``` -The alias template `support_copy` of `basic_facade_builder` adds copyability support to the template parameters. After the operation, `Copyability` becomes `std::max(Copyability, CL)`. +The alias template `support_copy` of `basic_facade_builder` adds copyability support to the template parameters. After the operation, `Copyability` becomes `std::max(Copyability, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_destruction.md b/docs/spec/basic_facade_builder/support_destruction.md index cda2e5a..8dd134c 100644 --- a/docs/spec/basic_facade_builder/support_destruction.md +++ b/docs/spec/basic_facade_builder/support_destruction.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_destruction = basic_facade_builder; ``` -The alias template `support_destruction` of `basic_facade_builder` adds destruction support to the template parameters. After the operation, `Destructibility` becomes `std::max(Destructibility, CL)`. +The alias template `support_destruction` of `basic_facade_builder` adds destruction support to the template parameters. After the operation, `Destructibility` becomes `std::max(Destructibility, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_relocation.md b/docs/spec/basic_facade_builder/support_relocation.md index 78e9b15..d3b0e60 100644 --- a/docs/spec/basic_facade_builder/support_relocation.md +++ b/docs/spec/basic_facade_builder/support_relocation.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_relocation = basic_facade_builder; ``` -The alias template `support_relocation` of `basic_facade_builder` adds relocatability support to the template parameters. After the operation, `Relocatability` becomes `std::max(Relocatability, CL)`. +The alias template `support_relocation` of `basic_facade_builder` adds relocatability support to the template parameters. After the operation, `Relocatability` becomes `std::max(Relocatability, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/docs/spec/is_bitwise_trivially_relocatable.md b/docs/spec/is_bitwise_trivially_relocatable.md index 0fbcd51..a344ea6 100644 --- a/docs/spec/is_bitwise_trivially_relocatable.md +++ b/docs/spec/is_bitwise_trivially_relocatable.md @@ -16,7 +16,7 @@ constexpr bool is_bitwise_trivially_relocatable_v = The class template `is_bitwise_trivially_relocatable` is a type trait whose `value` is `true` when objects of (complete) type `T` can be *bitwise trivially relocated*: a new object of type `T` can be created at an arbitrary suitably aligned storage location by performing a raw byte-wise copy (as if by `std::memcpy`) of `sizeof(T)` bytes from the original object's storage, and the original object can then be considered destroyed (its lifetime ends) without invoking its destructor. Otherwise the `value` is `false`. -Semantics follow the model described in [P3780R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3780r0.html), not the `std::trivially_relocatable` facility from [P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html) (C++26). The library keeps this separate trait for portability and because its internal optimizations rely on the "memcpy relocation" guarantee. +Semantics follow the model described in [P3780R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3780r0.html). The library keeps this separate trait for portability and because its internal optimizations rely on the "memcpy relocation" guarantee. ## Definition @@ -45,12 +45,6 @@ These specializations reflect empirical knowledge of the representations of comm ## Notes -### Relationship to `std::trivially_relocatable` - -C++26 `std::trivially_relocatable` may encompass additional nuances (it can be satisfied by types whose relocation uses compiler transformations beyond an as-if byte copy). A type for which this trait is `true` is always a good candidate to be `std::trivially_relocatable`, but the converse is not required by this library. - -### Customization Guidelines - You may provide additional specializations of `is_bitwise_trivially_relocatable` (in namespace `pro`) to opt in types you own. A correct specialization must ensure the type does not depend on its *address* remaining stable (self-pointers, intrusive container hooks, pointer provenance, etc.). A positive specialization is a promise you must uphold. Violating the contract results in undefined behavior in any operation that uses the fast relocation path (e.g., certain `proxy` conversions or assignments). diff --git a/docs/spec/msft_lib_proxy.md b/docs/spec/msft_lib_proxy.md index b3c8593..71612e6 100644 --- a/docs/spec/msft_lib_proxy.md +++ b/docs/spec/msft_lib_proxy.md @@ -10,6 +10,7 @@ Starting with 3.0.0, Proxy ships a feature-test macro that encodes the library v | Version | Value of `__msft_lib_proxy` | | ------- | --------------------------- | +| 4.0.2 | `202603L` | | 4.0.1 | `202510L` | | 4.0.0 | `202508L` | | 3.4.0 | `202505L` | diff --git a/docs/spec/proxy/assignment.md b/docs/spec/proxy/assignment.md index a96426f..d3558bc 100644 --- a/docs/spec/proxy/assignment.md +++ b/docs/spec/proxy/assignment.md @@ -38,7 +38,7 @@ Assigns a new value to `proxy` or destroys the contained value. - `(1)` Destroys the current contained value if it exists. After the call, `*this` does not contain a value. - `(2)` Copy assignment operator copies the contained value of `rhs` to `*this`. If `rhs` does not contain a value, it destroys the contained value of `*this` (if any) as if by `auto(rhs).swap(*this)`. The copy assignment is trivial when `F::copyability == constraint_level::trivial` is `true`. - `(3)` Move assignment operator moves the contained value of `rhs` to `*this`. If `rhs` does not contain a value, it destroys the contained value of `*this` (if any). If the move construction throws when `F::relocatability == constraint_level::nontrivial`, `*this` does not contain a value. After move assignment, `rhs` is in a valid state with an unspecified value. The move assignment operator does not participate in overload resolution when `F::copyability == constraint_level::trivial`, falling back to the trivial copy assignment operator. -- `(4)` Let `VP` be `std::decay_t

`. Sets the contained value to an object of type `VP`, direct-non-list-initialized with `std::forward

(ptr)`. This overload participates in overload resolution only if `std::decay_t

` is not a specialization of `proxy`. *Since 3.3.0*: If [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. +- `(4)` Let `VP` be `std::decay_t

`. Sets the contained value to an object of type `VP`, direct-non-list-initialized with `std::forward

(ptr)`. Participates in overload resolution only if `VP` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). *Since 3.3.0*: If [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and a diagnostic is generated. ## Return Value diff --git a/docs/spec/proxy/constructor.md b/docs/spec/proxy/constructor.md index 076e517..61f9182 100644 --- a/docs/spec/proxy/constructor.md +++ b/docs/spec/proxy/constructor.md @@ -44,9 +44,9 @@ Creates a new `proxy`. - `(1)` Default constructor and the constructor taking `nullptr` construct a `proxy` that does not contain a value. - `(2)` Copy constructor constructs a `proxy` whose contained value is that of `rhs` if `rhs` contains a value, or otherwise, constructs a `proxy` that does not contain a value. As per the `requires` clause, the copy constructor is trivial when `F::copyability == constraint_level::trivial`. - `(3)` Move constructor constructs a `proxy` whose contained value is that of `rhs` if `rhs` contains a value, or otherwise, constructs a `proxy` that does not contain a value. `rhs` is in a valid but unspecified state after move construction. As per the `requires` clause, the move constructor does not participate in overload resolution when `F::copyability == constraint_level::trivial`, so that a move construction falls back to the trivial copy constructor. -- `(4)` Let `VP` be `std::decay_t

`. Constructor taking a value of pointer constructs a `proxy` whose contained value is of type `VP` and direct-non-list-initialized with `std::forward

(ptr)`. This overload participates in overload resolution only if `std::decay_t

` is not a specialization of `proxy` nor a specialization of `std::in_place_type_t`. -- `(5)` Constructs a `proxy` whose contained value is of type `P` and direct-non-list-initialized with `std::forward(args)...`. -- `(6)` Constructs a `proxy` whose contained value is of type `P` and direct-non-list-initialized with `il, std::forward(args)...`. +- `(4)` Let `VP` be `std::decay_t

`. Constructs a `proxy` whose contained value is of type `VP`, direct-non-list-initialized with `std::forward

(ptr)`. Participates in overload resolution only if `VP` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). +- `(5)` Constructs a `proxy` whose contained value is of type `P`, direct-non-list-initialized with `std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. +- `(6)` Constructs a `proxy` whose contained value is of type `P`, direct-non-list-initialized with `il, std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. *Since 3.3.0*: For `(4-6)`, if [`proxiable, F>`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. diff --git a/docs/spec/proxy/emplace.md b/docs/spec/proxy/emplace.md index c5570b3..587c00c 100644 --- a/docs/spec/proxy/emplace.md +++ b/docs/spec/proxy/emplace.md @@ -23,8 +23,8 @@ The `emplace` function templates change the contained value to an object of type First, the current contained value (if any) is destroyed as if by calling [reset()](reset.md). Then: -- `(1)` Sets the contained value to an object of type `P` and direct-non-list-initialized with `std::forward(args)...`. -- `(2)` Sets the contained value to an object of type `P` and direct-non-list-initialized with `il, std::forward(args)...`. +- `(1)` Sets the contained value to an object of type `P`, direct-non-list-initialized with `std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). +- `(2)` Sets the contained value to an object of type `P`, direct-non-list-initialized with `il, std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. *Since 3.3.0*: For `(1-2)`, if [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. diff --git a/docs/spec/proxy/indirection.md b/docs/spec/proxy/indirection.md index 578a7d2..4bcdf1f 100644 --- a/docs/spec/proxy/indirection.md +++ b/docs/spec/proxy/indirection.md @@ -1,23 +1,15 @@ # `proxy::operator->`
`proxy::operator*` -The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant: - -```cpp -static constexpr bool has-indirection = see below; // exposition only -``` - -As per [`facade`](../facade.md), `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`. Let `Cs2` be the types in `Cs` where each type `C` meets the [*ProAccessible* requirements](../ProAccessible.md) of `F` and `C::is_direct` is `false`. *has-indirection* is `true` if `Cs2` contains at least one type; otherwise, it is `false`. *indirect-accessor* is a non-copyable type that inherits from every type in `Cs2`. - ```cpp // (1) -proxy_indirect_accessor* operator->() noexcept requires(has-indirection); -const proxy_indirect_accessor* operator->() const noexcept requires(has-indirection); +proxy_indirect_accessor* operator->() noexcept; +const proxy_indirect_accessor* operator->() const noexcept; // (2) -proxy_indirect_accessor& operator*() & noexcept requires(has-indirection); -const proxy_indirect_accessor& operator*() const& noexcept requires(has-indirection); -proxy_indirect_accessor&& operator*() && noexcept requires(has-indirection); -const proxy_indirect_accessor&& operator*() const&& noexcept requires(has-indirection); +proxy_indirect_accessor& operator*() & noexcept; +const proxy_indirect_accessor& operator*() const& noexcept; +proxy_indirect_accessor&& operator*() && noexcept; +const proxy_indirect_accessor&& operator*() const&& noexcept; ``` These operators access the accessors of the indirect conventions, as if dereferencing the contained value. diff --git a/docs/spec/proxy_view.md b/docs/spec/proxy_view.md index 3fb1c5a..2f81c5d 100644 --- a/docs/spec/proxy_view.md +++ b/docs/spec/proxy_view.md @@ -37,6 +37,7 @@ using proxy_view = proxy>; ## Example ```cpp +#include #include #include diff --git a/include/proxy/proxy.h b/include/proxy/proxy.h index b6eb09e..fa2b431 100644 --- a/include/proxy/proxy.h +++ b/include/proxy/proxy.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_PROXY_H_ diff --git a/include/proxy/proxy_fmt.h b/include/proxy/proxy_fmt.h index db6c792..5dbbbcd 100644 --- a/include/proxy/proxy_fmt.h +++ b/include/proxy/proxy_fmt.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_PROXY_FMT_H_ diff --git a/include/proxy/proxy_macros.h b/include/proxy/proxy_macros.h index 542ae68..cba1bdf 100644 --- a/include/proxy/proxy_macros.h +++ b/include/proxy/proxy_macros.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_PROXY_MACROS_H_ diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index fec34fd..ae9903f 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_V4_PROXY_H_ @@ -23,6 +24,10 @@ #if __has_include() #include #endif // __has_include() +#if __cpp_lib_format >= 201907L || \ + (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 170000) +#define PRO4D_HAS_FORMAT +#endif // __cpp_lib_format || _LIBCPP_VERSION >= 170000 #endif // __STDC_HOSTED__ #if __cpp_rtti >= 199711L @@ -46,12 +51,6 @@ #define PROD_UNREACHABLE() std::abort() #endif // __cpp_lib_unreachable >= 202202L -#if __cpp_trivial_relocatability >= 202502L -#define PROD_TR_IF_ELIGIBLE trivially_relocatable_if_eligible -#else -#define PROD_TR_IF_ELIGIBLE -#endif // __cpp_trivial_relocatability >= 202502L - namespace pro::inline v4 { // ============================================================================= @@ -630,21 +629,16 @@ struct refl_accessor_traits template using refl_accessor_t = typename refl_accessor_traits::type; -struct tr_blocker { - tr_blocker() = default; - tr_blocker(const tr_blocker&) noexcept {} - tr_blocker& operator=(const tr_blocker&) noexcept { return *this; } -}; - template struct ptr_traits : inapplicable_traits {}; template - requires(requires { typename std::pointer_traits

::element_type; }) + requires(( + requires { *std::declval(); } || + requires { typename P::element_type; }) && + requires { typename std::pointer_traits

::element_type; }) struct ptr_traits

: applicable_traits {}; template struct ptr_traits> : inapplicable_traits {}; -template <> -struct ptr_traits : inapplicable_traits {}; template consteval bool diagnose_proxiable_size_too_large() { @@ -680,6 +674,12 @@ consteval bool diagnose_proxiable_insufficient_destructibility() { return verdict; } +consteval bool is_layout_well_formed(std::size_t size, std::size_t align) { + return size > 0u && std::has_single_bit(align) && size % align == 0u; +} +consteval bool is_cl_well_formed(constraint_level cl) { + return cl >= constraint_level::none && cl <= constraint_level::trivial; +} template consteval bool is_facade_constraints_well_formed() { if constexpr (requires { @@ -693,8 +693,10 @@ consteval bool is_facade_constraints_well_formed() { return std::tuple{F::max_size, F::max_align, F::copyability, F::relocatability, F::destructibility}; })) { - return std::has_single_bit(F::max_align) && - F::max_size % F::max_align == 0u; + return is_layout_well_formed(F::max_size, F::max_align) && + is_cl_well_formed(F::copyability) && + is_cl_well_formed(F::relocatability) && + is_cl_well_formed(F::destructibility); } } return false; @@ -789,11 +791,9 @@ struct facade_traits using indirect_accessor = composite_t; - using direct_accessor = composite_t< - typename facade_traits::conv_direct_accessor, - typename facade_traits::refl_direct_accessor, - std::conditional_t>; + using direct_accessor = + composite_t; template static consteval void diagnose_proxiable() { @@ -914,6 +914,7 @@ add_qualifier_t, Q> template concept proxiable = facade && details::facade_traits::applicable && + details::ptr_traits

::applicable && details::facade_traits::template applicable_ptr

; template @@ -926,9 +927,8 @@ class proxy_indirect_accessor }; template -class proxy PROD_TR_IF_ELIGIBLE - : public details::facade_traits::direct_accessor, - public details::inplace_ptr> { +class proxy : public details::facade_traits::direct_accessor, + public details::inplace_ptr> { friend struct details::proxy_helper; static_assert(details::facade_traits::applicable); @@ -1289,10 +1289,26 @@ struct converter { template operator T() && noexcept( std::is_nothrow_invocable_r_v>) - requires(std::is_invocable_r_v>) + requires(std::is_invocable_r_v> && + !std::is_invocable_r_v> && + !std::is_invocable_r_v>) { return std::move(f_)(std::in_place_type); } + template + operator T&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } + template + operator T&&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } private: F f_; @@ -1551,9 +1567,8 @@ class indirect_ptr { }; template -class PRO4D_ENFORCE_EBO allocated_ptr PROD_TR_IF_ELIGIBLE - : private alloc_aware, - public indirect_ptr> { +class PRO4D_ENFORCE_EBO allocated_ptr : private alloc_aware, + public indirect_ptr> { public: template allocated_ptr(const Alloc& alloc, Args&&... args) @@ -1580,8 +1595,7 @@ struct PRO4D_ENFORCE_EBO compact_ptr_storage : alloc_aware, inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class compact_ptr PROD_TR_IF_ELIGIBLE - : public indirect_ptr> { +class compact_ptr : public indirect_ptr> { using Storage = compact_ptr_storage; public: @@ -1613,7 +1627,7 @@ struct PRO4D_ENFORCE_EBO shared_compact_ptr_storage inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class shared_compact_ptr PROD_TR_IF_ELIGIBLE +class shared_compact_ptr : public indirect_ptr> { using Storage = shared_compact_ptr_storage; @@ -1652,49 +1666,48 @@ struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base, template class weak_compact_ptr; template -class strong_compact_ptr PROD_TR_IF_ELIGIBLE - : public indirect_ptr> { +class strong_compact_ptr { using Storage = strong_weak_compact_ptr_storage; friend class weak_compact_ptr; public: using weak_type = weak_compact_ptr; - explicit strong_compact_ptr(Storage* ptr) noexcept - : indirect_ptr(ptr) {} + explicit strong_compact_ptr(Storage* ptr) noexcept : ptr_(ptr) {} template strong_compact_ptr(const Alloc& alloc, Args&&... args) - : indirect_ptr( - allocate(alloc, alloc, std::forward(args)...)) {} - strong_compact_ptr(const strong_compact_ptr& rhs) noexcept - : indirect_ptr(rhs.ptr_) { - this->ptr_->strong_count.fetch_add(1, std::memory_order::relaxed); + : ptr_(allocate(alloc, alloc, std::forward(args)...)) {} + strong_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { + ptr_->strong_count.fetch_add(1, std::memory_order::relaxed); } strong_compact_ptr(strong_compact_ptr&& rhs) = delete; ~strong_compact_ptr() noexcept(std::is_nothrow_destructible_v) { - if (this->ptr_->strong_count.fetch_sub(1, std::memory_order::acq_rel) == - 1) { + if (ptr_->strong_count.fetch_sub(1, std::memory_order::acq_rel) == 1) { std::destroy_at(operator->()); - if (this->ptr_->weak_count.fetch_sub(1u, std::memory_order::release) == - 1) { - deallocate(this->ptr_->alloc, this->ptr_); + if (ptr_->weak_count.fetch_sub(1u, std::memory_order::release) == 1) { + deallocate(ptr_->alloc, ptr_); } } } T* operator->() noexcept { - return std::launder(reinterpret_cast(&this->ptr_->value)); + return std::launder(reinterpret_cast(&ptr_->value)); } const T* operator->() const noexcept { - return std::launder(reinterpret_cast(&this->ptr_->value)); + return std::launder(reinterpret_cast(&ptr_->value)); } T& operator*() & noexcept { return *operator->(); } const T& operator*() const& noexcept { return *operator->(); } T&& operator*() && noexcept { return std::move(*operator->()); } const T&& operator*() const&& noexcept { return std::move(*operator->()); } + +private: + strong_weak_compact_ptr_storage* ptr_; }; template -class weak_compact_ptr PROD_TR_IF_ELIGIBLE { +class weak_compact_ptr { public: + using element_type = T; + weak_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { ptr_->weak_count.fetch_add(1, std::memory_order::relaxed); @@ -2044,22 +2057,25 @@ struct basic_facade_builder { details::merge_constraint(Destructibility, F::destructibility)>; template - requires(std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) + requires(details::is_layout_well_formed(PtrSize, PtrAlign)) using restrict_layout = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_copy = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_relocation = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_destruction = basic_facade_builder { template using weak_conversion_overload = weak_proxy() const noexcept; -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template struct format_overload_traits; template <> @@ -2171,7 +2187,7 @@ struct format_dispatch { return impl.format(self, fc); } }; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L struct proxy_cast_context { @@ -2278,7 +2294,7 @@ struct proxy_typeid_reflector { namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template using format = typename FB::template add_convention using wformat = typename FB::template add_convention>; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L template @@ -2347,14 +2363,29 @@ struct sign { template sign(const char (&str)[N]) -> sign; -struct wildcard { - wildcard() = delete; +// When std::reference_constructs_from_temporary_v (C++23) is not available, we +// fall back to a conservative approximation that disallows binding a temporary +// to a reference type if the source type is not a reference or if the source +// and target reference types are not compatible. +template +concept explicitly_convertible = + std::is_constructible_v && +#if __cpp_lib_reference_from_temporary >= 202202L + !std::reference_constructs_from_temporary_v; +#else + (!std::is_reference_v || + (std::is_reference_v && + std::is_convertible_v>, + std::add_pointer_t>>)); +#endif // __cpp_lib_reference_from_temporary >= 202202L +struct noreturn_conversion { template - [[noreturn]] operator T() const { + [[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t) { PROD_UNREACHABLE(); } }; +using wildcard = converter; } // namespace details @@ -2461,7 +2492,7 @@ struct operator_dispatch; #define PROD_DEF_RHS_ASSIGNMENT_OP_ACCESSOR(oq, pq, ne, ...) \ template \ struct accessor { \ - friend Arg& operator __VA_ARGS__(Arg & arg, P pq self) ne { \ + friend Arg& operator __VA_ARGS__(Arg& arg, P pq self) ne { \ proxy_invoke(static_cast

(self), arg); \ return arg; \ } \ @@ -2578,9 +2609,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base { PRO4D_STATIC_CALL(auto, T&& self) noexcept { return details::converter{ [&self](std::in_place_type_t) noexcept( - std::is_nothrow_constructible_v) - requires(std::is_constructible_v) - { return U{std::forward(self)}; }}; + std::is_nothrow_constructible_v) -> U + requires(details::explicitly_convertible < T &&, U >) + { return static_cast(std::forward(self)); }}; } }; using conversion_dispatch = explicit_conversion_dispatch; @@ -2609,7 +2640,7 @@ struct weak_dispatch : D { // == Adapters (std::formatter) == // ============================================================================= -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT namespace std { template @@ -2640,9 +2671,8 @@ struct formatter, CharT> { }; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT -#undef PROD_TR_IF_ELIGIBLE #undef PROD_UNREACHABLE #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE diff --git a/include/proxy/v4/proxy.ixx b/include/proxy/v4/proxy.ixx index f1ad598..37fb981 100644 --- a/include/proxy/v4/proxy.ixx +++ b/include/proxy/v4/proxy.ixx @@ -41,10 +41,10 @@ using v4::weak_proxy; namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT using skills::format; using skills::wformat; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L using skills::direct_rtti; @@ -60,10 +60,10 @@ using skills::slim; } // namespace pro::inline v4 -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT export namespace std { using std::formatter; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT diff --git a/include/proxy/v4/proxy_fmt.h b/include/proxy/v4/proxy_fmt.h index 7d2c483..378310c 100644 --- a/include/proxy/v4/proxy_fmt.h +++ b/include/proxy/v4/proxy_fmt.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_V4_PROXY_FMT_H_ @@ -11,31 +12,25 @@ #error Please ensure that proxy.h is included before proxy_fmt.h. #endif // __msft_lib_proxy4 -#if FMT_VERSION >= 60100 -static_assert(fmt::is_char::value, - "The {fmt} library must have wchar_t support enabled. " - "Include fmt/xchar.h before including proxy_fmt.h."); -#else +#if FMT_VERSION < 60100 #error Please ensure that the appropriate {fmt} headers (version 6.1.0 or \ later) are included before proxy_fmt.h. -#endif // FMT_VERSION >= 60100 +#endif // FMT_VERSION < 60100 namespace pro::inline v4 { namespace details { template -struct fmt_format_overload_traits; -template <> -struct fmt_format_overload_traits - : std::type_identity {}; -template <> -struct fmt_format_overload_traits - : std::type_identity {}; +#if FMT_VERSION >= 110000 +using fmt_buffered_context = fmt::buffered_context; +#else +using fmt_buffered_context = fmt::buffer_context; +#endif // FMT_VERSION + template -using fmt_format_overload_t = typename fmt_format_overload_traits::type; +using fmt_format_overload_t = fmt_buffered_context::iterator( + std::basic_string_view spec, fmt_buffered_context& fc) const; struct fmt_format_dispatch { template diff --git a/include/proxy/v4/proxy_macros.h b/include/proxy/v4/proxy_macros.h index c21c80a..74ad2f4 100644 --- a/include/proxy/v4/proxy_macros.h +++ b/include/proxy/v4/proxy_macros.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef MSFT_PROXY_V4_PROXY_MACROS_H_ @@ -28,7 +29,7 @@ #define PRO4D_DEBUG(...) __VA_ARGS__ #endif // NDEBUG -#define __msft_lib_proxy4 202510L +#define __msft_lib_proxy4 202603L #define PRO4D_DIRECT_FUNC_IMPL(...) \ noexcept(noexcept(__VA_ARGS__)) \ @@ -141,7 +142,7 @@ PRO4D_STATIC_CALL(decltype(auto), ProT&& pro_self, ProArgs&&... pro_args) \ PRO4D_DIRECT_FUNC_IMPL(impl(::std::forward(pro_self), \ ::std::forward(pro_args)...)) \ - PRO4D_DEF_ACCESSOR_TEMPLATE(FREE, PRO4D_DEF_MEM_ACCESSOR, func) \ + PRO4D_DEF_ACCESSOR_TEMPLATE(MEM, PRO4D_DEF_MEM_ACCESSOR, func) \ } #define PRO4D_DEF_FREE_AS_MEM_DISPATCH_2(name, impl) \ PRO4D_DEF_FREE_AS_MEM_DISPATCH_IMPL(name, impl, impl) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..5905e86 --- /dev/null +++ b/meson.build @@ -0,0 +1,118 @@ +project( + 'msft_proxy4', + 'cpp', + version: '4.0.2', + license: 'MIT', + license_files: 'LICENSE', + meson_version: '>=1.3', + default_options: { + 'cpp_std': ['vc++latest', 'c++26', 'c++23', 'c++20'], + 'warning_level': '3', + }, +) + +cxx = meson.get_compiler('cpp') +if cxx.compute_int('__has_cpp_attribute(msvc::no_unique_address)') != 0 and not cxx.compiles( + ''' + struct A {}; + + struct B { + [[msvc::no_unique_address]] A val; + }; + + struct C : B { + char x; + }; + + static_assert(sizeof(C) == 1); + ''', + name: 'msvc::no_unique_address behaves correctly', +) + error( + 'unsupported compiler:', + 'https://github.com/llvm/llvm-project/issues/143245', + ) +endif +if cxx.get_argument_syntax() == 'msvc' + add_project_arguments( + '/bigobj', + language: 'cpp', + ) +endif + +inc = include_directories('include') + +hdrs = files( + 'include/proxy/proxy.h', + 'include/proxy/proxy_fmt.h', + 'include/proxy/proxy_macros.h', +) +hdrs_v4 = files( + 'include/proxy/v4/proxy.h', + 'include/proxy/v4/proxy_fmt.h', + 'include/proxy/v4/proxy_macros.h', +) +hdrs_mod = files('include/proxy/v4/proxy.ixx') + +msft_proxy4_dep = declare_dependency( + sources: hdrs + hdrs_v4, + include_directories: inc, +) + +install_headers( + hdrs, + subdir: 'proxy', +) +install_headers( + hdrs_v4, + subdir: 'proxy/v4', +) +install_headers( + hdrs_mod, + subdir: 'proxy/v4', +) +pkgconfig = import( + 'pkgconfig', + required: false, +) +if pkgconfig.found() + pkgconfig.generate( + name: meson.project_name(), + description: 'Next Generation Polymorphism in C++', + url: 'https://ngcpp.github.io/proxy/', + ) +endif + +meson.override_dependency(meson.project_name(), msft_proxy4_dep) + +tests = get_option('tests').disable_auto_if(meson.is_subproject()) +gtest_dep = dependency( + 'gtest_main', + main: true, + required: tests, +) +fmt_dep = dependency( + 'fmt', + version: '>=6.1.0', + required: false, + disabler: true, +) +subdir( + 'tests', + if_found: gtest_dep, +) + +benchmarks = get_option('benchmarks').disable_auto_if(meson.is_subproject()) +benchmark_dep = dependency( + 'benchmark_main', + required: benchmarks, +) +subdir( + 'benchmarks', + if_found: benchmark_dep, +) + +examples = get_option('examples').disable_auto_if(meson.is_subproject()) +if examples.allowed() + subdir('docs') +endif diff --git a/meson.format b/meson.format new file mode 100644 index 0000000..66e2f72 --- /dev/null +++ b/meson.format @@ -0,0 +1,4 @@ +indent_by=' ' +end_of_line=lf +insert_final_newline=true +kwargs_force_multiline=true diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..584693a --- /dev/null +++ b/meson.options @@ -0,0 +1,18 @@ +option( + 'tests', + type: 'feature', + value: 'auto', + description: 'Build tests', +) +option( + 'benchmarks', + type: 'feature', + value: 'auto', + description: 'Build benchmarks', +) +option( + 'examples', + type: 'feature', + value: 'auto', + description: 'Extract and build examples from docs', +) diff --git a/mkdocs.yml b/mkdocs.yml index 3923ea9..556f54a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,9 @@ --- site_name: Proxy 4 -site_url: https://microsoft.github.io/proxy +site_url: https://ngcpp.github.io/proxy site_description: Next Generation Polymorphism in C++ -copyright: Copyright (c) Microsoft Corporation -repo_url: https://github.com/microsoft/proxy/ +copyright: Copyright (c) 2022-2026 Microsoft Corporation
Copyright (c) 2026-Present Next Gen C++ Foundation +repo_url: https://github.com/ngcpp/proxy/ edit_uri: "" theme: diff --git a/mkdocs/overrides/main.html b/mkdocs/overrides/main.html index 5e52e2c..86d8a73 100644 --- a/mkdocs/overrides/main.html +++ b/mkdocs/overrides/main.html @@ -6,6 +6,6 @@ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); - })(window, document, "clarity", "script", "roby7oe11n"); + })(window, document, "clarity", "script", "v8vxusocp2"); {% endblock %} diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap new file mode 100644 index 0000000..6544857 --- /dev/null +++ b/subprojects/fmt.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = fmt-12.1.0 +source_url = https://github.com/fmtlib/fmt/archive/12.1.0.tar.gz +source_filename = fmt-12.1.0.tar.gz +source_hash = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea +source_fallback_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt-12.1.0.tar.gz +patch_filename = fmt_12.1.0-4_patch.zip +patch_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt_12.1.0-4_patch.zip +patch_hash = 65b7fe3c29f25528011bc295e83e4f6f10028c922407e003b7856bb79789f345 +3rdparty_wrapdb_version = 12.1.0-4 + +[provide] +dependency_names = fmt diff --git a/subprojects/google-benchmark.wrap b/subprojects/google-benchmark.wrap new file mode 100644 index 0000000..7e2e6e7 --- /dev/null +++ b/subprojects/google-benchmark.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = benchmark-1.8.4 +source_url = https://github.com/google/benchmark/archive/refs/tags/v1.8.4.tar.gz +source_filename = benchmark-1.8.4.tar.gz +source_hash = 3e7059b6b11fb1bbe28e33e02519398ca94c1818874ebed18e504dc6f709be45 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/google-benchmark_1.8.4-5/benchmark-1.8.4.tar.gz +patch_filename = google-benchmark_1.8.4-5_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/google-benchmark_1.8.4-5/get_patch +patch_hash = 671ffed65f1e95e8c20edb7a06eb54476797e58169160b255f52dc71f4b83957 +wrapdb_version = 1.8.4-5 + +[provide] +dependency_names = benchmark, benchmark_main diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 0000000..9902a4f --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,16 @@ +[wrap-file] +directory = googletest-1.17.0 +source_url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz +source_filename = googletest-1.17.0.tar.gz +source_hash = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c +patch_filename = gtest_1.17.0-4_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-4/get_patch +patch_hash = 3abf7662d09db706453a5b064a1e914678c74b9d9b0b19382747ca561d0d8750 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-4/googletest-1.17.0.tar.gz +wrapdb_version = 1.17.0-4 + +[provide] +gtest = gtest_dep +gtest_main = gtest_main_dep +gmock = gmock_dep +gmock_main = gmock_main_dep diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 775e958..ff508a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,31 +1,18 @@ project(msft_proxy_tests) -include(FetchContent) -# The policy uses the download time for timestamp, instead of the timestamp in the archive. This -# allows for proper rebuilds when a projects URL changes. -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - FetchContent_Declare( googletest - URL https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz - URL_HASH SHA256=7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926 + URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz + URL_HASH SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # For Windows: Prevent overriding the parent project's compiler/linker settings set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # Disable GMock FetchContent_MakeAvailable(googletest) include(GoogleTest) -FetchContent_Declare( - fmt - URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz - URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e -) -FetchContent_MakeAvailable(fmt) - add_executable(msft_proxy_tests proxy_creation_tests.cpp + proxy_details_tests.cpp proxy_dispatch_tests.cpp proxy_fmt_format_tests.cpp proxy_format_tests.cpp diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..fbf36f2 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,67 @@ +test_srcs = files( + 'proxy_creation_tests.cpp', + 'proxy_dispatch_tests.cpp', + 'proxy_format_tests.cpp', + 'proxy_integration_tests.cpp', + 'proxy_invocation_tests.cpp', + 'proxy_lifetime_tests.cpp', + 'proxy_reflection_tests.cpp', + 'proxy_regression_tests.cpp', + 'proxy_rtti_tests.cpp', + 'proxy_traits_tests.cpp', + 'proxy_view_tests.cpp', +) + +test_deps = [msft_proxy4_dep, gtest_dep] + +if fmt_dep.found() + test_srcs += files( + 'proxy_fmt_format_tests.cpp', + ) + test_deps += fmt_dep +endif + +test( + 'ProxyTests', + executable( + 'msft_proxy_tests', + test_srcs, + implicit_include_directories: false, + dependencies: test_deps, + build_by_default: false, + ), + protocol: 'gtest', +) + +freestanding_cflags = ['-ffreestanding'] +freestanding_ldflags = ['-nodefaultlibs'] + +if cxx.has_multi_arguments( + freestanding_cflags, + freestanding_ldflags, + required: false, +) + libc_dep = cxx.find_library( + 'c', + required: false, + ) + + if libc_dep.found() + test( + 'ProxyFreestandingSupportTests', + executable( + 'msft_proxy_freestanding_tests', + files('freestanding/proxy_freestanding_tests.cpp'), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, libc_dep], + cpp_args: freestanding_cflags + freestanding_ldflags, + link_args: get_option('b_sanitize') == 'none' ? freestanding_ldflags : [], + override_options: { + 'cpp_eh': 'none', + 'cpp_rtti': false, + }, + build_by_default: false, + ), + ) + endif +endif diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 8c03e8b..d88dc24 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_details_tests.cpp b/tests/proxy_details_tests.cpp new file mode 100644 index 0000000..a10cd4f --- /dev/null +++ b/tests/proxy_details_tests.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2026-Present Next Gen C++ Foundation. +// Licensed under the MIT License. + +#include + +namespace proxy_details_tests_details { + +struct Base { + int v; +}; +struct Derived : Base {}; + +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert( + pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); + +} // namespace proxy_details_tests_details diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index e2a71aa..1dde852 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include @@ -783,11 +784,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) { TEST(ProxyDispatchTests, TestIndirectConversion) { struct TestFacade - : pro::facade_builder::add_convention::build {}; + : pro::facade_builder // + ::add_convention // + ::build {}; short v = 123; pro::proxy p = &v; - ASSERT_EQ(static_cast(*p), 123); + static_cast(*p) = 456; + ASSERT_EQ(static_cast(*p), 456); } TEST(ProxyDispatchTests, TestDirectConversion) { @@ -829,6 +832,7 @@ TEST(ProxyDispatchTests, TestFreeAsMemDispatch) { } TEST(ProxyDispatchTests, TestSubstitutionDispatch) { +#ifdef PRO4D_HAS_FORMAT struct Base : pro::facade_builder // ::add_skill // ::build {}; @@ -844,4 +848,7 @@ TEST(ProxyDispatchTests, TestSubstitutionDispatch) { pro::proxy p3 = std::move(p1); ASSERT_FALSE(p1.has_value()); ASSERT_EQ(std::format("{}", *p3), "123"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_fmt_format_tests.cpp b/tests/proxy_fmt_format_tests.cpp index 74293f9..7a4a93e 100644 --- a/tests/proxy_fmt_format_tests.cpp +++ b/tests/proxy_fmt_format_tests.cpp @@ -1,21 +1,11 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include -#if defined(__NVCOMPILER) -#pragma diagnostic push -#pragma diag_suppress inline_gnu_noinline_conflict -#pragma diagnostic push -#pragma diag_suppress code_is_unreachable -#endif // defined(__NVCOMPILER) #include #include -#if defined(__NVCOMPILER) -#pragma diagnostic pop -#pragma diagnostic pop -#endif // defined(__NVCOMPILER) - #include #include diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp index 7b0d224..848cef0 100644 --- a/tests/proxy_format_tests.cpp +++ b/tests/proxy_format_tests.cpp @@ -1,9 +1,11 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include #include +#ifdef PRO4D_HAS_FORMAT namespace proxy_format_tests_details { struct NonFormattable : pro::facade_builder::build {}; @@ -29,17 +31,26 @@ static_assert( } // namespace proxy_format_tests_details namespace details = proxy_format_tests_details; +#endif // PRO4D_HAS_FORMAT TEST(ProxyFormatTests, TestFormat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format("{}", *p), "123"); ASSERT_EQ(std::format("{:*<6}", *p), "123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } TEST(ProxyFormatTests, TestWformat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format(L"{}", *p), L"123"); ASSERT_EQ(std::format(L"{:*<6}", *p), L"123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index a9a6188..d54d415 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 6664720..69eacd0 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include @@ -161,6 +162,9 @@ std::string Dump(T&& value) noexcept { PRO_DEF_FREE_DISPATCH(FreeDump, Dump); +PRO_DEF_FREE_DISPATCH(FreeInvoke, std::invoke, Invoke); +PRO_DEF_FREE_AS_MEM_DISPATCH(MemInvoke, std::invoke, Invoke); + } // namespace proxy_invocation_tests_details namespace details = proxy_invocation_tests_details; @@ -247,7 +251,7 @@ TEST(ProxyInvocationTests, TestRecursiveDefinition) { ASSERT_EQ(sum, 21); } -TEST(ProxyInvocationTests, TestOverloadResolution) { +TEST(ProxyInvocationTests, TestOverloadResolution_Member) { struct OverloadedCallable : pro::facade_builder // ::add_convention, void(int), void(double), @@ -272,6 +276,54 @@ TEST(ProxyInvocationTests, TestOverloadResolution) { ASSERT_FALSE((std::is_invocable_v>)); } +TEST(ProxyInvocationTests, TestOverloadResolution_Free) { + struct OverloadedCallable + : pro::facade_builder // + ::add_convention // + ::build {}; + std::vector side_effect; + auto p = pro::make_proxy([&](auto&&... args) { + side_effect = details::GetTypeIndices...>(); + }); + Invoke(*p, 123); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, 1.23); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + char foo[2]; + Invoke(*p, foo); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, "lalala"); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, "lalala", 0); + ASSERT_EQ(side_effect, (details::GetTypeIndices())); +} + +TEST(ProxyInvocationTests, TestOverloadResolution_FreeAsMem) { + struct OverloadedInvocable + : pro::facade_builder // + ::add_convention // + ::build {}; + std::vector side_effect; + auto p = pro::make_proxy([&](auto&&... args) { + side_effect = details::GetTypeIndices...>(); + }); + p->Invoke(123); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke(1.23); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + char foo[2]; + p->Invoke(foo); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke("lalala"); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke("lalala", 0); + ASSERT_EQ(side_effect, (details::GetTypeIndices())); +} + TEST(ProxyInvocationTests, TestNoexcept) { std::vector side_effect; auto p = pro::make_proxy>( diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 0a338a0..24d5e49 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 2041e9a..ab24402 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" @@ -17,7 +18,8 @@ struct TraitsReflector { is_copy_constructible_(std::is_copy_constructible_v), is_nothrow_move_constructible_(std::is_nothrow_move_constructible_v), is_nothrow_destructible_(std::is_nothrow_destructible_v), - is_trivial_(std::is_trivial_v) {} + is_trivial_(std::is_trivially_default_constructible_v && + std::is_trivially_copyable_v) {} template struct accessor { diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 5ab2aa3..d338908 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -1,8 +1,17 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning( \ + disable : 4702) // False alarm from MSVC: warning C4702: unreachable code +#endif // defined(_MSC_VER) && !defined(__clang__) #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__clang__) #include namespace proxy_regression_tests_details { @@ -39,6 +48,8 @@ struct Range : pro::facade_builder // ::template add_convention>()> // ::build {}; +PRO_DEF_MEM_DISPATCH(MemFun, Fun); + } // namespace proxy_regression_tests_details namespace details = proxy_regression_tests_details; @@ -69,3 +80,16 @@ TEST(ProxyRegressionTests, TestProxiableSelfDependency) { } EXPECT_EQ(expected, original); } + +// https://github.com/ngcpp/proxy/issues/10 +TEST(ProxyRegressionTests, TestWeakDispatchReferenceReturningOverload) { + struct MyFacade + : pro::facade_builder // + ::add_convention, int&()> // + ::build {}; + static_assert(pro::proxiable); + + int v = 123; + pro::proxy p = &v; + EXPECT_THROW(p->Fun(), pro::not_implemented); +} diff --git a/tests/proxy_rtti_tests.cpp b/tests/proxy_rtti_tests.cpp index 814c832..1545939 100644 --- a/tests/proxy_rtti_tests.cpp +++ b/tests/proxy_rtti_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index 0527f50..0f092e6 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" @@ -325,28 +326,61 @@ struct BadFacade_BadConstraints_UnexpectedType { }; static_assert(!pro::facade); -struct BadFacade_BadConstraints_BadAlignment { - using convention_types = std::tuple<>; - using reflection_types = std::tuple<>; - static constexpr std::size_t max_size = 6u; - static constexpr std::size_t max_align = 6u; // Should be a power of 2 - static constexpr auto copyability = pro::constraint_level::none; - static constexpr auto relocatability = pro::constraint_level::nothrow; - static constexpr auto destructibility = pro::constraint_level::nothrow; -}; -static_assert(!pro::facade); +// Well-formed facade_impl specialization +static_assert( + pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); -struct BadFacade_BadConstraints_BadSize { - using convention_types = std::tuple<>; - using reflection_types = std::tuple<>; - static constexpr std::size_t max_size = - 6u; // Should be a multiple of max_alignment - static constexpr std::size_t max_align = 4u; - static constexpr auto copyability = pro::constraint_level::none; - static constexpr auto relocatability = pro::constraint_level::nothrow; - static constexpr auto destructibility = pro::constraint_level::nothrow; -}; -static_assert(!pro::facade); +// Bad size (max_size should be positive) +static_assert( + !pro::facade, std::tuple<>, 0, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad size (max_size should be a multiple of max_align) +static_assert( + !pro::facade, std::tuple<>, 10, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad alignment (max_align should be a power of 2) +static_assert( + !pro::facade, std::tuple<>, 6, 6, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad copyability (less than constraint_level::none) +static_assert( + !pro::facade, std::tuple<>, 8, 4, (pro::constraint_level)-1, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad copyability (greater than constraint_level::trivial) +static_assert( + !pro::facade, std::tuple<>, 8, 4, (pro::constraint_level)100, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad relocatability (less than constraint_level::none) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + (pro::constraint_level)-1, pro::constraint_level::nothrow>>); + +// Bad relocatability (greater than constraint_level::trivial) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + (pro::constraint_level)100, pro::constraint_level::nothrow>>); + +// Bad destructibility (less than constraint_level::none) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, (pro::constraint_level)-1>>); + +// Bad destructibility (greater than constraint_level::trivial) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, (pro::constraint_level)100>>); struct BadFacade_BadConstraints_NotConstant { using convention_types = std::tuple<>; @@ -402,6 +436,43 @@ static_assert(pro::facade); static_assert(FacadeWithSizeOfNonPowerOfTwo::max_size == 6u); static_assert(FacadeWithSizeOfNonPowerOfTwo::max_align == 2u); +template +concept IsFacadeBuilderWellFormedWithGivenLayout = + requires { typename pro::facade_builder::restrict_layout; }; +static_assert(IsFacadeBuilderWellFormedWithGivenLayout<6u, 1u>); +static_assert(!IsFacadeBuilderWellFormedWithGivenLayout<6u, 3u>); +static_assert(!IsFacadeBuilderWellFormedWithGivenLayout<1u, 2u>); + +template +concept IsFacadeBuilderWellFormedWithGivenCopyability = + requires { typename pro::facade_builder::support_copy; }; +static_assert( + IsFacadeBuilderWellFormedWithGivenCopyability); +static_assert( + !IsFacadeBuilderWellFormedWithGivenCopyability<(pro::constraint_level)-1>); +static_assert( + !IsFacadeBuilderWellFormedWithGivenCopyability<(pro::constraint_level)100>); + +template +concept IsFacadeBuilderWellFormedWithGivenRelocatability = + requires { typename pro::facade_builder::support_relocation; }; +static_assert(IsFacadeBuilderWellFormedWithGivenRelocatability< + pro::constraint_level::none>); +static_assert(!IsFacadeBuilderWellFormedWithGivenRelocatability< + (pro::constraint_level)-1>); +static_assert(!IsFacadeBuilderWellFormedWithGivenRelocatability< + (pro::constraint_level)100>); + +template +concept IsFacadeBuilderWellFormedWithGivenDestructibility = + requires { typename pro::facade_builder::support_destruction; }; +static_assert(IsFacadeBuilderWellFormedWithGivenDestructibility< + pro::constraint_level::none>); +static_assert(!IsFacadeBuilderWellFormedWithGivenDestructibility< + (pro::constraint_level)-1>); +static_assert(!IsFacadeBuilderWellFormedWithGivenDestructibility< + (pro::constraint_level)100>); + static_assert(!std::is_default_constructible_v< pro::proxy_indirect_accessor>); static_assert( @@ -413,4 +484,18 @@ static_assert( static_assert( !std::is_move_assignable_v>); +// proxy shall not be constructible from an arbitrary class template +// instantiation. See https://github.com/microsoft/proxy/issues/366 +template +struct ProxyWrapperTemplate { + explicit ProxyWrapperTemplate(pro::proxy p) + : p_(std::move(p)) {} + + pro::proxy p_; +}; +static_assert(!pro::proxiable); +static_assert(!std::is_constructible_v, + ProxyWrapperTemplate>); +static_assert(std::is_move_constructible_v>); + } // namespace proxy_traits_tests_details diff --git a/tests/proxy_view_tests.cpp b/tests/proxy_view_tests.cpp index 97dd1c9..f47f5b0 100644 --- a/tests/proxy_view_tests.cpp +++ b/tests/proxy_view_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/utils.h b/tests/utils.h index f1835d2..000b356 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef _MSFT_PROXY_TEST_UTILS_ diff --git a/tools/extract_example_code_from_docs.py b/tools/extract_example_code_from_docs.py index 405c1dc..becbb94 100644 --- a/tools/extract_example_code_from_docs.py +++ b/tools/extract_example_code_from_docs.py @@ -1,42 +1,79 @@ -import os +#!/usr/bin/env python3 +# pyright: strict + import re -import sys +from typing import Optional +from pathlib import Path + +EXAMPLE_PATTERN = re.compile(r"## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```", re.DOTALL) + -def extract_cpp_code(md_path, cpp_path): - with open(md_path, 'r', encoding='utf-8') as f: +def extract_cpp_code(md_path: Path) -> Optional[str]: + with open(md_path, "r", encoding="utf-8") as f: content = f.read() - pattern = r'## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```' - code_blocks = re.findall(pattern, content, re.DOTALL) + code_blocks: list[str] = re.findall(EXAMPLE_PATTERN, content) if len(code_blocks) == 0: return # No match, skip elif len(code_blocks) > 1: - raise ValueError(f"File '{md_path}' contains more than one '## Example' C++ code block.") + msg = f"File '{md_path}' contains more than one '## Example' C++ code block." + raise ValueError(msg) cpp_code = code_blocks[0] - header = f"// This file was auto-generated from:\n// {md_path}\n\n" + header = f""" +// This file was auto-generated from: +// {md_path} + +""".lstrip() + + if "pro::skills::format" in cpp_code: + cpp_code = f""" +#include +#ifdef PRO4D_HAS_FORMAT +{cpp_code} +#else +int main() {{ + // std::format not available + return 77; +}} +#endif +""".strip() - with open(cpp_path, 'w', encoding='utf-8') as out: - out.write(header) - out.write(cpp_code) + return header + cpp_code -def main(): - if len(sys.argv) != 3: - print("Usage: python extract_example_code_from_docs.py ") - sys.exit(1) - input_dir = sys.argv[1] - output_dir = sys.argv[2] +def main() -> None: + import argparse + import os + from dataclasses import dataclass + + @dataclass(frozen=True) + class Args: + input_dir: Path + output_dir: Path + + parser = argparse.ArgumentParser() + _ = parser.add_argument("input_dir", type=Path, help="Path to Markdown documents") + _ = parser.add_argument("output_dir", type=Path, help="Source code output path") + args = parser.parse_args(namespace=Args) + + input_dir = args.input_dir + output_dir = args.output_dir for root, _, files in os.walk(input_dir): for file in files: - if file.endswith('.md'): - md_path = os.path.join(root, file) - rel_path = os.path.relpath(md_path, input_dir) - rel_base = os.path.splitext(rel_path)[0].replace(os.sep, '_') - cpp_path = os.path.join(output_dir, f"example_{rel_base}.cpp") - extract_cpp_code(md_path, cpp_path) - -if __name__ == '__main__': + if file.endswith(".md"): + md_path = Path(root) / file + rel_path = md_path.relative_to(input_dir) + rel_base = "_".join([*rel_path.parent.parts, rel_path.stem]) + cpp_path = output_dir / f"example_{rel_base}.cpp" + cpp_code = extract_cpp_code(md_path) + if cpp_code is None: + continue + with open(cpp_path, "w", encoding="utf-8") as f: + _ = f.write(cpp_code) + + +if __name__ == "__main__": main() diff --git a/tools/meson_extract_doctest.py b/tools/meson_extract_doctest.py new file mode 100644 index 0000000..63bfc46 --- /dev/null +++ b/tools/meson_extract_doctest.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# pyright: strict + +import sys +from pathlib import Path + +from extract_example_code_from_docs import extract_cpp_code + +infile = Path(sys.argv[1]) +if infile.is_dir(): + for path in infile.glob("**/*.md"): + if extract_cpp_code(path) is None: + continue + print(str(path.relative_to(infile).as_posix()), end="\0") + exit() + +outfile = Path(sys.argv[2]) + +cpp_code = extract_cpp_code(infile) + +if not cpp_code: + raise ValueError + +with open(outfile, "w") as f: + _ = f.write(cpp_code) diff --git a/tools/report_generator/CMakeLists.txt b/tools/report_generator/CMakeLists.txt index bd99f38..63eda32 100644 --- a/tools/report_generator/CMakeLists.txt +++ b/tools/report_generator/CMakeLists.txt @@ -11,8 +11,8 @@ endif() FetchContent_Declare( nlohmann_json - URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz - URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d + URL https://github.com/nlohmann/json/archive/refs/tags/v3.12.0.tar.gz + URL_HASH SHA256=4b92eb0c06d10683f7447ce9406cb97cd4b453be18d7279320f7b2f025c10187 ) FetchContent_MakeAvailable(nlohmann_json) diff --git a/tools/report_generator/main.cpp b/tools/report_generator/main.cpp index af2820a..a83dcde 100644 --- a/tools/report_generator/main.cpp +++ b/tools/report_generator/main.cpp @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include @@ -6,8 +7,8 @@ #include #include #include -#include #include +#include #include #include @@ -76,12 +77,32 @@ struct Metric { } }; +struct MetricGroup { + std::string Name; + std::string Description; + std::vector Metrics; + + friend void to_json(nlohmann::json& j, const MetricGroup& m) { + j = nlohmann::json{ + {"Name", m.Name}, + {"Description", m.Description}, + {"Metrics", m.Metrics}, + }; + } + + friend void from_json(const nlohmann::json& j, MetricGroup& m) { + j.at("Name").get_to(m.Name); + j.at("Description").get_to(m.Description); + j.at("Metrics").get_to(m.Metrics); + } +}; + struct ReportConfig { std::string TargetName; double YellowIndicatorThreshold; std::string OutputPath; std::vector Environments; - std::vector Metrics; + std::vector MetricGroups; friend void to_json(nlohmann::json& j, const ReportConfig& rc) { j = nlohmann::json{ @@ -89,7 +110,7 @@ struct ReportConfig { {"YellowIndicatorThreshold", rc.YellowIndicatorThreshold}, {"OutputPath", rc.OutputPath}, {"Environments", rc.Environments}, - {"Metrics", rc.Metrics}, + {"MetricGroups", rc.MetricGroups}, }; } @@ -98,7 +119,7 @@ struct ReportConfig { j.at("YellowIndicatorThreshold").get_to(rc.YellowIndicatorThreshold); j.at("OutputPath").get_to(rc.OutputPath); j.at("Environments").get_to(rc.Environments); - j.at("Metrics").get_to(rc.Metrics); + j.at("MetricGroups").get_to(rc.MetricGroups); } }; @@ -113,7 +134,8 @@ EnvironmentInfo ParseEnvironmentInfo(const std::filesystem::path& file) { return obj.get(); } -std::unordered_map ParseBenchmarkingResults(const std::filesystem::path& file) { +std::unordered_map + ParseBenchmarkingResults(const std::filesystem::path& file) { nlohmann::json obj; { std::ifstream in; @@ -146,56 +168,66 @@ void GenerateReport(const std::filesystem::path& config_path) { std::vector> benchmarks; benchmarks.reserve(config.Environments.size()); for (auto& environment : config.Environments) { - benchmarks.push_back(ParseBenchmarkingResults(environment.BenchmarkingResultsPath)); + benchmarks.push_back( + ParseBenchmarkingResults(environment.BenchmarkingResultsPath)); } std::ofstream out; out.exceptions(std::ios_base::failbit | std::ios_base::badbit); - out.open(config.OutputPath, std::ios_base::out | std::ios_base::app | std::ios_base::binary); - out << "| |"; - for (auto& environment : config.Environments) { - out << " " << environment.Description << " |"; - } - out << "\n"; - out << "| - |"; - for (std::size_t i = 0; i < config.Environments.size(); ++i) { - out << " - |"; - } - out << "\n"; - for (auto& metric : config.Metrics) { - out << "| " << metric.Name << " |"; - for (auto& benchmark : benchmarks) { - double target = benchmark.at(metric.TargetBenchmarkName); - double baseline = benchmark.at(metric.BaselineBenchmarkName); - double rate = (baseline - target) * 100 / target; - bool is_negative = rate < 0; - if (is_negative) { - rate = -rate; - } - out << " "; - if (rate < config.YellowIndicatorThreshold) { - out << "\xf0\x9f\x9f\xa1"; // Yellow circle - } else if (is_negative) { - out << "\xf0\x9f\x94\xb4"; // Red circle - } else { - out << "\xf0\x9f\x9f\xa2"; // Green circle - } - auto rate_str = std::format("{:.1f}", rate); - std::string message; - if (rate_str == "0.0") { - out << config.TargetName << " has similar performance"; - } else { - out << config.TargetName << " is about **" << rate_str << "% " << (is_negative ? "slower" : "faster") << "**"; + out.open(config.OutputPath, + std::ios_base::out | std::ios_base::app | std::ios_base::binary); + for (auto& metric_group : config.MetricGroups) { + out << "## " << metric_group.Name << "\n\n"; + out << metric_group.Description << "\n\n"; + out << "| |"; + for (auto& environment : config.Environments) { + out << " " << environment.Description << " |"; + } + out << "\n"; + out << "| - |"; + for (std::size_t i = 0; i < config.Environments.size(); ++i) { + out << " - |"; + } + out << "\n"; + for (auto& metric : metric_group.Metrics) { + out << "| " << metric.Name << " |"; + for (auto& benchmark : benchmarks) { + double target = benchmark.at(metric.TargetBenchmarkName); + double baseline = benchmark.at(metric.BaselineBenchmarkName); + double rate = (baseline - target) * 100 / target; + bool is_negative = rate < 0; + if (is_negative) { + rate = -rate; + } + out << " "; + if (rate < config.YellowIndicatorThreshold) { + out << "\xf0\x9f\x9f\xa1"; // Yellow circle + } else if (is_negative) { + out << "\xf0\x9f\x94\xb4"; // Red circle + } else { + out << "\xf0\x9f\x9f\xa2"; // Green circle + } + auto rate_str = std::format("{:.1f}", rate); + std::string message; + if (rate_str == "0.0") { + out << config.TargetName << " has similar performance"; + } else { + out << config.TargetName << " is about **" << rate_str << "% " + << (is_negative ? "slower" : "faster") << "**"; + } + out << " |"; } - out << " |"; + out << "\n"; } out << "\n"; } - out << "\n## Environments\n\n"; + out << "## Environments\n\n"; out << "| | Operating System | Kernel Version | Architecture | Compiler |\n"; out << "| - | - | - | - | - |\n"; for (auto& environment : config.Environments) { EnvironmentInfo env_info = ParseEnvironmentInfo(environment.InfoPath); - out << "| **" << environment.Description << "** | " << env_info.OS << " | " << env_info.KernelVersion << " | " << env_info.Architecture << " | " << env_info.Compiler << " |\n"; + out << "| **" << environment.Description << "** | " << env_info.OS << " | " + << env_info.KernelVersion << " | " << env_info.Architecture << " | " + << env_info.Compiler << " |\n"; } } diff --git a/tools/report_generator/report-config.json b/tools/report_generator/report-config.json index f0da16a..adcb22f 100644 --- a/tools/report_generator/report-config.json +++ b/tools/report_generator/report-config.json @@ -34,106 +34,154 @@ "BenchmarkingResultsPath": "artifacts/drop-oneapi/benchmarking-results.json" } ], - "Metrics": [ - { - "Name": "Indirect invocation on small objects via `proxy` vs. virtual functions", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on small objects via `proxy_view` vs. virtual functions", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxyView", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on small objects via `proxy` vs. virtual functions (shared ownership)", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy_Shared", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_Shared" - }, - { - "Name": "Indirect invocation on large objects via `proxy` vs. virtual functions", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on large objects via `proxy_view` vs. virtual functions", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxyView", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on large objects via `proxy` vs. virtual functions (shared ownership)", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy_Shared", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_Shared" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (without memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (with memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy_Shared", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy_SharedPooled", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` vs. `std::any`", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithAny" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (without memory pool) vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (with memory pool) vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Shared", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_SharedPooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (without memory pool) vs. `std::any`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithAny" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (with memory pool) vs. `std::any`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithAny" + "MetricGroups": [ + { + "Name": "Core Metrics", + "Description": "These benchmarks define the library's primary success criteria. They should generally reflect positive outcomes and remain stable as the library evolves, with regressions carefully monitored and addressed.", + "Metrics": [ + { + "Name": "Indirect invocation on small objects via `proxy` vs. `std::unique_ptr` + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" + }, + { + "Name": "Indirect invocation on small objects via `proxy_view` vs. raw pointer + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxyView", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_RawPtr" + }, + { + "Name": "Indirect invocation on small objects via `proxy` (shared ownership) vs. `std::shared_ptr` + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy_Shared", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_Shared" + }, + { + "Name": "Indirect invocation on large objects via `proxy` vs. `std::unique_ptr` + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" + }, + { + "Name": "Indirect invocation on large objects via `proxy_view` vs. raw pointer + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxyView", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_RawPtr" + }, + { + "Name": "Indirect invocation on large objects via `proxy` (shared ownership) vs. `std::shared_ptr` + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy_Shared", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_Shared" + }, + { + "Name": "Creation for small objects with `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (with memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for large objects with `proxy` (with memory pool) vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_SharedPooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr_Pooled" + } + ] + }, + { + "Name": "Comparative Metrics", + "Description": "These benchmarks provide perspective by comparison with other well-known implementations. They are not required to consistently show positive results, but instead highlight relative strengths, trade-offs, and areas of differentiation.", + "Metrics": [ + { + "Name": "Creation for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (without memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy_Shared", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy_SharedPooled", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for small objects with `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithAny" + }, + { + "Name": "Creation for large objects with `proxy` (without memory pool) vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Shared", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for large objects with `proxy` (without memory pool) vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithAny" + }, + { + "Name": "Creation for large objects with `proxy` (with memory pool) vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithAny" + } + ] + }, + { + "Name": "Observational Metrics", + "Description": "These benchmarks are included for completeness and transparency. While they may not represent target outcomes, they offer valuable context for users to understand the broader characteristics of the library.", + "Metrics": [ + { + "Name": "Relocation on small objects via `proxy` (trivially-relocatable by default) vs. `proxy` (nothrow-relocatable)", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaProxy_NothrowRelocatable" + }, + { + "Name": "Relocation on small objects via `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaUniquePtr" + }, + { + "Name": "Relocation on small objects via `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaAny" + }, + { + "Name": "Relocation on large objects via `proxy` (trivially-relocatable by default) vs. `proxy` (nothrow-relocatable)", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaProxy_NothrowRelocatable" + }, + { + "Name": "Relocation on large objects via `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaUniquePtr" + }, + { + "Name": "Relocation on large objects via `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaAny" + } + ] } ] } \ No newline at end of file