diff --git a/.devcontainer/clang18/devcontainer.json b/.devcontainer/clang18/devcontainer.json index 5ff70469..3573703e 100644 --- a/.devcontainer/clang18/devcontainer.json +++ b/.devcontainer/clang18/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:18-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:18", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang19/devcontainer.json b/.devcontainer/clang19/devcontainer.json index bc617a36..4fdfdd8a 100644 --- a/.devcontainer/clang19/devcontainer.json +++ b/.devcontainer/clang19/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:19-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:19", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang20/devcontainer.json b/.devcontainer/clang20/devcontainer.json index 5522774f..13c4e962 100644 --- a/.devcontainer/clang20/devcontainer.json +++ b/.devcontainer/clang20/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:20-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:20", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/clang21/devcontainer.json b/.devcontainer/clang21/devcontainer.json index e09e5c3a..2018216b 100644 --- a/.devcontainer/clang21/devcontainer.json +++ b/.devcontainer/clang21/devcontainer.json @@ -5,7 +5,7 @@ "build": { "dockerfile": "../Dockerfile", "args": { - "BASE_IMAGE_NAME": "silkeh/clang:21-bullseye", + "BASE_IMAGE_NAME": "silkeh/clang:21", "DEBIAN_SNAPSHOT": "none" }, "context": "../context" diff --git a/.devcontainer/context/.gitkeep b/.devcontainer/context/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index b41765a2..1521793d 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -2,9 +2,37 @@ name: bazel-gcc-c++23-no-stl on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,10 +41,10 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build - run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL + run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --//:etl_profile=//:no_profile - name: Run tests - run: bazel test //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --test_output=all + run: bazel test //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --//:etl_profile=//:no_profile --test_output=all diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index edf2f2e5..39c0a0a4 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -2,9 +2,37 @@ name: clang-c++11 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,13 +44,12 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -38,7 +65,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -51,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -67,16 +93,15 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index b11f3407..e7909302 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -2,9 +2,37 @@ name: clang-c++14 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,13 +44,12 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -38,7 +65,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -51,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -67,16 +93,15 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index 917d8da0..54b05957 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -2,9 +2,37 @@ name: clang-c++17 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,13 +44,12 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -38,7 +65,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -51,7 +78,6 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -67,16 +93,15 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 681d03c0..eb6d6bf5 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -2,9 +2,37 @@ name: clang-c++20 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,7 +44,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -34,7 +62,6 @@ jobs: export CXX=clang++-17 export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -50,7 +77,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -63,7 +90,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -79,7 +105,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -92,7 +118,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -108,7 +133,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - name: Install newer Clang @@ -121,7 +146,6 @@ jobs: run: | export CC=clang-17 export CXX=clang++-17 - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -137,13 +161,12 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -159,13 +182,12 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -181,13 +203,12 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -203,17 +224,15 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v - diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index 2cdb909d..97ba5af3 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -2,9 +2,37 @@ name: clang-c++23 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,7 +44,7 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install libc++ run: | @@ -29,12 +57,19 @@ jobs: export CXX=clang++ export CXXFLAGS="-stdlib=libc++" export LDFLAGS="-stdlib=libc++" - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests + # The system libc++/libc++abi are not built with ASan. Exception objects + # (e.g. std::runtime_error's reference counted string) are allocated with + # operator new inside libc++.so, but released with free() inside + # libc++abi.so, which AddressSanitizer reports as an alloc-dealloc-mismatch. + # This is a libc++ packaging issue, not an ETL defect, so the check is + # disabled for this job only. + env: + ASAN_OPTIONS: alloc_dealloc_mismatch=0 run: ./test/etl_tests -v build-clang-cpp23-linux-no-stl: @@ -45,14 +80,13 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -67,14 +101,13 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -89,14 +122,13 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -111,14 +143,13 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -133,14 +164,13 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -155,14 +185,13 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -177,17 +206,15 @@ jobs: os: [macos-15] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 -DETL_ENABLE_SANITIZER=ON ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v - diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index 61dc931f..5905289a 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -2,9 +2,37 @@ name: clang-c++26 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -27,7 +55,6 @@ jobs: export CXX=clang++ && \ export CXXFLAGS=-stdlib=libc++ && \ export LDFLAGS=-stdlib=libc++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -38,7 +65,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -49,7 +76,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -60,7 +86,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -71,7 +97,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -82,7 +107,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -93,7 +118,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -104,7 +128,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -115,7 +139,6 @@ jobs: cd /workspaces/etl && \ export CC=clang && \ export CXX=clang++ && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ clang --version && \ make -j \$(getconf _NPROCESSORS_ONLN) && \ @@ -129,13 +152,12 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -151,13 +173,12 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -173,13 +194,12 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" @@ -195,13 +215,12 @@ jobs: os: [macos-26] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | export CC=clang export CXX=clang++ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ clang --version make -j "$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-format.yaml b/.github/workflows/clang-format.yaml index 083f265f..8f98f80d 100644 --- a/.github/workflows/clang-format.yaml +++ b/.github/workflows/clang-format.yaml @@ -7,12 +7,16 @@ on: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: clang-format: name: clang-format runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 with: fetch-depth: 0 diff --git a/.github/workflows/clang-format_update.yaml b/.github/workflows/clang-format_update.yaml index acaa3453..a15cc3f4 100644 --- a/.github/workflows/clang-format_update.yaml +++ b/.github/workflows/clang-format_update.yaml @@ -3,12 +3,16 @@ name: clang-format-update on: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: clang-format: runs-on: ubuntu-24.04 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 - name: Install clang-format run: | diff --git a/.github/workflows/clang-syntax-checks.yml b/.github/workflows/clang-syntax-checks.yml index 2c6000e7..25795ebe 100644 --- a/.github/workflows/clang-syntax-checks.yml +++ b/.github/workflows/clang-syntax-checks.yml @@ -2,9 +2,37 @@ name: clang syntax checks on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build-clang-cpp03-linux-STL: @@ -15,7 +43,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -33,7 +61,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -51,7 +79,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -69,7 +97,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -87,7 +115,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -105,7 +133,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -123,7 +151,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -141,7 +169,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -159,7 +187,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -177,7 +205,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -195,7 +223,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -213,7 +241,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -231,7 +259,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -249,7 +277,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -267,7 +295,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -285,7 +313,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -303,7 +331,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -321,7 +349,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -339,7 +367,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -357,7 +385,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -375,7 +403,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -393,7 +421,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -408,7 +436,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -428,7 +456,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -448,7 +476,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -468,7 +496,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 214d30b2..ff368d50 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -3,16 +3,44 @@ name: clang-tidy on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: clang-tidy: name: clang-tidy runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 with: fetch-depth: 0 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5c05ebef..bf138cf9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,13 +3,36 @@ name: coverage on: push: branches: [ master, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' -# Allow only one concurrent deployment to GitHub Pages concurrency: - group: coverage-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Grant GITHUB_TOKEN the minimum permissions needed at the workflow level @@ -23,7 +46,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install dependencies run: | diff --git a/.github/workflows/deploy-etl-documentation.yml b/.github/workflows/deploy-etl-documentation.yml index b7636de1..b33941b0 100644 --- a/.github/workflows/deploy-etl-documentation.yml +++ b/.github/workflows/deploy-etl-documentation.yml @@ -4,6 +4,10 @@ on: push: branches: [ master ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: deploy: runs-on: ubuntu-latest @@ -15,7 +19,7 @@ jobs: steps: # Check out the repository code - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index c9706aeb..71a5809e 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -2,9 +2,37 @@ name: gcc-c++11 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,12 +44,11 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | git fetch - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ @@ -39,12 +66,11 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | git fetch - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index a3277b66..0cb6dc2c 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -2,9 +2,37 @@ name: gcc-c++14 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,11 +44,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ @@ -38,11 +65,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ @@ -50,4 +76,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index dbeef7fa..4968a87d 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -2,9 +2,37 @@ name: gcc-c++17 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,11 +44,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ @@ -38,11 +65,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index be4e6fb2..68be97d8 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -2,9 +2,37 @@ name: gcc-c++20 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,11 +44,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ @@ -38,11 +65,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ @@ -60,11 +86,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ @@ -82,11 +107,10 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ @@ -94,4 +118,4 @@ jobs: make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests - run: ./test/etl_tests -v \ No newline at end of file + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++23-armhf.yml b/.github/workflows/gcc-c++23-armhf.yml index 1bd2912b..00cb78d6 100644 --- a/.github/workflows/gcc-c++23-armhf.yml +++ b/.github/workflows/gcc-c++23-armhf.yml @@ -2,9 +2,37 @@ name: gcc-c++23-armhf on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-armhf -f .devcontainer/armhf/Dockerfile . diff --git a/.github/workflows/gcc-c++23-i386.yml b/.github/workflows/gcc-c++23-i386.yml index 31bfc331..dec5833f 100644 --- a/.github/workflows/gcc-c++23-i386.yml +++ b/.github/workflows/gcc-c++23-i386.yml @@ -2,9 +2,37 @@ name: gcc-c++23-i386 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-i386 -f .devcontainer/i386/Dockerfile . diff --git a/.github/workflows/gcc-c++23-powerpc.yml b/.github/workflows/gcc-c++23-powerpc.yml index 7494ae55..71a90f20 100644 --- a/.github/workflows/gcc-c++23-powerpc.yml +++ b/.github/workflows/gcc-c++23-powerpc.yml @@ -2,9 +2,37 @@ name: gcc-c++23-powerpc on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-powerpc -f .devcontainer/powerpc/Dockerfile . diff --git a/.github/workflows/gcc-c++23-riscv64.yml b/.github/workflows/gcc-c++23-riscv64.yml index 695056a8..b7a2611d 100644 --- a/.github/workflows/gcc-c++23-riscv64.yml +++ b/.github/workflows/gcc-c++23-riscv64.yml @@ -2,9 +2,37 @@ name: gcc-c++23-riscv64 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-riscv64 -f .devcontainer/riscv64/Dockerfile . diff --git a/.github/workflows/gcc-c++23-s390x.yml b/.github/workflows/gcc-c++23-s390x.yml index f6a15aca..365f44dc 100644 --- a/.github/workflows/gcc-c++23-s390x.yml +++ b/.github/workflows/gcc-c++23-s390x.yml @@ -2,9 +2,37 @@ name: gcc-c++23-s390x on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-s390x -f .devcontainer/s390x/Dockerfile . diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index 842b6152..65cf7dc4 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -2,9 +2,37 @@ name: gcc-c++23 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -16,11 +44,10 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -38,11 +65,10 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -60,11 +86,10 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ @@ -82,11 +107,10 @@ jobs: os: [ubuntu-24.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc-14 export CXX=g++-14 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml index b7ff42d3..38506641 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -2,9 +2,37 @@ name: gcc-c++26 on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -22,7 +50,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ @@ -35,7 +62,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -44,7 +71,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ @@ -57,7 +83,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -66,7 +92,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ @@ -79,7 +104,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -88,7 +113,6 @@ jobs: run: | docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ cd /workspaces/etl && \ - export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ export CC=gcc && \ export CXX=g++ && \ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ diff --git a/.github/workflows/gcc-syntax-checks.yml b/.github/workflows/gcc-syntax-checks.yml index b6b77bad..0faa94c0 100644 --- a/.github/workflows/gcc-syntax-checks.yml +++ b/.github/workflows/gcc-syntax-checks.yml @@ -2,9 +2,37 @@ name: gcc syntax checks on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build-gcc-cpp03-linux-STL: @@ -15,7 +43,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -33,7 +61,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -51,7 +79,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -69,7 +97,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -87,7 +115,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -105,7 +133,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -123,7 +151,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -141,7 +169,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -159,7 +187,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -177,7 +205,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -195,7 +223,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -213,7 +241,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -231,7 +259,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -249,7 +277,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -267,7 +295,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -285,7 +313,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -303,7 +331,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -321,7 +349,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -339,7 +367,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -357,7 +385,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -375,7 +403,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -393,7 +421,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build run: | @@ -408,7 +436,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -428,7 +456,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -448,7 +476,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . @@ -468,7 +496,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Build Docker image run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index 1829c342..6cad5c54 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -6,13 +6,17 @@ on: branches: [ master, pull-request/* ] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: generator-run: name: Header Generator runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: Install dependencies run: | diff --git a/.github/workflows/meson-gcc-c++23-no-stl.yml b/.github/workflows/meson-gcc-c++23-no-stl.yml index d8767317..0902f2f9 100644 --- a/.github/workflows/meson-gcc-c++23-no-stl.yml +++ b/.github/workflows/meson-gcc-c++23-no-stl.yml @@ -2,9 +2,37 @@ name: meson-gcc-c++23-no-stl on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: @@ -13,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.3 - name: Install Meson run: sudo apt-get install -y meson diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 32d7aee8..8f7d4c00 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -2,9 +2,37 @@ name: msvc on: push: branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' pull_request: branches: [ master, development, pull-request/* ] types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - 'BUILD.bazel' + - 'MODULE.bazel*' + - 'meson.build' + - 'meson_options.txt' + - 'subprojects/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build-windows-msvc-stl: @@ -13,7 +41,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -34,7 +62,7 @@ jobs: runs-on: [windows-2022] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -56,7 +84,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -78,7 +106,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: submodules: recursive @@ -93,4 +121,3 @@ jobs: - name: Run tests run: test/Debug/etl_tests.exe -v - diff --git a/.github/workflows/platformio-update.yml b/.github/workflows/platformio-update.yml index 318cf953..ebc02081 100644 --- a/.github/workflows/platformio-update.yml +++ b/.github/workflows/platformio-update.yml @@ -4,13 +4,17 @@ on: release: types: [published] # Trigger only when a GitHub release is published +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: publish: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.gitignore b/.gitignore index 60d4e469..b5cc3865 100644 --- a/.gitignore +++ b/.gitignore @@ -422,3 +422,4 @@ test/build-coverage include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build docs/Messaging +test/vs2026/Build diff --git a/BUILD.bazel b/BUILD.bazel index 59ea5184..83d337ac 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -20,14 +20,23 @@ package(default_visibility = ["//visibility:public"]) # strip_include_prefix = ".", # ) # -# The default is empty, so ETL's built-in defaults are used unless overridden. +# The default of this label_flag is incompatible. This makes sure that the consumer +# explicitly sets a profile, even if it is the `no_profile` defined below. + label_flag( name = "etl_profile", - build_setting_default = ":no_profile", + build_setting_default = ":incompatible_profile", ) cc_library(name = "no_profile") +cc_library( + name = "incompatible_profile", + target_compatible_with = [ + "@platforms//:incompatible", + ], +) + cc_library( name = "etl", hdrs = glob(["include/**/*.h"]), diff --git a/arduino/library-arduino.json b/arduino/library-arduino.json index 8bdb2a1b..3d192f5b 100644 --- a/arduino/library-arduino.json +++ b/arduino/library-arduino.json @@ -1,6 +1,6 @@ { "name": "Embedded Template Library ETL", - "version": "20.48.1", + "version": "20.49.0", "authors": { "name": "John Wellbelove", "email": "john.wellbelove@etlcpp.com" diff --git a/arduino/library-arduino.properties b/arduino/library-arduino.properties index 55f4e119..ede330f3 100644 --- a/arduino/library-arduino.properties +++ b/arduino/library-arduino.properties @@ -1,5 +1,5 @@ name=Embedded Template Library ETL -version=20.48.1 +version=20.49.0 author= John Wellbelove maintainer=John Wellbelove license=MIT diff --git a/docs/IO/buffer_descriptors.md b/docs/IO/buffer_descriptors.md index 4e49278e..df7f77b6 100644 --- a/docs/IO/buffer_descriptors.md +++ b/docs/IO/buffer_descriptors.md @@ -188,7 +188,7 @@ A nested class that is sent to the user defined callback function. notification() ``` **Description** -Default contructor. +Default constructor. Initialises to a default constructed descriptor and a count of zero. --- diff --git a/docs/_index.md b/docs/_index.md index 6c86bf57..472fb0ac 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -39,7 +39,7 @@ type: hextra-home
- coverage + coverage Codacy
diff --git a/docs/blog/graphics/determining-line-line-intersections.md b/docs/blog/graphics/determining-line-line-intersections.md index 279103ae..bcc6b12f 100644 --- a/docs/blog/graphics/determining-line-line-intersections.md +++ b/docs/blog/graphics/determining-line-line-intersections.md @@ -87,7 +87,7 @@ This means you can find intersection points relative to a fixed reference line. ![LineCross2](images/LineCross2.png) -## Refection +## Reflection This describes reflecting a point across a reference line. It uses the intersection method described above. diff --git a/docs/callbacks/function.md b/docs/callbacks/function.md index 7785b0f1..42f83b4e 100644 --- a/docs/callbacks/function.md +++ b/docs/callbacks/function.md @@ -18,7 +18,7 @@ This may be used to implement a platform abstraction layer that allows an embedd | Template | Description | | ----------------- | ------------------------------------------------------------------------- | | etl::function | Callbacks to free or member functions taking zero or one parameter.
Function pointer at runtime. | -| etl::function_fv | Callback to a free function taking no parameters. (Functiion Void).
Function pointer at compile time. | +| etl::function_fv | Callback to a free function taking no parameters. (Function Void).
Function pointer at compile time. | | etl::function_fp | Callback to a free function taking one parameter. (Function Parameter).
Function pointer at compile time. | etl::function_mv | Callback to a member function taking no parameters. (Member Void).
Function pointer at compile time. | | etl::function_imv | Callback to a member function taking no parameters. (Instance Member Void ).
Instance reference and function pointer at compile time. | diff --git a/docs/callbacks/signal.md b/docs/callbacks/signal.md index a8c19efc..53d05534 100644 --- a/docs/callbacks/signal.md +++ b/docs/callbacks/signal.md @@ -15,7 +15,7 @@ template ` ## Types diff --git a/docs/chrono/duration/duration.md b/docs/chrono/duration/duration.md index c75c6a7b..7900e8b3 100644 --- a/docs/chrono/duration/duration.md +++ b/docs/chrono/duration/duration.md @@ -112,7 +112,7 @@ ETL_CONSTEXPR14 etl::chrono::duration operator =(const etl::chrono::duration& other) ETL_NOEXCEPT ``` -### Convertion +### Conversion ```cpp ETL_CONSTEXPR14 TRep count() const diff --git a/docs/chrono/month/month_weekday.md b/docs/chrono/month/month_weekday.md index ab73505e..3d7037c6 100644 --- a/docs/chrono/month/month_weekday.md +++ b/docs/chrono/month/month_weekday.md @@ -20,7 +20,7 @@ class month_weekday ETL_CONSTEXPR month_weekday() ``` -**Decription** +**Description** Default constructor. --- @@ -31,7 +31,7 @@ month_weekday(const etl::chrono::month& m, const etl::chrono::weekday_indexed& wd) ETL_NOEXCEPT ``` -**Decription** +**Description** Construct from month and weekday_indexed. ## Access @@ -75,7 +75,7 @@ bool operator ==(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Equality operator. --- @@ -86,7 +86,7 @@ bool operator !=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Inequality operator. --- @@ -97,7 +97,7 @@ bool operator <(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Less-than operator. --- @@ -108,7 +108,7 @@ bool operator <=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Less-than-equal operator. --- @@ -119,7 +119,7 @@ bool operator <(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Greater-than operator. --- @@ -130,7 +130,7 @@ bool operator <=(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Greater-than-equal operator. --- @@ -141,7 +141,7 @@ auto operator <=>(const etl::chrono::month_weekday& lhs, const etl::chrono::month_weekday& rhs) ETL_NOEXCEPT ``` -**Decription** +**Description** Spaceship operator. C++20 @@ -151,6 +151,6 @@ C++20 template struct hash ``` -**Decription** +**Description** Hash function for `etl::chrono::month_weekday`. diff --git a/docs/chrono/year/year_month_day_last.md b/docs/chrono/year/year_month_day_last.md index 426a51ab..dc19db52 100644 --- a/docs/chrono/year/year_month_day_last.md +++ b/docs/chrono/year/year_month_day_last.md @@ -56,7 +56,7 @@ The year. --- -```coo +```cpp ETL_NODISCARD ETL_CONSTEXPR14 etl::chrono::month month() const diff --git a/docs/containers/Views/span.md b/docs/containers/Views/span.md index 8fb131f7..9e619b4e 100644 --- a/docs/containers/Views/span.md +++ b/docs/containers/Views/span.md @@ -356,7 +356,7 @@ bool equal(const etl::span& lhs, const etl::span& rhs) ``` **Description** Equality function. -Performs a comparision of the range values. +Performs a comparison of the range values. Returns `true` if one of the following are `true` 1. Both spans are empty. 2. They both point to the same range of data. diff --git a/docs/containers/arrays/array.md b/docs/containers/arrays/array.md index e2cda55d..96e58195 100644 --- a/docs/containers/arrays/array.md +++ b/docs/containers/arrays/array.md @@ -38,13 +38,23 @@ Defines data as an array of 'int', of length '10', containing the supplied data. **C++11 and above** ```cpp -template -constexpr auto make_array(TValues&&... values) +template +constexpr auto make_array(TValues&&... values) -> etl::array ``` +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). An empty +array requires the element type to be supplied explicitly, e.g. +`etl::make_array()`; a call with neither an element type nor arguments +is ill-formed, as there is nothing to deduce the element type from. + ### Example ```cpp -auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::array. +auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::array. Each argument is converted. +auto chars = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types @@ -314,3 +324,42 @@ operator >= ``` **Description** `true` if the contents of the lhs are lexicographically greater than or equal to the contents of the rhs, otherwise `false`. + +--- + +```cpp +template +T& get(etl::array& a) + +template +const T& get(const etl::array& a) +``` +**Description** +Gets a reference to the element at `Index` in the array. + +## Structured bindings +`etl::array` provides the tuple-like interface required for C++17 structured bindings. + +```cpp +etl::array a = { 1, 2, 3 }; + +auto& [x, y, z] = a; // x, y, z are references to a[0], a[1], a[2] +``` + +```cpp +template +struct tuple_size> +``` +**Description** +Gets the number of elements in the array. +Specialised in namespaces `etl` and `std` (the `std` specialisation allows the use of C++ structured bindings). + +--- + +```cpp +template +struct tuple_element> +``` +**Description** +Gets the type of the element at `Index` in the array. +Specialised in namespaces `etl` and `std` (the `std` specialisation allows the use of C++ structured bindings). diff --git a/docs/containers/lists/forward-list.md b/docs/containers/lists/forward-list.md index 2be721e0..f51acc9e 100644 --- a/docs/containers/lists/forward-list.md +++ b/docs/containers/lists/forward-list.md @@ -65,13 +65,20 @@ Defines data as an `forward_list` of int, of length 10, containing the supplied ## Make template **C++11 and above** ```cpp -template - constexpr auto make_forward_list(TValues&&... values) +template +constexpr auto make_forward_list(TValues&&... values) -> etl::forward_list ``` +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + ### Example ```cpp -auto data = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::forward_list. +auto data = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::forward_list. Each argument is converted. +auto chars = etl::make_forward_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/lists/intrusive-forward-list.md b/docs/containers/lists/intrusive-forward-list.md index 5f1ee051..09b8a30d 100644 --- a/docs/containers/lists/intrusive-forward-list.md +++ b/docs/containers/lists/intrusive-forward-list.md @@ -8,7 +8,7 @@ weight: 4 Similar to: `std::forward_list` {{< /callout >}} -An intrusive forward list.https://www.messenger.com/t/837634551/ +An intrusive forward list. ```cpp template diff --git a/docs/containers/lists/list.md b/docs/containers/lists/list.md index 8ae13fd3..598b900b 100644 --- a/docs/containers/lists/list.md +++ b/docs/containers/lists/list.md @@ -69,13 +69,20 @@ Defines data as an list of `int`, of length `10`, containing the supplied data. ## Make template **C++11 and above** ```cpp -template -constexpr auto make_list(TValues&&... values) +template +constexpr auto make_list(TValues&&... values) -> etl::list ``` +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + ### Example ```cpp -auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::list. +auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::list. Each argument is converted. +auto chars = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/maps/flat-map.md b/docs/containers/maps/flat-map.md index dd1d1850..0231aac5 100644 --- a/docs/containers/maps/flat-map.md +++ b/docs/containers/maps/flat-map.md @@ -40,13 +40,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_flat_map(TPairs&&... pairs) -> etl::flat_map ``` ### Example ```cpp -auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types @@ -239,7 +239,7 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const value_type& value)) +pair emplace(const value_type& value) pair emplace(const key_type& key, const mapped_type& value) ``` **Description** diff --git a/docs/containers/maps/flat-multimap.md b/docs/containers/maps/flat-multimap.md index b6f6832d..4fede72c 100644 --- a/docs/containers/maps/flat-multimap.md +++ b/docs/containers/maps/flat-multimap.md @@ -33,20 +33,20 @@ etl::flat_multimap data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4, 5}, etl: ``` Defines data as an `flat_multimap` of `int`/`int` pairs, of length 4, containing the supplied data. -## make_flat_map +## make_flat_multimap C++11 and above ```cpp template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_flat_multimap(TPairs&&... pairs) -> etl::flat_multimap ``` ### Example ```cpp -auto data = etl::make_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_flat_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types @@ -239,7 +239,7 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const value_type& value)) +pair emplace(const value_type& value) pair emplace(const key_type& key, const mapped_type& value) ``` **Description** diff --git a/docs/containers/maps/map.md b/docs/containers/maps/map.md index c2c26396..8b747fb8 100644 --- a/docs/containers/maps/map.md +++ b/docs/containers/maps/map.md @@ -38,13 +38,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_map(TPairs&&... pairs) -> etl::map ``` ### Example ```cpp -auto data = etl::make_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/multimap.md b/docs/containers/maps/multimap.md index 09fda167..67c02108 100644 --- a/docs/containers/maps/multimap.md +++ b/docs/containers/maps/multimap.md @@ -38,13 +38,13 @@ template , typename... TPairs> -constexpr auto make_flat_map(TValues&&... values) +constexpr auto make_multimap(TPairs&&... pairs) -> etl::multimap ``` ### Example ```cpp -auto data = etl::make_multimap(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/reference-flat-map.md b/docs/containers/maps/reference-flat-map.md index 57f7dda1..baa0a013 100644 --- a/docs/containers/maps/reference-flat-map.md +++ b/docs/containers/maps/reference-flat-map.md @@ -36,19 +36,32 @@ etl::reference_flat_map data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4, 5}, Defines data as an reference_flat_map of int, of length 4, containing the supplied data. ## Make template -C++11 and above - +**C++11 and above** ```cpp -template , +template , typename... TPairs> -constexpr auto make_reference_flat_map(TValues&&... values) +constexpr auto make_reference_flat_map(TPairs&&... pairs) -> etl::reference_flat_map ``` ### Example -auto data = etl::make_reference_flat_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_map` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +etl::pair p0{0, 1}; +etl::pair p1{2, 3}; +etl::pair p2{4, 5}; +etl::pair p3{6, 7}; + +auto data = etl::make_reference_flat_map(p0, p1, p2, p3); +``` ## Member types diff --git a/docs/containers/maps/reference-flat-multimap.md b/docs/containers/maps/reference-flat-multimap.md index 9d4ccd64..01bdb556 100644 --- a/docs/containers/maps/reference-flat-multimap.md +++ b/docs/containers/maps/reference-flat-multimap.md @@ -36,19 +36,32 @@ etl::reference_flat_multimap data{ etl::pair{0, 1}, etl::pair{2, 3}, etl::pair{4 Defines data as an reference_flat_multimap of int, of length 4, containing the supplied data. ## Make template -C++11 and above - +**C++11 and above** ```cpp -template , +template , typename... TPairs> -constexpr auto make_reference_flat_map(TValues&&... values) +constexpr auto make_reference_flat_multimap(TPairs&&... pairs) -> etl::reference_flat_multimap ``` ### Example -auto data = etl::make_reference_flat_multimap(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_multimap` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +etl::pair p0{0, 1}; +etl::pair p1{2, 3}; +etl::pair p2{4, 5}; +etl::pair p3{6, 7}; + +auto data = etl::make_reference_flat_multimap(p0, p1, p2, p3); +``` ## Member types diff --git a/docs/containers/maps/unordered-map.md b/docs/containers/maps/unordered-map.md index bf2810ad..ba576cd6 100644 --- a/docs/containers/maps/unordered-map.md +++ b/docs/containers/maps/unordered-map.md @@ -44,17 +44,17 @@ C++11 and above ```cpp template , - typename TKeyEqual = etl::equal_to + typename T, + typename THash = etl::hash, + typename TKeyEqual = etl::equal_to, typename... TPairs> -constexpr auto make_unordered_map(TValues&&... values) +constexpr auto make_unordered_map(TPairs&&... pairs) -> etl::unordered_map ``` ### Example ```cpp -auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/maps/unordered-multimap.md b/docs/containers/maps/unordered-multimap.md index 5e50cba3..9622f558 100644 --- a/docs/containers/maps/unordered-multimap.md +++ b/docs/containers/maps/unordered-multimap.md @@ -42,17 +42,17 @@ C++11 and above ```cpp template , - typename TKeyEqual = etl::equal_to + typename T, + typename THash = etl::hash, + typename TKeyEqual = etl::equal_to, typename... TPairs> -constexpr auto make_unordered_map(TValues&&... values) +constexpr auto make_unordered_multimap(TPairs&&... pairs) -> etl::unordered_multimap ``` ### Example ```cpp -auto data = etl::make_unordered_map(etl::pair{0, 1}, etl::pair{2, 3}, - etl::pair{4, 5}, etl::pair{6, 7}); +auto data = etl::make_unordered_multimap(etl::pair{0, 1}, etl::pair{2, 3}, + etl::pair{4, 5}, etl::pair{6, 7}); ``` ## Member types diff --git a/docs/containers/pools/generic-pool.md b/docs/containers/pools/generic-pool.md index eb400d36..d4dba97b 100644 --- a/docs/containers/pools/generic-pool.md +++ b/docs/containers/pools/generic-pool.md @@ -72,7 +72,7 @@ generic_pool_ext(char* buffer, size_t size) ``` **Description** For `etl::generic_pool_ext`. -Constructs a generic_pool from an external bufffer. +Constructs a generic_pool from an external buffer. No elements are constructed. ## Operations diff --git a/docs/containers/pools/pool.md b/docs/containers/pools/pool.md index 4b1e6fbf..4f6b1c6e 100644 --- a/docs/containers/pools/pool.md +++ b/docs/containers/pools/pool.md @@ -94,7 +94,7 @@ pool_ext(char* buffer, size_t size) ``` **Description** For `etl::pool_ext`. -Constructs a pool from an external bufffer. +Constructs a pool from an external buffer. No elements are constructed. ## Operations diff --git a/docs/containers/queues & stacks/circular-buffer.md b/docs/containers/queues & stacks/circular-buffer.md index 687ac4b9..3c3faf2a 100644 --- a/docs/containers/queues & stacks/circular-buffer.md +++ b/docs/containers/queues & stacks/circular-buffer.md @@ -69,11 +69,19 @@ const_reverse_iterator reverse_iterator ```cpp circular_buffer(); +circular_buffer(size_t initial_size); + +circular_buffer(size_t initial_size, const_reference value); + circular_buffer(const etl::circular_buffer& other); circular_buffer(etl::circular_buffer&& other); ``` +The `initial_size` constructor fills the buffer with default-constructed values. +The `initial_size` and `value` constructor fills the buffer with copies of `value`. +If `initial_size` exceeds `SIZE`, the buffer is filled to its capacity. + --- ```cpp @@ -81,6 +89,10 @@ circular_buffer_ext(void* buffer, size_t max_size) circular_buffer_ext(size_t max_size) 20.32.1 +circular_buffer_ext(size_t initial_size, void* buffer, size_t max_size) + +circular_buffer_ext(size_t initial_size, const_reference value, void* buffer, size_t max_size) + template circular_buffer_ext(TIterator first, const TIterator& last, void* buffer, size_t max_size) @@ -89,6 +101,10 @@ circular_buffer_ext(std::initializer_list init, void* buffer, size_t max_size circular_buffer_ext(const circular_buffer_ext& other, void* buffer, size_t max_size) ``` +The `initial_size` constructor fills the buffer with default-constructed values. +The `initial_size` and `value` constructor fills the buffer with copies of `value`. +If `initial_size` exceeds `max_size`, the buffer is filled to its capacity. The external storage must contain at least `max_size + 1` elements. + ## Status ```cpp @@ -225,6 +241,26 @@ Returns the remaining available capacity in the `circular_buffer`. ## Modifiers +```cpp +void assign(iterator first, const iterator& last) +void assign(const_iterator first, const const_iterator& last) + +template +void assign(TIterator first, const TIterator& last) +``` +**Description** +Replaces the contents of the circular buffer with the elements in the range `[first, last)`. If the range exceeds the capacity, only the last `capacity()` elements are retained. The range may refer to the circular buffer being assigned. + +--- + +```cpp +void assign(size_type n, const_reference value) +``` +**Description** +Replaces the contents of the circular buffer with `n` copies of `value`. If `n` exceeds the capacity, the buffer is filled to its capacity. `value` may refer to an element in the circular buffer being assigned. + +--- + ```cpp void set_buffer(void* buffer) ``` diff --git a/docs/containers/queues & stacks/deque.md b/docs/containers/queues & stacks/deque.md index a4960607..b0bb1ad1 100644 --- a/docs/containers/queues & stacks/deque.md +++ b/docs/containers/queues & stacks/deque.md @@ -32,13 +32,22 @@ etl::deque data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Defines data as an `deque` of `int`, of length `10`, containing the supplied data. ## Make template -template - constexpr auto make_deque(TValues&&... values) - C++11 and above +**C++11 and above** +```cpp +template +constexpr auto make_deque(TValues&&... values) -> etl::deque +``` + +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). ### Example ```cpp -auto data = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +// Deduced element type: etl::deque. +auto data = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::deque. Each argument is converted. +auto chars = etl::make_deque(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); ``` ## Member types diff --git a/docs/containers/sets/const-multiset.md b/docs/containers/sets/const-multiset.md index 8d8fc79a..13add134 100644 --- a/docs/containers/sets/const-multiset.md +++ b/docs/containers/sets/const-multiset.md @@ -17,7 +17,7 @@ Uses `etl::less` as the default key comparison method. etl::const_multiset ``` -**Enternal storage** +**Internal storage** ```cpp etl::const_multiset_ext ``` @@ -333,4 +333,4 @@ operator >= ## Technical stuff -The const set is implemented as a sorted array key/value pairs. +The const set is implemented as a sorted array of values. diff --git a/docs/containers/sets/const-set.md b/docs/containers/sets/const-set.md index 706eef0e..dbb3b3ad 100644 --- a/docs/containers/sets/const-set.md +++ b/docs/containers/sets/const-set.md @@ -17,7 +17,7 @@ Uses `etl::less` as the default key comparison method. etl::const_set ``` -**Enternal storage** +**Internal storage** ```cpp etl::const_set_ext ``` @@ -333,4 +333,4 @@ operator >= ## Technical stuff -The const set is implemented as a sorted array key/value pairs. +The const set is implemented as a sorted array of values. diff --git a/docs/containers/sets/flat-multiset.md b/docs/containers/sets/flat-multiset.md index 890fcd52..29bd454d 100644 --- a/docs/containers/sets/flat-multiset.md +++ b/docs/containers/sets/flat-multiset.md @@ -33,17 +33,18 @@ etl::flat_multiset data{ 0, 1, 2, 3 }; ``` Defines data as an `flat_multiset` of `int`, of length 4, containing the supplied data. -## make_flat_set +## make_flat_multiset C++11 and above ```cpp -template > -constexpr auto make_flat_set(TValues&&... values) +template , + typename... T> +constexpr auto make_flat_multiset(T&&... keys) -> etl::flat_multiset ``` ### Example ```cpp -auto data = etl::make_flat_set({0, 1, 2, 3 }); +auto data = etl::make_flat_multiset(0, 1, 2, 3); ``` ## Member types @@ -234,55 +235,60 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const_reference value)) -pair emplace(const key_type& key, const mapped_type& value) +pair emplace(const_reference value) ``` **Description** -Inserts key/value pairs into the set by constructing directly into storage. +Inserts a value into the set by constructing directly into storage. The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- **C++03** -The emplace functions differ from that of std::set in that, due to C++03 not supporting 'perfect forwarding'. +The emplace functions differ from that of std::multiset in that, due to C++03 not supporting 'perfect forwarding'. + +```cpp +pair emplace() +``` +**Description** +Emplaces a default constructed value into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1) +pair emplace(const T1& value1) ``` **Description** -Emplaces a value constructed from `key` and 1 argument into the set. +Emplaces a value constructed from 1 argument into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2) +pair emplace(const T1& value1, const T2& value2) ``` **Description** -Emplaces a value constructed from `key` and 2 arguments into the set. +Emplaces a value constructed from 2 arguments into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3) +pair emplace(const T1& value1, const T2& value2, const T3& value3) ``` **Description** -Emplaces a value constructed from `key` and 3 arguments into the set. +Emplaces a value constructed from 3 arguments into the set. ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) +pair emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4) ``` **Description** -Emplaces a value constructed from `key` and 4 arguments into the set. +Emplaces a value constructed from 4 arguments into the set. --- **C++11** ```cpp template -pair emplace(const key_type& key, Args&& ... args) +pair emplace(Args&& ... args) ``` **Description** -Emplaces a value constructed from the `key` and arguments into the set. +Emplaces a value constructed from the arguments into the set. --- @@ -492,9 +498,9 @@ operator != ## Technical stuff -Flat maps are usually implemented internally as a sorted vector of key/value pairs. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial keys or values. +Flat sets are usually implemented internally as a sorted vector of values. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial values. As inserting requires that all of the items to the right of the insert position must be shifted this can become an expensive operation for larger containers. -To improve insertion performance ETL flat maps are implemented as vectors of pointers to key/value pairs, sorted by key value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. +To improve insertion performance ETL flat sets are implemented as vectors of pointers to values, sorted by value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. The downside is that access to an item via an iterator will involve one indirection and the overhead of the container will be one pointer per item. A normal flat set implementation does not have this overhead. diff --git a/docs/containers/sets/flat-set.md b/docs/containers/sets/flat-set.md index 6f9c0540..4216b30b 100644 --- a/docs/containers/sets/flat-set.md +++ b/docs/containers/sets/flat-set.md @@ -36,14 +36,15 @@ Defines data as an `flat_set` of `int`, of length 4, containing the supplied dat ## make_flat_set C++11 and above ```cpp -template > -constexpr auto make_flat_set(TValues&&... values) +template , + typename... T> +constexpr auto make_flat_set(T&&... keys) -> etl::flat_set ``` ### Example ```cpp -auto data = etl::make_flat_set({0, 1, 2, 3 }); +auto data = etl::make_flat_set(0, 1, 2, 3); ``` ## Member types @@ -234,11 +235,10 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- ```cpp -pair emplace((const_reference value)) -pair emplace(const key_type& key, const mapped_type& value) +pair emplace(const_reference value) ``` **Description** -Inserts key/value pairs into the set by constructing directly into storage. +Inserts a value into the set by constructing directly into storage. The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) --- @@ -247,39 +247,48 @@ The return type is either `std::pair` (default) or `etl::pair` (`ETL_NO_STL`) The emplace functions differ from that of `std::set` due to C++03 not supporting 'perfect forwarding'. ```cpp -template -pair emplace(const key_type& key, const T1& value1) +pair emplace() ``` **Description** -Emplaces a value constructed from `key` and 1 argument into the set. +Emplaces a default constructed value into the set. + +--- + +```cpp +template +pair emplace(const T1& value1) +``` +**Description** + +Emplaces a value constructed from 1 argument into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2) +pair emplace(const T1& value1, const T2& value2) ``` **Description** -Emplaces a value constructed from `key` and 2 arguments into the set. +Emplaces a value constructed from 2 arguments into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3) +pair emplace(const T1& value1, const T2& value2, const T3& value3) ``` **Description** -Emplaces a value constructed from `key` and 3 arguments into the set. +Emplaces a value constructed from 3 arguments into the set. --- ```cpp template -pair emplace(const key_type& key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) +pair emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4) ``` **Description** -Emplaces a value constructed from `key` and 4 arguments into the set. +Emplaces a value constructed from 4 arguments into the set. --- @@ -287,10 +296,10 @@ Emplaces a value constructed from `key` and 4 arguments into the set. ```cpp template -pair emplace(const key_type& key, Args&& ... args) +pair emplace(Args&& ... args) ``` **Description** -Emplaces a value constructed from the `key` and arguments into the set. +Emplaces a value constructed from the arguments into the set. --- @@ -508,9 +517,9 @@ operator != ## Technical stuff -Flat maps are usually implemented internally as a sorted vector of key/value pairs. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial keys or values. +Flat sets are usually implemented internally as a sorted vector of values. Whilst this makes searching fast, it can have a detrimental effect when items are inserted into a container that stores complex, non-trivial values. As inserting requires that all of the items to the right of the insert position must be shifted this can become an expensive operation for larger containers. -To improve insertion performance ETL flat maps are implemented as vectors of pointers to key/value pairs, sorted by key value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. +To improve insertion performance ETL flat sets are implemented as vectors of pointers to values, sorted by value. An insertion will involve a copy of a range of pointers; an operation that can be very fast. The downside is that access to an item via an iterator will involve one indirection and the overhead of the container will be one pointer per item. A normal flat set implementation does not have this overhead. diff --git a/docs/containers/sets/multiset.md b/docs/containers/sets/multiset.md index 9bf091c8..4e3de78f 100644 --- a/docs/containers/sets/multiset.md +++ b/docs/containers/sets/multiset.md @@ -37,13 +37,13 @@ Defines data as an multiset of `int`, of length 8, containing the supplied data. ```cpp template , - typename... TPairs> -constexpr auto make_set(TValues&&... values) + typename... T> +constexpr auto make_multiset(T&&... keys) -> etl::multiset ``` ### Example ```cpp -auto data = etl::make_set(0, 1, 2, 3, 4, 5, 6, 7); +auto data = etl::make_multiset(0, 1, 2, 3, 4, 5, 6, 7); ``` ## Member types diff --git a/docs/containers/sets/reference-flat-multiset.md b/docs/containers/sets/reference-flat-multiset.md index 9e7dd725..9bb622dc 100644 --- a/docs/containers/sets/reference-flat-multiset.md +++ b/docs/containers/sets/reference-flat-multiset.md @@ -32,13 +32,35 @@ etl::reference_flat_setdata{ 0, 1, 2, 3, 4, 5, 6, 7 }; Defines data as a set of int/int pairs, of length 8, containing the supplied data. ## Make template -C++11 and above -template , typename... T> -constexpr auto make_reference_flat_set(T&&... values) +constexpr auto make_reference_flat_multiset(T&&... keys) -> etl::reference_flat_multiset +``` ### Example -auto data = etl::make_reference_flat_set(0, 1, 2, 3, 4, 5, 6, 7); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_multiset` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +int k0 = 0; +int k1 = 1; +int k2 = 2; +int k3 = 3; +int k4 = 4; +int k5 = 5; +int k6 = 6; +int k7 = 7; + +auto data = etl::make_reference_flat_multiset(k0, k1, k2, k3, k4, k5, k6, k7); +``` ## Member types diff --git a/docs/containers/sets/reference-flat-set.md b/docs/containers/sets/reference-flat-set.md index da2c9dc9..8ff1e296 100644 --- a/docs/containers/sets/reference-flat-set.md +++ b/docs/containers/sets/reference-flat-set.md @@ -32,13 +32,35 @@ etl::reference_flat_setdata{ 0, 1, 2, 3, 4, 5, 6, 7 }; Defines data as a set of int/int pairs, of length 8, containing the supplied data. ## Make template -C++11 and above -template , typename... T> -constexpr auto make_reference_flat_set(T&&... values) +constexpr auto make_reference_flat_set(T&&... keys) -> etl::reference_flat_set +``` ### Example -auto data = etl::make_reference_flat_set(0, 1, 2, 3, 4, 5, 6, 7); + +{{< callout type="warning" >}} + This example does not currently compile: `make_reference_flat_set` cannot be + instantiated, because the reference containers have no `initializer_list` + constructor for its braced return value to bind to. This is a known defect. +{{< /callout >}} + +```cpp +// The referenced objects must outlive the container. +int k0 = 0; +int k1 = 1; +int k2 = 2; +int k3 = 3; +int k4 = 4; +int k5 = 5; +int k6 = 6; +int k7 = 7; + +auto data = etl::make_reference_flat_set(k0, k1, k2, k3, k4, k5, k6, k7); +``` ## Member types diff --git a/docs/containers/sets/set.md b/docs/containers/sets/set.md index cec118bd..b6da157f 100644 --- a/docs/containers/sets/set.md +++ b/docs/containers/sets/set.md @@ -36,9 +36,9 @@ Defines data as an set of `int`, of length 8, containing the supplied data. ```cpp template , - typename... TPairs> -constexpr auto make_set(TValues&&... values) + typename TCompare = etl::less, + typename... T> +constexpr auto make_set(T&&... keys) -> etl::set ``` ### Example diff --git a/docs/containers/variadics/_index.md b/docs/containers/variadics/_index.md index 7c051c71..3789c5e4 100644 --- a/docs/containers/variadics/_index.md +++ b/docs/containers/variadics/_index.md @@ -3,4 +3,4 @@ title: "Variadics" weight: 100 --- -Containers tha accept multiple types. \ No newline at end of file +Containers that accept multiple types. \ No newline at end of file diff --git a/docs/containers/variadics/packet.md b/docs/containers/variadics/packet.md index 4533be50..9a420646 100644 --- a/docs/containers/variadics/packet.md +++ b/docs/containers/variadics/packet.md @@ -22,7 +22,7 @@ etl::packet `base_t` = `TBase` -## Contructors +## Constructors **C++03** ```cpp diff --git a/docs/containers/vectors/indirect-vector.md b/docs/containers/vectors/indirect-vector.md index 2588f453..b1e0766a 100644 --- a/docs/containers/vectors/indirect-vector.md +++ b/docs/containers/vectors/indirect-vector.md @@ -42,6 +42,26 @@ etl::indirect_vector data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; ``` Defines data as an `indirect_vector` of `int`, of length `10`, containing the supplied data. +## Make template +**C++11 and above** +```cpp +template +constexpr auto make_indirect_vector(TValues&&... values) + -> etl::indirect_vector +``` + +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + +### Example +```cpp +// Deduced element type: etl::indirect_vector. +auto data = etl::make_indirect_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::indirect_vector. Each argument is converted. +auto chars = etl::make_indirect_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +``` + ## Member types ```cpp diff --git a/docs/containers/vectors/vector.md b/docs/containers/vectors/vector.md index 216169a7..5b47dff1 100644 --- a/docs/containers/vectors/vector.md +++ b/docs/containers/vectors/vector.md @@ -54,6 +54,25 @@ etl::vector data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Defines data as an vector of int, of length 10, containing the supplied data. +## Make template +**C++11 and above** +```cpp +template +constexpr auto make_vector(TValues&&... values) -> etl::vector +``` + +The element type is `T` when supplied, otherwise the decayed common type of +the arguments (the Library Fundamentals TS `make_array` design). + +### Example +```cpp +// Deduced element type: etl::vector. +auto data = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + +// Explicit element type: etl::vector. Each argument is converted. +auto chars = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +``` + ## Member types ```cpp diff --git a/docs/getting-started/contribute.md b/docs/getting-started/contribute.md index 003fbef7..4d75959f 100644 --- a/docs/getting-started/contribute.md +++ b/docs/getting-started/contribute.md @@ -30,7 +30,7 @@ Lower case class and function names, separated by an underscore. `funky_container` `do_something` -### Nameing convension +### Naming convention Use the same naming convention as the STL. If appropriate for the class, a set of `typedef`s should often be present to define types such as... @@ -159,7 +159,7 @@ class my_new_class ``` ### Layout -When possible, try to tabluate the initialisation of multiple variables. It's a lot easier to read. +When possible, try to tabulate the initialisation of multiple variables. It's a lot easier to read. Don't be too strict about it, if it would result in giant amounts of whitespace. ```cpp @@ -240,4 +240,4 @@ When opening a new pull request, ensure that you include the following informati - Branches should be based on the branch `master`. `development` can change quite frequently, so I will rebase the your PR against it before merging. - For formatting help, you can use `clang-format`, or the convenience wrapper `treefmt`. See also [Source formatting]({{% relref "source-formatting.md" %}}). -- Update the relevent documentation markdown file, found in `etl/docs`. \ No newline at end of file +- Update the relevant documentation markdown file, found in `etl/docs`. \ No newline at end of file diff --git a/docs/getting-started/macros.md b/docs/getting-started/macros.md index 3444a3a9..23a6f37d 100644 --- a/docs/getting-started/macros.md +++ b/docs/getting-started/macros.md @@ -83,19 +83,19 @@ These may be defined in the project settings or the user created `etl_profile.h` | `ETL_NODISCARD` | If `ETL_CPP11_SUPPORTED` is defined as `1` then this macro is defined as `[[nodiscard]]`, otherwise defined as blank.| | `ETL_DEPRECATED` | If `ETL_CPP14_SUPPORTED` is defined as `1` then this macro is defined as `[[deprecated]]`, otherwise defined as blank.| | `ETL_DEPRECATED_REASON(reason)` | If `ETL_CPP14_SUPPORTED` is defined as `1` then this macro is defined as `[[deprecated(reason)]]`, otherwise defined as blank.| -| `ETL_FALLTHROUGH` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `[[falltrough]]`, otherwise defined as blank.| +| `ETL_FALLTHROUGH` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `[[fallthrough]]`, otherwise defined as blank.| | `ETL_NORETURN` | If `ETL_CPP11_SUPPORTED` is defined as `1` then this macro is defined as `[[noreturn]]`, otherwise defined as blank.| | `ETL_OR_STD` | If ETL_NO_STL is defined and ETL_IN_UNIT_TEST is not then `ETL_OR_STD` is defined as `etl`, otherwise it is defined as `std`.| | `ETL_IN_UNIT_TEST` | If defined, then the code is being compiled in the unit tests.
For internal ETL use only.| | `ETL_HAS_ATOMIC` | This is defined as `1` if the compiler supplies an atomic class, otherwise `0`.| | `ETL_INLINE_VAR` | If `ETL_CPP17_SUPPORTED` is defined as `1` then this macro is defined as `inline`, otherwise defined as blank.| -| `ETL_USING_STL` | This macro will be defined as `0` & `1` dependant of whether `ETL_NO_STL` is defined or not.| +| `ETL_USING_STL` | This macro will be defined as `0` & `1` depending on whether `ETL_NO_STL` is defined or not.| | `ETL_NOT_USING_STL` | Inversion of `ETL_USING_STL`.| -| `ETL_USING_STLPORT` | This macro will be defined as `0` & `1` dependant of whether `ETL_STLPORT` is defined or not.| +| `ETL_USING_STLPORT` | This macro will be defined as `0` & `1` depending on whether `ETL_STLPORT` is defined or not.| | `ETL_NOT_USING_STLPORT` | Inversion of `ETL_USING_STLPORT`.| -| `ETL_USING_8BIT_TYPES` | This macro will be defined as `0` & `1` dependant of whether `CHAR_BIT == 8` or not.| +| `ETL_USING_8BIT_TYPES` | This macro will be defined as `0` & `1` depending on whether `CHAR_BIT == 8` or not.| | `ETL_NOT_USING_8BIT_TYPES` | Inversion of `ETL_USING_8BIT_TYPES`.| -| `ETL_USING_64BIT_TYPES` | This macro will be defined as `0` & `1` dependant of whether `ETL_NO_64BIT_TYPES` is defined or not.| +| `ETL_USING_64BIT_TYPES` | This macro will be defined as `0` & `1` depending on whether `ETL_NO_64BIT_TYPES` is defined or not.| | `ETL_NOT_USING_64BIT_TYPES` | Inversion of `ETL_USING_64BIT_TYPES`.| | `ETL_HAS_ISTRING_REPAIR` | Set to `1` if the repair functionality for `etl::istring` is enabled, otherwise `0`.| | `ETL_HAS_IVECTOR_REPAIR` | Set to `1` if the repair functionality for `etl::ivector` is enabled, otherwise `0`.| @@ -103,7 +103,7 @@ These may be defined in the project settings or the user created `etl_profile.h` | `ETL_IS_DEBUG_BUILD` | Set to `1` if in a debug build, otherwise `0`.| | `ETL_HAS_POLYMORPHIC_MESSAGES` | Set to `1` if messages are polymorphic, otherwise `0`.| | `ETL_HAS_ERROR_ON_STRING_TRUNCATION` | Set to `1` if truncated strings are an error, otherwise `0`.| -| `ETL_USING_LIBC_WCHAR_H` | These macros will be defined as `0` & `1` dependant of whether `ETL_NO_LIBC_WCHAR_H` is defined or not.| +| `ETL_USING_LIBC_WCHAR_H` | These macros will be defined as `0` & `1` depending on whether `ETL_NO_LIBC_WCHAR_H` is defined or not.| | `ETL_NOT_USING_LIBC_WCHAR_H` | Inversion of `ETL_USING_LIBC_WCHAR_H`.| | `ETL_USING_CPP11` | This is defined as `1` if the compiler supports C++11, otherwise `0`.| | `ETL_USING_CPP14` | This is defined as `1` if the compiler supports C++14, otherwise `0`.| diff --git a/docs/iterators/Iterator.md b/docs/iterators/Iterator.md index c95017b7..c6028f8c 100644 --- a/docs/iterators/Iterator.md +++ b/docs/iterators/Iterator.md @@ -133,6 +133,8 @@ ETL_CONSTEXPR17 typename std::iterator_traits::difference_type distance(TIterator first, TIterator last) ``` +See [Ranges](../../ranges/ranges) for the `etl::ranges` range access primitives, including `etl::ranges::distance`. + ## iterator A base class provided to simplify definitions of the required types for iterators. https://en.cppreference.com/w/cpp/iterator/iterator diff --git a/docs/iterators/fixed_iterator.md b/docs/iterators/fixed_iterator.md index 207a16d5..033081cc 100644 --- a/docs/iterators/fixed_iterator.md +++ b/docs/iterators/fixed_iterator.md @@ -30,13 +30,13 @@ etl::fixed_iterator(const etl::fixed_iterator&); ```cpp Titerator get() const; ``` -**Decscription** +**Description** Get the internal iterator. ```cpp void get(TIterator it); ``` -**Decscription** +**Description** Set the iterator. ## Operators @@ -44,7 +44,7 @@ Set the iterator. typename etl::iterator_traits::value_type operator *() const typename etl::iterator_traits::value_type operator *() const ``` -**Decscription** +**Description** Dereference operators --- @@ -53,7 +53,7 @@ Dereference operators TIterator operator ->() const TIterator operator ->() const ``` -**Decscription** +**Description** Member dereference operators --- @@ -61,7 +61,7 @@ Member dereference operators ```cpp operator TIterator() const ``` -**Decscription** +**Description** Conversion operator ## Example diff --git a/docs/messaging/message-bus.md b/docs/messaging/message-bus.md index 921e5b86..7277c01d 100644 --- a/docs/messaging/message-bus.md +++ b/docs/messaging/message-bus.md @@ -177,7 +177,7 @@ bus2.subscribe(routerD); bus2.subscribe(routerC); // Subscribe routerE to bus3. -bus3.subscibe(routerE); +bus3.subscribe(routerE); // Assume all routers accept the same messages. diff --git a/docs/ranges/ranges.md b/docs/ranges/ranges.md index 48b9b495..32cffcd8 100644 --- a/docs/ranges/ranges.md +++ b/docs/ranges/ranges.md @@ -119,6 +119,63 @@ Output: This first filters the even numbers and then squares them. Each `|` passes the result of the previous stage as input to the next view adaptor. +## Range Access and Primitives + +These are declared in the `etl::ranges` namespace in `iterator.h`, which is included by `ranges.h`. + +### Range access + +Function objects, equivalent to their `std::ranges` counterparts. + +| Name | Description | +|---|---| +| `begin`, `end` | The iterator to the first element, and the sentinel. | +| `cbegin`, `cend` | The const iterator to the first element, and the const sentinel. | +| `rbegin`, `rend` | The reverse iterator to the last element in forward order, and the past-the-end reverse iterator. | +| `crbegin`, `crend` | The const reverse iterator to the last element in forward order, and the const past-the-end reverse iterator. | +| `size` | The number of elements, as a `size_t`. | +| `ssize` | The number of elements, as a `ptrdiff_t`. | +| `empty` | Whether the range has no elements. | +| `data`, `cdata` | A pointer to the first element. | +| `distance` | The number of elements between two iterators, or in a range. | +| `advance` | Advances an iterator by a given distance and/or to a bound. | +| `next`, `prev` | An iterator advanced forwards or backwards by a given distance and/or to a bound. | + +`size` and `ssize` use the range's `size()` member if it has one; otherwise they count the elements by traversing the range, which requires at least a forward range. + +#### distance + +```cpp +// Iterator and sentinel of the same type. +template +constexpr etl::iter_difference_t distance(I first, I last) + +// Iterator and sentinel of different types. +template +constexpr etl::iter_difference_t distance(I first, S last) + +// Range. +template +constexpr etl::iter_difference_t> distance(R&& r) +``` + +The range overload uses the range's `size()` member if it has one; otherwise it counts the elements by traversing from `begin()` to `end()`. + +### Range primitives + +Alias templates that give the associated types of a range. + +| Name | Description | +|---|---| +| `iterator_t` | The iterator type. | +| `const_iterator_t` | The const iterator type. | +| `sentinel_t` | The sentinel type. | +| `const_sentinel_t` | The const sentinel type. | +| `range_size_t` | The size type. | +| `range_difference_t` | The difference type. | +| `range_value_t` | The element value type. | +| `range_reference_t` | The element reference type. | + ## Supported Views All views are in the `etl::ranges` namespace. Corresponding range adaptor objects are available in `etl::ranges::views`. diff --git a/docs/releases/_index.md b/docs/releases/_index.md index 15553d90..fe0ec154 100644 --- a/docs/releases/_index.md +++ b/docs/releases/_index.md @@ -5,6 +5,79 @@ weight: 2002 Click on the links to see the original Github page. +## 20.49.0 + +**Pull requests:** + +[#1502](https://github.com/ETLCPP/etl/pull/1502) Remove the documentation page for `etl::type_id` +[#1503](https://github.com/ETLCPP/etl/pull/1503) Add `difference_type` to span +[#1504](https://github.com/ETLCPP/etl/pull/1504) Add missing `array` `get<>` overloads, and noexcept/constexpr +[#1505](https://github.com/ETLCPP/etl/pull/1505) Support structured bindings for array +[#1506](https://github.com/ETLCPP/etl/pull/1506) Add safe integer comparisons +[#1507](https://github.com/ETLCPP/etl/pull/1507) Various fixes and cleanup +[#1508](https://github.com/ETLCPP/etl/pull/1508) Remove redundant `reinterpret_cast` +[#1509](https://github.com/ETLCPP/etl/pull/1509) Add `etl::apply` +[#1510](https://github.com/ETLCPP/etl/pull/1510) Fix `variant` `emplace` to be valueless after a throwing construction +[#1511](https://github.com/ETLCPP/etl/pull/1511) Fix deep const-cast in `ivector` pointer repair for const pointee types +[#1513](https://github.com/ETLCPP/etl/pull/1513) `string_view`: clamp n in `remove_prefix` and remove_suffix +[#1514](https://github.com/ETLCPP/etl/pull/1514) Fix `variant` copy operations enabled with non-copyable alternatives +[#1515](https://github.com/ETLCPP/etl/pull/1515) Clean up the use of type traits by using C++11 forms wherever possible +[#1516](https://github.com/ETLCPP/etl/pull/1516) Restore .devcontainer/context directory removed by #1422 +[#1517](https://github.com/ETLCPP/etl/pull/1517) Update Github Actions to checkout@v6.0.3 +[#1519](https://github.com/ETLCPP/etl/pull/1519) fix(devcontainer): resolve missing packages in bullseye image +[#1522](https://github.com/ETLCPP/etl/pull/1522) Fix ODR violation in `optional` +[#1524](https://github.com/ETLCPP/etl/pull/1524) Fixed signed/unsigned mismatch warnings in VS2022 +[#1525](https://github.com/ETLCPP/etl/pull/1525) Fix `operator[]` for const `iota_view` and repeat_view +[#1526](https://github.com/ETLCPP/etl/pull/1526) Fix `make_array` and `make_deque` rejecting const-qualified lvalues of the element type +[#1528](https://github.com/ETLCPP/etl/pull/1528) Change `make_list` to take its arguments by forwarding reference +[#1529](https://github.com/ETLCPP/etl/pull/1529) Fix `etl::common_type` decaying both arguments to `T2` +[#1530](https://github.com/ETLCPP/etl/pull/1530) Correct the documented make_xxx factory signatures and examples +[#1534](https://github.com/ETLCPP/etl/pull/1534) Prevent usage of `N` as template value parameter +[#1535](https://github.com/ETLCPP/etl/pull/1535) Make `etl_profile` Incompatible by Default +[#1537](https://github.com/ETLCPP/etl/pull/1537) Fix ambiguous `byte_stream_reader` construction from `etl::span` +[#1538](https://github.com/ETLCPP/etl/pull/1538) Fix `expected::operator*() const&` and `&&` failing to compile +[#1540](https://github.com/ETLCPP/etl/pull/1540) Fix flat_map/reference_flat_map to use random-access iterators in lower_bound +[#1542](https://github.com/ETLCPP/etl/pull/1542) Remove redundant `noexcept` expr- #1542 +[#1544](https://github.com/ETLCPP/etl/pull/1544) Various cleanup +[#1545](https://github.com/ETLCPP/etl/pull/1545) Add etl::`is_standard_layout` and other type traits according to std +[#1546](https://github.com/ETLCPP/etl/pull/1546) Fix `delegate` clear through assignment +[#1548](https://github.com/ETLCPP/etl/pull/1548) `delegate`: restore argument deduction behaviour +[#1550](https://github.com/ETLCPP/etl/pull/1550) Add span deduction guide for types with `data()` and `size()` +[#1552](https://github.com/ETLCPP/etl/pull/1552) Add Macro for Inline Control +[#1553](https://github.com/ETLCPP/etl/pull/1553) Adopt the Library Fundamentals TS `make_array` design for the sequence factories +[#1554](https://github.com/ETLCPP/etl/pull/1554) Fix PowerPC Dockerfile to use Snapshot consistently +[#1555](https://github.com/ETLCPP/etl/pull/1555) Constexpr for unaligned_type +[#1556](https://github.com/ETLCPP/etl/pull/1556) `circular_buffer`: add initialization and assignment overloads +[#1557](https://github.com/ETLCPP/etl/pull/1557) CI: cancel superseded workflow runs +[#1558](https://github.com/ETLCPP/etl/pull/1558) CI: Filter compiler workflows by path +[#1559](https://github.com/ETLCPP/etl/pull/1559) Remove redundant `ETL_USING_STL` in tests +[#1560](https://github.com/ETLCPP/etl/pull/1560) Set default compare for `iconst_map` +[#1561](https://github.com/ETLCPP/etl/pull/1561) Allow easier construction of const_(multi_)map +[#1562](https://github.com/ETLCPP/etl/pull/1562) Various cleanup +[#1563](https://github.com/ETLCPP/etl/pull/1563) Cherry pick the Docker fixes for PowerPC +[#1564](https://github.com/ETLCPP/etl/pull/1564) Update `etl::expected` documentation +[#1565](https://github.com/ETLCPP/etl/pull/1565) Implement clamped_value utility +[#1566](https://github.com/ETLCPP/etl/pull/1566) Fix guards in `array.h` and `test/test_array.h` +[#1568](https://github.com/ETLCPP/etl/pull/1568) For optional monadic operations, add docs, tests, and complete type +[#1569](https://github.com/ETLCPP/etl/pull/1569) Add missing `etl::ranges::distance` overload +[#1570](https://github.com/ETLCPP/etl/pull/1570) Fix `FreeRTOS.h/semphr.h` include paths for ESP-IDF (fixes #1439) +[#1571](https://github.com/ETLCPP/etl/pull/1571) Drop Debian 11 (bullseye) support +[#1572](https://github.com/ETLCPP/etl/pull/1572) Fixed building and unit tests run under XCode 2027 Beta 6 +[#1573](https://github.com/ETLCPP/etl/pull/1573) Add missing type traits +[#1574](https://github.com/ETLCPP/etl/pull/1574) Make `etl::variant` trivially copyable +[#1576](https://github.com/ETLCPP/etl/pull/1576) Remove spurious link in documentation +[#1578](https://github.com/ETLCPP/etl/pull/1578) Fix implicit conversion in binary rotations +[#1579](https://github.com/ETLCPP/etl/pull/1579) Fix sanitizer builds +[#1580](https://github.com/ETLCPP/etl/pull/1580) Support variable number of arguments to inject at runtime +[#1581](https://github.com/ETLCPP/etl/pull/1581) Detect builtins +[#1582](https://github.com/ETLCPP/etl/pull/1582) Fix symbols to be exportable from C++20 named modules +[#1583](https://github.com/ETLCPP/etl/pull/1583) absolute and casting updates +[#1586](https://github.com/ETLCPP/etl/pull/1586) Fix size type of `string_view` +[#1588](https://github.com/ETLCPP/etl/pull/1588) Optimize `format.h` +[#1589](https://github.com/ETLCPP/etl/pull/1589) Fix MSVC C4244 warning in format specifiers +[#1591](https://github.com/ETLCPP/etl/pull/1591) Fix false positive warning from GCC 16 +[#1595](https://github.com/ETLCPP/etl/pull/1595) Support runtime floating-point clamped_value before C++20 + ## 20.48.1 **Pull requests:** @@ -73,7 +146,7 @@ Click on the links to see the original Github page. [#1432](https://github.com/ETLCPP/etl/pull/1432) Simplify and fix test/run-syntax-checks.sh [#1433](https://github.com/ETLCPP/etl/pull/1433) Fix the case of using STL without pthreads but with mutexes [#1434](https://github.com/ETLCPP/etl/pull/1434) Remove unnecessary includes -[#1437](https://github.com/ETLCPP/etl/pull/1437) ranges: Fix confict with Zephyr preprocessor definition +[#1437](https://github.com/ETLCPP/etl/pull/1437) ranges: Fix conflict with Zephyr preprocessor definition [#1440](https://github.com/ETLCPP/etl/pull/1440) Make etl::variant capable for ROM placement and optimize runtime size [#1441](https://github.com/ETLCPP/etl/pull/1441) Make etl::variant capable for ROM placement and optimize runtime size [#1442](https://github.com/ETLCPP/etl/pull/1442) Fix format: float zero-padding, nested replacement width, octal alter… @@ -152,7 +225,7 @@ Click on the links to see the original Github page. **Issues:** [#1184](https://github.com/ETLCPP/etl/issues/1184) Request: Adding a method to check whether a etl::callback_timer_atomic is currently running -[#1189](https://github.com/ETLCPP/etl/issues/1189) Request: make useage of osMutexRobust in etl::mutex optional +[#1189](https://github.com/ETLCPP/etl/issues/1189) Request: make usage of osMutexRobust in etl::mutex optional [#1207](https://github.com/ETLCPP/etl/issues/1207) Monadic Operations for etl::expected [#1240](https://github.com/ETLCPP/etl/issues/1240) Add redundant semicolon checks to CMakeLists.txt and fix issues [#1241](https://github.com/ETLCPP/etl/issues/1241) Add -pedantic-errors to unit test CMakeLists.txt and fix issues @@ -335,7 +408,7 @@ Removed redundant ETL_USING_WIDE_CHARACTERS Fixes: #1126 to_arithmetic-does-not-compile-on-C++98 #1128 Request: Make Delegate Methods noexcept -#1130 Use of etl::mem_copy for overlaped areas +#1130 Use of etl::mem_copy for overlapped areas #1132 Make type_def Methods Noexcept #1143 v20.41.x, risc-v (esp32-c3) "No user defined specialisation of etl::underlying_type for this type" @@ -510,11 +583,11 @@ Fixes: #942 Warning on pragma c++20-compat #951 Can't get call_if to work because of etl::optional #956 Fix build error (etl::circular_buffer) -#957 Support heterogenous lookup for maps +#957 Support heterogeneous lookup for maps #959 Treat bitset with size_type #972 Template not allowed warning in parameter pack #979 QueuedMessageRouter does not work : message are not sent to queue -#980 Documentation clarification: Add link from endianess to unaligned_type +#980 Documentation clarification: Add link from endianness to unaligned_type #982 Added return to etl::optional emplace, fixed typo #984 Update Base64 Documentation #986 Added const iterators to span @@ -639,7 +712,7 @@ Pull Requests: #905 Added support for type identity Other: -Reenabled address sanitizer for VS2022 +Re-enabled address sanitizer for VS2022 20.38.16 diff --git a/docs/strings/to-arithmetic.md b/docs/strings/to-arithmetic.md index be571377..297ae45d 100644 --- a/docs/strings/to-arithmetic.md +++ b/docs/strings/to-arithmetic.md @@ -327,7 +327,7 @@ Inequality test for etl::to_arithmetic_result ```cpp template ETL_CONSTEXPR14 bool operator !=(const T& lhs, - const etl::to_arithmetic_result& rhs) + const etl::to_arithmetic_result& rhs) ``` Inequality test for etl::to_arithmetic_result diff --git a/docs/timers/_index.md b/docs/timers/_index.md index 127fda77..a9129c17 100644 --- a/docs/timers/_index.md +++ b/docs/timers/_index.md @@ -3,4 +3,4 @@ title: "Timers" weight: 100 --- -Classes the implement timer based services. +Classes that implement timer-based services. diff --git a/docs/timers/callback-timer-interrupt.md b/docs/timers/callback-timer-interrupt.md index 45834642..54d3b116 100644 --- a/docs/timers/callback-timer-interrupt.md +++ b/docs/timers/callback-timer-interrupt.md @@ -196,7 +196,7 @@ Test test; //*************************************************************************** // The interrupt guard type. -// Saves and disables on contruction. +// Saves and disables interrupts on construction. // Restores on destruction. //*************************************************************************** struct InterruptGuard diff --git a/docs/timers/message-timer-interrupt.md b/docs/timers/message-timer-interrupt.md index 9cbc6d70..c41dc227 100644 --- a/docs/timers/message-timer-interrupt.md +++ b/docs/timers/message-timer-interrupt.md @@ -193,7 +193,7 @@ Message3 message3; //*************************************************************************** // The interrupt guard type. -// Saves and disables on contruction. +// Saves and disables on construction. // Restores on destruction. //*************************************************************************** struct InterruptGuard diff --git a/docs/timers/timer.md b/docs/timers/timer.md index b09785b3..d0d4ed63 100644 --- a/docs/timers/timer.md +++ b/docs/timers/timer.md @@ -15,7 +15,7 @@ struct timer { // Timer modes. struct mode - { + { enum { SINGLE_SHOT = false, diff --git a/docs/tutorials/callback-service.md b/docs/tutorials/callback-service.md index 972e97c1..fccf4a77 100644 --- a/docs/tutorials/callback-service.md +++ b/docs/tutorials/callback-service.md @@ -19,7 +19,7 @@ This code demonstrates using the callback service for five example ARM interrupt `Timer3` has no entry in the callback service and will therefore trigger execution of the unhandled handler. `USART1` and `USART2` interrupts are handled by instances of Uart. -There callbacks are defined withing the class and are initialised in the Uart constructor. +Their callbacks are defined within the class and are initialised in the Uart constructor. ```cpp #include @@ -146,7 +146,7 @@ Uart uart1(0, USART1_IRQ_HANDLER); Uart uart2(1, USART2_IRQ_HANDLER); // Declare a global callback for the timer. -// Uses the most efficient callback type for a class, as everthing is known at compile time. +// Uses the most efficient callback type for a class, as everything is known at compile time. etl::function_imp timer_member_callback; // Declare the callbacks for the free functions. diff --git a/docs/tutorials/delegate-service.md b/docs/tutorials/delegate-service.md index edb85f68..cc7dd612 100644 --- a/docs/tutorials/delegate-service.md +++ b/docs/tutorials/delegate-service.md @@ -13,7 +13,7 @@ This code demonstrates using the delegate service for five example ARM interrupt `Timer3` has no entry in the callback service and will therefore trigger execution of the unhandled handler. `USART1` and `USART2` interrupts are handled by instances of `Uart`. -There callbacks are defined withing the class and are initialised in the `Uart` constructor. +Their callbacks are defined within the class and are initialised in the `Uart` constructor. ```cpp #include @@ -137,7 +137,7 @@ Uart uart1(0, USART1_IRQ_HANDLER); Uart uart2(1, USART2_IRQ_HANDLER); // Declare a global callback for the timer. -// Uses the most efficient callback type for a class, as everthing is known at compile time. +// Uses the most efficient callback type for a class, as everything is known at compile time. etl::delegate timer_member_callback = etl::delegate::create(); diff --git a/docs/tutorials/shared-message_tutorial.md b/docs/tutorials/shared-message_tutorial.md index f94efb1b..e692a8d7 100644 --- a/docs/tutorials/shared-message_tutorial.md +++ b/docs/tutorials/shared-message_tutorial.md @@ -34,7 +34,7 @@ To this end a template alias is defined; `etl::atomic_counted_message`, which us In a muti-threaded or interrupt driven system, a method must be derived to manage access to the pool. All reference counted messages have an owner. -Owners are obects based on `etl::ireference_counted_message_pool`. +Owners are objects based on `etl::ireference_counted_message_pool`. A reference counted message pool will allocate reference counted messages. The storage for these are allocated from a supplied memory block allocator. @@ -256,7 +256,7 @@ private: }; //***************************************************************************** -// A message bus that can accomodate two subscribers. +// A message bus that can accommodate two subscribers. //***************************************************************************** struct Bus : public etl::message_bus<2U> { diff --git a/docs/types/tuple.md b/docs/types/tuple.md index 5981e34c..666719fa 100644 --- a/docs/types/tuple.md +++ b/docs/types/tuple.md @@ -249,3 +249,21 @@ template ETL_CONSTEXPR14 const T&& get(const tuple&&); ``` + +--- + +```cpp +template +ETL_CONSTEXPR14 +auto apply(TFunction&& f, TTuple&& t); +``` + +Invokes the callable object `f` (via `etl::invoke`) with the elements of the tuple `t` expanded as its arguments. Equivalent to `std::apply`. + +```cpp +int add(int a, int b, int c) { return a + b + c; } + +auto t = etl::make_tuple(1, 2, 3); +int sum = etl::apply(add, t); // sum == 6 +``` + diff --git a/docs/types/type-traits.md b/docs/types/type-traits.md index 721fdfc3..cf718739 100644 --- a/docs/types/type-traits.md +++ b/docs/types/type-traits.md @@ -32,6 +32,28 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `is_compound` `is_array` `is_pointer` +`is_null_pointer` +`is_scalar` +`is_union` +`is_empty` +`is_polymorphic` +`is_abstract` +`is_final` +`is_aggregate` +`is_trivial` +`is_standard_layout` +`is_layout_compatible` +`is_pointer_interconvertible_base_of` +`is_destructible` +`is_nothrow_destructible` +`has_virtual_destructor` +`has_unique_object_representations` +`is_bounded_array` +`is_unbounded_array` +`is_swappable` +`is_swappable_with` +`is_nothrow_swappable` +`is_nothrow_swappable_with` `is_reference` `is_base_of` `make_signed` @@ -52,6 +74,7 @@ Not all traits have been defined as some rely on compiler intrinsics that are no `declvar` `20.28.0` `common_type` `is_enum` `20.30.0` +`is_scoped_enum` `20.49.0` `underlying_type` `20.42.0`  Unless the ETL is set to use builtins, the user must specialise the template for their enumerations. @@ -67,8 +90,12 @@ The following will be defined according to the C++ standard and user defined mac `is_move_constructible` `is_trivially_constructible` `is_trivially_copy_constructible` +`is_trivially_default_constructible` +`is_trivially_move_constructible` `is_trivially_destructible` +`is_trivially_assignable` `is_trivially_copy_assignable` +`is_trivially_move_assignable` `is_trivially_copyable` ## Scenario 1 @@ -476,3 +503,499 @@ template inline constexpr bool is_object_pointer_v = etl::is_object_pointer::value; ``` C++17 + +## is_pod +From: `20.49.0` + +```cpp +template +struct is_pod +``` +Checks if `T` is a POD (Plain Old Data) type. +The result is found in the member `value`. + When using the STL, this is defined as `is_standard_layout && is_trivially_default_constructible && is_trivially_copyable`. + When not using the STL, this is conservatively defined as `is_fundamental || is_pointer`. + +```cpp +template +inline constexpr bool is_pod_v = etl::is_pod::value; +``` +C++17 + +## is_standard_layout +From: `20.49.0` + +```cpp +template +struct is_standard_layout +``` +Checks if `T` is a standard-layout type. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_standard_layout_v = etl::is_standard_layout::value; +``` +C++17 + +## is_trivial +From: `20.49.0` + +```cpp +template +struct is_trivial +``` +Checks if `T` is a trivial type; i.e. it is trivially copyable and has one or more +default constructors, all of which are trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivial_v = etl::is_trivial::value; +``` +C++17 + +**Note:** When not using the STL, this trait requires compiler support (the `__is_trivial` intrinsic). +If that is not available then the trait is not defined, as there is no portable way of +determining whether a type is trivial. + +## is_bounded_array +From: `20.49.0` + +```cpp +template +struct is_bounded_array +``` +Checks if `T` is an array type of known bound. +`T[N]` returns `true`, `T[]` and all other types return `false`. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_bounded_array_v = etl::is_bounded_array::value; +``` +C++17 + +## is_unbounded_array +From: `20.49.0` + +```cpp +template +struct is_unbounded_array +``` +Checks if `T` is an array type of unknown bound. +`T[]` returns `true`, `T[N]` and all other types return `false`. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_unbounded_array_v = etl::is_unbounded_array::value; +``` +C++17 + +## is_destructible +From: `20.49.0` + +```cpp +template +struct is_destructible +``` +Checks if `T` is a destructible type; that is, an object type whose destructor is not deleted and is accessible, a reference type, or an array of a destructible type. +Incomplete types, `void`, function types and arrays of unknown bound are not destructible. +The result is found in the member `value`. + When not using the STL, the trait is detected by checking whether `declval().~T()` is a valid expression. + Requires either compiler support or C++11. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_destructible_v = etl::is_destructible::value; +``` +C++17 + +## is_nothrow_destructible +From: `20.49.0` + +```cpp +template +struct is_nothrow_destructible +``` +Checks if `T` is a destructible type whose destructor is known not to throw. +Types that are not destructible are not nothrow destructible. +The result is found in the member `value`. + When not using the STL, the trait is detected by checking `noexcept(declval().~T())` for destructible types. + Requires either compiler support or C++11. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_nothrow_destructible_v = etl::is_nothrow_destructible::value; +``` +C++17 + +## is_swappable +From: `20.49.0` + +```cpp +template +struct is_swappable +``` +Checks if `T` is a referenceable type and lvalues of type `T` can be swapped by an unqualified call to `swap`. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_swappable`. + Otherwise the trait checks whether `swap(declval(), declval())` is a valid expression, where the `swap` considered is `etl::swap`, `std::swap` if it is available, and any `swap` found by argument dependent lookup. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when using the STL with C++11 or C++14, the accuracy of this trait depends on `std::swap` being constrained by the standard library implementation. Standard libraries that only constrain `std::swap` from C++17 will cause this trait to report `true` for types that cannot actually be swapped. + +```cpp +template +inline constexpr bool is_swappable_v = etl::is_swappable::value; +``` +C++17 + +## is_swappable_with +From: `20.49.0` + +```cpp +template +struct is_swappable_with +``` +Checks if expressions of type `T` and `U` can be swapped by unqualified calls to `swap`, in both directions. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_swappable_with`. + Otherwise the trait checks whether both `swap(declval(), declval())` and `swap(declval(), declval())` are valid expressions, where the `swap` considered is `etl::swap`, `std::swap` if it is available, and any `swap` found by argument dependent lookup. + Note that, unlike `is_swappable`, no references are added to `T` and `U`, so the value categories of the arguments are those of `T` and `U`. `is_swappable_with::value` is `false`, whereas `is_swappable_with::value` is `true`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when using the STL with C++11 or C++14, the accuracy of this trait depends on `std::swap` being constrained by the standard library implementation. Standard libraries that only constrain `std::swap` from C++17 will cause this trait to report `true` for types that cannot actually be swapped. + +```cpp +template +inline constexpr bool is_swappable_with_v = etl::is_swappable_with::value; +``` +C++17 + +## is_nothrow_swappable +From: `20.49.0` + +```cpp +template +struct is_nothrow_swappable +``` +Checks if `T` is a referenceable type and lvalues of type `T` can be swapped by an unqualified call to `swap`, and that the swap cannot throw. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_nothrow_swappable`. + Otherwise the trait checks that `etl::is_swappable::value` is `true` and that `swap(declval(), declval())` is `noexcept`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when neither the STL nor the type traits builtins are available, `etl::is_nothrow_move_constructible` and `etl::is_nothrow_move_assignable` are conservatively `false` for class types, so `etl::swap` is not `noexcept` for them and this trait is conservatively `false` too. + +```cpp +template +inline constexpr bool is_nothrow_swappable_v = etl::is_nothrow_swappable::value; +``` +C++17 + +## is_nothrow_swappable_with +From: `20.49.0` + +```cpp +template +struct is_nothrow_swappable_with +``` +Checks if expressions of type `T` and `U` can be swapped by unqualified calls to `swap`, in both directions, and that neither swap can throw. +The result is found in the member `value`. + When using the STL and C++17, this is defined as `std::is_nothrow_swappable_with`. + Otherwise the trait checks that `etl::is_swappable_with::value` is `true` and that both `swap(declval(), declval())` and `swap(declval(), declval())` are `noexcept`. + Note that, as for `is_swappable_with`, no references are added to `T` and `U`, so the value categories of the arguments are those of `T` and `U`. `is_nothrow_swappable_with::value` is `false`, whereas `is_nothrow_swappable_with::value` is `true`. + Requires C++11. Without it there is no way of detecting the validity of an expression, so this trait is not defined. + Note that when neither the STL nor the type traits builtins are available, `etl::is_nothrow_move_constructible` and `etl::is_nothrow_move_assignable` are conservatively `false` for class types, so `etl::swap` is not `noexcept` for them and this trait is conservatively `false` too. + +```cpp +template +inline constexpr bool is_nothrow_swappable_with_v = etl::is_nothrow_swappable_with::value; +``` +C++17 + +## is_union +From: `20.49.0` + +```cpp +template +struct is_union +``` +Checks if `T` is a union type. +The result is found in the member `value`. + When the compiler does not provide the required intrinsic, this trait conservatively reports `false` for every type. + +```cpp +template +inline constexpr bool is_union_v = etl::is_union::value; +``` +C++17 + +## is_empty +From: `20.49.0` + +```cpp +template +struct is_empty +``` +Checks if `T` is an empty class type; that is, a non-union class type with no non-static data members other than zero-sized bit-fields, no virtual functions, no virtual base classes and no non-empty base classes. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_empty_v = etl::is_empty::value; +``` +C++17 + +## is_polymorphic +From: `20.49.0` + +```cpp +template +struct is_polymorphic +``` +Checks if `T` is a polymorphic class type; that is, a class that declares or inherits at least one virtual function. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_polymorphic_v = etl::is_polymorphic::value; +``` +C++17 + +## is_abstract +From: `20.49.0` + +```cpp +template +struct is_abstract +``` +Checks if `T` is an abstract class type; that is, a class that declares or inherits at least one pure virtual function. +The result is found in the member `value`. + Requires compiler support. When the compiler does not provide the required intrinsic, this trait is not defined. + +```cpp +template +inline constexpr bool is_abstract_v = etl::is_abstract::value; +``` +C++17 + +## is_final +From: `20.49.0` + +```cpp +template +struct is_final +``` +Checks if `T` is a class or union type marked `final`, optionally cv-qualified. +The result is found in the member `value`. + When using the STL, this requires C++14 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_final_v = etl::is_final::value; +``` +C++17 + +## is_aggregate +From: `20.49.0` + +```cpp +template +struct is_aggregate +``` +Checks if `T` is an aggregate type, optionally cv-qualified. +An aggregate is an array type or a class type that has no user-declared or inherited constructors, no private or protected non-static data members, no virtual functions and no virtual, private or protected base classes. +The result is found in the member `value`. + When using the STL, this requires C++17 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool is_aggregate_v = etl::is_aggregate::value; +``` +C++17 + +## has_virtual_destructor +From: `20.49.0` + +```cpp +template +struct has_virtual_destructor +``` +Checks if `T` is a class type with a virtual destructor, optionally cv-qualified. +The result is found in the member `value`. + When not using the STL, this requires compiler support. When it is not available, this trait is not defined. + +```cpp +template +inline constexpr bool has_virtual_destructor_v = etl::has_virtual_destructor::value; +``` +C++17 + +## has_unique_object_representations +From: `20.49.0` + +```cpp +template +struct has_unique_object_representations +``` +Checks if `T` is trivially copyable and any two objects of type `T` with the same value have the same object representation; that is, the type has no padding bits and no two distinct object representations compare equal. +Arrays and cv-qualifiers are ignored; the trait is applied to `etl::remove_all_extents::type>::type`. +The result is found in the member `value`. + When using the STL, this requires C++17 or above. Otherwise it requires compiler support. When neither is available, this trait is not defined. + +```cpp +template +inline constexpr bool has_unique_object_representations_v = etl::has_unique_object_representations::value; +``` +C++17 + +## is_null_pointer +From: `20.49.0` + +```cpp +template +struct is_null_pointer +``` +Checks if `T` is `etl::nullptr_t`, optionally cv-qualified. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_null_pointer_v = etl::is_null_pointer::value; +``` +C++17 + +## is_scalar +From: `20.49.0` + +```cpp +template +struct is_scalar +``` +Checks if `T` is a scalar type; that is, an arithmetic, enumeration, pointer, pointer to member or `etl::nullptr_t` type, optionally cv-qualified. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_scalar_v = etl::is_scalar::value; +``` +C++17 + +## is_scoped_enum +From: `20.49.0` + +```cpp +template +struct is_scoped_enum +``` +Checks if `T` is a scoped enumeration type; that is, an enumeration type that is not implicitly convertible to an integer. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; +``` +C++17 + +## is_trivially_default_constructible +From: `20.49.0` + +```cpp +template +struct is_trivially_default_constructible +``` +Checks if `T` is default constructible and the default construction is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_default_constructible_v = etl::is_trivially_default_constructible::value; +``` +C++17 + +## is_trivially_move_constructible +From: `20.49.0` + +```cpp +template +struct is_trivially_move_constructible +``` +Checks if `T` is move constructible and the move construction is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_move_constructible_v = etl::is_trivially_move_constructible::value; +``` +C++17 + +## is_trivially_move_assignable +From: `20.49.0` + +```cpp +template +struct is_trivially_move_assignable +``` +Checks if `T` is move assignable and the move assignment is trivial. +The result is found in the member `value`. + +```cpp +template +inline constexpr bool is_trivially_move_assignable_v = etl::is_trivially_move_assignable::value; +``` +C++17 + +## is_trivially_assignable + +```cpp +template +struct is_trivially_assignable +``` +Checks if an expression of type `T2` can be assigned to an lvalue expression of type `T1`, and that the assignment is trivial. +The result is found in the member `value`. + See Scenarios 1 to 4 above for how this trait is defined when the STL or the compiler built-ins are unavailable. + +```cpp +template +inline constexpr bool is_trivially_assignable_v = etl::is_trivially_assignable::value; +``` +C++17 + +## is_layout_compatible +From: `20.49.0` + +```cpp +template +struct is_layout_compatible +``` +Checks if `T` and `U` are layout-compatible types. +The result is found in the member `value`. + When using the STL with C++20 or above, this is defined as `std::is_layout_compatible`. + Otherwise it requires compiler support (the `__is_layout_compatible` intrinsic). When neither is available, this trait is not defined, as there is no portable way of determining layout compatibility. + +```cpp +template +inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; +``` +C++17 + +## is_pointer_interconvertible_base_of +From: `20.49.0` + +```cpp +template +struct is_pointer_interconvertible_base_of +``` +Checks if `TBase` is a pointer-interconvertible base class of `TDerived`; that is, every object of type `TDerived` has its `TBase` base class subobject at the same address as the complete object. +The result is found in the member `value`. + When using the STL with C++20 or above, this is defined as `std::is_pointer_interconvertible_base_of`. + Otherwise it requires compiler support (the `__is_pointer_interconvertible_base_of` intrinsic). When neither is available, this trait is not defined, as there is no portable way of determining pointer interconvertibility. + Note that, as required by the standard, the program is ill-formed if `TDerived` is a complete non-union class type and `TBase` is an incomplete class type. The trait cannot be used to probe possibly-incomplete types. + +```cpp +template +inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; +``` +C++17 diff --git a/docs/utilities/bit-stream-reader.md b/docs/utilities/bit-stream-reader.md index bba3e54c..64acaa66 100644 --- a/docs/utilities/bit-stream-reader.md +++ b/docs/utilities/bit-stream-reader.md @@ -248,15 +248,15 @@ namespace etl etl::optional read_unchecked(etl::bit_stream_reader& stream) { char c = stream.read_unchecked(); - short s = stream.read_unchecked(); - int32_t i = stream.read_unchecked(); + short s = stream.read_unchecked(11U); + int32_t i = stream.read_unchecked(25U); return CustomType { c, s, i }; } } std::array storage; // Assume the buffer gets filled with bit stream data. -etl::bit_stream_reader bit_stream(storage.data(), storage.size()); +etl::bit_stream_reader bit_stream(storage.data(), storage.size(), etl::bit_order::msb_first); // Read unchecked values from the stream. char c = etl::read_unchecked(bit_stream, 6U); diff --git a/docs/utilities/bit-stream-writer.md b/docs/utilities/bit-stream-writer.md index 3d83c0e4..5cc3f4a6 100644 --- a/docs/utilities/bit-stream-writer.md +++ b/docs/utilities/bit-stream-writer.md @@ -328,7 +328,7 @@ Sets the delegate to call after every write. callback_type get_callback() const ``` **Description** -Gets the function to call afer every write. +Gets the function to call after every write. --- @@ -397,13 +397,13 @@ namespace etl void write_unchecked(etl::bit_stream_reader& stream) { stream.write_unchecked(); - stream.write_unchecked(); + stream.write_unchecked(); stream.write_unchecked(); } } std::array storage; // Assume the buffer gets filled with bit stream data. -etl::bit_stream_reader bit_stream(storage.data(), storage.size()); +etl::bit_stream_writer bit_stream(storage.data(), storage.size(), etl::bit_order::msb_first); // Write unchecked values to the stream. etl::write_unchecked(bit_stream, 6U); diff --git a/docs/utilities/clamped-value.md b/docs/utilities/clamped-value.md new file mode 100644 index 00000000..5bae0543 --- /dev/null +++ b/docs/utilities/clamped-value.md @@ -0,0 +1,171 @@ +--- +title: "clamped_value" +--- + +{{< callout type="info">}} + Header: `clamped_value.h` + Since: `TBC` +{{< /callout >}} + +Provides a value constrained between a minimum and maximum value. Values are +clamped using `etl::clamp`. The type supports compile-time bounds to minimise +storage and runtime bounds when the range must be changed. Integral types are +supported in all language modes. Floating-point types with runtime bounds are +supported in C++11 and later when `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is +`1`. Floating-point compile-time bounds require C++20 support for +floating-point non-type template arguments. + +## Availability + +`ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is always defined by this header as +either `0` or `1`. It defaults to `1` in C++11 and later. The related +`ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE` macro defaults to `1` only +when C++20 is enabled and the compiler supports floating-point non-type +template arguments. A platform profile or build may define either macro before +including the header to override automatic detection. + +When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral +type. When it is `1`, `float`, `double`, and `long double` may also be used with +runtime bounds. + +```cpp +// C++11 and later +template +class clamped_value; +``` + +```cpp +etl::clamped_value value_ct; // Fixed range [2, 7]. + +etl::clamped_value value_rt(2, 7, 5); // Runtime range and initial value. + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value float_rt(-1.5, 2.5, 0.5); +#endif + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value float_ct(0.5f); +#endif +``` + +## Types + +`difference_type` is the signed counterpart of `T` for integral values. For +floating-point values, `difference_type` is `T`, allowing fractional steps. +`limits_type` and `difference_limits_type` provide the corresponding +`etl::numeric_limits` specializations. + +## Constructors + +```cpp +clamped_value value; +``` + +Creates a compile-time clamped value initialized to `Min`. + +```cpp +clamped_value value(min, max); +clamped_value value(min, max, initial); +``` + +Creates a runtime clamped value. The two-argument form initializes the value +to `min`; the three-argument form clamps `initial` to the range. + +`min` must not be greater than `max`. Omitting the template bounds selects the +runtime-bound specialization. Supplying `Min` and `Max` selects fixed +compile-time bounds. NaN bounds and initial values are rejected. Infinite +bounds and values are permitted. + +## Modifiers + +```cpp +void set(T value); +void set(T min, T max); +void to_min(); +void to_max(); +void advance(difference_type n) ETL_NOEXCEPT; +``` + +`set(T)` clamps the current value. The runtime `set(min, max)` changes the +range and resets the value to the new minimum. `to_min()` and `to_max()` move +the value directly to a bound. For integral values, `advance` uses the signed +counterpart of `T` as its step type. For floating-point values, the step type +is `T` and may be fractional. Steps saturate in constant time at the minimum +or maximum rather than wrapping. NaN steps are rejected. + +Positive steps move toward `max`; negative steps move toward `min`. Infinite +steps saturate at a finite bound. Infinite values and bounds remain valid. + +```cpp +etl::clamped_value value(0.25f); +value.advance(0.5f); // 0.75f +value.advance(10.0f); // 1.0f +value.advance(-0.5f); // 0.5f +``` + +Increment and decrement move by one and also saturate, including when a +floating-point bound is less than one unit away: + +```cpp +++value; // Remains Max when already at Max. +--value; // Remains Min when already at Min. +``` + +## Access + +```cpp +ETL_NODISCARD T get() const noexcept; +ETL_NODISCARD T min() const noexcept; +ETL_NODISCARD T max() const noexcept; +``` + +Gets the current value and its bounds. Both compile-time and runtime +specializations provide these accessors as const-qualified member functions. + +## Operators + +```cpp +ETL_NODISCARD operator T() const noexcept; +clamped_value& operator++() & noexcept; +clamped_value operator++(int) noexcept; +clamped_value& operator--() & noexcept; +clamped_value operator--(int) noexcept; +clamped_value& operator=(T value) & noexcept; +clamped_value& operator+=(difference_type n) & noexcept; +clamped_value& operator-=(difference_type n) & noexcept; +``` + +Conversion and assignment to the underlying type are supported. Assignment is +clamped to the configured range. The lvalue reference qualification on +assignment prevents assignment through temporary values where supported. For +floating-point values, assignment and compound-assignment reject NaN. + +Prefix increment and decrement return a reference to the updated object. +Postfix increment and decrement return a copy of the value before it was +updated. All four operations move by one and saturate at the applicable bound. + +## Operations + +```cpp +ETL_CONSTEXPR14 void swap(clamped_value& other); +ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs); +``` + +Swaps clamped values. Runtime values also swap their bounds. + +Equality, inequality, and relational comparisons are provided. Comparisons +against arithmetic values use the implicit conversion to `T`. Equality +compares the current values; runtime bounds do not affect equality. Runtime +ranges must satisfy `min <= max`; invalid ranges trigger an ETL assertion. The +default runtime specialization uses the full representable range of `T`. + +Floating-point comparisons use the underlying values directly; no approximate +or tolerance-based comparison is applied. + +## Error Handling + +Runtime ranges with `min > max` trigger an ETL assertion. NaN values, bounds, +assignments, and steps also trigger an ETL assertion. Depending on the ETL +error-handler configuration, an assertion may throw `etl::exception`, invoke a +configured handler, or terminate. Operations that validate input are only +`noexcept` when exceptions are disabled. diff --git a/docs/utilities/endian.md b/docs/utilities/endian.md index 1aa2c969..4de8014d 100644 --- a/docs/utilities/endian.md +++ b/docs/utilities/endian.md @@ -7,11 +7,11 @@ title: "endian" Since: `TBC` {{< /callout >}} -Constants & utilities for endianess. +Constants & utilities for endianness. For endian specific integral types, see `unaligned_type`. -The values for endianness are dependant on the platform setup. +The values for endianness are dependent on the platform setup. If `ETL_ENDIAN_NATIVE` is defined by the user, then  `etl::endian::little = 0` diff --git a/docs/utilities/error-handler.md b/docs/utilities/error-handler.md index ab317500..074b199e 100644 --- a/docs/utilities/error-handler.md +++ b/docs/utilities/error-handler.md @@ -16,7 +16,7 @@ You have a choice of: - Error log - No error checking -The type of error handler used is dependant on the compile time macro defined. +The type of error handler used is dependent on the compile time macro defined. Note: This are usually set as a project wide definition. | Macro | Actions | diff --git a/docs/utilities/expected.md b/docs/utilities/expected.md index f6cbcd63..ab021424 100644 --- a/docs/utilities/expected.md +++ b/docs/utilities/expected.md @@ -192,13 +192,13 @@ Swap with another etl::unexpected ```cpp template class expected -```cpp +``` Specialisation for void value type. ```cpp template class expected -```cpp +``` ### Types @@ -236,6 +236,72 @@ Since: C++11 --- +```cpp +template +ETL_CONSTEXPR14 expected(U&& value) +``` +**Description** +Construct the value type from a value implicitly convertible to it. +Not selected when U is an etl::expected, etl::unexpected, etl::in_place_t, etl::unexpect_t or value_type itself. +Since: C++11 + +--- + +```cpp +template +ETL_CONSTEXPR14 explicit expected(U&& value) +``` +**Description** +Construct the value type from a value that is only explicitly convertible to it. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +expected(const etl::expected& other) +``` +**Description** +Copy construct from an etl::expected whose value and error types are both implicitly convertible. +Not selected when value_type can be constructed from, or etl::unexpected built from, the source etl::expected itself. +Since: C++11 + +--- + +```cpp +template +explicit expected(const etl::expected& other) +``` +**Description** +Copy construct from an etl::expected whose value or error type is only explicitly convertible. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +expected(etl::expected&& other) +``` +**Description** +Move construct from an etl::expected whose value and error types are both implicitly convertible. +Constrained as above. +Since: C++11 + +--- + +```cpp +template +explicit expected(etl::expected&& other) +``` +**Description** +Move construct from an etl::expected whose value or error type is only explicitly convertible. +Constrained as above. +Since: C++11 + +--- + ```cpp ETL_CONSTEXPR14 expected(const expected& other) ETL_NOEXCEPT ``` @@ -353,6 +419,17 @@ Since: C++11 --- +```cpp +template +expected& operator =(U&& value) +``` +**Description** +Assign from a value convertible to value_type. +Constrained like the value constructor. +Since: C++11 + +--- + ```cpp expected& operator =(const unexpected_type& error) ``` @@ -421,6 +498,7 @@ value_type& value() const Get the value. Undefined if has_value() returns false. Not valid for void specialisation. +Before: C++11 --- @@ -536,13 +614,46 @@ Before: C++11 --- +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +error_type error_or(G&& default_error) const& +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Since: C++11 + +--- + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +error_type error_or(G&& default_error)&& +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Since: C++11 + +--- + +```cpp +template +error_type error_or(const G& default_error) const +``` +**Description** +Returns default_error if has_value() returns true; otherwise returns the stored error. +Before: C++11 + +--- + ```cpp template ETL_CONSTEXPR14 value_type& emplace(TArgs&&... args) ETL_NOEXCEPT ``` **Description** -Returns the error if has_value() returns false. -Undefined if has_value() returns true. +Create from arguments. Since: C++11 --- @@ -574,15 +685,411 @@ Const class member access operator. --- ```cpp -value_type& operator *() +value_type& operator *() & ``` -**Description** +**Description** Dereference operator. --- ```cpp -const value_type& operator *() const +const value_type& operator *() const& ``` -**Description** +**Description** Dereference operator. + +--- + +```cpp +value_type&& operator *() && +``` +**Description** +Dereference operator. +Since: C++11 + +--- + +```cpp +const value_type&& operator *() const&& +``` +**Description** +Dereference operator. +Since: C++11 + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +value_type* begin() ETL_NOEXCEPT +``` +**Description** +Returns a pointer to the value if has_value() returns true, otherwise returns nullptr. +Allows expected to be used as a range of 0 or 1 elements. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +const value_type* begin() const ETL_NOEXCEPT +``` +**Description** +Returns a pointer to the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +value_type* end() ETL_NOEXCEPT +``` +**Description** +Returns a pointer past the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +ETL_NODISCARD +ETL_CONSTEXPR14 +const value_type* end() const ETL_NOEXCEPT +``` +**Description** +Returns a pointer past the value if has_value() returns true, otherwise returns nullptr. Not valid for void specialisation. + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) & -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) & -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const& -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) && -> expected +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) && -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const&& -> expected +``` +**Description** +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform(F&& f) const&& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result wrapped in an expected object; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) & -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type> +auto and_then(F&& f) & -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const& -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) && -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) && -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const&& -> U +``` +**Description** +If has_value() returns true, it invokes f on the value and returns the result; otherwise it returns an expected object that contains error(). For void specialisation see below. +Since: C++11 + +--- + +```cpp +template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> +auto and_then(F&& f) const&& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it invokes f and returns the result; otherwise it returns an expected object that contains error(). +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) & -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) & -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const& -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) && -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) && -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const&& -> U +``` +**Description** +If has_value() returns true, it returns an expected object that contains value(); otherwise it invokes f on error() and returns the result. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto or_else(F&& f) const&& -> U +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) & -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) & -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const& -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) && -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) && -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const&& -> expected +``` +**Description** +If has_value() returns true, it returns an expected that contains value(); otherwise it invokes f on error() and returns the result wrapped in an expected object. For void specialisation see below. +Since: C++11 + +--- + +```cpp +template ::type>::type> +auto transform_error(F&& f) const&& -> expected +``` +**Description** +For void specialisation. +If has_value() returns true, it returns a default constructed expected object of the appropriate type; otherwise it invokes f on error() and returns the result wrapped in an expected object. +Since: C++11 + diff --git a/docs/utilities/initializer-list.md b/docs/utilities/initializer-list.md index 48cd46a1..054e1373 100644 --- a/docs/utilities/initializer-list.md +++ b/docs/utilities/initializer-list.md @@ -18,7 +18,7 @@ The `initializer_list` template class is a special case in that it *must* have a This header will either include the standard `` header, or attempt to define a `std::initializer_list` template class that is compatible with the current compiler. -The header will define `ETL_USING_INITIALIZER_LIST` to either `1` or `0` dependant of whether `std::initializer_list` is available or not. +The header will define `ETL_USING_INITIALIZER_LIST` to either `1` or `0` dependent on whether `std::initializer_list` is available or not. The ETL will select the STL version if you are using the STL and `ETL_FORCE_ETL_INITIALIZER_LIST` is not defined or, `ETL_FORCE_STD_INITIALIZER_LIST` is defined. diff --git a/docs/utilities/optional.md b/docs/utilities/optional.md index a12aac75..0c41a829 100644 --- a/docs/utilities/optional.md +++ b/docs/utilities/optional.md @@ -222,6 +222,64 @@ bool has_value() const Checks whether *this is in a valid state, i.e. whether the contained value is initialised. Returns `true` if valid, otherwise `false`. +## Monadic operations +C++11 and above. + +Each operation is supplied as four reference qualified overloads (`&`, `const&`, `&&`, `const&&`), +so that the contained value is passed to the supplied callable with the value category of `*this`. + +```cpp +template +etl::optional transform(F&& f) +``` +**Description** +If `*this` contains a value, returns an `etl::optional` containing the result of invoking `f` +with the contained value, otherwise returns an empty `etl::optional`. + +`U` is the result type of `f`, with any reference and cv qualification removed. + +### Note +Unlike `std::optional::transform`, which only removes cv qualification, a callable returning a +reference is accepted, and the referenced value is copied into the resulting optional. + +--- + +```cpp +template +U and_then(F&& f) +``` +**Description** +If `*this` contains a value, returns the result of invoking `f` with the contained value, +otherwise returns an empty `U`. + +`U` is the result type of `f`, with any reference and cv qualification removed, and must be an +`etl::optional`. The overload takes no part in overload resolution otherwise. + +--- + +```cpp +template +U or_else(F&& f) +``` +**Description** +If `*this` contains a value, returns a copy of `*this`, otherwise returns the result of invoking +`f` with no arguments. + +`U` is the result type of `f`, with any reference and cv qualification removed, and must be an +`etl::optional` whose `value_type` is `T`. The overload takes no part in overload resolution +otherwise. + +### Example + +```cpp +etl::optional opt = 6; + +etl::optional result = opt.and_then([](int i) { return i > 0 ? etl::optional(i * 2) : etl::optional(); }) + .or_else([]() { return etl::optional(1); }) + .transform([](int i) { return double(i) / 2.0; }); +// result contains 6.0 +``` + ## Non-member functions ```cpp @@ -269,3 +327,15 @@ operator >= ``` **Description** `true` if the contents of the lhs are lexicographically greater than or equal to the contents of the rhs, otherwise `false`. + +--- + +```cpp +template +struct etl::is_optional; + +template +inline constexpr bool etl::is_optional_v; // C++17 and above +``` +**Description** +`true` if `T` is an `etl::optional`, otherwise `false`. Any cv qualification on `T` is ignored. diff --git a/docs/utilities/type-id.md b/docs/utilities/type-id.md deleted file mode 100644 index 1686c1bd..00000000 --- a/docs/utilities/type-id.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: "type_id" ---- - -{{< callout type="info">}} - Header: `type_id.h` - Since: `TBC` -{{< /callout >}} - -Below is a concise, technical overview of the `etl::type_id` facility, suitable for design documentation or code review context. - -## Overview: -`etl::type_id` is a lightweight, RTTI-free mechanism for uniquely identifying C++ types at runtime. It is designed for environments where standard RTTI (`typeid`, `std::type_info`) is unavailable, undesirable, or too costly—such as embedded systems or performance-constrained platforms. - -The implementation guarantees uniqueness per type within a program image while remaining constexpr-friendly and requiring no dynamic allocation. - -## Design Concept - -The core idea is to associate each distinct type `T` with the address of a unique static object. That address serves as the type’s identifier. - -This is achieved through: - -```cpp -template -struct type_id_anchor -{ - static char value; -}; -``` - -Each instantiation of `type_id_anchor` owns its own `static char value`, and the address of that variable is guaranteed to be unique for each distinct `T`. The address is stable for the lifetime of the program. - -## Key Components - -### Type Normalization - -When requesting a type ID via `type_id::get()`, the type is first normalised: - -```cpp -using type = typename etl::remove_cvref::type; -``` - -This ensures that: - -* `T`, `const T`, `T&`, and `T&&` all map to the same `type_id` -* Only the underlying type identity matters - -### Type ID Generation - -```cpp -template -static ETL_CONSTEXPR type_id get() ETL_NOEXCEPT; -``` - -* Returns a `type_id` that uniquely represents `T` -* Internally stores the address of `type_id_anchor::value` -* Requires no RTTI, no dynamic memory, and no runtime registration - -### Invalid Type ID - -```cpp -static ETL_CONSTEXPR type_id invalid_id() ETL_NOEXCEPT; -``` - -* Represents an invalid or uninitialised type ID -* Implemented as a null pointer (`id == nullptr`) -* Default constructor produces this state - -## `type_id` Class Semantics - -### Storage - -```cpp -const void* id; -``` - -* The identifier is stored as a pointer -* Comparison is purely pointer comparison (fast and deterministic) - -### Supported Operations - -| Operation | Behavior | -| ------------------ | -------------------------------------------------- | -| Equality (`==`) | True if both type IDs refer to the same type | -| Inequality (`!=`) | Logical negation of equality | -| Ordering (`<`) | Pointer comparison (useful for ordered containers) | -| Boolean conversion | `true` if valid, `false` if invalid | -| Integer conversion | Explicit conversion to `intptr_t` / `uintptr_t` | - -### Copy and Assignment - -* Copyable and assignable -* Copy semantics preserve identity (pointer value) - -## Guarantees and Properties - -* **Uniqueness**: Each distinct type has exactly one unique `type_id` -* **Stability**: Type IDs remain valid for the entire program lifetime -* **Zero overhead**: No dynamic allocation, no lookup tables -* **RTTI-free**: Suitable for builds with RTTI disabled -* **Comparable**: Can be used as keys in associative containers - -## Typical Use Cases - -* Type-safe containers (e.g., variant, any, message buses) -* Plugin or component systems -* Compile-time / runtime type discrimination in embedded systems -* Replacement for `std::type_index` in restricted environments - -## Summary - -`etl::type_id` provides a robust, minimal, and efficient alternative to C++ RTTI by encoding type identity as the address of a unique static object. Its design is particularly well-suited to embedded and low-level systems where determinism, performance, and binary size are critical. diff --git a/docs/utilities/utility.md b/docs/utilities/utility.md index 3e36bc7a..48c0c3e3 100644 --- a/docs/utilities/utility.md +++ b/docs/utilities/utility.md @@ -71,6 +71,82 @@ T exchange(T& object, const U& new_value) Copies the new value to object and returns the old value. Note: This is not an atomic operation. +## Integer comparison functions + +Safe comparison of integers of different signedness. Unlike the built-in +comparison operators, negative signed integers always compare less than (and +never equal to) unsigned integers, so the result is never affected by +value-changing implicit conversions. +The arguments must be standard signed or unsigned integer types, including +`signed char` and `unsigned char`. `bool`, `char` and the extended character +types are not permitted. +When the STL is available and the compiler provides +`__cpp_lib_integer_comparison_functions` (C++20), the `std` versions are used. +Since: `20.49.0` + +```cpp +template +constexpr bool cmp_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_not_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is not equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_less(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is less than `u`. + +--- + +```cpp +template +constexpr bool cmp_greater(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is greater than `u`. + +--- + +```cpp +template +constexpr bool cmp_less_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is less than or equal to `u`. + +--- + +```cpp +template +constexpr bool cmp_greater_equal(T t, U u) noexcept +``` +**Description** +Returns `true` if `t` is greater than or equal to `u`. + +--- + +```cpp +template +constexpr bool in_range(T t) noexcept +``` +**Description** +Returns `true` if the value of `t` is in the range of values that can be +represented by `R`, that is, if `t` can be converted to `R` without loss of its +value. + ## add_const ```cpp @@ -529,3 +605,49 @@ struct nontype_t; ```cpp using FortyTwo = etl::nontype_t<42> ``` + +--- + +## to_unsigned + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_unsigned::type + to_unsigned(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the unsigned type. + +**Example** + +```cpp +int8_t a = -50; +auto b = etl::to_unsigned(a); +``` +`b` is of type `uint8_t` and contains the value `206`. + +--- + +## to_signed + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_signed::type + to_signed(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the signed type. + +**Example** + +```cpp +uint8_t a = 206; +auto b = etl::to_signed(a); +``` +`b` is of type `int8_t` and contains the value `-50`. diff --git a/hugo/assets/version.txt b/hugo/assets/version.txt index 09bbfc15..e38061bf 100644 --- a/hugo/assets/version.txt +++ b/hugo/assets/version.txt @@ -1 +1 @@ -20.48.1 +20.49.0 diff --git a/include/etl/absolute.h b/include/etl/absolute.h index bfa68642..14988ffa 100644 --- a/include/etl/absolute.h +++ b/include/etl/absolute.h @@ -58,14 +58,13 @@ namespace etl // For signed types. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && etl::is_integral::value, T>::type absolute(T value) + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute(T value) { return (value == etl::integral_limits::min) ? etl::private_absolute::signed_min_error() : static_cast((value < T(0)) ? -value : value); } template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && !etl::is_integral::value, T>::type absolute(T value) - ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && !etl::is_integral::value, T>::type absolute(T value) ETL_NOEXCEPT { return (value < T(0)) ? -value : value; } @@ -84,14 +83,13 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type absolute_unsigned(T value) - ETL_NOEXCEPT + ETL_NODISCARD + ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, typename etl::make_unsigned::type>::type + absolute_unsigned(T value) ETL_NOEXCEPT { typedef typename etl::make_unsigned::type TReturn; - return (value == etl::integral_limits::min) ? (etl::integral_limits::max / 2U) + 1U - : (value < T(0)) ? TReturn(-value) - : TReturn(value); + return (value < T(0)) ? 0U - static_cast(value) : static_cast(value); } //*************************************************************************** @@ -99,9 +97,10 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, T>::type absolute_unsigned(T value) ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute_unsigned(T value) + ETL_NOEXCEPT { - return etl::absolute(value); + return value; } } // namespace etl diff --git a/include/etl/alignment.h b/include/etl/alignment.h index 023acac2..497151bd 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -384,7 +384,7 @@ namespace etl /// constructor. //*************************************************************************** template - typed_storage(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage(TArgs&&... args) : valid(false) { create(etl::forward(args)...); @@ -456,7 +456,7 @@ namespace etl /// the internal byte array. //*************************************************************************** template - reference create(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference create(TArgs&&... args) { ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); pointer p = ::new (&storage.value) value_type(etl::forward(args)...); @@ -536,7 +536,7 @@ namespace etl //*************************************************************************** /// \returns a pointer of type T and asserts if has_value() is false. //*************************************************************************** - pointer operator->() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + pointer operator->() { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -546,7 +546,7 @@ namespace etl //*************************************************************************** /// \returns a const pointer of type T and asserts if has_value() is false. //*************************************************************************** - const_pointer operator->() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_pointer operator->() const { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -556,7 +556,7 @@ namespace etl //*************************************************************************** /// \returns reference of type T and asserts if has_value() is false. //*************************************************************************** - reference operator*() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference operator*() { return *operator->(); } @@ -564,15 +564,15 @@ namespace etl //*************************************************************************** /// \returns const_reference of type T and asserts if has_value() is false. //*************************************************************************** - const_reference operator*() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_reference operator*() const { return *operator->(); } private: - typed_storage(etl::typed_storage&) ETL_DELETE; - typed_storage& operator=(etl::typed_storage&) ETL_DELETE; + typed_storage(const etl::typed_storage&) ETL_DELETE; + typed_storage& operator=(const etl::typed_storage&) ETL_DELETE; struct dummy_t { @@ -620,7 +620,7 @@ namespace etl //*************************************************************************** /// Constructor. //*************************************************************************** - typed_storage_ext(void* pbuffer_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(void* pbuffer_) : pbuffer(reinterpret_cast(pbuffer_)) , valid(false) { @@ -633,7 +633,7 @@ namespace etl /// constructor. //*************************************************************************** template - typed_storage_ext(void* pbuffer_, TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(void* pbuffer_, TArgs&&... args) : pbuffer(reinterpret_cast(pbuffer_)) , valid(false) { @@ -645,7 +645,7 @@ namespace etl /// Move constructor. /// Transfers ownership of the buffer from \p other to this. //*************************************************************************** - typed_storage_ext(typed_storage_ext&& other) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + typed_storage_ext(typed_storage_ext&& other) : pbuffer(other.pbuffer) , valid(other.valid) { @@ -727,7 +727,7 @@ namespace etl /// the external buffer. //*************************************************************************** template - reference create(TArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference create(TArgs&&... args) { ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); pointer p = ::new (pbuffer) value_type(etl::forward(args)...); @@ -807,7 +807,7 @@ namespace etl //*************************************************************************** /// \returns a pointer of type T and asserts if has_value() is false. //*************************************************************************** - pointer operator->() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + pointer operator->() { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -817,7 +817,7 @@ namespace etl //*************************************************************************** /// \returns a const pointer of type T and asserts if has_value() is false. //*************************************************************************** - const_pointer operator->() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_pointer operator->() const { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -827,7 +827,7 @@ namespace etl //*************************************************************************** /// \returns reference of type T and asserts if has_value() is false. //*************************************************************************** - reference operator*() ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + reference operator*() { return *operator->(); } @@ -835,15 +835,15 @@ namespace etl //*************************************************************************** /// \returns const_reference of type T and asserts if has_value() is false. //*************************************************************************** - const_reference operator*() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + const_reference operator*() const { return *operator->(); } private: - typed_storage_ext(etl::typed_storage_ext&) ETL_DELETE; - typed_storage_ext& operator=(etl::typed_storage_ext&) ETL_DELETE; + typed_storage_ext(const etl::typed_storage_ext&) ETL_DELETE; + typed_storage_ext& operator=(const etl::typed_storage_ext&) ETL_DELETE; pointer pbuffer; bool valid; diff --git a/include/etl/array.h b/include/etl/array.h index ffa3a307..c1d7d6e4 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -40,6 +40,9 @@ SOFTWARE. #include "static_assert.h" #include "type_traits.h" +#include "private/tuple_element.h" +#include "private/tuple_size.h" + #include ///\defgroup array array @@ -111,7 +114,7 @@ namespace etl /// Returns a reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 reference at(size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_NODISCARD ETL_CONSTEXPR14 reference at(size_t i) { ETL_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -122,7 +125,7 @@ namespace etl /// Returns a const reference to the value at index 'i'. ///\param i The index of the element to access. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 const_reference at(size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_NODISCARD ETL_CONSTEXPR14 const_reference at(size_t i) const { ETL_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); @@ -709,7 +712,7 @@ namespace etl //************************************************************************* /// Returns a const reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR const_reference back() const + ETL_NODISCARD ETL_CONSTEXPR const_reference back() const ETL_NOEXCEPT { return *data(); } @@ -1049,11 +1052,18 @@ namespace etl //************************************************************************* /// Make //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_array(TValues&&... values) ETL_NOEXCEPT -> etl::array +#if ETL_USING_CPP11 + template + constexpr auto make_array(TValues&&... values) -> etl::array, sizeof...(TValues)> { - return {etl::forward(values)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards arguments that already have the element type and + // converts the rest with static_cast, so const-qualified lvalues, + // narrowing initialisers such as make_array(0, 1) and non-movable + // element types all work. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif @@ -1063,7 +1073,7 @@ namespace etl ///\param rhs The second array. //************************************************************************* template - void swap(etl::array& lhs, etl::array& rhs) + ETL_CONSTEXPR14 void swap(etl::array& lhs, etl::array& rhs) ETL_NOEXCEPT_FROM(ETL_OR_STD::swap(etl::declval(), etl::declval())) { lhs.swap(rhs); } @@ -1100,7 +1110,7 @@ namespace etl /// second, otherwise false //************************************************************************* template - bool operator<(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator<(const etl::array& lhs, const etl::array& rhs) { return etl::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); } @@ -1114,7 +1124,7 @@ namespace etl ///< b>false //************************************************************************* template - bool operator<=(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator<=(const etl::array& lhs, const etl::array& rhs) { return !(lhs > rhs); } @@ -1127,7 +1137,7 @@ namespace etl /// the second, otherwise false template //************************************************************************* - bool operator>(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator>(const etl::array& lhs, const etl::array& rhs) { return (rhs < lhs); } @@ -1141,7 +1151,7 @@ namespace etl ///< b>false //************************************************************************* template - bool operator>=(const etl::array& lhs, const etl::array& rhs) + ETL_CONSTEXPR14 bool operator>=(const etl::array& lhs, const etl::array& rhs) { return !(lhs < rhs); } @@ -1155,7 +1165,7 @@ namespace etl ///\return A reference to the element //************************************************************************* template - inline T& get(array& a) + ETL_NODISCARD ETL_CONSTEXPR14 T& get(array& a) ETL_NOEXCEPT { ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); return a[Index]; @@ -1170,11 +1180,106 @@ namespace etl ///\return A const reference to the element //************************************************************************* template - inline const T& get(const array& a) + ETL_NODISCARD ETL_CONSTEXPR14 const T& get(const array& a) ETL_NOEXCEPT { ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); return a[Index]; } + +#if ETL_USING_CPP11 + //************************************************************************* + /// Gets an rvalue reference to an element in the array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + ///\param a The array. + ///\return An rvalue reference to the element + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T&& get(array&& a) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + return static_cast(a[Index]); + } + + //************************************************************************* + /// Gets a const rvalue reference to an element in the array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + ///\param a The array. + ///\return A const rvalue reference to the element + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 const T&& get(const array&& a) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + return static_cast(a[Index]); + } + + //************************************************************************* + /// Gets the number of elements in an array. + ///\tparam T The type. + ///\tparam Size The array size. + //************************************************************************* + template + struct tuple_size > : etl::integral_constant + { + }; + + //************************************************************************* + /// Gets the type of the element at Index in an array. + ///\tparam Index The index. + ///\tparam T The type. + ///\tparam Size The array size. + //************************************************************************* + template + struct tuple_element > + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + + using type = T; + }; +#endif } // namespace etl +#if ETL_USING_CPP11 +namespace std +{ + // libc++ already declares std::tuple_size / std::tuple_element in its inline + // namespace (std::__1), so re-declaring them here would make the name + // ambiguous. Detect libc++ via _LIBCPP_VERSION and skip the forward + // declarations in that case, even when not using the STL. + #if ETL_NOT_USING_STL && !defined(_LIBCPP_VERSION) \ + && !((defined(ETL_DEVELOPMENT_OS_APPLE) || (ETL_COMPILER_FULL_VERSION >= 190000) && (ETL_COMPILER_FULL_VERSION < 210000)) \ + && defined(ETL_COMPILER_CLANG)) + template + struct tuple_size; + + template + struct tuple_element; + #endif + + //*************************************************************************** + /// Specialisation of tuple_size to allow the use of C++ structured bindings. + //*************************************************************************** + template + struct tuple_size > : etl::integral_constant + { + }; + + //*************************************************************************** + /// Specialisation of tuple_element to allow the use of C++ structured + /// bindings. + //*************************************************************************** + template + struct tuple_element > + { + ETL_STATIC_ASSERT(Index < Size, "Index out of bounds"); + + using type = T; + }; +} // namespace std +#endif + #endif diff --git a/include/etl/atomic/atomic_std.h b/include/etl/atomic/atomic_std.h index 06fcef6c..76a22c93 100644 --- a/include/etl/atomic/atomic_std.h +++ b/include/etl/atomic/atomic_std.h @@ -135,12 +135,12 @@ namespace etl using memory_order = std::memory_order; - static ETL_CONSTANT etl::memory_order memory_order_relaxed = std::memory_order_relaxed; - static ETL_CONSTANT etl::memory_order memory_order_consume = std::memory_order_consume; - static ETL_CONSTANT etl::memory_order memory_order_acquire = std::memory_order_acquire; - static ETL_CONSTANT etl::memory_order memory_order_release = std::memory_order_release; - static ETL_CONSTANT etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; - static ETL_CONSTANT etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_relaxed = std::memory_order_relaxed; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_consume = std::memory_order_consume; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_acquire = std::memory_order_acquire; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_release = std::memory_order_release; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; + ETL_INLINE_VAR ETL_CONSTANT etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; using atomic_bool = etl::atomic; using atomic_char = etl::atomic; diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index dc198f54..1ad83322 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -135,65 +135,65 @@ namespace etl //*************************************************************************** // Stream formatting manipulators. //*************************************************************************** - static ETL_CONSTEXPR private_basic_format_spec::base_spec setbase(uint32_t base) + inline ETL_CONSTEXPR private_basic_format_spec::base_spec setbase(uint32_t base) { - return private_basic_format_spec::base_spec(base); + return private_basic_format_spec::base_spec(static_cast(base)); } //********************************* - static ETL_CONSTEXPR private_basic_format_spec::width_spec setw(uint32_t width) + inline ETL_CONSTEXPR private_basic_format_spec::width_spec setw(uint32_t width) { - return private_basic_format_spec::width_spec(width); + return private_basic_format_spec::width_spec(static_cast(width)); } //********************************* template - static ETL_CONSTEXPR private_basic_format_spec::fill_spec setfill(TChar fill) + ETL_CONSTEXPR private_basic_format_spec::fill_spec setfill(TChar fill) { return private_basic_format_spec::fill_spec(fill); } //********************************* - static ETL_CONSTEXPR private_basic_format_spec::precision_spec setprecision(uint32_t precision) + inline ETL_CONSTEXPR private_basic_format_spec::precision_spec setprecision(uint32_t precision) { - return private_basic_format_spec::precision_spec(precision); + return private_basic_format_spec::precision_spec(static_cast(precision)); } //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec bin(2U); + static ETL_CONSTANT private_basic_format_spec::base_spec bin(static_cast(2U)); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec oct(8U); + static ETL_CONSTANT private_basic_format_spec::base_spec oct(static_cast(8U)); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec dec(10U); + static ETL_CONSTANT private_basic_format_spec::base_spec dec(static_cast(10U)); //********************************* - static ETL_CONSTANT private_basic_format_spec::base_spec hex(16U); + static ETL_CONSTANT private_basic_format_spec::base_spec hex(static_cast(16U)); //********************************* - static ETL_CONSTANT private_basic_format_spec::left_spec left = private_basic_format_spec::left_spec(); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::left_spec left = private_basic_format_spec::left_spec(); //********************************* - static ETL_CONSTANT private_basic_format_spec::right_spec right = private_basic_format_spec::right_spec(); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::right_spec right = private_basic_format_spec::right_spec(); //********************************* - static ETL_CONSTANT private_basic_format_spec::boolalpha_spec boolalpha(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::boolalpha_spec boolalpha(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::boolalpha_spec noboolalpha(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::boolalpha_spec noboolalpha(false); //********************************* - static ETL_CONSTANT private_basic_format_spec::uppercase_spec uppercase(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::uppercase_spec uppercase(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::uppercase_spec nouppercase(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::uppercase_spec nouppercase(false); //********************************* - static ETL_CONSTANT private_basic_format_spec::showbase_spec showbase(true); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::showbase_spec showbase(true); //********************************* - static ETL_CONSTANT private_basic_format_spec::showbase_spec noshowbase(false); + ETL_INLINE_VAR ETL_CONSTANT private_basic_format_spec::showbase_spec noshowbase(false); //*************************************************************************** /// basic_format_spec diff --git a/include/etl/binary.h b/include/etl/binary.h index 230fbf4a..2e02c41e 100644 --- a/include/etl/binary.h +++ b/include/etl/binary.h @@ -2128,7 +2128,7 @@ namespace etl s = (s | (s << 2U)) & 0x33333333UL; s = (s | (s << 1U)) & 0x55555555UL; - return (f | (s << 1U)); + return static_cast(f | (s << 1U)); } #if ETL_USING_64BIT_TYPES @@ -2156,7 +2156,7 @@ namespace etl s = (s | (s << 2U)) & 0x3333333333333333ULL; s = (s | (s << 1U)) & 0x5555555555555555ULL; - return (f | (s << 1U)); + return static_cast(f | (s << 1U)); } #endif diff --git a/include/etl/bit.h b/include/etl/bit.h index 4947a17b..861c776d 100644 --- a/include/etl/bit.h +++ b/include/etl/bit.h @@ -50,15 +50,22 @@ namespace etl //*************************************************************************** template ETL_NODISCARD - typename etl::enable_if< !(etl::is_integral::value && etl::is_integral::value) && (sizeof(TDestination) == sizeof(TSource)) - && etl::is_trivially_copyable::value && etl::is_trivially_copyable::value, - TDestination>::type bit_cast(const TSource& source) ETL_NOEXCEPT +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + typename etl::enable_if< !(etl::is_integral::value && etl::is_integral::value) && (sizeof(TDestination) == sizeof(TSource)) + && etl::is_trivially_copyable::value && etl::is_trivially_copyable::value, + TDestination>::type bit_cast(const TSource& source) ETL_NOEXCEPT { +#if ETL_USING_BUILTIN_BIT_CAST + return __builtin_bit_cast(TDestination, source); +#else TDestination destination; memcpy(&destination, &source, sizeof(TDestination)); return destination; +#endif } //*************************************************************************** diff --git a/include/etl/bit_stream.h b/include/etl/bit_stream.h index 8248d9ae..349aa12f 100644 --- a/include/etl/bit_stream.h +++ b/include/etl/bit_stream.h @@ -695,7 +695,7 @@ namespace etl template typename etl::enable_if::value, bool>::type write(T value, uint_least8_t nbits = CHAR_BIT * sizeof(T)) { - bool success = (available(nbits) > 0U); + bool success = (nbits > 0U) && (available(nbits) > 0U); if (success) { @@ -914,6 +914,7 @@ namespace etl template void write_data(T value, uint_least8_t nbits) { + ETL_ASSERT(nbits > 0U, ETL_ERROR_GENERIC("bit_stream_writer::write_data: nbits is zero")); ETL_ASSERT(nbits <= (CHAR_BIT * sizeof(T)), ETL_ERROR_GENERIC("bit_stream_writer::write_data: nbits too large")); // Apply the byte order (endianness). @@ -1305,6 +1306,7 @@ namespace etl template T read_value(uint_least8_t nbits, bool is_signed) { + ETL_ASSERT(nbits > 0U, ETL_ERROR_GENERIC("bit_stream_reader::read_value: nbits is zero")); ETL_ASSERT(nbits <= (CHAR_BIT * sizeof(T)), ETL_ERROR_GENERIC("bit_stream_reader::read_value: nbits too large")); T value = 0; diff --git a/include/etl/byte_stream.h b/include/etl/byte_stream.h index 51dc9e92..76a4bf04 100644 --- a/include/etl/byte_stream.h +++ b/include/etl/byte_stream.h @@ -508,6 +508,28 @@ namespace etl { } + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + byte_stream_reader(etl::span span_, etl::endian stream_endianness_) + : pdata(reinterpret_cast(span_.begin())) + , pcurrent(reinterpret_cast(span_.begin())) + , stream_length(span_.size_bytes()) + , stream_endianness(stream_endianness_) + { + } + + //*************************************************************************** + /// Construct from span. + //*************************************************************************** + byte_stream_reader(etl::span span_, etl::endian stream_endianness_) + : pdata(reinterpret_cast(span_.begin())) + , pcurrent(reinterpret_cast(span_.begin())) + , stream_length(span_.size_bytes()) + , stream_endianness(stream_endianness_) + { + } + //*************************************************************************** /// Construct from range. //*************************************************************************** diff --git a/include/etl/circular_buffer.h b/include/etl/circular_buffer.h index 4c8290c4..b0586cfb 100644 --- a/include/etl/circular_buffer.h +++ b/include/etl/circular_buffer.h @@ -900,6 +900,53 @@ namespace etl return pbuffer[(out + index) % buffer_size]; } + //************************************************************************* + /// Assigns an iterator range to the circular buffer. + //************************************************************************* + void assign(iterator first, const iterator& last) + { + assign_range(first, last); + } + + //************************************************************************* + /// Assigns a const iterator range to the circular buffer. + //************************************************************************* + void assign(const_iterator first, const const_iterator& last) + { + assign_range(first, last); + } + + //************************************************************************* + /// Assigns a range to the circular buffer. + //************************************************************************* + template + typename etl::enable_if::value, void>::type assign(TIterator first, const TIterator& last) + { + clear(); + push(first, last); + } + + //************************************************************************* + /// Assigns 'n' copies of a value to the circular buffer. + //************************************************************************* + void assign(size_type n, const_reference value) + { + if (n == 0U) + { + clear(); + return; + } + + const T value_copy(value); + + clear(); + + while (n-- != 0U) + { + push(value_copy); + } + } + //************************************************************************* /// push. /// Adds an item to the buffer. @@ -1036,6 +1083,43 @@ namespace etl protected: + //************************************************************************* + /// Assigns a circular buffer iterator range. + //************************************************************************* + template + void assign_range(TIterator first, const TIterator& last) + { + const bool is_self_range = (&first.container() == this) && (&last.container() == this); + + if (!is_self_range) + { + clear(); + push(first, last); + } + else + { + const size_type first_index = static_cast(first.get_index()); + const size_type last_index = static_cast(last.get_index()); + + while (out != first_index) + { + pop(); + } + + while (in != last_index) + { + if (in == 0U) + { + in = buffer_size; + } + + --in; + pbuffer[in].~T(); + ETL_DECREMENT_DEBUG_COUNT; + } + } + } + //************************************************************************* /// Protected constructor. //************************************************************************* @@ -1127,6 +1211,24 @@ namespace etl { } + //************************************************************************* + /// Constructor, with size. + //************************************************************************* + explicit circular_buffer(size_t initial_size) + : icircular_buffer(reinterpret_cast(buffer.raw), MAX_SIZE) + { + this->assign(initial_size, T()); + } + + //************************************************************************* + /// Constructor, from initial size and value. + //************************************************************************* + circular_buffer(size_t initial_size, typename etl::icircular_buffer::const_reference value) + : icircular_buffer(reinterpret_cast(buffer.raw), MAX_SIZE) + { + this->assign(initial_size, value); + } + //************************************************************************* /// Constructor. /// Constructs a buffer from an iterator range. @@ -1274,6 +1376,24 @@ namespace etl { } + //************************************************************************* + /// Constructor, with size. + //************************************************************************* + explicit circular_buffer_ext(size_t initial_size, void* buffer, size_t max_size) + : icircular_buffer(reinterpret_cast(buffer), max_size) + { + this->assign(initial_size, T()); + } + + //************************************************************************* + /// Constructor, from initial size and value. + //************************************************************************* + circular_buffer_ext(size_t initial_size, typename etl::icircular_buffer::const_reference value, void* buffer, size_t max_size) + : icircular_buffer(reinterpret_cast(buffer), max_size) + { + this->assign(initial_size, value); + } + //************************************************************************* /// Constructor. /// Constructs a buffer from an iterator range. diff --git a/include/etl/clamped_value.h b/include/etl/clamped_value.h new file mode 100644 index 00000000..d7f3dcf9 --- /dev/null +++ b/include/etl/clamped_value.h @@ -0,0 +1,983 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_CLAMPED_VALUE_INCLUDED +#define ETL_CLAMPED_VALUE_INCLUDED + +#include "platform.h" +#include "absolute.h" +#include "algorithm.h" +#include "error_handler.h" +#include "limits.h" +#include "math.h" +#include "static_assert.h" +#include "type_traits.h" +#include "utility.h" + +///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +/// Set to `1` when clamped_value supports floating-point types. +/// Runtime floating-point support requires C++11 variadic templates. +/// The macro may be defined by the build or platform profile to override +/// automatic detection. +#if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE) + #if ETL_USING_CPP11 + #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1 + #else + #define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0 + #endif +#endif + +///\def ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE +/// Set to `1` when clamped_value supports floating-point compile-time bounds. +#if !defined(ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE) + #if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L) + #define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 1 + #else + #define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 0 + #endif +#endif + +///\defgroup clamped_value clamped_value +/// Provides a value that is clamped between two limits. +/// Integral types are supported in all language modes. Floating-point types +/// with runtime bounds are supported when +/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. +/// \ingroup utilities + +namespace etl +{ + /// \cond INTERNAL + namespace private_clamped_value + { + //************************************************************************* + /// Type policy for clamped_value. + /// Integral values use a signed difference type and unsigned saturation + /// arithmetic. Floating-point values use `T` as the difference type. + //************************************************************************* + template ::value> + struct traits; + + template + struct traits + { + typedef typename etl::make_signed::type difference_type; + typedef typename etl::make_unsigned::type unsigned_type; + typedef etl::numeric_limits limits_type; + typedef etl::numeric_limits difference_limits_type; + }; + + template + struct traits + { + ETL_STATIC_ASSERT(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE&& etl::is_floating_point::value, + "clamped_value requires an integral type, or a floating-point type when supported"); + + typedef T difference_type; + typedef etl::numeric_limits limits_type; + typedef etl::numeric_limits difference_limits_type; + }; + + template ::value> + struct arithmetic; + + //************************************************************************* + /// Saturating arithmetic policy for integral values. + //************************************************************************* + template + struct arithmetic + { + typedef typename traits::unsigned_type unsigned_type; + + ETL_NODISCARD + static + ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + if (n == 0) + { + return value; + } + + const unsigned_type current = etl::to_unsigned(value); // static_cast(value); + const unsigned_type step = etl::absolute_unsigned(n); + + if ((n > 0) && (value < max_value)) + { + const unsigned_type distance = etl::to_unsigned(max_value) - current; // static_cast(max_value) - current; + + value = (step >= distance) ? max_value : static_cast(current + step); + } + else if ((n < 0) && (value > min_value)) + { + const unsigned_type distance = current - etl::to_unsigned(min_value); // static_cast(min_value); + + value = (step >= distance) ? min_value : static_cast(current - step); + } + + return value; + } + + ETL_NODISCARD + static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + if (n == traits::difference_limits_type::lowest()) ETL_UNLIKELY + { + value = advance(value, min_value, max_value, traits::difference_limits_type::max()); + return advance(value, min_value, max_value, 1); + } + + return advance(value, min_value, max_value, static_cast::difference_type>(-n)); + } + }; + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + /// Saturating arithmetic policy for floating-point values. + //************************************************************************* + template + struct arithmetic + { + ETL_NODISCARD + static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, T n) ETL_NOEXCEPT + { + if ((n > T(0)) && (value < max_value)) + { + const T distance = max_value - value; + value = (n >= distance) ? max_value : static_cast(value + n); + } + else if ((n < T(0)) && (value > min_value)) + { + const T distance = value - min_value; + value = (-n >= distance) ? min_value : static_cast(value + n); + } + + return value; + } + + ETL_NODISCARD + static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, T n) ETL_NOEXCEPT + { + return advance(value, min_value, max_value, -n); + } + }; +#endif + + //************************************************************************* + /// Advances a value and saturates it at the supplied bounds. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param n The number of steps. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + return arithmetic::advance(value, min_value, max_value, n); + } + + //************************************************************************* + /// Subtracts a value and saturates it at the supplied bounds. + /// Handles the lowest integral difference without signed overflow. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param n The number of steps to subtract. + ///\return The value after subtraction. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits::difference_type n) ETL_NOEXCEPT + { + return arithmetic::subtract(value, min_value, max_value, n); + } + + //************************************************************************* + /// Verifies that a clamped value, bound, or difference is not NaN. + /// Integral values always pass validation. + ///\param value The value to validate. + //************************************************************************* + template + ETL_CONSTEXPR14 void validate(T value) + { + ETL_ASSERT(!etl::is_nan(value), ETL_ERROR_GENERIC("clamped_value: NaN is not supported")); + } + + //************************************************************************* + /// Validates a value and its bounds before clamping. + ///\param value The value to clamp. + ///\param min_value The minimum permitted value. + ///\param max_value The maximum permitted value. + ///\return The value clamped to the supplied range. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T validated_clamp(T value, T min_value, T max_value) + { + validate(value); + validate(min_value); + validate(max_value); + ETL_ASSERT(min_value <= max_value, ETL_ERROR_GENERIC("clamped_value: invalid range")); + return etl::clamp(value, min_value, max_value); + } + } // namespace private_clamped_value + /// \endcond + +#if ETL_USING_CPP11 + template + class clamped_value; +#else // ETL_NOT_USING_CPP11 + #include "private/diagnostic_float_equal_push.h" + template + class clamped_value; + #include "private/diagnostic_pop.h" +#endif + + //*************************************************************************** + /// Provides a value that is clamped between two compile-time limits. + /// Supports incrementing, decrementing and arbitrary advance. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///@tparam Min The minimum value of the range. + ///@tparam Max The maximum value of the range. + ///\ingroup clamped_value + //*************************************************************************** +#if ETL_USING_CPP11 + template + class clamped_value +#else // ETL_NOT_USING_CPP11 + template + class clamped_value +#endif + { + public: + + /// The type used to advance or subtract from the stored value. + typedef typename private_clamped_value::traits::difference_type difference_type; + + /// Numeric limits for the stored value type. + typedef typename private_clamped_value::traits::limits_type limits_type; + + /// Numeric limits for difference_type. + typedef typename private_clamped_value::traits::difference_limits_type difference_limits_type; + + /// \cond INTERNAL + ETL_STATIC_ASSERT(Min <= Max, "clamped_value minimum must not exceed maximum"); + /// \endcond + + //************************************************************************* + /// Default constructor. + /// The initial value is set to the minimum value. + //************************************************************************* + ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT + : value(Min) + { + } + + //************************************************************************* + /// Constructor. + /// Set to an initial value. + /// Clamped to the range. + /// NaN is rejected for floating-point values. + ///\param initial The initial value. + //************************************************************************* + ETL_CONSTEXPR14 explicit clamped_value(T initial) + : value(private_clamped_value::validated_clamp(initial, Min, Max)) + { + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT + : value(other.value) + { + } + + //************************************************************************* + /// Assignment operator. + ///\param other The value to copy. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this == &other) + { + return *this; + } + + set(other.value); + return *this; + } + + //************************************************************************* + /// Sets the value. + /// Clamps to the Min/Max range. + /// NaN is rejected for floating-point values. + ///\param value_ The value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T value_) + { + value = private_clamped_value::validated_clamp(value_, Min, Max); + } + + //************************************************************************* + /// Resets the value to the minimum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT + { + value = Min; + } + + //************************************************************************* + /// Resets the value to the maximum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT + { + value = Max; + } + + //************************************************************************* + /// Advances the value by a number of steps. + /// Saturates at the Min/Max range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + //************************************************************************* + ETL_CONSTEXPR14 void advance(difference_type n) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, Min, Max, n); + } + + //************************************************************************* + /// Conversion operator. + /// \return The value of the underlying type. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// ++ operator. + /// Advances by one. + /// Saturates at Max. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, Min, Max, difference_type(1)); + return *this; + } + + //************************************************************************* + /// ++ operator. + /// Saturates at Max. + ///\return The value before incrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// -- operator. + /// Subtracts one. + /// Saturates at Min. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, Min, Max, difference_type(-1)); + return *this; + } + + //************************************************************************* + /// -- operator. + /// Saturates at Min. + ///\return The value before decrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// = operator. + /// Clamps to the Min/Max range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER + { + set(value_); + return *this; + } + + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, Min, Max, n); + return *this; + } + + //************************************************************************* + /// Gets the value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum value. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return Min; + } + + //************************************************************************* + /// Gets the maximum value. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return Max; + } + + //************************************************************************* + /// Swaps the values. + ///\param other The value to swap with. + //************************************************************************* + ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + } + + //************************************************************************* + /// Swaps the values. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Operator ==. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { +#include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; +#include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Operator !=. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are not equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Operator <. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + + /// @} + + //************************************************************************* + /// Operator <=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + /// @} + + //************************************************************************* + /// Operator >. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + /// @} + + //************************************************************************* + /// Operator >=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + /// @} + + private: + + T value; + }; + + //*************************************************************************** + /// Provides a value that is clamped between two runtime limits. + /// Supports incrementing, decrementing and arbitrary advance. + ///@tparam T An integral type, or a floating-point type when + /// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`. + ///@tparam Min Ignored for this specialisation. + ///@tparam Max Ignored for this specialisation. + ///\ingroup clamped_value + //*************************************************************************** +#if ETL_USING_CPP11 + template + class clamped_value +#else // ETL_NOT_USING_CPP11 + template + class clamped_value +#endif + { + public: + + /// The type used to advance or subtract from the stored value. + typedef typename private_clamped_value::traits::difference_type difference_type; + + /// Numeric limits for the stored value type. + typedef typename private_clamped_value::traits::limits_type limits_type; + + /// Numeric limits for difference_type. + typedef typename private_clamped_value::traits::difference_limits_type difference_limits_type; + + //************************************************************************* + /// Default constructor. + /// Sets the value to the minimum representable value of `T` and the + /// bounds to the full range of `T`. + //************************************************************************* + ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT + : value(limits_type::lowest()) + , min_value(limits_type::lowest()) + , max_value(limits_type::max()) + { + } + + //************************************************************************* + /// Constructor. + /// Sets the value to the minimum of the range. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value(T min_, T max_) + : value(min_) + , min_value(min_) + , max_value(max_) + { + private_clamped_value::validate(min_); + private_clamped_value::validate(max_); + ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range")); + } + + //************************************************************************* + /// Constructor. + /// Set to an initial value. + /// Clamped to the range. + /// Reversed bounds and NaN values or bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + ///\param initial The initial value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value(T min_, T max_, T initial) + : value(private_clamped_value::validated_clamp(initial, min_, max_)) + , min_value(min_) + , max_value(max_) + { + } + + //************************************************************************* + /// Copy constructor. + ///\param other The value to copy. + //************************************************************************* + ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT + : value(other.value) + , min_value(other.min_value) + , max_value(other.max_value) + { + } + + //************************************************************************* + /// Sets the range. + /// Sets the value to the minimum of the range. + /// Reversed bounds and NaN bounds are rejected. + ///\param min_ The minimum value. + ///\param max_ The maximum value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T min_, T max_) + { + private_clamped_value::validate(min_); + private_clamped_value::validate(max_); + ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range")); + min_value = min_; + max_value = max_; + value = min_; + } + + //************************************************************************* + /// Sets the value. + /// Clamps to the runtime Min/Max range. + /// NaN is rejected for floating-point values. + ///\param value_ The value. + //************************************************************************* + ETL_CONSTEXPR14 void set(T value_) + { + value = private_clamped_value::validated_clamp(value_, min_value, max_value); + } + + //************************************************************************* + /// Resets the value to the minimum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT + { + value = min_value; + } + + //************************************************************************* + /// Resets the value to the maximum in the range. + //************************************************************************* + ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT + { + value = max_value; + } + + //************************************************************************* + /// Advances the value by a number of steps. + /// Saturates at the runtime Min/Max range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + //************************************************************************* + ETL_CONSTEXPR14 void advance(difference_type n) + { + private_clamped_value::validate(n); + value = private_clamped_value::advance(value, min_value, max_value, n); + } + + //************************************************************************* + /// Conversion operator. + /// \return The value of the underlying type. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// ++ operator. + /// Advances by one. + /// Saturates at the maximum. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min_value, max_value, difference_type(1)); + return *this; + } + + //************************************************************************* + /// ++ operator. + /// Saturates at the maximum. + ///\return The value before incrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + ++(*this); + return temp; + } + + //************************************************************************* + /// -- operator. + /// Subtracts one. + /// Saturates at the minimum. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_clamped_value::advance(value, min_value, max_value, difference_type(-1)); + return *this; + } + + //************************************************************************* + /// -- operator. + /// Saturates at the minimum. + ///\return The value before decrementing. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT + { + clamped_value temp(*this); + --(*this); + return temp; + } + + //************************************************************************* + /// = operator. + /// Clamps to the runtime Min/Max range. + ///\param value_ The value to assign. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER + { + set(value_); + return *this; + } + + //************************************************************************* + /// = operator. + /// Copies the value and runtime bounds. + ///\param other The value to copy. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + if (this == &other) + { + return *this; + } + + value = other.value; + min_value = other.min_value; + max_value = other.max_value; + return *this; + } + + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER + { + private_clamped_value::validate(n); + value = private_clamped_value::subtract(value, min_value, max_value, n); + return *this; + } + + //************************************************************************* + /// Gets the value. + ///\return The current value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT + { + return value; + } + + //************************************************************************* + /// Gets the minimum value. + ///\return The minimum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT + { + return min_value; + } + + //************************************************************************* + /// Gets the maximum value. + ///\return The maximum value. + //************************************************************************* + ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT + { + return max_value; + } + + //************************************************************************* + /// Swaps the values and runtime bounds. + ///\param other The value to swap with. + //************************************************************************* + ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + swap(value, other.value); + swap(min_value, other.min_value); + swap(max_value, other.max_value); + } + + //************************************************************************* + /// Swaps the values and runtime bounds. + ///\param lhs The first value. + ///\param rhs The second value. + //************************************************************************* + friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //************************************************************************* + /// Operator ==. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values are equal. + //************************************************************************* + friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { +#include "private/diagnostic_float_equal_push.h" + return lhs.value == rhs.value; +#include "private/diagnostic_pop.h" + } + + //************************************************************************* + /// Operator !=. + ///\param lhs The left-hand value. + ///\param rhs The right-hand value. + ///\return `true` if the values differ. + //************************************************************************* + friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //************************************************************************* + /// Operator <. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + /// @} + + //************************************************************************* + /// Operator <=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator<=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + /// @} + + //************************************************************************* + /// Operator >. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + /// @} + + //************************************************************************* + /// Operator >=. + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than or equal to rhs. + /// @{ + friend ETL_CONSTEXPR bool operator>=(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + + /// @} + + private: + + T value; + T min_value; + T max_value; + }; +} // namespace etl + +#endif diff --git a/include/etl/const_map.h b/include/etl/const_map.h index d624b5ac..d4a4b502 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -48,7 +48,7 @@ SOFTWARE. namespace etl { - template + template > class iconst_map { public: @@ -449,7 +449,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_map(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} @@ -511,18 +510,24 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); static_assert((etl::is_default_constructible::value), "mapped_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. - /// Static asserts if the elements are not of type value_type. + /// Static asserts if the elements are not constructible into + /// value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_map(TElements&&... elements) ETL_NOEXCEPT : iconst_map(element_list, sizeof...(elements), Size) , element_list{etl::forward(elements)...} { - static_assert((etl::are_all_same...>::value), "All elements must be value_type"); + static_assert((etl::conjunction>...>::value), + "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); if (!this->is_valid()) @@ -531,6 +536,7 @@ namespace etl // runtime assert here for run time variables? } } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index dea60ee1..1ec056a3 100644 --- a/include/etl/const_multimap.h +++ b/include/etl/const_multimap.h @@ -398,7 +398,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_multimap(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} @@ -455,21 +454,28 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); static_assert((etl::is_default_constructible::value), "mapped_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_map from a variadic list of elements. /// Static asserts if the element type is not constructible. - /// Static asserts if the elements are not of type value_type. + /// Static asserts if the elements are not constructible into + /// value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_map. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_multimap(TElements&&... elements) ETL_NOEXCEPT : iconst_multimap(element_list, sizeof...(elements), Size) , element_list{etl::forward(elements)...} { - static_assert((etl::are_all_same...>::value), "All elements must be value_type"); + static_assert((etl::conjunction>...>::value), + "All elements must be constructible into value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_multiset.h b/include/etl/const_multiset.h index 6184f9e7..f0bb70fb 100644 --- a/include/etl/const_multiset.h +++ b/include/etl/const_multiset.h @@ -354,7 +354,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_multiset(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} @@ -410,6 +409,7 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_set from a variadic list of elements. /// Static asserts if the element type is not constructible. @@ -417,6 +417,9 @@ namespace etl /// Static asserts if the number of elements is greater than the capacity of /// the const_set. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_multiset(TElements&&... elements) ETL_NOEXCEPT : iconst_multiset(element_list, sizeof...(elements), Size) @@ -425,6 +428,7 @@ namespace etl static_assert((etl::are_all_same...>::value), "All elements must be value_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/const_set.h b/include/etl/const_set.h index df7f0989..92bbad2c 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -48,7 +48,7 @@ SOFTWARE. namespace etl { - template + template > class iconst_set { public: @@ -345,7 +345,6 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - template ETL_CONSTEXPR14 explicit iconst_set(const value_type* element_list_, size_type size_, size_type max_elements_) ETL_NOEXCEPT : element_list(element_list_) , element_list_end{element_list_ + size_} @@ -404,12 +403,16 @@ namespace etl static_assert((etl::is_default_constructible::value), "key_type must be default constructible"); + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* ///\brief Construct a const_set from a variadic list of elements. /// Static asserts if the elements are not of type value_type. /// Static asserts if the number of elements is greater than the capacity of /// the const_set. //************************************************************************* + // The base class is passed the address of 'element_list' before that member has been + // initialised. The base class only stores the address and never reads through it during + // construction, so the compiler's 'may be used uninitialized' warning is a false positive. template ETL_CONSTEXPR14 explicit const_set(TElements&&... elements) ETL_NOEXCEPT : iconst_set(element_list, sizeof...(elements), Size) @@ -418,6 +421,7 @@ namespace etl static_assert((etl::are_all_same...>::value), "All elements must be key_type"); static_assert(sizeof...(elements) <= Size, "Number of elements exceeds capacity"); } + #include "private/diagnostic_pop.h" private: diff --git a/include/etl/debounce.h b/include/etl/debounce.h index 2618d5e3..20a13ab5 100644 --- a/include/etl/debounce.h +++ b/include/etl/debounce.h @@ -536,7 +536,10 @@ namespace etl } //************************************************************************* - /// Constructor. + /// Sets the count thresholds. + ///\param valid The count for a valid state. + ///\param hold The count after valid for a hold state. Default = 0. + ///\param repeat The count after hold for a key repeat. Default = 0. //************************************************************************* void set(count_t valid, count_t hold = 0, count_t repeat = 0) { diff --git a/include/etl/deque.h b/include/etl/deque.h index 4ac0ac92..c3ff33d9 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -348,16 +348,7 @@ namespace etl } //*************************************************** - reference operator[](size_t i) - { - iterator result(*this); - result += i; - - return *result; - } - - //*************************************************** - const_reference operator[](size_t i) const + reference operator[](difference_type i) const { iterator result(*this); result += i; @@ -607,10 +598,10 @@ namespace etl } //*************************************************** - const_reference operator[](size_t i) const + const_reference operator[](difference_type i) const { const_iterator result(*this); - result += static_cast(i); + result += i; return *result; } @@ -2532,10 +2523,17 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_deque(TValues&&... values) -> etl::deque + template + constexpr auto make_deque(TValues&&... values) -> etl::deque, sizeof...(TValues)> { - return {etl::forward(values)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards arguments that already have the element type and + // converts the rest with static_cast, so const-qualified lvalues, + // narrowing initialisers such as make_deque(0, 1) and non-movable + // element types all work. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/expected.h b/include/etl/expected.h index 655be8b1..092434dc 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -249,6 +249,92 @@ namespace etl static const unexpect_t unexpect; #endif +#if ETL_USING_CPP11 + namespace private_expected + { + //***************************************************************************** + // Detects etl::unexpected, which the value constructors must not accept as a value. + //***************************************************************************** + template + struct is_unexpected : etl::false_type + { + }; + + template + struct is_unexpected > : etl::true_type + { + }; + + //***************************************************************************** + // True when TValue can be built from the source expected, which must be wrapped rather than unwrapped. + //***************************************************************************** + template + struct constructible_from_expected + : etl::integral_constant< + bool, etl::is_constructible&>::value || etl::is_constructible >::value + || etl::is_constructible&>::value + || etl::is_constructible >::value || etl::is_convertible&, TValue>::value + || etl::is_convertible&&, TValue>::value || etl::is_convertible&, TValue>::value + || etl::is_convertible&&, TValue>::value> + { + }; + + //***************************************************************************** + // True when TError can be built from the source expected, which must be wrapped rather than unwrapped. + //***************************************************************************** + template + struct unexpected_constructible_from_expected + : etl::integral_constant, etl::expected&>::value + || etl::is_constructible, etl::expected >::value + || etl::is_constructible, const etl::expected&>::value + || etl::is_constructible, const etl::expected >::value> + { + }; + + //***************************************************************************** + // Constraints shared by the implicit and explicit value constructors. + //***************************************************************************** + template + struct is_value_constructor_candidate + : etl::integral_constant::type, etl::expected >::value + && !etl::is_same::type, TValue>::value + && !etl::is_same::type, etl::in_place_t>::value + && !etl::is_same::type, etl::unexpect_t>::value + && !is_unexpected::type>::value && etl::is_constructible::value> + { + }; + + //***************************************************************************** + // Constraints on the source expected that hold whatever its value category. + //***************************************************************************** + template + struct is_expected_conversion_allowed + : etl::integral_constant, etl::expected >::value + && !constructible_from_expected::value + && !unexpected_constructible_from_expected::value> + { + }; + + //***************************************************************************** + // UArgument and GArgument are the source members as the constructor receives them. + //***************************************************************************** + template + struct is_expected_conversion_constructible + : etl::integral_constant::value && etl::is_constructible::value> + { + }; + + //***************************************************************************** + // A converting constructor is implicit only when both the value and the error convert implicitly. + //***************************************************************************** + template + struct is_expected_conversion_implicit + : etl::integral_constant::value && etl::is_convertible::value> + { + }; + } // namespace private_expected +#endif + //***************************************************************************** /// Expected type. //***************************************************************************** @@ -293,6 +379,114 @@ namespace etl } #endif +#if ETL_USING_CPP11 + //******************************************* + /// Constructor from a value convertible to value_type. + //******************************************* + template ::value && etl::is_convertible::value, int>::type = 0> + ETL_CONSTEXPR14 expected(U&& value_) + : storage(etl::in_place_index_t(), etl::forward(value_)) + { + } + + //******************************************* + /// Constructor from a value explicitly convertible to value_type. + //******************************************* + template ::value + && !etl::is_convertible::value, + int>::type = 0> + ETL_CONSTEXPR14 explicit expected(U&& value_) + : storage(etl::in_place_index_t(), etl::forward(value_)) + { + } + + //******************************************* + /// Copy constructor from an expected with convertible value and error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + expected(const etl::expected& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(other.value()); + } + else + { + storage.template emplace(other.error()); + } + } + + //******************************************* + /// Copy constructor from an expected with explicitly convertible value or error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && !private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + explicit expected(const etl::expected& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(other.value()); + } + else + { + storage.template emplace(other.error()); + } + } + + //******************************************* + /// Move constructor from an expected with convertible value and error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + expected(etl::expected&& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(etl::move(other.value())); + } + else + { + storage.template emplace(etl::move(other.error())); + } + } + + //******************************************* + /// Move constructor from an expected with explicitly convertible value or error types. + //******************************************* + template ::value + && private_expected::is_expected_conversion_constructible::value + && !private_expected::is_expected_conversion_implicit::value, + int>::type = 0> + explicit expected(etl::expected&& other) + : storage(private_variant::valueless_t()) + { + if (other.has_value()) + { + storage.template emplace(etl::move(other.value())); + } + else + { + storage.template emplace(etl::move(other.error())); + } + } +#endif + //******************************************* /// Copy constructor //******************************************* @@ -315,13 +509,13 @@ namespace etl //******************************************* /// Copy construct from unexpected type. //******************************************* - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 explicit expected(const etl::unexpected& ue) : storage(etl::in_place_index_t(), ue.error()) { } - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 expected(const etl::unexpected& ue) : storage(etl::in_place_index_t(), ue.error()) { @@ -338,13 +532,13 @@ namespace etl //******************************************* /// Move construct from unexpected type. //******************************************* - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 explicit expected(etl::unexpected&& ue) : storage(etl::in_place_index_t(), etl::move(ue.error())) { } - template ::value, bool>::type = false> + template ::value, bool> = false> ETL_CONSTEXPR14 expected(etl::unexpected&& ue) : storage(etl::in_place_index_t(), etl::move(ue.error())) { @@ -451,6 +645,17 @@ namespace etl return *this; } + + //******************************************* + /// Assign from a value convertible to value_type. + //******************************************* + template ::value, int>::type = 0> + expected& operator=(U&& value) + { + storage.template emplace(etl::forward(value)); + + return *this; + } #endif //******************************************* @@ -746,7 +951,7 @@ namespace etl //******************************************* const value_type& operator*() const ETL_LVALUE_REF_QUALIFIER { - ETL_ASSERT_OR_RETURN_VALUE(has_value(), ETL_ERROR(expected_invalid), ETL_NULLPTR); + ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::get(storage); } @@ -757,7 +962,7 @@ namespace etl //******************************************* value_type&& operator*() && { - ETL_ASSERT_OR_RETURN_VALUE(has_value(), ETL_ERROR(expected_invalid), ETL_NULLPTR); + ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::move(etl::get(storage)); } @@ -807,97 +1012,97 @@ namespace etl } #if ETL_USING_CPP11 - template ::type>::type> + template >> auto transform(F&& f) & -> expected { return transform_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform(F&& f) const& -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) && -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform(F&& f) const&& -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) & -> U { return and_then_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto and_then(F&& f) const& -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) && -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto and_then(F&& f) const&& -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto or_else(F&& f) & -> U { return or_else_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto or_else(F&& f) const& -> U { return or_else_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto or_else(F&& f) && -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto or_else(F&& f) const&& -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform_error(F&& f) & -> expected { return transform_error_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform_error(F&& f) const& -> expected { return transform_error_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform_error(F&& f) && -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> auto transform_error(F&& f) const&& -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); @@ -916,7 +1121,7 @@ namespace etl storage_type storage; #if ETL_USING_CPP11 - template < typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -929,7 +1134,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -944,8 +1149,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, typename TValueRef, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t::value && etl::is_expected::value + && etl::is_same::value>> auto and_then_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -959,8 +1164,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, typename TErrorRef, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t::value && etl::is_expected::value + && etl::is_same::value>> auto or_else_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -973,7 +1178,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = etl::enable_if_t::value>> auto transform_error_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1216,97 +1421,97 @@ namespace etl } #if ETL_USING_CPP11 - template ::type>::type> + template >> auto transform(F&& f) & -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) const& -> expected { return transform_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform(F&& f) && -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform(F&& f) const&& -> expected { return transform_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) & -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) const& -> U { return and_then_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto and_then(F&& f) && -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto and_then(F&& f) const&& -> U { return and_then_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto or_else(F&& f) & -> U { return or_else_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto or_else(F&& f) const& -> U { return or_else_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto or_else(F&& f) && -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto or_else(F&& f) const&& -> U { return or_else_impl(etl::forward(f), etl::move(*this)); } - template ::type>::type> + template >> auto transform_error(F&& f) & -> expected { return transform_error_impl(etl::forward(f), *this); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto transform_error(F&& f) const& -> expected { return transform_error_impl(etl::forward(f), *this); } - template ::type>::type> + template >> auto transform_error(F&& f) && -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); } - template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> + template < typename F, typename U = etl::remove_cvref_t>> auto transform_error(F&& f) const&& -> expected { return transform_error_impl(etl::forward(f), etl::move(*this)); @@ -1324,7 +1529,7 @@ namespace etl etl::variant storage; #if ETL_USING_CPP11 - template < typename F, typename TExp, typename TRet, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1337,7 +1542,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename = etl::enable_if_t::value>> auto transform_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1352,8 +1557,8 @@ namespace etl } template < typename F, typename TExp, typename TRet, - typename = typename etl::enable_if< !etl::is_void::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t< !etl::is_void::value && etl::is_expected::value + && etl::is_same::value>> auto and_then_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -1367,8 +1572,8 @@ namespace etl } template ::value && etl::is_expected::value - && etl::is_same::value>::type> + typename = etl::enable_if_t< !etl::is_void::value && etl::is_expected::value + && etl::is_same::value>> auto or_else_impl(F&& f, TExp&& exp) const -> TRet { if (exp.has_value()) @@ -1381,7 +1586,7 @@ namespace etl } } - template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if::value>::type> + template < typename F, typename TExp, typename TRet, typename TErrorRef, typename = etl::enable_if_t::value>> auto transform_error_impl(F&& f, TExp&& exp) const -> expected { if (exp.has_value()) @@ -1435,6 +1640,11 @@ namespace etl return lhs.error() == rhs.error(); } +#include "private/diagnostic_uninitialized_push.h" + //******************************************* + // The error is only read when the expected does not have a value, in which + // case it is always fully constructed. GCC can emit a false positive + // -Wmaybe-uninitialized when these are inlined at high optimisation levels. //******************************************* template ETL_CONSTEXPR14 bool operator==(const etl::expected& lhs, const etl::expected& rhs) @@ -1460,6 +1670,7 @@ namespace etl } return lhs.error() == rhs.error(); } +#include "private/diagnostic_pop.h" //******************************************* template diff --git a/include/etl/flat_multiset.h b/include/etl/flat_multiset.h index a3d5b048..e0d13220 100644 --- a/include/etl/flat_multiset.h +++ b/include/etl/flat_multiset.h @@ -321,7 +321,6 @@ namespace etl //************************************************************************* /// Emplaces a value to the set. //************************************************************************* - template ETL_OR_STD::pair emplace(const_reference value) { return insert(value); diff --git a/include/etl/format.h b/include/etl/format.h index 75ac5952..288454fd 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -35,7 +35,6 @@ SOFTWARE. #include "algorithm.h" #include "array.h" -#include "array_view.h" #include "error_handler.h" #include "limits.h" #include "math.h" @@ -91,44 +90,70 @@ namespace etl POINTER // const void* }; - // Map a type to its category. Decays and removes cv-qualifiers. + // Trait mapping a type (with references and cv-qualifiers removed) to its type_category. + // + // NOTE: signed char/unsigned char (typically int8_t/uint8_t) are NOT + // categorised as CHAR here - only plain 'char' gets character semantics. + // signed/unsigned char are formatted as integers, matching basic_format_arg + // storage and std::format's formatter/. This is + // enforced structurally: the INTEGER partial specialisation is constrained + // with is_integral && !is_same && !is_same, so + // signed/unsigned char (is_integral == true) fall into INTEGER while the + // dedicated bool/char full specialisations take priority for those exact types. + template + struct type_category_trait + { + static constexpr type_category value = type_category::NONE; // unknown type: custom formatter, be permissive + }; + + template <> + struct type_category_trait + { + static constexpr type_category value = type_category::BOOLEAN; + }; + + template <> + struct type_category_trait + { + static constexpr type_category value = type_category::CHAR; + }; + + template + struct type_category_trait< + T, typename etl::enable_if::value && !etl::is_same::value && !etl::is_same::value>::type> + { + static constexpr type_category value = type_category::INTEGER; + }; + + template + struct type_category_trait::value>::type> + { + static constexpr type_category value = type_category::FLOAT; + }; + + template + struct type_category_trait::value || etl::is_same::value + || etl::is_same::value || etl::is_base_of::value>::type> + { + static constexpr type_category value = type_category::STRING; + }; + + template + struct type_category_trait< + T, typename etl::enable_if::value && !etl::is_same::value && !etl::is_same::value>::type> + { + static constexpr type_category value = type_category::POINTER; + }; + + // Map a type to its category. Removes references and cv-qualifiers, then defers + // entirely to the type_category_trait specialisations above. template constexpr type_category get_type_category() { using U = typename etl::remove_cv::type>::type; - // Order matters: bool before integral, char before integral - if (etl::is_same::value) - return type_category::BOOLEAN; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_same::value) - return type_category::CHAR; - if (etl::is_integral::value) - return type_category::INTEGER; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::FLOAT; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_same::value) - return type_category::STRING; - if (etl::is_base_of::value) - return type_category::STRING; - if (etl::is_pointer::value) - return type_category::POINTER; - if (etl::is_same::value) - return type_category::POINTER; - if (etl::is_same::value) - return type_category::POINTER; - return type_category::NONE; // unknown type: custom formatter, be permissive + return type_category_trait::value; } // Check if a format type character is valid for a given type category. @@ -601,7 +626,7 @@ namespace etl }; template - struct has_formatter_parse&>().parse(etl::declval()))> > + struct has_formatter_parse&>().parse(etl::declval()))>> : etl::true_type { }; @@ -616,7 +641,7 @@ namespace etl template struct has_formatter_format&>().format( - etl::declval(), etl::declval&>()))> > : etl::true_type + etl::declval(), etl::declval&>()))>> : etl::true_type { }; @@ -627,6 +652,197 @@ namespace etl struct is_formattable : etl::bool_constant::value && has_formatter_format::value> { }; + + //************************************************************************* + // Compile-time argument mask. + // + // basic_format_arg stores its value in a variant with a fixed set of + // alternatives, and etl::visit dispatches over every one of them. That + // would instantiate etl::formatter for all alternatives - including the + // whole floating point formatting chain - even for a call that only ever + // passes an int. + // + // The mask records which alternatives can actually occur for a given pack + // of argument types. format_visitor turns the unreachable alternatives into + // empty functions, so their formatters are never instantiated and the + // linker never sees the code. + // + // A single table, arg_type_mask, drives both halves: it builds the mask + // from the argument pack, and it tells the visitor which bit an alternative + // corresponds to. + // + // Soundness: the mask is only ever narrowed for argument types that are + // recognised explicitly below. Anything else - including user-defined types + // and pointers other than void* - falls back to mask_all, which reproduces + // the unmasked behaviour exactly. + //************************************************************************* + typedef unsigned int arg_mask_t; + + static ETL_CONSTANT arg_mask_t mask_monostate = 1U << 0; + static ETL_CONSTANT arg_mask_t mask_bool = 1U << 1; + static ETL_CONSTANT arg_mask_t mask_char = 1U << 2; + static ETL_CONSTANT arg_mask_t mask_int = 1U << 3; + static ETL_CONSTANT arg_mask_t mask_uint = 1U << 4; + static ETL_CONSTANT arg_mask_t mask_llong = 1U << 5; + static ETL_CONSTANT arg_mask_t mask_ullong = 1U << 6; + static ETL_CONSTANT arg_mask_t mask_float = 1U << 7; + static ETL_CONSTANT arg_mask_t mask_double = 1U << 8; + static ETL_CONSTANT arg_mask_t mask_ldouble = 1U << 9; + static ETL_CONSTANT arg_mask_t mask_cstring = 1U << 10; + static ETL_CONSTANT arg_mask_t mask_string_view = 1U << 11; + static ETL_CONSTANT arg_mask_t mask_voidp = 1U << 12; + static ETL_CONSTANT arg_mask_t mask_all = ~static_cast(0); + + // Maps a type onto the set of alternatives a value of that type can be + // stored as. Mirrors the converting constructors of basic_format_arg. + // + // This serves both directions of the problem. For a user argument type it + // gives the alternatives that argument may end up in, which is how the + // per-call mask is built. For a variant alternative it degenerates to that + // alternative's own bit, which is how format_visitor tests reachability. + // + // The primary template is deliberately conservative: an unrecognised type + // yields mask_all, so it is never wrongly excluded. + template + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_all; + }; + + // Not constructible as a user argument; listed so that the table covers + // every variant alternative. Every mask contains this bit regardless, as + // the args_mask recursion bottoms out at mask_monostate. + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_monostate; + }; + + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_bool; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_char; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_int; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_uint; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_llong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ullong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_llong; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ullong; + }; + #if ETL_USING_FORMAT_FLOATING_POINT + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_float; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_double; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_ldouble; + }; + #endif + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_cstring; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_cstring; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_string_view; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_voidp; + }; + template <> + struct arg_type_mask + { + static ETL_CONSTANT arg_mask_t value = mask_voidp; + }; + + // etl::string, etl::istring and friends are stored as a string_view. + template + struct arg_type_mask, T>::value>> + { + static ETL_CONSTANT arg_mask_t value = mask_string_view; + }; + + // The empty alternative is always reachable, so monostate is always set. + template + struct args_mask; + + template <> + struct args_mask<> + { + static ETL_CONSTANT arg_mask_t value = mask_monostate; + }; + + template + struct args_mask + { + static ETL_CONSTANT arg_mask_t value = arg_type_mask>::value | args_mask::value; + }; } // namespace private_format template @@ -727,13 +943,16 @@ namespace etl { } + // int8_t / uint8_t are typically defined as signed char / unsigned char, but + // (unlike plain char) they are formatted as integers by default, matching + // std::format's formatter/formatter behaviour. basic_format_arg(const signed char v) - : data(static_cast(v)) + : data(static_cast(v)) { } basic_format_arg(const unsigned char v) - : data(static_cast(v)) + : data(static_cast(v)) { } @@ -777,7 +996,7 @@ namespace etl // Converting constructor for user-defined types that provide an // etl::formatter specialisation. The value is stored type-erased in a // handle, matching the std::basic_format_arg behaviour for custom types. - template ::value> > + template ::value>> basic_format_arg(const T& v) : data(handle(v)) { @@ -834,7 +1053,7 @@ namespace etl return _args.get(i); } - etl::array_view> get() + etl::span> get() { return _args; } @@ -855,6 +1074,15 @@ namespace etl { } + // non-standard + // Constructs from externally owned storage, for cases where the number of + // arguments is only known at runtime and so no format_arg_store + // can be formed. The referenced storage must outlive this object. + explicit basic_format_args(etl::span> args_) + : _args(args_) + { + } + basic_format_args(const basic_format_args& other) : _args(other._args) { @@ -879,7 +1107,7 @@ namespace etl private: - etl::array_view> _args; + etl::span> _args; }; namespace private_format @@ -1169,11 +1397,11 @@ namespace etl return false; } - template - void parse_format_spec(format_parse_context& parse_ctx, format_context& fmt_context) + // Not templated on the output iterator: the parse phase only reads the + // parse context and writes the format spec, so a single instantiation is + // shared by every output iterator type. + inline void parse_format_spec(format_parse_context& parse_ctx, format_spec_t& format_spec) { - auto& format_spec = fmt_context.format_spec; - format_spec = format_spec_t(); // reset format_spec to defaults format_spec.index = parse_num(parse_ctx); // optional explicit index @@ -1487,7 +1715,7 @@ namespace etl } // for integers - template + template void format_num(OutputIt& it, T value, const format_spec_t& spec) { size_t width = 0; @@ -1499,6 +1727,19 @@ namespace etl } #if ETL_USING_FORMAT_FLOATING_POINT + //*********************************** + // On many targets (e.g. ARM EABI, MSVC) 'long double' has exactly the same + // representation as 'double'. There, formatting a long double through the + // 'double' instantiation is value-preserving, and avoids emitting a second, + // byte-identical copy of the whole floating point formatting chain. + // On targets where the two differ (e.g. x86 80-bit) 'long double' is kept. + typedef etl::conditional_t<(sizeof(long double) == sizeof(double)) + && (etl::numeric_limits::digits == etl::numeric_limits::digits) + && (etl::numeric_limits::max_exponent == etl::numeric_limits::max_exponent) + && (etl::numeric_limits::min_exponent == etl::numeric_limits::min_exponent), + double, long double> + long_double_format_type; + #if ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH //*********************************** // Math function wrappers to handle toolchains that don't provide @@ -1967,7 +2208,7 @@ namespace etl } #endif - template + template struct format_visitor { using output_iterator = OutputIt; @@ -1981,6 +2222,28 @@ namespace etl // for all the built-in alternatives stored in basic_format_arg template void operator()(T value) + { + // T is a variant alternative here, so arg_type_mask is that + // alternative's own bit. + // Tag dispatch rather than 'if constexpr' so that the formatter for an + // unreachable alternative is not instantiated in C++11/14 either. + typedef etl::conditional_t<(arg_type_mask::value & Mask) != 0, etl::true_type, etl::false_type> reachable; + dispatch(value, reachable()); + } + + // for user-defined types routed through basic_format_arg::handle + void operator()(typename basic_format_arg>::handle h) + { + h.format(parse_ctx, fmt_ctx); + } + + format_parse_context& parse_ctx; + format_context& fmt_ctx; + + private: + + template + void dispatch(T value, etl::true_type) { formatter f; format_parse_context::iterator it = f.parse(parse_ctx); @@ -1989,14 +2252,12 @@ namespace etl fmt_ctx.advance_to(fit); } - // for user-defined types routed through basic_format_arg::handle - void operator()(typename basic_format_arg >::handle h) + // This alternative cannot be reached for the argument types of this call, + // so no formatter is instantiated for it. + template + void dispatch(T, etl::false_type) { - h.format(parse_ctx, fmt_ctx); } - - format_parse_context& parse_ctx; - format_context& fmt_ctx; }; template @@ -2608,7 +2869,8 @@ namespace etl template typename format_context::iterator format(long double arg, format_context& fmt_ctx) { - return private_format::format_aligned_floating(arg, fmt_ctx); + typedef private_format::long_double_format_type type; + return private_format::format_aligned_floating(static_cast(arg), fmt_ctx); } }; #endif @@ -2678,12 +2940,15 @@ namespace etl } }; - template + // Mask is a compile-time set of the basic_format_arg alternatives that the + // caller can actually supply. It only removes unreachable code paths; the + // default of mask_all formats every alternative, as before. + template OutputIt vformat_to(OutputIt out, etl::string_view fmt, format_args args) { - format_parse_context parse_context(fmt, args.size()); - format_context fmt_context(out, args); - private_format::format_visitor v(parse_context, fmt_context); + format_parse_context parse_context(fmt, args.size()); + format_context fmt_context(out, args); + private_format::format_visitor v(parse_context, fmt_context); while (parse_context.begin() != parse_context.end()) { @@ -2699,7 +2964,7 @@ namespace etl } else { - private_format::parse_format_spec(parse_context, fmt_context); + private_format::parse_format_spec(parse_context, fmt_context.format_spec); // Resolve nested replacement fields for width/precision private_format::resolve_nested_replacements(fmt_context.format_spec, args); @@ -2737,14 +3002,14 @@ namespace etl OutputIt format_to(OutputIt out, format_string fmt, Args&&... args) { auto the_args{make_format_args(args...)}; - return vformat_to(etl::move(out), fmt.get(), format_args(the_args)); + return vformat_to::value>(etl::move(out), fmt.get(), format_args(the_args)); } template , class... Args> OutputIt format_to_n(OutputIt out, size_t n, format_string fmt, Args&&... args) { auto the_args{make_format_args(args...)}; - return vformat_to(WrapperIt(out, n), fmt.get(), format_args(the_args)).get(); + return vformat_to::value>(WrapperIt(out, n), fmt.get(), format_args(the_args)).get(); } // non std in the following, specific to etl diff --git a/include/etl/forward_list.h b/include/etl/forward_list.h index cfba7bf9..f46ec9f5 100644 --- a/include/etl/forward_list.h +++ b/include/etl/forward_list.h @@ -1793,10 +1793,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_forward_list(T&&... t) -> etl::forward_list, sizeof...(T)> + template + constexpr auto make_forward_list(TValues&&... values) -> etl::forward_list, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/index_of_type.h b/include/etl/index_of_type.h index 2d799832..7471ef22 100644 --- a/include/etl/index_of_type.h +++ b/include/etl/index_of_type.h @@ -41,7 +41,7 @@ namespace etl //*************************************************************************** /// Defines a no-position constant. //*************************************************************************** - static ETL_CONSTANT size_t index_of_type_npos = etl::integral_limits::max; + ETL_INLINE_VAR ETL_CONSTANT size_t index_of_type_npos = etl::integral_limits::max; //*************************************************************************** /// Finds the index of a type in a variadic type parameter. diff --git a/include/etl/indirect_vector.h b/include/etl/indirect_vector.h index 0f0849c9..2fcac9ba 100644 --- a/include/etl/indirect_vector.h +++ b/include/etl/indirect_vector.h @@ -1551,10 +1551,15 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_indirect_vector(T&&... t) -> etl::indirect_vector, sizeof...(T)> + template + constexpr auto + make_indirect_vector(TValues&&... values) -> etl::indirect_vector, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/inplace_function.h b/include/etl/inplace_function.h index 63760d15..e9211702 100644 --- a/include/etl/inplace_function.h +++ b/include/etl/inplace_function.h @@ -1126,8 +1126,13 @@ namespace etl { } + #include "private/diagnostic_uninitialized_push.h" //************************************************************************* // clone_from + // + // GCC can emit a false positive -Wmaybe-uninitialized for the source + // storage when this is inlined into a copy constructor at high + // optimisation levels. The source object is always fully constructed. //************************************************************************* template void clone_from(const etl::inplace_function& other) @@ -1139,6 +1144,7 @@ namespace etl vtable->copy(&other.storage, &storage); } } + #include "private/diagnostic_pop.h" //************************************************************************* // move_from diff --git a/include/etl/invoke.h b/include/etl/invoke.h index 52ad388c..d2644748 100644 --- a/include/etl/invoke.h +++ b/include/etl/invoke.h @@ -39,6 +39,8 @@ SOFTWARE. #if ETL_USING_CPP11 + #include "private/diagnostic_array_bounds_push.h" + namespace etl { //**************************************************************************** @@ -387,5 +389,7 @@ namespace etl #endif } // namespace etl + #include "private/diagnostic_pop.h" + #endif // ETL_USING_CPP11 #endif // ETL_INVOKE_INCLUDED diff --git a/include/etl/iterator.h b/include/etl/iterator.h index a990fb6c..01de257a 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -1656,6 +1656,22 @@ namespace etl } return n; } + + // Overload for ranges + template ()))> + constexpr etl::iter_difference_t operator()(R&& r) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_size_member::value) + { + return static_cast>(r.size()); + } + else + { + return (*this)(ETL_OR_STD::begin(r), ETL_OR_STD::end(r)); + } + } }; struct size diff --git a/include/etl/list.h b/include/etl/list.h index c2bbdfc2..433aebef 100644 --- a/include/etl/list.h +++ b/include/etl/list.h @@ -2218,10 +2218,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_list(T... t) -> etl::list, sizeof...(T)> + template + constexpr auto make_list(TValues&&... values) -> etl::list, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/memory.h b/include/etl/memory.h index f923a6ce..742766c5 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -53,6 +53,44 @@ SOFTWARE. namespace etl { + namespace private_memory + { + //************************************************************************* + /// Checks whether a type can be copied to uninitialised storage by using + /// assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_copy_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_copy_constructible::value + && etl::is_trivially_copy_assignable::value> + { + }; + +#if ETL_USING_CPP11 + //************************************************************************* + /// Checks whether a type can be moved to uninitialised storage by using + /// assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_move_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_move_constructible::value + && etl::is_trivially_move_assignable::value> + { + }; +#endif + + //************************************************************************* + /// Checks whether a value-initialised type can be copied to uninitialised + /// storage by using assignment instead of placement new. + //************************************************************************* + template + struct is_trivially_value_assignable_to_uninitialised_storage + : etl::bool_constant::value && etl::is_trivially_copyable::value + && etl::is_trivially_copy_assignable::value> + { + }; + } // namespace private_memory + //***************************************************************************** /// Obtain the address represented by p without forming a reference to the /// object pointed to by p. Defined when not using the STL or C++20 @@ -428,7 +466,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { etl::fill(o_begin, o_end, value); @@ -442,8 +482,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { typedef typename etl::iterator_traits::value_type value_type; @@ -464,7 +505,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += static_cast(etl::distance(o_begin, o_end)); @@ -481,8 +524,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += static_cast(etl::distance(o_begin, o_end)); @@ -577,7 +621,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { return etl::copy(i_begin, i_end, o_begin); @@ -589,8 +635,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -614,7 +661,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::copy(i_begin, i_end, o_begin); @@ -630,8 +679,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_copy_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_copy(i_begin, i_end, o_begin); @@ -910,7 +960,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { return etl::move(i_begin, i_end, o_begin); @@ -922,8 +974,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -947,7 +1000,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::move(i_begin, i_end, o_begin); @@ -963,8 +1018,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_move(i_begin, i_end, o_begin); @@ -1037,7 +1093,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin) { return etl::move(i_begin, i_begin + n, o_begin); @@ -1049,8 +1107,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin) { typedef typename etl::iterator_traits::value_type value_type; @@ -1074,7 +1133,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, TOutputIterator>::type + typename etl::enable_if< + etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::move(i_begin, i_begin + n, o_begin); @@ -1090,8 +1151,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, - TOutputIterator>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_move_assignable_to_uninitialised_storage::value_type>::value, + TOutputIterator>::type uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_move(i_begin, i_begin + n, o_begin); @@ -1527,7 +1589,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, void>::type + typename etl::enable_if< + etl::private_memory::is_trivially_value_assignable_to_uninitialised_storage::value_type>::value, + void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename etl::iterator_traits::value_type value_type; @@ -1541,7 +1605,9 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if< !etl::is_trivially_constructible< typename etl::iterator_traits::value_type>::value, void>::type + typename etl::enable_if< + !etl::private_memory::is_trivially_value_assignable_to_uninitialised_storage::value_type>::value, + void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename etl::iterator_traits::value_type value_type; diff --git a/include/etl/message_packet.h b/include/etl/message_packet.h index fdaed2b3..de56fb9c 100644 --- a/include/etl/message_packet.h +++ b/include/etl/message_packet.h @@ -305,6 +305,7 @@ namespace etl (add_new_message_type(etl::move(msg)) || ...); } + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** /// Only enabled for types that are in the typelist. @@ -318,6 +319,9 @@ namespace etl } #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" + + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** template @@ -336,6 +340,9 @@ namespace etl } #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" + + #include "private/diagnostic_array_bounds_push.h" #include "private/diagnostic_uninitialized_push.h" //******************************************** template @@ -352,6 +359,8 @@ namespace etl return false; } } + #include "private/diagnostic_pop.h" + #include "private/diagnostic_pop.h" typename etl::aligned_storage::type data; diff --git a/include/etl/mutex/mutex_freertos.h b/include/etl/mutex/mutex_freertos.h index 080b571a..6fde9d2c 100644 --- a/include/etl/mutex/mutex_freertos.h +++ b/include/etl/mutex/mutex_freertos.h @@ -31,8 +31,26 @@ SOFTWARE. #include "../platform.h" -#include "FreeRTOS.h" -#include +#if defined(__has_include) // Compiler supports __has_include + + #if __has_include() && __has_include() // Standard FreeRTOS include layout + #include + #include + + #elif __has_include() && __has_include() // ESP-IDF style include layout + #include + #include + + #else // Neither include layout found + #error "FreeRTOS.h and semphr.h not found. Please include path to FreeRTOS in your project settings" + #endif + +#else + + #include + #include + +#endif namespace etl { diff --git a/include/etl/not_null.h b/include/etl/not_null.h index 25823d68..de0ebe22 100644 --- a/include/etl/not_null.h +++ b/include/etl/not_null.h @@ -35,6 +35,7 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "memory.h" +#include "nullptr.h" #include "type_traits.h" namespace etl @@ -46,7 +47,7 @@ namespace etl { public: - not_null_exception(string_type reason_, string_type file_name_, numeric_type line_number_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + not_null_exception(string_type reason_, string_type file_name_, numeric_type line_number_) : exception(reason_, file_name_, line_number_) { } @@ -59,7 +60,7 @@ namespace etl { public: - not_null_contains_null(string_type file_name_, numeric_type line_number_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + not_null_contains_null(string_type file_name_, numeric_type line_number_) : not_null_exception(ETL_ERROR_TEXT("not_null:contains null", ETL_NOT_NULL_FILE_ID"A"), file_name_, line_number_) { } @@ -92,10 +93,19 @@ namespace etl /// Constructs a not_null from a pointer. /// Asserts if the pointer is null. //********************************* - ETL_CONSTEXPR14 explicit not_null(underlying_type ptr_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 explicit not_null(underlying_type ptr_) : ptr(ptr_) { - ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + if (etl::is_constant_evaluated()) + { + // ETL_IS_FOLDED_NULL keeps the compile time rejection of null working + // when the undefined behaviour sanitizer instruments the comparison. + ETL_ASSERT(!ETL_IS_FOLDED_NULL(ptr_), ETL_ERROR(not_null_contains_null)); + } + else + { + ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + } } //********************************* @@ -110,9 +120,16 @@ namespace etl /// Assignment from a pointer. /// Asserts if the pointer is null. //********************************* - ETL_CONSTEXPR14 not_null& operator=(underlying_type rhs) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 not_null& operator=(underlying_type rhs) { - ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); + if (etl::is_constant_evaluated()) + { + ETL_ASSERT_OR_RETURN_VALUE(!ETL_IS_FOLDED_NULL(rhs), ETL_ERROR(not_null_contains_null), *this); + } + else + { + ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); + } ptr = rhs; @@ -193,7 +210,7 @@ namespace etl /// Asserts if the unique_ptr contains null. /// Moves from the unique_ptr. //********************************* - ETL_CONSTEXPR14 explicit not_null(underlying_type&& u_ptr_) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 explicit not_null(underlying_type&& u_ptr_) : u_ptr(etl::move(u_ptr_)) { ETL_ASSERT(u_ptr.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); @@ -204,7 +221,7 @@ namespace etl /// Asserts if the unique_ptr contains null. /// Moves from the unique_ptr. //********************************* - ETL_CONSTEXPR14 not_null& operator=(underlying_type&& rhs) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + ETL_CONSTEXPR14 not_null& operator=(underlying_type&& rhs) { ETL_ASSERT_OR_RETURN_VALUE(rhs.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); diff --git a/include/etl/nullptr.h b/include/etl/nullptr.h index 581305ae..57f1c8bd 100644 --- a/include/etl/nullptr.h +++ b/include/etl/nullptr.h @@ -78,4 +78,26 @@ namespace etl #endif } // namespace etl +//***************************************************************************** +/// True when the compiler can fold the comparison at compile time and it yields +/// null. +/// +/// Normally the comparison folds for any pointer that is a constant expression, +/// so this is equivalent to a plain null check. Under GCC's undefined behaviour +/// sanitizer, however, the comparison is instrumented and only folds for a null +/// pointer constant, not for the address of an object or function. +/// __builtin_constant_p filters out the latter, which would otherwise make the +/// enclosing expression non-constant and reject perfectly valid code. +/// +/// Only meaningful during constant evaluation. At run time +/// __builtin_constant_p is false for any pointer that is not a compile time +/// constant, so this reports false for a genuine null pointer. Callers must +/// use a plain comparison against ETL_NULLPTR on the run time path. +//***************************************************************************** +#if ETL_USING_BUILTIN_CONSTANT_P + #define ETL_IS_FOLDED_NULL(p) (__builtin_constant_p((p) == ETL_NULLPTR) && ((p) == ETL_NULLPTR)) +#else + #define ETL_IS_FOLDED_NULL(p) ((p) == ETL_NULLPTR) +#endif + #endif diff --git a/include/etl/optional.h b/include/etl/optional.h index 371c9f2a..f675354e 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -36,6 +36,7 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "initializer_list.h" +#include "invoke.h" #include "memory.h" #include "placement_new.h" #include "type_traits.h" @@ -49,6 +50,41 @@ namespace etl template class optional; + //***************************************************************************** + /// Determines if a type is an etl::optional. + /// Ignores any cv qualification on the type being tested. + ///\ingroup utilities + //***************************************************************************** + template + struct is_optional : etl::false_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + + template + struct is_optional > : etl::true_type + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_optional_v = etl::is_optional::value; +#endif + //***************************************************************************** /// A null option type. ///\ingroup utilities @@ -59,7 +95,7 @@ namespace etl // Convertible to any type of null non-member pointer. template - operator T*() const + ETL_CONSTEXPR operator T*() const ETL_NOEXCEPT { return 0; } @@ -74,7 +110,7 @@ namespace etl /// A null option. ///\ingroup utilities //***************************************************************************** - const nullopt_t nullopt = {}; + ETL_INLINE_VAR ETL_CONSTANT nullopt_t nullopt = {}; //*************************************************************************** /// Exception for optional. @@ -174,10 +210,9 @@ namespace etl /// Constructs T in-place from U&&, without requiring T to be /// copy/move constructible. //*************************************************************************** - template ::value && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, optional_impl>::value, - int>::type = 0> + template ::value && !etl::is_same, etl::in_place_t>::value + && !etl::is_same, optional_impl>::value, + int> = 0> ETL_CONSTEXPR20_STL optional_impl(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); @@ -276,9 +311,7 @@ namespace etl /// Assignment operator from value type. //*************************************************************************** #if ETL_USING_CPP11 - template ::value && !etl::is_same::type, optional_impl>::value, - int>::type = 0> + template ::value && !etl::is_same, optional_impl>::value, int> = 0> ETL_CONSTEXPR20_STL optional_impl& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); @@ -511,8 +544,7 @@ namespace etl //******************************************* /// Emplaces a value from initializer_list and arbitrary constructor arguments. //******************************************* - template &, Args...>::value, int>::type = 0 > + template &, Args...>::value, int> = 0 > ETL_CONSTEXPR20_STL T& emplace(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { @@ -671,6 +703,11 @@ namespace etl } #endif +#include "private/diagnostic_uninitialized_push.h" + //******************************* + // GCC may warn that 'valid' is used uninitialised when the storage of a + // nested optional is inspected through an inlined destructor chain. + // The flag is always initialised by the constructors, so this is a false positive. //******************************* ETL_CONSTEXPR20_STL void destroy() @@ -681,6 +718,7 @@ namespace etl valid = false; } } +#include "private/diagnostic_pop.h" //******************************* union union_type @@ -1217,8 +1255,8 @@ namespace etl }; } // namespace private_optional -#define ETL_OPTIONAL_ENABLE_CPP14 typename etl::enable_if< etl::is_pod::type>::value, int>::type = 0 -#define ETL_OPTIONAL_ENABLE_CPP20_STL typename etl::enable_if< !etl::is_pod::type>::value, int>::type = 0 +#define ETL_OPTIONAL_ENABLE_CPP14 etl::enable_if_t< etl::is_pod::type>::value, int> = 0 +#define ETL_OPTIONAL_ENABLE_CPP20_STL etl::enable_if_t< !etl::is_pod::type>::value, int> = 0 #define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP14 \ ETL_CONSTEXPR14 typename etl::enable_if< etl::is_pod::type>::value, bool>::type @@ -1319,12 +1357,10 @@ namespace etl /// Constructs T in-place from U&&, without requiring T to be /// copy/move constructible. //*************************************************************************** - template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, etl::nullopt_t>::value - && etl::is_pod::type>::value, - int>::type = 0> + template < typename U, etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::in_place_t>::value + && !etl::is_same, etl::nullopt_t>::value && etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR14 optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { @@ -1334,11 +1370,10 @@ namespace etl /// Converting constructor from value type. //*************************************************************************** template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::in_place_t>::value - && !etl::is_same::type, etl::nullopt_t>::value - && !etl::is_pod::type>::value, - int>::type = 0> + etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::in_place_t>::value && !etl::is_same, etl::nullopt_t>::value + && !etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR20_STL optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { @@ -1446,11 +1481,9 @@ namespace etl //*************************************************************************** /// Converting assignment operator from value type. //*************************************************************************** - template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::nullopt_t>::value - && etl::is_pod::type>::value, - int>::type = 0> + template < typename U, etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::nullopt_t>::value && etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR14 optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -1462,10 +1495,9 @@ namespace etl /// Converting assignment operator from value type. //*************************************************************************** template < typename U, - typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::optional>::value - && !etl::is_same::type, etl::nullopt_t>::value - && !etl::is_pod::type>::value, - int>::type = 0> + etl::enable_if_t< etl::is_constructible::value && !etl::is_same, etl::optional>::value + && !etl::is_same, etl::nullopt_t>::value && !etl::is_pod>::value, + int> = 0> ETL_CONSTEXPR20_STL optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -1519,6 +1551,124 @@ namespace etl { return this->has_value() ? this->operator->() + 1 : ETL_NULLPTR; } + +#if ETL_USING_CPP11 + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) & -> etl::optional + { + return transform_impl(etl::forward(f), *this); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) const& -> etl::optional + { + return transform_impl(etl::forward(f), *this); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) && -> etl::optional + { + return transform_impl(etl::forward(f), etl::move(*this)); + } + + template >, etl::enable_if_t::value, int> = 0> + auto transform(F&& f) const&& -> etl::optional + { + return transform_impl(etl::forward(f), etl::move(*this)); + } + + template >> + auto and_then(F&& f) & -> U + { + return and_then_impl(etl::forward(f), *this); + } + + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> + auto and_then(F&& f) const& -> U + { + return and_then_impl(etl::forward(f), *this); + } + + template >> + auto and_then(F&& f) && -> U + { + return and_then_impl(etl::forward(f), etl::move(*this)); + } + + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> + auto and_then(F&& f) const&& -> U + { + return and_then_impl(etl::forward(f), etl::move(*this)); + } + + template >> + auto or_else(F&& f) & -> U + { + return or_else_impl(etl::forward(f), *this); + } + + template < typename F, typename U = etl::remove_cvref_t>> + auto or_else(F&& f) const& -> U + { + return or_else_impl(etl::forward(f), *this); + } + + template >> + auto or_else(F&& f) && -> U + { + return or_else_impl(etl::forward(f), etl::move(*this)); + } + + template < typename F, typename U = etl::remove_cvref_t< etl::invoke_result_t>> + auto or_else(F&& f) const&& -> U + { + return or_else_impl(etl::forward(f), etl::move(*this)); + } + + private: + + template < typename F, typename TOpt, typename TRet, typename TValueRef, typename = etl::enable_if_t::value>> + auto transform_impl(F&& f, TOpt&& opt) const -> etl::optional + { + if (opt.has_value()) + { + return etl::optional(etl::invoke(etl::forward(f), etl::forward(*opt))); + } + else + { + return etl::optional(); + } + } + + template < typename F, typename TOpt, typename TRet, typename TValueRef, + typename = etl::enable_if_t::value && etl::is_optional::value>> + auto and_then_impl(F&& f, TOpt&& opt) const -> TRet + { + if (opt.has_value()) + { + return etl::invoke(etl::forward(f), etl::forward(*opt)); + } + else + { + return TRet(); + } + } + + template < + typename F, typename TOpt, typename TRet, + typename = etl::enable_if_t< !etl::is_void::value && etl::is_optional::value && etl::is_same::value>> + auto or_else_impl(F&& f, TOpt&& opt) const -> TRet + { + if (opt.has_value()) + { + return TRet(etl::forward(opt)); + } + else + { + return etl::invoke(etl::forward(f)); + } + } +#endif }; #include "private/diagnostic_uninitialized_push.h" @@ -2132,17 +2282,17 @@ namespace etl /// This overload participates in overload resolution only if /// `etl::is_constructible_v` is true. //*************************************************************************** - template ::value, int>::type = 0, // + template ::value, int> = 0, // typename U = T, ETL_OPTIONAL_ENABLE_CPP14> - ETL_CONSTEXPR14 etl::optional make_optional(Args&&... args) // + ETL_CONSTEXPR14 etl::optional make_optional(Args&&... args) // ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { return etl::optional(etl::in_place_t{}, etl::forward(args)...); } - template ::value, int>::type = 0, // - typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + template ::value, int> = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> ETL_CONSTEXPR20_STL etl::optional make_optional(Args&&... args) // ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { @@ -2156,17 +2306,17 @@ namespace etl /// This overload participates in overload resolution only if /// `etl::is_constructible_v&, Args...>` is true. //*************************************************************************** - template &, Args...>::value, int>::type = 0, // + template &, Args...>::value, int> = 0, // typename U = T, ETL_OPTIONAL_ENABLE_CPP14> ETL_CONSTEXPR14 etl::optional make_optional(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { return etl::optional(etl::in_place_t{}, ilist, etl::forward(args)...); } - template &, Args...>::value, int>::type = 0, // - typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + template &, Args...>::value, int> = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> ETL_CONSTEXPR20_STL etl::optional make_optional(std::initializer_list ilist, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) { diff --git a/include/etl/platform.h b/include/etl/platform.h index 82e31588..494b4981 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -524,6 +524,20 @@ SOFTWARE. #define ETL_INDETERMINATE #endif +//************************************* +// inline +#if ETL_USING_GCC_COMPILER || ETL_USING_CLANG_COMPILER + #define ETL_NO_INLINE __attribute__((noinline)) + #define ETL_ALWAYS_INLINE __attribute__((always_inline)) +#elif ETL_USING_MICROSOFT_COMPILER + #define ETL_NO_INLINE __declspec(noinline) + #define ETL_ALWAYS_INLINE __forceinline +#else + // will trigger a compilation error if used + #define ETL_NO_INLINE unsupported compiler + #define ETL_ALWAYS_INLINE unsupported compiler +#endif + //************************************* // Determine if the ETL can use char8_t type. #if ETL_NO_SMALL_CHAR_SUPPORT diff --git a/include/etl/poly_span.h b/include/etl/poly_span.h index 0c73ffd4..544befa5 100644 --- a/include/etl/poly_span.h +++ b/include/etl/poly_span.h @@ -77,6 +77,7 @@ namespace etl typedef ptrdiff_t difference_type; typedef TBase* pointer; typedef TBase& reference; + typedef const TBase& const_reference; typedef ETL_OR_STD::random_access_iterator_tag iterator_category; //***************************************** @@ -114,6 +115,15 @@ namespace etl return ptr; } + //*************************************************** + reference operator[](difference_type i) const + { + iterator result(*this); + result += i; + + return *result; + } + //***************************************** iterator& operator++() { diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index ea9df835..50174501 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -121,14 +121,14 @@ namespace etl ETL_STATIC_ASSERT(sizeof(TContainer) == 0, "Unsupported container type for etl::ipriority_queue"); }; - template - struct priority_queue_container_interface > + template + struct priority_queue_container_interface > { typedef etl::ivector type; }; - template - struct priority_queue_container_interface > + template + struct priority_queue_container_interface > { typedef etl::ideque type; }; diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index 0770e0f0..c351da9c 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -413,32 +413,23 @@ namespace etl //************************************************************************* /// Execute the delegate. //************************************************************************* - template - ETL_CONSTEXPR14 return_type operator()(TCallArgs&&... args) const + ETL_CONSTEXPR14 return_type operator()(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - ETL_ASSERT(is_valid(), ETL_ERROR(delegate_uninitialised)); - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } //************************************************************************* /// Execute the delegate if valid. /// 'void' return delegate. //************************************************************************* - template - ETL_CONSTEXPR14 typename etl::enable_if_t::value, bool> call_if(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 typename etl::enable_if_t::value, bool> call_if(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - (*invocation.stub)(invocation, etl::forward(args)...); + (*invocation.stub)(invocation, etl::forward(args)...); return true; } else @@ -451,18 +442,14 @@ namespace etl /// Execute the delegate if valid. /// Non 'void' return delegate. //************************************************************************* - template - ETL_CONSTEXPR14 typename etl::enable_if_t::value, etl::optional> call_if(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 typename etl::enable_if_t::value, etl::optional> call_if(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - etl::optional result; if (is_valid()) { - result = (*invocation.stub)(invocation, etl::forward(args)...); + result = (*invocation.stub)(invocation, etl::forward(args)...); } return result; @@ -472,20 +459,16 @@ namespace etl /// Execute the delegate if valid or call alternative. /// Run time alternative. //************************************************************************* - template - ETL_CONSTEXPR14 TReturn call_or(TAlternative&& alternative, TCallArgs&&... args) const + template + ETL_CONSTEXPR14 TReturn call_or(TAlternative&& alternative, TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { - return etl::forward(alternative)(etl::forward(args)...); + return etl::forward(alternative)(etl::forward(args)...); } } @@ -493,27 +476,23 @@ namespace etl /// Execute the delegate if valid or call alternative. /// Compile time alternative. //************************************************************************* - template - ETL_CONSTEXPR14 TReturn call_or(TCallArgs&&... args) const + template + ETL_CONSTEXPR14 TReturn call_or(TArgs... args) const { - ETL_STATIC_ASSERT((sizeof...(TCallArgs) == sizeof...(TArgs)), "Incorrect number of parameters passed to delegate"); - ETL_STATIC_ASSERT((etl::type_lists_are_convertible, argument_types>::value), - "Incompatible parameter types passed to delegate"); - if (is_valid()) { - return (*invocation.stub)(invocation, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { - return (Method)(etl::forward(args)...); + return (Method)(etl::forward(args)...); } } //************************************************************************* /// Assignment //************************************************************************* - delegate& operator=(const delegate& rhs) = default; + ETL_CONSTEXPR14 delegate& operator=(const delegate& rhs) = default; //************************************************************************* /// Create from Lambda or Functor. @@ -690,6 +669,14 @@ namespace etl //*********************************************************************** ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT { + if (stub == function_ptr_stub) + { + ptr.fp = ETL_NULLPTR; + } + else + { + ptr.object = ETL_NULLPTR; + } stub = ETL_NULLPTR; } diff --git a/include/etl/private/dynamic_extent.h b/include/etl/private/dynamic_extent.h index afcc9687..cd34d3c1 100644 --- a/include/etl/private/dynamic_extent.h +++ b/include/etl/private/dynamic_extent.h @@ -36,7 +36,10 @@ SOFTWARE. namespace etl { - static ETL_CONSTANT size_t dynamic_extent = etl::integral_limits::max; + // ETL_INLINE_VAR gives external linkage in C++17 and later, so that etl::span, + // which references dynamic_extent in its public interface, can be exported from + // a C++20 named module without exposing a TU-local entity. See issue #1543. + ETL_INLINE_VAR ETL_CONSTANT size_t dynamic_extent = etl::integral_limits::max; } #endif diff --git a/include/etl/private/variant_legacy.h b/include/etl/private/variant_legacy.h index 59d454d4..585f02e1 100644 --- a/include/etl/private/variant_legacy.h +++ b/include/etl/private/variant_legacy.h @@ -468,7 +468,7 @@ namespace etl explicit variant(etl::in_place_index_t, T const& value) : type_id(Index) { - ETL_STATIC_ASSERT(Type_Id_Lookup::type_id == Index, "Missmatched type"); + ETL_STATIC_ASSERT(Type_Id_Lookup::type_id == Index, "Mismatched type"); ::new (static_cast(data)) T(value); type_id = Index; } diff --git a/include/etl/private/variant_select_do_operator.h b/include/etl/private/variant_select_do_operator.h index 1fb1794a..42bcb0d0 100644 --- a/include/etl/private/variant_select_do_operator.h +++ b/include/etl/private/variant_select_do_operator.h @@ -34,7 +34,7 @@ SOFTWARE. namespace private_variant { //*************************************************************************** - // Selects a do_operator inplementation that is configured for the number of + // Selects a do_operator implementation that is configured for the number of // types. //*************************************************************************** template diff --git a/include/etl/private/variant_select_do_visitor.h b/include/etl/private/variant_select_do_visitor.h index e770af68..58a6e2d6 100644 --- a/include/etl/private/variant_select_do_visitor.h +++ b/include/etl/private/variant_select_do_visitor.h @@ -34,7 +34,7 @@ SOFTWARE. namespace private_variant { //*************************************************************************** - // Selects a do_visitor inplementation that is configured for the number of + // Selects a do_visitor implementation that is configured for the number of // types. //*************************************************************************** template diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 545fd5e8..4161aa31 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -69,6 +69,14 @@ namespace etl { namespace private_variant { + //******************************************* + /// Tag for constructing a variant with no alternative. + //******************************************* + struct valueless_t + { + explicit ETL_CONSTEXPR valueless_t() {} + }; + //******************************************* /// Switch-based dispatch for destroy/copy/move. /// Replaces the per-instance function pointer with @@ -86,12 +94,23 @@ namespace etl static void destroy(char*, size_t) {} static void copy(char*, const char*, size_t) {} static void move(char*, const char*, size_t) {} + static void copy_assign(char*, const char*, size_t) {} + static void move_assign(char*, const char*, size_t) {} }; // Recursive case. template struct variant_operations { + #include "diagnostic_uninitialized_push.h" + //************************************************************************* + // destroy + // + // GCC can emit a false positive -Wmaybe-uninitialized when this is inlined + // into a variant destructor at high optimisation levels, as it cannot + // prove that the branch for an inactive alternative is never taken. + // The alternative identified by type_id is always fully constructed. + //************************************************************************* static void destroy(char* data, size_t type_id) { if (type_id == Index) @@ -103,6 +122,7 @@ namespace etl variant_operations::destroy(data, type_id); } } + #include "diagnostic_pop.h" static void copy(char* dst, const char* src, size_t type_id) { @@ -128,8 +148,40 @@ namespace etl } } + static void copy_assign(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + copy_assign_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::copy_assign(dst, src, type_id); + } + } + + static void move_assign(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + move_assign_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::move_assign(dst, src, type_id); + } + } + private: + #include "diagnostic_uninitialized_push.h" + //************************************************************************* + // The *_impl functions below read the alternative identified by type_id, + // which is always fully constructed. GCC can emit a false positive + // -Wmaybe-uninitialized when these are inlined at high optimisation + // levels, as it cannot prove that the branch for an inactive alternative + // is never taken. + //************************************************************************* static void copy_impl(char* dst, const char* src, etl::true_type) { ::new (dst) THead(*reinterpret_cast(src)); @@ -143,6 +195,21 @@ namespace etl } static void move_impl(char*, const char*, etl::false_type) {} + + static void copy_assign_impl(char* dst, const char* src, etl::true_type) + { + *reinterpret_cast(dst) = *reinterpret_cast(src); + } + + static void copy_assign_impl(char*, const char*, etl::false_type) {} + + static void move_assign_impl(char* dst, const char* src, etl::true_type) + { + *reinterpret_cast(dst) = etl::move(*reinterpret_cast(const_cast(src))); + } + + static void move_assign_impl(char*, const char*, etl::false_type) {} + #include "diagnostic_pop.h" }; //******************************************* @@ -255,7 +322,7 @@ namespace etl } // namespace private_variant /// Definition of variant_npos. - constexpr size_t variant_npos = etl::integral_limits::max; + ETL_INLINE_VAR constexpr size_t variant_npos = etl::integral_limits::max; //*********************************** // variant. Forward declaration @@ -431,6 +498,80 @@ namespace etl { } + variant_base(const variant_base& other) noexcept(etl::conjunction...>::value) + : type_id(other.type_id) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + } + } + + variant_base(variant_base&& other) noexcept(etl::conjunction...>::value) + : type_id(other.type_id) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + } + } + + variant_base& operator=(const variant_base& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::copy_assign(data, other.data, type_id); + } + else + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + type_id = other.type_id; + } + } + } + + return *this; + } + + variant_base& operator=(variant_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::move_assign(data, other.data, type_id); + } + else + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + type_id = other.type_id; + } + } + } + + return *this; + } + ~variant_base() { if (type_id != variant_npos) @@ -441,40 +582,343 @@ namespace etl } }; - //*************************************************************************** - /// variant_base specialisation for trivially destructible types. - /// Uses variadic_union storage. Destructor is trivial (defaulted), making - /// the variant a literal type eligible for constexpr / ROM placement. - /// No operation function pointer is needed since destroy/copy/move are - /// all handled without indirection for trivially destructible types. - //*************************************************************************** + //******************************************* + /// Trait: are all types trivially copyable? + //******************************************* template - struct variant_base + struct are_all_trivially_copyable : etl::conjunction...> + { + }; + + //*************************************************************************** + /// Storage and copy/move operations used by the trivially destructible + /// variant_base specialisation. + /// The general case defines the copy and move operations in terms of the + /// operations of the currently active alternative. + //*************************************************************************** + template + struct variant_trivially_destructible_base { variadic_union data; size_t type_id; - constexpr variant_base() noexcept + constexpr variant_trivially_destructible_base() noexcept : data() , type_id(variant_npos) { } - constexpr variant_base(size_t id) noexcept + constexpr variant_trivially_destructible_base(size_t id) noexcept : data() , type_id(id) { } template - constexpr variant_base(etl::in_place_index_t, T&& value, - size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + constexpr variant_trivially_destructible_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) : data(etl::in_place_index_t{}, etl::forward(value)) , type_id(id) { } - ~variant_base() = default; + variant_trivially_destructible_base(const variant_trivially_destructible_base& other) noexcept( + etl::conjunction...>::value) + : data() + , type_id(other.type_id) + { + copy_construct_from(other); + } + + variant_trivially_destructible_base(variant_trivially_destructible_base&& other) noexcept( + etl::conjunction...>::value) + : data() + , type_id(other.type_id) + { + move_construct_from(other); + } + + variant_trivially_destructible_base& + operator=(const variant_trivially_destructible_base& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + copy_assign_from(other); + } + return *this; + } + + variant_trivially_destructible_base& + operator=(variant_trivially_destructible_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) + { + if (this != &other) + { + move_assign_from(other); + } + return *this; + } + + ~variant_trivially_destructible_base() = default; + + private: + + //******************************************* + // Dispatch to the active alternative's own copy/move operations. + // The active union member shares its address with the union, so the + // raw-pointer dispatch places the new object in the correct storage. + //******************************************* + void copy_construct_from(const variant_trivially_destructible_base& other) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + } + } + + void move_construct_from(variant_trivially_destructible_base& other) + { + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + } + } + + void copy_assign_from(const variant_trivially_destructible_base& other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::copy_assign(reinterpret_cast(&data), reinterpret_cast(&other.data), type_id); + return; + } + + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(reinterpret_cast(&data), type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::copy(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + type_id = other.type_id; + } + } + + void move_assign_from(variant_trivially_destructible_base& other) + { + if ((type_id != variant_npos) && (type_id == other.type_id)) + { + variant_operations<0, TTypes...>::move_assign(reinterpret_cast(&data), reinterpret_cast(&other.data), type_id); + return; + } + + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(reinterpret_cast(&data), type_id); + type_id = variant_npos; + } + + if (other.type_id != variant_npos) + { + variant_operations<0, TTypes...>::move(reinterpret_cast(&data), reinterpret_cast(&other.data), other.type_id); + type_id = other.type_id; + } + } + }; + + //*************************************************************************** + /// Specialisation for when all of the types are trivially copyable. + /// All of the copy and move operations are defaulted, so that they are + /// trivial and the variant itself is trivially copyable. See P0602R4. + //*************************************************************************** + template + struct variant_trivially_destructible_base + { + variadic_union data; + size_t type_id; + + constexpr variant_trivially_destructible_base() noexcept + : data() + , type_id(variant_npos) + { + } + + constexpr variant_trivially_destructible_base(size_t id) noexcept + : data() + , type_id(id) + { + } + + template + constexpr variant_trivially_destructible_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : data(etl::in_place_index_t{}, etl::forward(value)) + , type_id(id) + { + } + + variant_trivially_destructible_base(const variant_trivially_destructible_base&) = default; + variant_trivially_destructible_base(variant_trivially_destructible_base&&) = default; + variant_trivially_destructible_base& operator=(const variant_trivially_destructible_base&) = default; + variant_trivially_destructible_base& operator=(variant_trivially_destructible_base&&) = default; + ~variant_trivially_destructible_base() = default; + }; + + //*************************************************************************** + /// variant_base specialisation for trivially destructible types. + /// Uses variadic_union storage. Destructor is trivial (defaulted), making + /// the variant a literal type eligible for constexpr / ROM placement. + /// No operation function pointer is needed since destroy/copy/move are + /// all handled without indirection for trivially destructible types. + /// The copy and move operations are inherited so that they are trivial + /// when every alternative is trivially copyable. + //*************************************************************************** + template + struct variant_base : public variant_trivially_destructible_base::value, TTypes...> + { + typedef variant_trivially_destructible_base::value, TTypes...> base_t; + + constexpr variant_base() noexcept + : base_t() + { + } + + constexpr variant_base(size_t id) noexcept + : base_t(id) + { + } + + template + constexpr variant_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : base_t(etl::in_place_index_t{}, etl::forward(value), id) + { + } + }; + + //******************************************* + /// Trait: are all types copy constructible? + //******************************************* + template + struct are_all_copy_constructible : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types move constructible? + //******************************************* + template + struct are_all_move_constructible : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types copy assignable? + //******************************************* + template + struct are_all_copy_assignable : etl::conjunction...> + { + }; + + //******************************************* + /// Trait: are all types move assignable? + //******************************************* + template + struct are_all_move_assignable : etl::conjunction...> + { + }; + + //******************************************* + /// Flat base that conditionally deletes the copy and move CONSTRUCTORS. + /// Every special member is declared directly (no intermediate bases) so + /// that MSVC does not spuriously delete the defaulted move members when a + /// sibling/base subobject has a deleted special member. + //******************************************* + template + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = default; + variant_constructor_control(variant_constructor_control&&) = default; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = delete; + variant_constructor_control(variant_constructor_control&&) = default; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = default; + variant_constructor_control(variant_constructor_control&&) = delete; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + template <> + struct variant_constructor_control + { + variant_constructor_control() = default; + variant_constructor_control(const variant_constructor_control&) = delete; + variant_constructor_control(variant_constructor_control&&) = delete; + variant_constructor_control& operator=(const variant_constructor_control&) = default; + variant_constructor_control& operator=(variant_constructor_control&&) = default; + }; + + //******************************************* + /// Flat base that conditionally deletes the copy and move ASSIGNMENT + /// operators. Every special member is declared directly (see the note on + /// variant_constructor_control above). + //******************************************* + template + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = default; + variant_assignment_control& operator=(variant_assignment_control&&) = default; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = delete; + variant_assignment_control& operator=(variant_assignment_control&&) = default; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = default; + variant_assignment_control& operator=(variant_assignment_control&&) = delete; + }; + + template <> + struct variant_assignment_control + { + variant_assignment_control() = default; + variant_assignment_control(const variant_assignment_control&) = default; + variant_assignment_control(variant_assignment_control&&) = default; + variant_assignment_control& operator=(const variant_assignment_control&) = delete; + variant_assignment_control& operator=(variant_assignment_control&&) = delete; }; } // namespace private_variant @@ -483,7 +927,13 @@ namespace etl ///\ingroup variant //*************************************************************************** template - class variant : private private_variant::variant_base::value, TTypes...> + class variant + : private private_variant::variant_base::value, TTypes...> + , private private_variant::variant_constructor_control< private_variant::are_all_copy_constructible::value, + private_variant::are_all_move_constructible::value> + , private private_variant::variant_assignment_control< + private_variant::are_all_copy_constructible::value && private_variant::are_all_copy_assignable::value, + private_variant::are_all_move_constructible::value && private_variant::are_all_move_assignable::value> { using base_type = private_variant::variant_base::value, TTypes...>; @@ -592,6 +1042,16 @@ namespace etl } #include "diagnostic_pop.h" + //*************************************************************************** + /// Construct with no alternative. + /// The caller is expected to emplace one; until it does the variant is valueless. + /// Lets a caller choose the alternative at run time without a temporary variant. + //*************************************************************************** + ETL_CONSTEXPR14 explicit variant(private_variant::valueless_t) ETL_NOEXCEPT + : base_type() + { + } + //*************************************************************************** /// Construct from arguments. //*************************************************************************** @@ -671,31 +1131,13 @@ namespace etl /// Copy constructor. ///\param other The other variant object to copy. //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction...>::value) - : base_type(other.type_id) - { - if (other.index() != variant_npos) - { - do_copy_from(other, etl::integral_constant{}); - } - } - #include "diagnostic_pop.h" + variant(const variant& other) = default; //*************************************************************************** /// Move constructor. ///\param other The other variant object to copy. //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction...>::value) - : base_type(other.type_id) - { - if (other.index() != variant_npos) - { - do_move_from(other, etl::integral_constant{}); - } - } - #include "diagnostic_pop.h" + variant(variant&& other) = default; //*************************************************************************** /// Destructor. @@ -806,51 +1248,13 @@ namespace etl /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - { - if (this != &other) - { - if (other.index() == variant_npos) - { - type_id = variant_npos; - } - else - { - do_destroy(); - - do_copy_from(other, etl::integral_constant{}); - - type_id = other.type_id; - } - } - - return *this; - } + variant& operator=(const variant& other) = default; //*************************************************************************** - /// Assignment operator for variant type. + /// Move assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - { - if (this != &other) - { - if (other.index() == variant_npos) - { - type_id = variant_npos; - } - else - { - do_destroy(); - - do_move_from(other, etl::integral_constant{}); - - type_id = other.type_id; - } - } - - return *this; - } + variant& operator=(variant&& other) = default; //*************************************************************************** /// Checks whether the variant doesn't contain a valid value. @@ -1084,6 +1488,8 @@ namespace etl void do_destroy() { do_destroy_impl(etl::integral_constant{}); + + type_id = variant_npos; } void do_destroy_impl(etl::integral_constant) @@ -1149,36 +1555,6 @@ namespace etl ::new (static_cast(data)) T(etl::forward(value)); } - //*************************************************************************** - /// Copy from another variant. - //*************************************************************************** - void do_copy_from(const variant& other, etl::integral_constant) - { - // Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union. - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void do_copy_from(const variant& other, etl::integral_constant) - { - // Non-trivially destructible: use switch-based dispatch. - private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); - } - - //*************************************************************************** - /// Move from another variant. - //*************************************************************************** - void do_move_from(variant& other, etl::integral_constant) - { - // Trivially destructible: copy via memcpy (trivial move == copy). - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void do_move_from(variant& other, etl::integral_constant) - { - // Non-trivially destructible: use switch-based dispatch. - private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id); - } - #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index b73c9ecd..994cbadf 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -53,14 +53,82 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE 1 + #endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1 + #endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE 1 #endif @@ -68,6 +136,10 @@ SOFTWARE. #if !defined(ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED) #define ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED 1 #endif + + #if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST 1 + #endif #endif #if defined(ETL_USE_BUILTIN_MEM_FUNCTIONS) // Set all of them to be true if not @@ -113,17 +185,91 @@ SOFTWARE. #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE) - #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE (__has_builtin(__has_trivial_constructor) || __has_builtin(__is_trivially_constructible)) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE __has_builtin(__is_trivially_constructible) + #endif + + // The legacy __has_trivial_constructor builtin. Deprecated by GCC 12 and + // removed by GCC 15, so it must be detected separately from the standard + // __is_trivially_constructible builtin. + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR __has_builtin(__has_trivial_constructor) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE __has_builtin(__is_trivially_assignable) #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) - #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE (__has_builtin(__has_trivial_destructor) || __has_builtin(__is_trivially_destructible)) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE __has_builtin(__is_trivially_destructible) + #endif + + // The legacy __has_trivial_destructor builtin. Deprecated by GCC 12 and + // removed by GCC 15, so it must be detected separately from the standard + // __is_trivially_destructible builtin. + #if !defined(ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR __has_builtin(__has_trivial_destructor) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE __has_builtin(__is_destructible) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE __has_builtin(__is_nothrow_destructible) #endif #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE (__has_builtin(__has_trivial_copy) || __has_builtin(__is_trivially_copyable)) #endif + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT __has_builtin(__is_standard_layout) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL __has_builtin(__is_trivial) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE __has_builtin(__is_layout_compatible) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF __has_builtin(__is_pointer_interconvertible_base_of) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION __has_builtin(__is_union) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY __has_builtin(__is_empty) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC __has_builtin(__is_polymorphic) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT __has_builtin(__is_abstract) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL __has_builtin(__is_final) + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE __has_builtin(__is_aggregate) + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR __has_builtin(__has_virtual_destructor) + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS __has_builtin(__has_unique_object_representations) + #endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE __has_builtin(__underlying_type) #endif @@ -132,6 +278,14 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED __has_builtin(__builtin_is_constant_evaluated) #endif + #if !defined(ETL_USING_BUILTIN_CONSTANT_P) + #define ETL_USING_BUILTIN_CONSTANT_P __has_builtin(__builtin_constant_p) + #endif + + #if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST __has_builtin(__builtin_bit_cast) + #endif + #if !defined(ETL_USING_BUILTIN_MEMCPY) #define ETL_USING_BUILTIN_MEMCPY __has_builtin(__builtin_memcpy) #endif @@ -174,6 +328,57 @@ SOFTWARE. #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 1 #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 1 + #endif + + #if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 1 + #endif + + #if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1 + #endif + + // MSVC has supported __builtin_bit_cast since VS2019 16.6. __has_builtin is not + // used for Microsoft (see above) to avoid a VS2022 intellisense issue, so the + // intrinsic is enabled here directly for the no-STL configuration. + #if !defined(ETL_USING_BUILTIN_BIT_CAST) && (_MSC_VER >= 1926) + #define ETL_USING_BUILTIN_BIT_CAST 1 + #endif #endif // The default. Set to 0, if not already set. @@ -197,14 +402,74 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + #define ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 0 +#endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_DESTRUCTIBLE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE) + #define ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE 0 +#endif + #if !defined(ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE) #define ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE 0 #endif +#if !defined(ETL_USING_BUILTIN_IS_STANDARD_LAYOUT) + #define ETL_USING_BUILTIN_IS_STANDARD_LAYOUT 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_TRIVIAL) + #define ETL_USING_BUILTIN_IS_TRIVIAL 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE) + #define ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF) + #define ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_UNION) + #define ETL_USING_BUILTIN_IS_UNION 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_EMPTY) + #define ETL_USING_BUILTIN_IS_EMPTY 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_POLYMORPHIC) + #define ETL_USING_BUILTIN_IS_POLYMORPHIC 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_ABSTRACT) + #define ETL_USING_BUILTIN_IS_ABSTRACT 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_FINAL) + #define ETL_USING_BUILTIN_IS_FINAL 0 +#endif + +#if !defined(ETL_USING_BUILTIN_IS_AGGREGATE) + #define ETL_USING_BUILTIN_IS_AGGREGATE 0 +#endif + +#if !defined(ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR) + #define ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR 0 +#endif + +#if !defined(ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + #define ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 0 +#endif + #if !defined(ETL_USING_BUILTIN_UNDERLYING_TYPE) #define ETL_USING_BUILTIN_UNDERLYING_TYPE 0 #endif @@ -245,6 +510,27 @@ SOFTWARE. #define ETL_USING_BUILTIN_BUILTIN_IS_CPP_TRIVIALLY_RELOCATABLE 0 #endif +// __builtin_constant_p predates __has_builtin on GCC and Clang, so enable it for +// those compilers when __has_builtin was not available to detect it. +#if !defined(ETL_USING_BUILTIN_CONSTANT_P) && (defined(__GNUC__) || defined(__clang__)) + #define ETL_USING_BUILTIN_CONSTANT_P 1 +#endif + +#if !defined(ETL_USING_BUILTIN_CONSTANT_P) + #define ETL_USING_BUILTIN_CONSTANT_P 0 +#endif + +#if !defined(ETL_USING_BUILTIN_BIT_CAST) + #define ETL_USING_BUILTIN_BIT_CAST 0 +#endif + +// __builtin_bit_cast is only of use to the ETL from C++11 onwards. +// Disable it for older standards, so that the generated code for those users is unchanged. +#if ETL_USING_BUILTIN_BIT_CAST && !ETL_USING_CPP11 + #undef ETL_USING_BUILTIN_BIT_CAST + #define ETL_USING_BUILTIN_BIT_CAST 0 +#endif + namespace etl { namespace traits @@ -256,10 +542,12 @@ namespace etl static ETL_CONSTANT bool using_builtin_is_nothrow_constructible = (ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE == 1); static ETL_CONSTANT bool using_builtin_is_nothrow_assignable = (ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_constructible = (ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE == 1); + static ETL_CONSTANT bool using_builtin_is_trivially_assignable = (ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_destructible = (ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE == 1); static ETL_CONSTANT bool using_builtin_is_trivially_copyable = (ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE == 1); static ETL_CONSTANT bool using_builtin_underlying_type = (ETL_USING_BUILTIN_UNDERLYING_TYPE == 1); static ETL_CONSTANT bool using_builtin_is_constant_evaluated = (ETL_USING_BUILTIN_IS_CONSTANT_EVALUATED == 1); + static ETL_CONSTANT bool using_builtin_constant_p = (ETL_USING_BUILTIN_CONSTANT_P == 1); static ETL_CONSTANT bool using_builtin_memcpy = (ETL_USING_BUILTIN_MEMCPY == 1); static ETL_CONSTANT bool using_builtin_memmove = (ETL_USING_BUILTIN_MEMMOVE == 1); static ETL_CONSTANT bool using_builtin_memset = (ETL_USING_BUILTIN_MEMSET == 1); @@ -268,6 +556,7 @@ namespace etl static ETL_CONSTANT bool using_builtin_is_virtual_base_of = (ETL_USING_BUILTIN_IS_VIRTUAL_BASE_OF == 1); static ETL_CONSTANT bool using_builtin_is_trivially_relocatable = (ETL_USING_BUILTIN_IS_TRIVIALLY_RELOCATABLE == 1); static ETL_CONSTANT bool using_builtin_builtin_is_cpp_trivially_relocatable = (ETL_USING_BUILTIN_BUILTIN_IS_CPP_TRIVIALLY_RELOCATABLE == 1); + static ETL_CONSTANT bool using_builtin_bit_cast = (ETL_USING_BUILTIN_BIT_CAST == 1); } // namespace traits } // namespace etl diff --git a/include/etl/ranges.h b/include/etl/ranges.h index b502f82d..82ae3502 100644 --- a/include/etl/ranges.h +++ b/include/etl/ranges.h @@ -150,7 +150,8 @@ namespace etl return *etl::prev(cend()); } - constexpr decltype(auto) operator[](size_t i) + template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> + constexpr decltype(auto) operator[](range_difference_t i) { auto it{static_cast(this)->begin()}; etl::advance(it, i); @@ -158,13 +159,13 @@ namespace etl } template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> - constexpr decltype(auto) operator[](size_t i) + constexpr decltype(auto) operator[](range_difference_t i) { return static_cast(this)->begin()[i]; } template < typename D2 = D, etl::enable_if_t().begin())>::value, int> = 0> - constexpr decltype(auto) operator[](size_t i) const + constexpr decltype(auto) operator[](range_difference_t i) const { return cbegin()[i]; } @@ -502,6 +503,11 @@ namespace etl return _i; } + constexpr value_type operator[](difference_type n) const + { + return *(*this + n); + } + private: value_type _i; @@ -673,6 +679,11 @@ namespace etl return _value; } + constexpr value_type operator[](difference_type n) const + { + return *(*this + n); + } + private: value_type _value; @@ -4489,7 +4500,7 @@ namespace etl /// An iterator adaptor that extracts the Nth element from a tuple-like /// value type using etl::get or std::get (found via ADL). //************************************************************************* - template + template class elements_iterator { public: @@ -4498,7 +4509,7 @@ namespace etl using base_iterator = typename trait::iterator; using base_value_type = typename trait::value_type; - using value_type = etl::tuple_element_t; + using value_type = etl::tuple_element_t; using difference_type = typename trait::difference_type; private: @@ -4510,7 +4521,7 @@ namespace etl static decltype(auto) deref_element(const base_iterator& it) { using etl::get; - return get(*it); + return get(*it); } public: @@ -4574,13 +4585,13 @@ namespace etl /// A range adaptor that takes a view of tuple-like values and produces /// a view of the Nth element of each tuple-like value. //************************************************************************* - template - class elements_view : public etl::ranges::view_interface> + template + class elements_view : public etl::ranges::view_interface> { public: - using iterator = elements_iterator; - using const_iterator = elements_iterator; + using iterator = elements_iterator; + using const_iterator = elements_iterator; elements_view(Range&& r) : _r{etl::move(r)} @@ -4614,29 +4625,29 @@ namespace etl mutable Range _r; }; - template - elements_view(Range&&, etl::integral_constant) -> elements_view, N>; + template + elements_view(Range&&, etl::integral_constant) -> elements_view, Index>; - template - struct elements_range_adapter_closure : public range_adapter_closure> + template + struct elements_range_adapter_closure : public range_adapter_closure> { template - using target_view_type = elements_view; + using target_view_type = elements_view; elements_range_adapter_closure() = default; template constexpr auto operator()(Range&& r) { - return elements_view, N>(views::all(etl::forward(r))); + return elements_view, Index>(views::all(etl::forward(r))); } }; - /// keys_view is an alias for elements_view with N=0. + /// keys_view is an alias for elements_view with Index=0. template using keys_view = elements_view; - /// values_view is an alias for elements_view with N=1. + /// values_view is an alias for elements_view with Index=1. template using values_view = elements_view; @@ -4644,39 +4655,39 @@ namespace etl { namespace private_views { - template + template struct elements_fn : public range_adapter_closure_base { template constexpr auto operator()(Range&& r) const { - return elements_view, N>(views::all(etl::forward(r))); + return elements_view, Index>(views::all(etl::forward(r))); } constexpr auto operator()() const { - return ranges::elements_range_adapter_closure(); + return ranges::elements_range_adapter_closure(); } }; } // namespace private_views - template - inline constexpr private_views::elements_fn elements{}; + template + inline constexpr private_views::elements_fn elements{}; inline constexpr private_views::elements_fn<0> keys{}; inline constexpr private_views::elements_fn<1> values{}; } // namespace views //************************************************************************* - /// Helper: create a tuple type that repeats T exactly N times. + /// Helper: create a tuple type that repeats T exactly Index times. //************************************************************************* namespace private_ranges { - template > + template > struct repeat_tuple; - template - struct repeat_tuple> + template + struct repeat_tuple> { template using always = T; @@ -4684,16 +4695,16 @@ namespace etl using type = etl::tuple...>; }; - template - using repeat_tuple_t = typename repeat_tuple::type; + template + using repeat_tuple_t = typename repeat_tuple::type; - /// Helper: compute invoke_result_t with T repeated N + /// Helper: compute invoke_result_t with T repeated Index /// times. - template > + template > struct repeat_invoke_result; - template - struct repeat_invoke_result> + template + struct repeat_invoke_result> { template using always = T; @@ -4701,20 +4712,20 @@ namespace etl using type = etl::invoke_result_t...>; }; - template - using repeat_invoke_result_t = typename repeat_invoke_result::type; + template + using repeat_invoke_result_t = typename repeat_invoke_result::type; } // namespace private_ranges //************************************************************************* /// adjacent_iterator - /// An iterator adaptor that produces tuples of N consecutive elements - /// from the underlying range. Each increment advances all N internal + /// An iterator adaptor that produces tuples of Index consecutive elements + /// from the underlying range. Each increment advances all Index internal /// iterators by one position. //************************************************************************* - template + template class adjacent_iterator { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: @@ -4723,17 +4734,17 @@ namespace etl using base_iterator = typename trait::iterator; using base_value_type = typename trait::value_type; using base_reference = decltype(*etl::declval()); - using value_type = private_ranges::repeat_tuple_t; + using value_type = private_ranges::repeat_tuple_t; using difference_type = typename trait::difference_type; // Each tuple element is a reference into the underlying range, so the // window elements remain mutable when the underlying range is non-const, // as required for std::ranges::adjacent_view. - using reference = private_ranges::repeat_tuple_t; + using reference = private_ranges::repeat_tuple_t; using pointer = value_type*; using iterator_category = ETL_OR_STD::forward_iterator_tag; - /// Construct from an array of N iterators (the sliding window). + /// Construct from an array of Index iterators (the sliding window). template constexpr adjacent_iterator(base_iterator first, base_iterator last, etl::index_sequence) : _iters{advance_copy(first, last, Is)...} @@ -4747,7 +4758,7 @@ namespace etl constexpr adjacent_iterator& operator++() { - increment(etl::make_index_sequence{}); + increment(etl::make_index_sequence{}); return *this; } @@ -4760,14 +4771,14 @@ namespace etl constexpr reference operator*() const { - return deref(etl::make_index_sequence{}); + return deref(etl::make_index_sequence{}); } friend constexpr bool operator==(const adjacent_iterator& lhs, const adjacent_iterator& rhs) { - // Compare the last iterator in the window (index N-1). + // Compare the last iterator in the window (index Index-1). // When it reaches end, the window is exhausted. - return lhs._iters[N - 1] == rhs._iters[N - 1]; + return lhs._iters[Index - 1] == rhs._iters[Index - 1]; } friend constexpr bool operator!=(const adjacent_iterator& lhs, const adjacent_iterator& rhs) @@ -4798,27 +4809,27 @@ namespace etl return reference(*_iters[Is]...); } - base_iterator _iters[N]; + base_iterator _iters[Index]; base_iterator _end; }; //************************************************************************* /// adjacent_view - /// A range adaptor that takes a range and a compile-time window size N + /// A range adaptor that takes a range and a compile-time window size Index /// and produces a view of tuples, where the i-th tuple contains the - /// elements at positions [i, i+1, ..., i+N-1] from the underlying range. - /// The resulting view has (size - N + 1) elements, or is empty if the - /// underlying range has fewer than N elements. + /// elements at positions [i, i+1, ..., i+Index-1] from the underlying range. + /// The resulting view has (size - Index + 1) elements, or is empty if the + /// underlying range has fewer than Index elements. //************************************************************************* - template - class adjacent_view : public etl::ranges::view_interface> + template + class adjacent_view : public etl::ranges::view_interface> { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: - using iterator = adjacent_iterator; - using const_iterator = adjacent_iterator; + using iterator = adjacent_iterator; + using const_iterator = adjacent_iterator; constexpr adjacent_view(Range&& r) : _r{etl::move(r)} @@ -4834,19 +4845,19 @@ namespace etl constexpr const_iterator begin() const { - return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr const_iterator end() const { - // The end iterator has all N internal iterators at the end position. - return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + // The end iterator has all Index internal iterators at the end position. + return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr size_t size() const { auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); - return (total >= N) ? (total - N + 1) : 0; + return (total >= Index) ? (total - Index + 1) : 0; } private: @@ -4854,21 +4865,21 @@ namespace etl mutable Range _r; }; - template - adjacent_view(Range&&, etl::integral_constant) -> adjacent_view, N>; + template + adjacent_view(Range&&, etl::integral_constant) -> adjacent_view, Index>; - template - struct adjacent_range_adapter_closure : public range_adapter_closure> + template + struct adjacent_range_adapter_closure : public range_adapter_closure> { template - using target_view_type = adjacent_view; + using target_view_type = adjacent_view; adjacent_range_adapter_closure() = default; template constexpr auto operator()(Range&& r) { - return adjacent_view, N>(views::all(etl::forward(r))); + return adjacent_view, Index>(views::all(etl::forward(r))); } }; @@ -4876,24 +4887,24 @@ namespace etl { namespace private_views { - template + template struct adjacent_fn : public range_adapter_closure_base { template constexpr auto operator()(Range&& r) const { - return adjacent_view, N>(views::all(etl::forward(r))); + return adjacent_view, Index>(views::all(etl::forward(r))); } constexpr auto operator()() const { - return ranges::adjacent_range_adapter_closure(); + return ranges::adjacent_range_adapter_closure(); } }; } // namespace private_views - template - inline constexpr private_views::adjacent_fn adjacent{}; + template + inline constexpr private_views::adjacent_fn adjacent{}; /// pairwise is an alias for adjacent<2>. inline constexpr private_views::adjacent_fn<2> pairwise{}; @@ -4901,14 +4912,14 @@ namespace etl //************************************************************************* /// adjacent_transform_iterator - /// An iterator adaptor that takes a sliding window of N consecutive + /// An iterator adaptor that takes a sliding window of Index consecutive /// elements from the underlying range and applies a transformation /// function to them, producing a single value per window position. //************************************************************************* - template + template class adjacent_transform_iterator { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: @@ -4916,7 +4927,7 @@ namespace etl using base_iterator = typename trait::const_iterator; using base_value_type = typename trait::value_type; - using value_type = private_ranges::repeat_invoke_result_t; + using value_type = private_ranges::repeat_invoke_result_t; using difference_type = typename trait::difference_type; using pointer = const value_type*; using reference = value_type; @@ -4939,7 +4950,7 @@ namespace etl constexpr adjacent_transform_iterator& operator++() { - increment(etl::make_index_sequence{}); + increment(etl::make_index_sequence{}); return *this; } @@ -4952,14 +4963,14 @@ namespace etl constexpr value_type operator*() const { - return deref(etl::make_index_sequence{}); + return deref(etl::make_index_sequence{}); } friend constexpr bool operator==(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) { - // Compare the last iterator in the window (index N-1). + // Compare the last iterator in the window (index Index-1). // When it reaches end, the window is exhausted. - return lhs._iters[N - 1] == rhs._iters[N - 1]; + return lhs._iters[Index - 1] == rhs._iters[Index - 1]; } friend constexpr bool operator!=(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) @@ -4991,28 +5002,28 @@ namespace etl } Fun _f; - base_iterator _iters[N]; + base_iterator _iters[Index]; base_iterator _end; }; //************************************************************************* /// adjacent_transform_view - /// A range adaptor that takes a range, a compile-time window size N, and + /// A range adaptor that takes a range, a compile-time window size Index, and /// a transformation function, and produces a view whose elements are the - /// result of applying the function to each sliding window of N consecutive + /// result of applying the function to each sliding window of Index consecutive /// elements from the underlying range. - /// The resulting view has (size - N + 1) elements, or is empty if the - /// underlying range has fewer than N elements. + /// The resulting view has (size - Index + 1) elements, or is empty if the + /// underlying range has fewer than Index elements. //************************************************************************* - template - class adjacent_transform_view : public etl::ranges::view_interface< adjacent_transform_view> + template + class adjacent_transform_view : public etl::ranges::view_interface< adjacent_transform_view> { - static_assert(N > 0, "adjacent window size must be > 0"); + static_assert(Index > 0, "adjacent window size must be > 0"); public: - using iterator = adjacent_transform_iterator; - using const_iterator = adjacent_transform_iterator; + using iterator = adjacent_transform_iterator; + using const_iterator = adjacent_transform_iterator; constexpr adjacent_transform_view(Fun f, Range&& r) : _f{f} @@ -5029,19 +5040,19 @@ namespace etl constexpr const_iterator begin() const { - return const_iterator(_f, ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + return const_iterator(_f, ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr const_iterator end() const { - // The end iterator has all N internal iterators at the end position. - return const_iterator(_f, ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + // The end iterator has all Index internal iterators at the end position. + return const_iterator(_f, ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); } constexpr size_t size() const { auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); - return (total >= N) ? (total - N + 1) : 0; + return (total >= Index) ? (total - Index + 1) : 0; } private: @@ -5050,14 +5061,14 @@ namespace etl mutable Range _r; }; - template - adjacent_transform_view(Fun, Range&&, etl::integral_constant) -> adjacent_transform_view, Fun, N>; + template + adjacent_transform_view(Fun, Range&&, etl::integral_constant) -> adjacent_transform_view, Fun, Index>; - template - struct adjacent_transform_range_adapter_closure : public range_adapter_closure< adjacent_transform_range_adapter_closure> + template + struct adjacent_transform_range_adapter_closure : public range_adapter_closure< adjacent_transform_range_adapter_closure> { template - using target_view_type = adjacent_transform_view; + using target_view_type = adjacent_transform_view; adjacent_transform_range_adapter_closure(Fun f) : _f{f} @@ -5067,7 +5078,7 @@ namespace etl template constexpr auto operator()(Range&& r) { - return adjacent_transform_view, Fun, N>(_f, views::all(etl::forward(r))); + return adjacent_transform_view, Fun, Index>(_f, views::all(etl::forward(r))); } Fun _f; @@ -5077,25 +5088,25 @@ namespace etl { namespace private_views { - template + template struct adjacent_transform_fn { template constexpr auto operator()(Range&& r, Fun&& f) const { - return adjacent_transform_view, etl::decay_t, N>(etl::forward(f), views::all(etl::forward(r))); + return adjacent_transform_view, etl::decay_t, Index>(etl::forward(f), views::all(etl::forward(r))); } template constexpr auto operator()(Fun&& f) const { - return ranges::adjacent_transform_range_adapter_closure< N, etl::decay_t>(etl::forward(f)); + return ranges::adjacent_transform_range_adapter_closure< Index, etl::decay_t>(etl::forward(f)); } }; } // namespace private_views - template - inline constexpr private_views::adjacent_transform_fn adjacent_transform{}; + template + inline constexpr private_views::adjacent_transform_fn adjacent_transform{}; /// pairwise_transform is an alias for adjacent_transform<2>. inline constexpr private_views::adjacent_transform_fn<2> pairwise_transform{}; diff --git a/include/etl/reference_flat_map.h b/include/etl/reference_flat_map.h index b6bf0447..c8be6dcb 100644 --- a/include/etl/reference_flat_map.h +++ b/include/etl/reference_flat_map.h @@ -330,6 +330,16 @@ namespace etl return comp(key, element.first); } + bool operator()(const value_type* element, const key_type& key) const + { + return comp(element->first, key); + } + + bool operator()(const key_type& key, const value_type* element) const + { + return comp(key, element->first); + } + #if ETL_USING_CPP11 template ::value, int> = 0> bool operator()(const value_type& element, const K& key) const @@ -342,6 +352,18 @@ namespace etl { return comp(key, element.first); } + + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(element->first, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, element->first); + } #endif key_compare comp; @@ -802,7 +824,7 @@ namespace etl //********************************************************************* iterator lower_bound(key_parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -810,7 +832,7 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -821,7 +843,7 @@ namespace etl //********************************************************************* const_iterator lower_bound(key_parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -829,7 +851,7 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -840,7 +862,7 @@ namespace etl //********************************************************************* iterator upper_bound(key_parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -848,7 +870,7 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -859,7 +881,7 @@ namespace etl //********************************************************************* const_iterator upper_bound(key_parameter_t key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -867,7 +889,7 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -878,9 +900,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -888,9 +911,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -901,9 +925,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -911,9 +936,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif diff --git a/include/etl/reference_flat_multimap.h b/include/etl/reference_flat_multimap.h index 57934215..9e077044 100644 --- a/include/etl/reference_flat_multimap.h +++ b/include/etl/reference_flat_multimap.h @@ -303,6 +303,16 @@ namespace etl return comp(key, element.first); } + bool operator()(const value_type* element, key_type key) const + { + return comp(element->first, key); + } + + bool operator()(key_type key, const value_type* element) const + { + return comp(key, element->first); + } + #if ETL_USING_CPP11 template ::value, int> = 0> bool operator()(const value_type& element, const K& key) const @@ -315,6 +325,18 @@ namespace etl { return comp(key, element.first); } + + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(element->first, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, element->first); + } #endif key_compare comp; @@ -730,7 +752,7 @@ namespace etl //********************************************************************* iterator lower_bound(key_parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -738,7 +760,7 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -749,7 +771,7 @@ namespace etl //********************************************************************* const_iterator lower_bound(key_parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -757,7 +779,7 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -768,7 +790,7 @@ namespace etl //********************************************************************* iterator upper_bound(key_parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #if ETL_USING_CPP11 @@ -776,7 +798,7 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, compare)); } #endif @@ -787,7 +809,7 @@ namespace etl //********************************************************************* const_iterator upper_bound(key_parameter_t key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #if ETL_USING_CPP11 @@ -795,7 +817,7 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(begin(), end(), key, compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, compare)); } #endif @@ -806,9 +828,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -816,9 +839,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - iterator i_lower = etl::lower_bound(begin(), end(), key, compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, end(), key, compare)); + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -829,9 +853,10 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(key_parameter_t key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -839,9 +864,10 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - const_iterator i_lower = etl::lower_bound(cbegin(), cend(), key, compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, compare); - return ETL_OR_STD::make_pair(i_lower, etl::upper_bound(i_lower, cend(), key, compare)); + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif diff --git a/include/etl/reference_flat_multiset.h b/include/etl/reference_flat_multiset.h index 8b2d0b41..3836dbcf 100644 --- a/include/etl/reference_flat_multiset.h +++ b/include/etl/reference_flat_multiset.h @@ -294,6 +294,47 @@ namespace etl typedef typename etl::parameter_type::type parameter_t; + //********************************************************************* + /// Compares a pointer to an element (as stored in 'lookup') against a + /// key, allowing binary searches directly over the random-access + /// 'lookup' container instead of the bidirectional iterator/const_iterator. + //********************************************************************* + class Compare + { + public: + + Compare(const TKeyCompare& comp_) + : comp(comp_) + { + } + + bool operator()(const value_type* element, parameter_t key) const + { + return comp(*element, key); + } + + bool operator()(parameter_t key, const value_type* element) const + { + return comp(key, *element); + } + +#if ETL_USING_CPP11 + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(*element, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, *element); + } +#endif + + const TKeyCompare& comp; + }; + public: typedef ETL_OR_STD::reverse_iterator reverse_iterator; @@ -458,7 +499,7 @@ namespace etl ETL_ASSERT(!lookup.full(), ETL_ERROR(flat_multiset_full)); - iterator i_element = etl::upper_bound(begin(), end(), value, compare); + iterator i_element = upper_bound(value); if (i_element == end()) { @@ -606,13 +647,14 @@ namespace etl //********************************************************************* iterator find(parameter_t key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -628,13 +670,14 @@ namespace etl template ::value, int> = 0> iterator find(const K& key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -653,13 +696,14 @@ namespace etl //********************************************************************* const_iterator find(parameter_t key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -675,13 +719,14 @@ namespace etl template ::value, int> = 0> const_iterator find(const K& key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -723,7 +768,8 @@ namespace etl //********************************************************************* iterator lower_bound(parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -731,7 +777,8 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -742,7 +789,8 @@ namespace etl //********************************************************************* const_iterator lower_bound(parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -750,7 +798,8 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -761,7 +810,8 @@ namespace etl //********************************************************************* iterator upper_bound(parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -769,7 +819,8 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -780,7 +831,8 @@ namespace etl //********************************************************************* const_iterator upper_bound(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -788,7 +840,8 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -799,7 +852,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -807,7 +864,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -835,7 +896,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) const { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } //************************************************************************* diff --git a/include/etl/reference_flat_set.h b/include/etl/reference_flat_set.h index 5da865aa..9cfd2d6e 100644 --- a/include/etl/reference_flat_set.h +++ b/include/etl/reference_flat_set.h @@ -295,6 +295,47 @@ namespace etl typedef typename etl::parameter_type::type parameter_t; + //********************************************************************* + /// Compares a pointer to an element (as stored in 'lookup') against a + /// key, allowing binary searches directly over the random-access + /// 'lookup' container instead of the bidirectional iterator/const_iterator. + //********************************************************************* + class Compare + { + public: + + Compare(const TKeyCompare& comp_) + : comp(comp_) + { + } + + bool operator()(const value_type* element, parameter_t key) const + { + return comp(*element, key); + } + + bool operator()(parameter_t key, const value_type* element) const + { + return comp(key, *element); + } + +#if ETL_USING_CPP11 + template ::value, int> = 0> + bool operator()(const value_type* element, const K& key) const + { + return comp(*element, key); + } + + template ::value, int> = 0> + bool operator()(const K& key, const value_type* element) const + { + return comp(key, *element); + } +#endif + + const TKeyCompare& comp; + }; + public: typedef ETL_OR_STD::reverse_iterator reverse_iterator; @@ -581,13 +622,14 @@ namespace etl //********************************************************************* iterator find(parameter_t key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -603,13 +645,14 @@ namespace etl template ::value, int> = 0> iterator find(const K& key) { - iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator itr = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.end()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return iterator(itr); } else { @@ -628,13 +671,14 @@ namespace etl //********************************************************************* const_iterator find(parameter_t key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -650,13 +694,14 @@ namespace etl template ::value, int> = 0> const_iterator find(const K& key) const { - const_iterator itr = etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator itr = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); - if (itr != end()) + if (itr != lookup.cend()) { - if (!key_compare()(*itr, key) && !key_compare()(key, *itr)) + if (!compare(**itr, key) && !compare(key, **itr)) { - return itr; + return const_iterator(itr); } else { @@ -694,7 +739,8 @@ namespace etl //********************************************************************* iterator lower_bound(parameter_t key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -702,7 +748,8 @@ namespace etl template ::value, int> = 0> iterator lower_bound(const K& key) { - return etl::lower_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -713,7 +760,8 @@ namespace etl //********************************************************************* const_iterator lower_bound(parameter_t key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -721,7 +769,8 @@ namespace etl template ::value, int> = 0> const_iterator lower_bound(const K& key) const { - return etl::lower_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -732,7 +781,8 @@ namespace etl //********************************************************************* iterator upper_bound(parameter_t key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -740,7 +790,8 @@ namespace etl template ::value, int> = 0> iterator upper_bound(const K& key) { - return etl::upper_bound(begin(), end(), key, compare); + Compare ptr_compare(compare); + return iterator(etl::upper_bound(lookup.begin(), lookup.end(), key, ptr_compare)); } #endif @@ -751,7 +802,8 @@ namespace etl //********************************************************************* const_iterator upper_bound(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #if ETL_USING_CPP11 @@ -759,7 +811,8 @@ namespace etl template ::value, int> = 0> const_iterator upper_bound(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + return const_iterator(etl::upper_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare)); } #endif @@ -770,7 +823,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #if ETL_USING_CPP11 @@ -778,7 +835,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) { - return etl::equal_range(begin(), end(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::iterator i_lower = etl::lower_bound(lookup.begin(), lookup.end(), key, ptr_compare); + typename lookup_t::iterator i_upper = etl::upper_bound(i_lower, lookup.end(), key, ptr_compare); + + return ETL_OR_STD::make_pair(iterator(i_lower), iterator(i_upper)); } #endif @@ -789,7 +850,11 @@ namespace etl //********************************************************************* ETL_OR_STD::pair equal_range(parameter_t key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #if ETL_USING_CPP11 @@ -797,7 +862,11 @@ namespace etl template ::value, int> = 0> ETL_OR_STD::pair equal_range(const K& key) const { - return etl::upper_bound(cbegin(), cend(), key, compare); + Compare ptr_compare(compare); + typename lookup_t::const_iterator i_lower = etl::lower_bound(lookup.cbegin(), lookup.cend(), key, ptr_compare); + typename lookup_t::const_iterator i_upper = etl::upper_bound(i_lower, lookup.cend(), key, ptr_compare); + + return ETL_OR_STD::make_pair(const_iterator(i_lower), const_iterator(i_upper)); } #endif diff --git a/include/etl/signal.h b/include/etl/signal.h index b78e7e58..eda5dff5 100644 --- a/include/etl/signal.h +++ b/include/etl/signal.h @@ -185,7 +185,7 @@ namespace etl ///\param slot: The slot to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(const slot_type& slot) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(const slot_type& slot) { if (!connected(slot)) { @@ -204,7 +204,7 @@ namespace etl ///\param slots: std::initializer_list of slots to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(std::initializer_list slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(std::initializer_list slots) { for (const slot_type& slot : slots) { @@ -226,7 +226,7 @@ namespace etl ///\param slots: etl::span of slots to connect. ///\return false if not all slots could be connected. //************************************************************************* - bool connect(const span_type slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + bool connect(const span_type slots) { for (const slot_type& slot : slots) { @@ -384,7 +384,7 @@ namespace etl //************************************************************************* /// For a delegate slot type. //************************************************************************* - template + template static typename etl::enable_if_t::value, bool> slot_is_valid(const TSlotType& s) ETL_NOEXCEPT { return s.is_valid(); @@ -393,7 +393,7 @@ namespace etl //************************************************************************* /// For a non-delegate slot type. //************************************************************************* - template + template static typename etl::enable_if_t::value, bool> slot_is_valid(const TSlotType&) ETL_NOEXCEPT { return true; diff --git a/include/etl/span.h b/include/etl/span.h index 1724a87d..d948c355 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -66,8 +66,8 @@ namespace etl { }; #if ETL_USING_STL && ETL_USING_CPP11 - template - struct is_std_array> : etl::true_type + template + struct is_std_array> : etl::true_type { }; #endif @@ -88,8 +88,8 @@ namespace etl struct is_etl_array : etl::false_type { }; - template - struct is_etl_array > : etl::true_type + template + struct is_etl_array > : etl::true_type { }; template @@ -211,6 +211,7 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; + typedef ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; @@ -248,7 +249,7 @@ namespace etl /// Construct from iterator + size //************************************************************************* template - explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) : pbegin(etl::to_address(begin_)) { ETL_ASSERT(size_ == Extent, ETL_ERROR(span_size_mismatch)); @@ -260,7 +261,7 @@ namespace etl //************************************************************************* template ETL_CONSTEXPR14 span(const TIteratorBegin begin_, const TIteratorEnd end_, - typename etl::enable_if< !etl::is_integral::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + typename etl::enable_if< !etl::is_integral::value, void>::type* = 0) : pbegin(etl::to_address(begin_)) { ETL_ASSERT(etl::distance(begin_, end_) == Extent, ETL_ERROR(span_size_mismatch)); @@ -292,7 +293,7 @@ namespace etl && etl::is_convertible< decltype(etl::declval< typename etl::remove_reference::type&>().data()), pointer>::value && etl::is_same< typename etl::remove_cv::type, typename etl::remove_cv::type::value_type>::type>::value, - void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + void>::type* = 0) : pbegin(a.data()) { ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); @@ -788,6 +789,7 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; + typedef ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; @@ -1507,6 +1509,11 @@ namespace etl template span(const std::array&) -> span; #endif + + // Any other type that supports data() and size() member functions + template ::value && etl::has_size::value + && etl::is_lvalue_reference::value>> + span(TContainer&&) -> span().data())>, etl::dynamic_extent>; #endif //************************************************************************* diff --git a/include/etl/string.h b/include/etl/string.h index 33e3d2d8..3f46a815 100644 --- a/include/etl/string.h +++ b/include/etl/string.h @@ -80,7 +80,7 @@ namespace etl /// Constructor. //************************************************************************* string() - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->initialise(); } @@ -90,7 +90,7 @@ namespace etl ///\param other The other string. //************************************************************************* string(const etl::string& other) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(other); } @@ -100,7 +100,7 @@ namespace etl ///\param other The other istring. //************************************************************************* string(const etl::istring& other) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(other); } @@ -112,7 +112,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* string(const etl::istring& other, size_t position, size_t length = npos) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -124,7 +124,7 @@ namespace etl ///\param text The initial text of the string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR string(const value_type* text) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(text); } @@ -135,7 +135,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* string(const value_type* text, size_t count) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -146,7 +146,7 @@ namespace etl ///\param value The value to fill the string with. //************************************************************************* string(size_type count, value_type c) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -160,7 +160,7 @@ namespace etl //************************************************************************* template string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(first, last); } @@ -170,7 +170,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* string(std::initializer_list init) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -181,7 +181,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit string(const etl::string_view& view) - : istring(reinterpret_cast(&buffer), MAX_SIZE) + : istring(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 1178e4c4..2cb20e37 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -128,10 +128,7 @@ namespace etl typedef const T* const_iterator; typedef ETL_OR_STD::reverse_iterator const_reverse_iterator; - enum - { - npos = etl::integral_limits::max - }; + static ETL_CONSTANT size_type npos = etl::integral_limits::max; //************************************************************************* /// Default constructor. @@ -172,7 +169,7 @@ namespace etl //************************************************************************* /// Construct from pointer/size. //************************************************************************* - ETL_CONSTEXPR basic_string_view(const T* begin_, size_t size_) ETL_NOEXCEPT + ETL_CONSTEXPR basic_string_view(const T* begin_, size_type size_) ETL_NOEXCEPT : mbegin(begin_) , mend(begin_ + size_) { @@ -304,15 +301,15 @@ namespace etl //************************************************************************* /// Returns the size of the array. //************************************************************************* - ETL_CONSTEXPR size_t size() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type size() const ETL_NOEXCEPT { - return static_cast(mend - mbegin); + return static_cast(mend - mbegin); } //************************************************************************* /// Returns the size of the array. //************************************************************************* - ETL_CONSTEXPR size_t length() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type length() const ETL_NOEXCEPT { return size(); } @@ -320,7 +317,7 @@ namespace etl //************************************************************************* /// Returns the maximum possible size of the array. //************************************************************************* - ETL_CONSTEXPR size_t max_size() const ETL_NOEXCEPT + ETL_CONSTEXPR size_type max_size() const ETL_NOEXCEPT { return size(); } @@ -347,7 +344,7 @@ namespace etl //************************************************************************* /// Assign from iterator and size. //************************************************************************* - ETL_CONSTEXPR14 void assign(const_pointer begin_, size_t size_) ETL_NOEXCEPT + ETL_CONSTEXPR14 void assign(const_pointer begin_, size_type size_) ETL_NOEXCEPT { mbegin = begin_; mend = begin_ + size_; @@ -358,7 +355,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::string_view_bounds /// if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) + ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds)); @@ -371,7 +368,7 @@ namespace etl //************************************************************************* /// Returns a const reference to the indexed value. //************************************************************************* - const_reference at(size_t i) const + const_reference at(size_type i) const { ETL_ASSERT((mbegin != ETL_NULLPTR && mend != ETL_NULLPTR), ETL_ERROR(string_view_uninitialised)); ETL_ASSERT(i < size(), ETL_ERROR(string_view_bounds)); @@ -394,7 +391,7 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type copy(T* destination, size_type count, size_type position = 0) const ETL_NOEXCEPT { - size_t n = 0UL; + size_type n = 0UL; if (position < size()) { @@ -415,7 +412,7 @@ namespace etl if (position < size()) { - size_t n = etl::min(count, size() - position); + size_type n = etl::min(count, size() - position); view = basic_string_view(mbegin + position, mbegin + position + n); } @@ -425,18 +422,28 @@ namespace etl //************************************************************************* /// Shrinks the view by moving its start forward. + /// When n >= size(), the view is set to empty. //************************************************************************* - ETL_CONSTEXPR14 void remove_prefix(size_type n) ETL_NOEXCEPT + ETL_CONSTEXPR14 void remove_prefix(size_type n) { - mbegin += n; + ETL_ASSERT(n <= size(), ETL_ERROR(string_view_bounds)); + if (n < size()) + mbegin += n; + else + mbegin = mend; } //************************************************************************* /// Shrinks the view by moving its end backward. + /// When n >= size(), the view is set to empty. //************************************************************************* - ETL_CONSTEXPR14 void remove_suffix(size_type n) ETL_NOEXCEPT + ETL_CONSTEXPR14 void remove_suffix(size_type n) { - mend -= n; + ETL_ASSERT(n <= size(), ETL_ERROR(string_view_bounds)); + if (n < size()) + mend -= n; + else + mend = mbegin; } //************************************************************************* @@ -516,9 +523,9 @@ namespace etl ETL_CONSTEXPR14 bool starts_with(const T* text) const ETL_NOEXCEPT { - size_t lengthtext = TTraits::length(text); + size_type length_text = TTraits::length(text); - return (size() >= lengthtext) && (compare(0, lengthtext, text) == 0); + return (size() >= length_text) && (compare(0, length_text, text) == 0); } //************************************************************************* @@ -536,10 +543,10 @@ namespace etl ETL_CONSTEXPR14 bool ends_with(const T* text) const { - size_t lengthtext = TTraits::length(text); - size_t lengthview = size(); + size_type length_text = TTraits::length(text); + size_type length_view = size(); - return (lengthview >= lengthtext) && (compare(lengthview - lengthtext, lengthtext, text) == 0); + return (length_view >= length_text) && (compare(length_view - length_text, length_text, text) == 0); } //************************************************************************* @@ -623,15 +630,15 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type find_first_of(etl::basic_string_view view, size_type position = 0) const ETL_NOEXCEPT { - const size_t lengthtext = size(); + const size_type length_text = size(); - if (position < lengthtext) + if (position < length_text) { - for (size_t i = position; i < lengthtext; ++i) + for (size_type i = position; i < length_text; ++i) { - const size_t lengthview = view.size(); + const size_type length_view = view.size(); - for (size_t j = 0UL; j < lengthview; ++j) + for (size_type j = 0UL; j < length_view; ++j) { if (mbegin[i] == view[j]) { @@ -675,9 +682,9 @@ namespace etl while (it != rend()) { - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[position] == view[j]) { @@ -712,17 +719,17 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 size_type find_first_not_of(etl::basic_string_view view, size_type position = 0) const ETL_NOEXCEPT { - const size_t lengthtext = size(); + const size_type length_text = size(); - if (position < lengthtext) + if (position < length_text) { - for (size_t i = position; i < lengthtext; ++i) + for (size_type i = position; i < length_text; ++i) { bool found = false; - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[i] == view[j]) { @@ -774,9 +781,9 @@ namespace etl { bool found = false; - const size_t viewlength = view.size(); + const size_type view_length = view.size(); - for (size_t j = 0UL; j < viewlength; ++j) + for (size_type j = 0UL; j < view_length; ++j) { if (mbegin[position] == view[j]) { @@ -890,6 +897,9 @@ namespace etl const_pointer mend; }; + template + ETL_CONSTANT typename basic_string_view::size_type basic_string_view::npos; + typedef etl::basic_string_view string_view; typedef etl::basic_string_view wstring_view; typedef etl::basic_string_view u8string_view; diff --git a/include/etl/to_arithmetic.h b/include/etl/to_arithmetic.h index 090ec110..bba59f88 100644 --- a/include/etl/to_arithmetic.h +++ b/include/etl/to_arithmetic.h @@ -713,7 +713,9 @@ namespace etl const uvalue_t uvalue = static_cast(accumulator_result.value()); // Convert from the accumulator type to the desired type. - result = (is_negative ? static_cast(static_cast(0) - static_cast(uvalue)) : etl::bit_cast(uvalue)); + // The negation is performed on the unsigned type to avoid signed overflow + // when the value is the most negative value for the type. + result = (is_negative ? etl::bit_cast(static_cast(static_cast(0) - uvalue)) : etl::bit_cast(uvalue)); } } } @@ -908,7 +910,7 @@ ETL_CONSTEXPR14 bool operator==(const etl::to_arithmetic_result& lhs, const e } else { - return (lhs.status() == rhs.status()); + return (lhs.error() == rhs.error()); } } @@ -952,7 +954,7 @@ ETL_CONSTEXPR14 bool operator!=(const etl::to_arithmetic_result& lhs, const U /// Inequality test for etl::to_arithmetic_result //*************************************************************************** template -ETL_CONSTEXPR14 bool operator!=(const T& lhs, const etl::to_arithmetic_result& rhs) +ETL_CONSTEXPR14 bool operator!=(const T& lhs, const etl::to_arithmetic_result& rhs) { return !(lhs == rhs); } diff --git a/include/etl/tuple.h b/include/etl/tuple.h index 5b7257ec..a20def79 100644 --- a/include/etl/tuple.h +++ b/include/etl/tuple.h @@ -39,6 +39,7 @@ SOFTWARE. #endif #include "functional.h" + #include "invoke.h" #include "nth_type.h" #include "type_list.h" #include "type_traits.h" @@ -934,6 +935,35 @@ namespace etl return tuple(etl::forward(args)...); } + namespace private_tuple + { + //********************************** + // Helper for apply. + // Invokes the callable with the tuple elements expanded via the index sequence. + // Uses an unqualified 'get' so that argument-dependent lookup finds the + // appropriate overload for etl::tuple, etl::array, std::tuple, std::pair, etc. + //********************************** + template + ETL_CONSTEXPR14 auto apply_impl(TFunction&& f, TTuple&& t, etl::index_sequence) + -> decltype(etl::invoke(etl::forward(f), get(etl::forward(t))...)) + { + return etl::invoke(etl::forward(f), get(etl::forward(t))...); + } + } // namespace private_tuple + + //*************************************************************************** + /// Invokes the callable object 'f' with the elements of the tuple 't' as + /// arguments. Equivalent to std::apply. + //*************************************************************************** + template + ETL_CONSTEXPR14 auto apply(TFunction&& f, TTuple&& t) + -> decltype(private_tuple::apply_impl(etl::forward(f), etl::forward(t), + etl::make_index_sequence>::value>{})) + { + return private_tuple::apply_impl(etl::forward(f), etl::forward(t), + etl::make_index_sequence>::value>{}); + } + namespace private_tuple { //********************************** diff --git a/include/etl/type_list.h b/include/etl/type_list.h index 7cfdfaa0..0a79c964 100644 --- a/include/etl/type_list.h +++ b/include/etl/type_list.h @@ -44,7 +44,7 @@ namespace etl //*************************************************************************** /// Defines a no-position constant. //*************************************************************************** - static ETL_CONSTANT size_t type_list_npos = etl::integral_limits::max; + ETL_INLINE_VAR ETL_CONSTANT size_t type_list_npos = etl::integral_limits::max; //*************************************************************************** // Type list forward declaration. diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 52fd9859..b01b308f 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -47,6 +47,12 @@ SOFTWARE. #include #endif +// Required for std::swap, which etl::is_swappable must consider in any +// configuration that does not define etl::swap. +#if ETL_USING_CPP11 && (ETL_USING_STL || defined(ETL_IN_UNIT_TEST)) + #include +#endif + namespace etl { #if ETL_USING_CPP11 @@ -741,6 +747,38 @@ namespace etl inline constexpr bool is_array_v = is_array::value; #endif + //*************************************************************************** + /// is_bounded_array + template + struct is_bounded_array : false_type + { + }; + template + struct is_bounded_array : true_type + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_bounded_array_v = is_bounded_array::value; + #endif + + //*************************************************************************** + /// is_unbounded_array + template + struct is_unbounded_array : false_type + { + }; + template + struct is_unbounded_array : true_type + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_unbounded_array_v = is_unbounded_array::value; + #endif + //*************************************************************************** /// is_pointer template @@ -846,6 +884,70 @@ namespace etl inline constexpr bool is_pod_v = etl::is_pod::value; #endif + //*************************************************************************** + /// is_standard_layout + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is standard layout, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_STANDARD_LAYOUT + template + struct is_standard_layout : etl::bool_constant<__is_standard_layout(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_standard_layout_v = etl::is_standard_layout::value; + #endif + #endif + + //*************************************************************************** + /// is_trivial + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is trivial, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_TRIVIAL + template + struct is_trivial : etl::bool_constant<__is_trivial(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = etl::is_trivial::value; + #endif + #endif + + //*************************************************************************** + /// is_layout_compatible + /// Requires compiler support. Without it there is no portable way to + /// determine whether two types are layout compatible, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE + template + struct is_layout_compatible : etl::bool_constant<__is_layout_compatible(T, U)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; + #endif + #endif + + //*************************************************************************** + /// is_pointer_interconvertible_base_of + /// Requires compiler support. Without it there is no portable way to + /// determine pointer interconvertibility, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF + template + struct is_pointer_interconvertible_base_of : etl::bool_constant<__is_pointer_interconvertible_base_of(TBase, TDerived)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; + #endif + #endif + //*************************************************************************** /// conditional template @@ -1144,6 +1246,137 @@ namespace etl inline constexpr bool is_class_v = is_class::value; #endif + //*************************************************************************** + /// is_union + /// Requires compiler support. Without it there is no portable way to + /// distinguish a union from a class, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_UNION + template + struct is_union : etl::bool_constant<__is_union(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_union_v = is_union::value; + #endif + #endif + + //*************************************************************************** + /// is_empty + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is empty, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_EMPTY + template + struct is_empty : etl::bool_constant<__is_empty(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_empty_v = is_empty::value; + #endif + #endif + + //*************************************************************************** + /// is_polymorphic + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is polymorphic, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_POLYMORPHIC + template + struct is_polymorphic : etl::bool_constant<__is_polymorphic(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_polymorphic_v = is_polymorphic::value; + #endif + #endif + + //*************************************************************************** + /// is_abstract + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is abstract, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_ABSTRACT + template + struct is_abstract : etl::bool_constant<__is_abstract(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_abstract_v = is_abstract::value; + #endif + #endif + + //*************************************************************************** + /// is_final + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type is final, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_FINAL + template + struct is_final : etl::bool_constant<__is_final(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_final_v = is_final::value; + #endif + #endif + + //*************************************************************************** + /// is_aggregate + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type is an aggregate, so the trait is not defined. + #if ETL_USING_BUILTIN_IS_AGGREGATE + template + struct is_aggregate : etl::bool_constant<__is_aggregate(typename etl::remove_cv::type)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_aggregate_v = is_aggregate::value; + #endif + #endif + + //*************************************************************************** + /// has_virtual_destructor + /// Requires compiler support. Without it there is no portable way to + /// determine whether a class type has a virtual destructor, so the trait is + /// not defined. + #if ETL_USING_BUILTIN_HAS_VIRTUAL_DESTRUCTOR + template + struct has_virtual_destructor : etl::bool_constant<__has_virtual_destructor(T)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_virtual_destructor_v = has_virtual_destructor::value; + #endif + #endif + + //*************************************************************************** + /// has_unique_object_representations + /// Requires compiler support. Without it there is no portable way to + /// determine whether a type has a unique object representation, so the trait + /// is not defined. + #if ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS + template + struct has_unique_object_representations + : etl::bool_constant<__has_unique_object_representations(typename etl::remove_cv::type>::type)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_unique_object_representations_v = has_unique_object_representations::value; + #endif + #endif + //*************************************************************************** /// is_base_of #if ETL_USING_CPP11 @@ -1464,6 +1697,146 @@ namespace etl template inline constexpr bool is_nothrow_convertible_v = etl::is_nothrow_convertible::value; + #endif + + //*************************************************************************** + /// is_scoped_enum + ///\ingroup type_traits + /// An enumeration is scoped if it is not implicitly convertible to an integer. + template + struct is_scoped_enum : etl::bool_constant::value && !etl::is_convertible::value> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; + #endif + + //*************************************************************************** + /// is_destructible + /// Requires either compiler support or C++11. Without them there is no + /// portable way to determine whether a type is destructible, so the trait + /// is not defined. + #if ETL_USING_BUILTIN_IS_DESTRUCTIBLE + template + struct is_destructible : etl::bool_constant<__is_destructible(T)> + { + }; + #elif ETL_USING_CPP11 + namespace private_type_traits + { + // Selected only if 'declval().~T()' is a valid expression. + // Void types, function types and types with an inaccessible or deleted + // destructor all fail substitution and select the fallback. + template + struct is_destructible_impl + { + private: + + template ().~U())> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + + template + struct is_destructible_helper : etl::bool_constant::type>::value> + { + }; + + // Reference types are always destructible. + template + struct is_destructible_helper : etl::true_type + { + }; + + template + struct is_destructible_helper : etl::true_type + { + }; + + // Arrays of unknown bound are incomplete types. + template + struct is_destructible_helper : etl::false_type + { + }; + } // namespace private_type_traits + + template + struct is_destructible : etl::bool_constant::value> + { + }; + #endif + + // C++17 implies C++11, so the trait is always defined when the alias is. + #if ETL_USING_CPP17 + template + inline constexpr bool is_destructible_v = etl::is_destructible::value; + #endif + + //*************************************************************************** + /// is_nothrow_destructible + /// Requires either compiler support or C++11. Without them there is no + /// portable way to determine whether a type is nothrow destructible, so the + /// trait is not defined. + #if ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE + template + struct is_nothrow_destructible : etl::bool_constant<__is_nothrow_destructible(T)> + { + }; + #elif ETL_USING_CPP11 + namespace private_type_traits + { + // Only instantiated for destructible types, so 'declval().~T()' is a valid expression. + template ::value> + struct is_nothrow_destructible_impl : etl::false_type + { + }; + + template + struct is_nothrow_destructible_impl : etl::bool_constant().~T())> + { + }; + + template + struct is_nothrow_destructible_helper : etl::bool_constant::type>::value> + { + }; + + // Reference types are always nothrow destructible. + template + struct is_nothrow_destructible_helper : etl::true_type + { + }; + + template + struct is_nothrow_destructible_helper : etl::true_type + { + }; + + // Arrays of unknown bound are incomplete types. + template + struct is_nothrow_destructible_helper : etl::false_type + { + }; + } // namespace private_type_traits + + template + struct is_nothrow_destructible : etl::bool_constant::value> + { + }; + #endif + + // C++17 implies C++11, so the trait is always defined when the alias is. + #if ETL_USING_CPP17 + template + inline constexpr bool is_nothrow_destructible_v = etl::is_nothrow_destructible::value; #endif //*************************************************************************** @@ -1849,6 +2222,56 @@ namespace etl #if ETL_USING_CPP17 template inline constexpr bool is_array_v = std::is_array_v; + #endif + + //*************************************************************************** + /// is_bounded_array + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + template + struct is_bounded_array : std::is_bounded_array + { + }; + #else + template + struct is_bounded_array : std::false_type + { + }; + + template + struct is_bounded_array : std::true_type + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_bounded_array_v = etl::is_bounded_array::value; + #endif + + //*************************************************************************** + /// is_unbounded_array + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + template + struct is_unbounded_array : std::is_unbounded_array + { + }; + #else + template + struct is_unbounded_array : std::false_type + { + }; + + template + struct is_unbounded_array : std::true_type + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_unbounded_array_v = etl::is_unbounded_array::value; #endif //*************************************************************************** @@ -1920,6 +2343,118 @@ namespace etl inline constexpr bool is_pod_v = std::is_standard_layout_v && std::is_trivially_default_constructible_v && std::is_trivially_copyable_v; #endif + //*************************************************************************** + /// is_standard_layout + ///\ingroup type_traits + template + struct is_standard_layout : std::is_standard_layout + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_standard_layout_v = std::is_standard_layout_v; + #endif + + //*************************************************************************** + /// is_trivial + ///\ingroup type_traits + #if ETL_USING_CPP26 + // std::is_trivial is deprecated in C++26 and later; avoid using it and + // compose the trait from is_trivially_copyable && is_trivially_default_constructible instead. + template + struct is_trivial : etl::bool_constant::value && std::is_trivially_default_constructible::value> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = std::is_trivially_copyable_v && std::is_trivially_default_constructible_v; + #endif + #else + template + struct is_trivial : std::is_trivial + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_trivial_v = std::is_trivial_v; + #endif + #endif + + //*************************************************************************** + /// is_layout_compatible + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible) + template + struct is_layout_compatible : std::is_layout_compatible + { + }; + + template + inline constexpr bool is_layout_compatible_v = std::is_layout_compatible_v; + #elif ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE + template + struct is_layout_compatible : etl::bool_constant<__is_layout_compatible(T, U)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_layout_compatible_v = etl::is_layout_compatible::value; + #endif + #endif + + //*************************************************************************** + /// is_pointer_interconvertible_base_of + ///\ingroup type_traits + #if ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible) + template + struct is_pointer_interconvertible_base_of : std::is_pointer_interconvertible_base_of + { + }; + + template + inline constexpr bool is_pointer_interconvertible_base_of_v = std::is_pointer_interconvertible_base_of_v; + #elif ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF + template + struct is_pointer_interconvertible_base_of : etl::bool_constant<__is_pointer_interconvertible_base_of(TBase, TDerived)> + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_pointer_interconvertible_base_of_v = etl::is_pointer_interconvertible_base_of::value; + #endif + #endif + + //*************************************************************************** + /// is_destructible + ///\ingroup type_traits + template + struct is_destructible : std::is_destructible + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_destructible_v = std::is_destructible_v; + #endif + + //*************************************************************************** + /// is_nothrow_destructible + ///\ingroup type_traits + template + struct is_nothrow_destructible : std::is_nothrow_destructible + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible_v; + #endif + #if defined(ETL_COMPILER_GCC) #if ETL_COMPILER_VERSION >= 5 #define ETL_GCC_V5_TYPE_TRAITS_SUPPORTED @@ -2100,6 +2635,127 @@ namespace etl inline constexpr bool is_class_v = is_class::value; #endif + //*************************************************************************** + /// is_union + template + struct is_union : std::is_union + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_union_v = std::is_union_v; + #endif + + //*************************************************************************** + /// is_empty + template + struct is_empty : std::is_empty + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_empty_v = std::is_empty_v; + #endif + + //*************************************************************************** + /// is_polymorphic + template + struct is_polymorphic : std::is_polymorphic + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_polymorphic_v = std::is_polymorphic_v; + #endif + + //*************************************************************************** + /// is_abstract + template + struct is_abstract : std::is_abstract + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_abstract_v = std::is_abstract_v; + #endif + + //*************************************************************************** + /// is_final + /// std::is_final is only available from C++14, so for C++11 the compiler + /// builtin is used, if available. Otherwise the trait is not defined. + #if ETL_USING_CPP14 + template + struct is_final : std::is_final + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool is_final_v = std::is_final_v; + #endif + #elif ETL_USING_BUILTIN_IS_FINAL + template + struct is_final : etl::bool_constant<__is_final(T)> + { + }; + #endif + + //*************************************************************************** + /// is_aggregate + /// std::is_aggregate is only available from C++17, so for C++11 and C++14 the + /// compiler builtin is used, if available. Otherwise the trait is not defined. + #if ETL_USING_CPP17 + template + struct is_aggregate : std::is_aggregate + { + }; + + template + inline constexpr bool is_aggregate_v = std::is_aggregate_v; + #elif ETL_USING_BUILTIN_IS_AGGREGATE + template + struct is_aggregate : etl::bool_constant<__is_aggregate(typename etl::remove_cv::type)> + { + }; + #endif + + //*************************************************************************** + /// has_virtual_destructor + template + struct has_virtual_destructor : std::has_virtual_destructor + { + }; + + #if ETL_USING_CPP17 + template + inline constexpr bool has_virtual_destructor_v = std::has_virtual_destructor_v; + #endif + + //*************************************************************************** + /// has_unique_object_representations + /// std::has_unique_object_representations is only available from C++17, so for + /// C++11 and C++14 the compiler builtin is used, if available. Otherwise the + /// trait is not defined. + #if ETL_USING_CPP17 + template + struct has_unique_object_representations : std::has_unique_object_representations + { + }; + + template + inline constexpr bool has_unique_object_representations_v = std::has_unique_object_representations_v; + #elif ETL_USING_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS + template + struct has_unique_object_representations + : etl::bool_constant<__has_unique_object_representations(typename etl::remove_cv::type>::type)> + { + }; + #endif + //*************************************************************************** /// add_lvalue_reference template @@ -2147,6 +2803,27 @@ namespace etl inline constexpr bool is_enum_v = etl::is_enum::value; #endif + //*************************************************************************** + /// is_scoped_enum + ///\ingroup type_traits + /// An enumeration is scoped if it is not implicitly convertible to an integer. + #if ETL_USING_CPP23 && defined(__cpp_lib_is_scoped_enum) + template + struct is_scoped_enum : std::is_scoped_enum + { + }; + #else + template + struct is_scoped_enum : etl::bool_constant::value && !std::is_convertible::value> + { + }; + #endif + + #if ETL_USING_CPP17 + template + inline constexpr bool is_scoped_enum_v = etl::is_scoped_enum::value; + #endif + #endif //*************************************************************************** @@ -2253,6 +2930,19 @@ namespace etl #endif // Condition = ETL_USING_STL && ETL_USING_CPP11 + //*************************************************************************** + /// is_null_pointer + ///\ingroup type_traits + template + struct is_null_pointer : etl::is_same::type, etl::nullptr_t> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_null_pointer_v = is_null_pointer::value; +#endif + //*************************************************************************** // ETL extended type traits. //*************************************************************************** @@ -2718,6 +3408,26 @@ namespace etl #else template using is_trivially_copy_constructible = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_default_constructible + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_default_constructible = std::is_trivially_default_constructible; + #else + template + using is_trivially_default_constructible = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_move_constructible + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_move_constructible = std::is_trivially_move_constructible; + #else + template + using is_trivially_move_constructible = etl::bool_constant::value || etl::is_pointer::value>; #endif //********************************************* @@ -2730,14 +3440,46 @@ namespace etl using is_trivially_destructible = etl::bool_constant::value || etl::is_pointer::value>; #endif + //********************************************* + // is_trivially_assignable + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_assignable = std::is_trivially_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_assignable = etl::bool_constant<__is_trivially_assignable(T1, T2)>; + #else + template + using is_trivially_assignable = etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) + && (etl::is_arithmetic::value || etl::is_pointer::value)>; + #endif + //********************************************* // is_trivially_copy_assignable #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copy_assignable = std::is_trivially_copy_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_copy_assignable = + etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type>; #else template using is_trivially_copy_assignable = etl::bool_constant::value || etl::is_pointer::value>; + #endif + + //********************************************* + // is_trivially_move_assignable + #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + template + using is_trivially_move_assignable = std::is_trivially_move_assignable; + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + template + using is_trivially_move_assignable = + etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type>; + #else + template + using is_trivially_move_assignable = etl::bool_constant::value || etl::is_pointer::value>; #endif //********************************************* @@ -2973,6 +3715,27 @@ namespace etl { }; + //********************************************* + // is_trivially_default_constructible + template + struct is_trivially_default_constructible : public is_trivially_constructible + { + }; + + //********************************************* + // is_trivially_move_constructible + #if ETL_USING_CPP11 + template + struct is_trivially_move_constructible : public is_trivially_constructible< T, typename add_rvalue_reference::type> + { + }; + #else + template + struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + #endif + //********************************************* // is_trivially_destructible template @@ -2985,18 +3748,37 @@ namespace etl #endif }; + //********************************************* + // is_trivially_assignable + template + struct is_trivially_assignable + { + static ETL_CONSTANT bool value = __is_trivially_assignable(T1, T2); + }; + //********************************************* // is_trivially_copy_assignable template struct is_trivially_copy_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type> { - #if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); - #else - static ETL_CONSTANT bool value = __is_trivially_copyable(T); - #endif }; + //********************************************* + // is_trivially_move_assignable + #if ETL_USING_CPP11 + template + struct is_trivially_move_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type> + { + }; + #else + template + struct is_trivially_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + #endif + //********************************************* // is_trivially_copyable template @@ -3026,8 +3808,9 @@ namespace etl template struct is_nothrow_relocatable { - // In builtins mode, conservatively use trivially_relocatable as the definition - static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value; + static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value + || (etl::is_nothrow_move_constructible::type>::value + && etl::is_nothrow_destructible::type>::value); }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) @@ -3248,6 +4031,32 @@ namespace etl template struct is_trivially_copy_constructible; + //********************************************* + // is_trivially_default_constructible + template ::value || etl::is_pointer::value> + struct is_trivially_default_constructible; + + template + struct is_trivially_default_constructible : public etl::true_type + { + }; + + template + struct is_trivially_default_constructible; + + //********************************************* + // is_trivially_move_constructible + template ::value || etl::is_pointer::value> + struct is_trivially_move_constructible; + + template + struct is_trivially_move_constructible : public etl::true_type + { + }; + + template + struct is_trivially_move_constructible; + //********************************************* // is_trivially_destructible template ::value || etl::is_pointer::value> @@ -3261,6 +4070,21 @@ namespace etl template struct is_trivially_destructible; + //********************************************* + // is_trivially_assignable + template ::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> + struct is_trivially_assignable; + + template + struct is_trivially_assignable : public etl::true_type + { + }; + + template + struct is_trivially_assignable; + //********************************************* // is_trivially_copy_assignable template ::value || etl::is_pointer::value> @@ -3274,6 +4098,19 @@ namespace etl template struct is_trivially_copy_assignable; + //********************************************* + // is_trivially_move_assignable + template ::value || etl::is_pointer::value> + struct is_trivially_move_assignable; + + template + struct is_trivially_move_assignable : public etl::true_type + { + }; + + template + struct is_trivially_move_assignable; + //********************************************* // is_trivially_copyable template ::value || etl::is_pointer::value> @@ -3317,15 +4154,36 @@ namespace etl #else //********************************************* - // Assume that anything other than arithmetics + // Assume that anything other than arithmetic // and pointers return false for the traits. //********************************************* //********************************************* // is_assignable + #if !ETL_USING_BUILTIN_IS_ASSIGNABLE && ETL_USING_CPP11 + namespace private_type_traits + { + template + struct is_assignable_ : etl::false_type + { + }; + + template + struct is_assignable_() = etl::declval())>, T1, T2> : etl::true_type + { + }; + } // namespace private_type_traits + #endif + template #if ETL_USING_BUILTIN_IS_ASSIGNABLE struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> + #elif ETL_USING_CPP11 + // Builtin-free assignability detection. Used when the __is_assignable builtin + // is unavailable (e.g. MSVC, where determine_builtin_support.h disables the + // __has_builtin block). Mirrors the void_t-based is_constructible above so + // that class types are correctly reported as (copy/move) assignable. + struct is_assignable : public private_type_traits::is_assignable_, T1, T2> #else struct is_assignable : public etl::bool_constant< (etl::is_arithmetic::value || etl::is_pointer::value) @@ -3335,8 +4193,9 @@ namespace etl }; #if ETL_USING_CPP11 - //*************************************************************************** - /// is_constructible + //*************************************************************************** + /// is_constructible + #if !ETL_USING_BUILTIN_IS_CONSTRUCTIBLE namespace private_type_traits { template @@ -3349,11 +4208,19 @@ namespace etl { }; } // namespace private_type_traits + #endif - //********************************************* - // is_constructible + //********************************************* + // is_constructible + #if ETL_USING_BUILTIN_IS_CONSTRUCTIBLE + template + struct is_constructible : public etl::bool_constant<__is_constructible(T, TArgs...)> + { + }; + #else template using is_constructible = private_type_traits::is_constructible_, T, TArgs...>; + #endif //********************************************* // is_copy_constructible @@ -3446,49 +4313,79 @@ namespace etl //********************************************* // is_nothrow_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_constructible : public etl::bool_constant<__is_nothrow_constructible(T, TArgs...)> + #else struct is_nothrow_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_default_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_default_constructible : public etl::is_nothrow_constructible + #else struct is_nothrow_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_copy_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_copy_constructible : public etl::is_nothrow_constructible::type> + #else struct is_nothrow_copy_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_move_constructible template + #if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE + struct is_nothrow_move_constructible : public etl::is_nothrow_constructible::type> + #else struct is_nothrow_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_assignable : public etl::bool_constant<__is_nothrow_assignable(T, U)> + #else struct is_nothrow_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_copy_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_copy_assignable + : public etl::is_nothrow_assignable::type, typename etl::add_lvalue_reference::type> + #else struct is_nothrow_copy_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_nothrow_move_assignable template + #if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE + struct is_nothrow_move_assignable + : public etl::is_nothrow_assignable::type, typename etl::add_rvalue_reference::type> + #else struct is_nothrow_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; #endif @@ -3496,28 +4393,105 @@ namespace etl //********************************************* // is_trivially_constructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_constructible : public etl::bool_constant<__is_trivially_constructible(T)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR + struct is_trivially_constructible : public etl::bool_constant<__has_trivial_constructor(T) && etl::is_constructible::value> + #else struct is_trivially_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_copy_constructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_copy_constructible + : public etl::bool_constant<__is_trivially_constructible(T, typename etl::add_lvalue_reference::type)> + #elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + // A trivially copyable type may still have a deleted copy constructor, so the + // copy constructor must be checked for as well. + struct is_trivially_copy_constructible : public etl::bool_constant<__is_trivially_copyable(T) && etl::is_copy_constructible::value> + #else struct is_trivially_copy_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif + { + }; + + //********************************************* + // is_trivially_default_constructible + template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE || ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR + struct is_trivially_default_constructible : public etl::is_trivially_constructible + #else + struct is_trivially_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif + { + }; + + //********************************************* + // is_trivially_move_constructible + template + #if ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE + struct is_trivially_move_constructible : public etl::bool_constant<__is_trivially_constructible(T, typename etl::add_rvalue_reference::type)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + // A trivially copyable type may still have a deleted move constructor, so the + // move constructor must be checked for as well. + struct is_trivially_move_constructible : public etl::bool_constant<__is_trivially_copyable(T) && etl::is_move_constructible::value> + #else + struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; //********************************************* // is_trivially_destructible template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE + struct is_trivially_destructible : public etl::bool_constant<__is_trivially_destructible(T)> + #elif ETL_USING_CPP11 && ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR + struct is_trivially_destructible : public etl::bool_constant<__has_trivial_destructor(T) && etl::is_destructible::value> + #else struct is_trivially_destructible : public etl::bool_constant::value || etl::is_pointer::value> + #endif + { + }; + + //********************************************* + // is_trivially_assignable + template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_assignable : public etl::bool_constant<__is_trivially_assignable(T1, T2)> + #else + struct is_trivially_assignable + : public etl::bool_constant< (etl::is_arithmetic::value || etl::is_pointer::value) + && (etl::is_arithmetic::value || etl::is_pointer::value)> + #endif { }; //********************************************* // is_trivially_copy_assignable template + #if ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_copy_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_lvalue_reference::type> + #else struct is_trivially_copy_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif + { + }; + + //********************************************* + // is_trivially_move_assignable + template + #if ETL_USING_CPP11 && ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE + struct is_trivially_move_assignable + : public etl::is_trivially_assignable< typename etl::add_lvalue_reference::type, typename etl::add_rvalue_reference::type> + #else + struct is_trivially_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + #endif { }; @@ -3634,12 +4608,24 @@ namespace etl template inline constexpr bool is_trivially_copy_constructible_v = etl::is_trivially_copy_constructible::value; + template + inline constexpr bool is_trivially_default_constructible_v = etl::is_trivially_default_constructible::value; + + template + inline constexpr bool is_trivially_move_constructible_v = etl::is_trivially_move_constructible::value; + template inline constexpr bool is_trivially_destructible_v = etl::is_trivially_destructible::value; + template + inline constexpr bool is_trivially_assignable_v = etl::is_trivially_assignable::value; + template inline constexpr bool is_trivially_copy_assignable_v = etl::is_trivially_copy_assignable::value; + template + inline constexpr bool is_trivially_move_assignable_v = etl::is_trivially_move_assignable::value; + template inline constexpr bool is_trivially_copyable_v = etl::is_trivially_copyable::value; @@ -3703,7 +4689,7 @@ namespace etl struct common_type : etl::conditional< etl::is_same::type>::value && etl::is_same::type>::value, private_common_type::common_type_2_impl, - common_type::type, typename etl::decay::type>>::type + common_type::type, typename etl::decay::type>>::type { }; @@ -3730,6 +4716,46 @@ namespace etl template using common_type_t = typename common_type::type; + + //*********************************** + // Implementation details of the make_xxx container factories. + namespace private_make + { + // Selects the element type, following the Library Fundamentals TS + // make_array design: TDesired if it was supplied explicitly, otherwise the + // common type of the arguments (common_type decays). The specialisation + // keeps common_type uninstantiated when TDesired is supplied, so arguments + // with no common type are still accepted in that case. + template + struct element_type + { + using type = TDesired; + }; + + template + struct element_type : etl::common_type + { + }; + + template + using element_type_t = typename element_type::type; + + // Converts a factory argument to the element type. An argument that + // already has the element type is forwarded unchanged; any other argument + // is converted with static_cast, which keeps narrowing initialisers such + // as make_array(0, 1) working. Forwarding rather than casting in the + // same-type case avoids materialising a temporary, which before C++17 + // requires an accessible copy or move constructor even though the copy is + // elided, and would reject copyable-but-non-movable element types. + template + using convert_type_t = typename etl::conditional::type, TElement>::value, TValue&&, TElement>::type; + + template + constexpr convert_type_t convert(TValue&& value) + { + return static_cast>(value); + } + } // namespace private_make #endif //*************************************************************************** @@ -3935,6 +4961,23 @@ namespace etl inline constexpr bool is_member_pointer_v = etl::is_member_pointer::value; #endif + //*************************************************************************** + /// is_scalar + /// An arithmetic, enumeration, pointer, pointer to member or + /// etl::nullptr_t type, optionally cv-qualified. + //*************************************************************************** + template + struct is_scalar + : etl::bool_constant::value || etl::is_enum::value || etl::is_pointer::value || etl::is_member_pointer::value + || etl::is_null_pointer::value> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_scalar_v = etl::is_scalar::value; +#endif + #if ETL_USING_CPP11 //********************************* /// Check if T is a function type @@ -4069,6 +5112,235 @@ namespace etl inline constexpr bool is_object_pointer_v = etl::is_object_pointer::value; #endif + //*************************************************************************** + /// swap + /// Declared here, and defined in utility.h, so that etl::is_swappable below + /// finds it by ordinary unqualified lookup. + /// We can't have std::swap and etl::swap templates coexisting in the unit + /// tests as the compiler will be unable to decide which one to use, due to + /// ADL. + //*************************************************************************** +#if ETL_NOT_USING_STL && !defined(ETL_IN_UNIT_TEST) + #if ETL_USING_CPP11 + namespace private_type_traits + { + template + struct is_swappable_helper; + + template + struct is_nothrow_swappable_helper; + } // namespace private_type_traits + + // Constrained, as the standard requires, so that an attempt to swap a type + // that cannot be moved is a substitution failure rather than a hard error. + // This is what allows etl::is_swappable to be implemented. + // The exception specification is conditional, as the standard requires, so + // that etl::is_nothrow_swappable can be implemented. + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_move_assignable::value>::type swap(T& a, T& b) + ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value&& etl::is_nothrow_move_assignable::value); + + template + ETL_CONSTEXPR14 typename etl::enable_if::value>::type swap(T (&a)[Size], T (&b)[Size]) + ETL_NOEXCEPT_IF(private_type_traits::is_nothrow_swappable_helper::value); + #else + template + ETL_CONSTEXPR14 void swap(T& a, T& b) ETL_NOEXCEPT; + + template + ETL_CONSTEXPR14 void swap(T (&a)[Size], T (&b)[Size]) ETL_NOEXCEPT; + #endif +#endif + + //*************************************************************************** + /// is_swappable + /// Determines whether lvalues of type T can be swapped by an unqualified + /// call to swap. + /// Requires C++11. Without it there is no way of detecting the validity of + /// an expression, so the trait is not defined. + //*************************************************************************** +#if ETL_USING_STL && ETL_USING_CPP17 + template + struct is_swappable : std::is_swappable + { + }; + + //*************************************************************************** + /// is_swappable_with + //*************************************************************************** + template + struct is_swappable_with : std::is_swappable_with + { + }; +#elif ETL_USING_CPP11 + namespace private_type_traits + { + namespace swap_detect + { + // Bring std::swap into scope, if it is available, so that the unqualified + // call below finds it in those configurations that do not define etl::swap. + // Otherwise etl::swap is found by ordinary lookup in the enclosing + // namespace. Either way, a swap found by argument dependent lookup takes + // part in overload resolution. + #if ETL_USING_STL || defined(ETL_IN_UNIT_TEST) + using std::swap; + #endif + + // Selected only if 'swap(declval(), declval())' is a valid expression. + template + struct is_swappable_test + { + private: + + template (), etl::declval()))> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + + // Selected only if both 'swap(declval(), declval())' and + // 'swap(declval(), declval())' are valid expressions. + template + struct is_swappable_with_test + { + private: + + template (), etl::declval())), + typename = decltype(swap(etl::declval(), etl::declval()))> + static etl::true_type test(int); + + template + static etl::false_type test(...); + + public: + + static ETL_CONSTANT bool value = decltype(test(0))::value; + }; + } // namespace swap_detect + + template + struct is_swappable_helper : etl::bool_constant::value> + { + }; + + template + struct is_swappable_with_helper : etl::bool_constant::value> + { + }; + } // namespace private_type_traits + + template + struct is_swappable : etl::bool_constant::value> + { + }; + + //*************************************************************************** + /// is_swappable_with + //*************************************************************************** + template + struct is_swappable_with : etl::bool_constant::value> + { + }; +#endif + + //*************************************************************************** + /// is_nothrow_swappable + /// Determines whether lvalues of type T can be swapped by an unqualified + /// call to swap, and that the swap cannot throw. + /// Requires C++11. Without it there is no way of detecting the validity of + /// an expression, so the trait is not defined. + //*************************************************************************** +#if ETL_USING_STL && ETL_USING_CPP17 + template + struct is_nothrow_swappable : std::is_nothrow_swappable + { + }; + + //*************************************************************************** + /// is_nothrow_swappable_with + //*************************************************************************** + template + struct is_nothrow_swappable_with : std::is_nothrow_swappable_with + { + }; +#elif ETL_USING_CPP11 + namespace private_type_traits + { + namespace swap_detect + { + // Only 'true' if the type is swappable and the swap expression is noexcept. + template ::value> + struct is_nothrow_swappable_test + { + static ETL_CONSTANT bool value = false; + }; + + template + struct is_nothrow_swappable_test + { + static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())); + }; + + // Only 'true' if the types are swappable with each other and both of the + // swap expressions are noexcept. + template ::value> + struct is_nothrow_swappable_with_test + { + static ETL_CONSTANT bool value = false; + }; + + template + struct is_nothrow_swappable_with_test + { + static ETL_CONSTANT bool value = noexcept(swap(etl::declval(), etl::declval())) && noexcept(swap(etl::declval(), etl::declval())); + }; + } // namespace swap_detect + + template + struct is_nothrow_swappable_helper : etl::bool_constant::value> + { + }; + + template + struct is_nothrow_swappable_with_helper : etl::bool_constant::value> + { + }; + } // namespace private_type_traits + + template + struct is_nothrow_swappable : etl::bool_constant::value> + { + }; + + //*************************************************************************** + /// is_nothrow_swappable_with + //*************************************************************************** + template + struct is_nothrow_swappable_with : etl::bool_constant::value> + { + }; +#endif + + // C++17 implies C++11, so the trait is always defined when the alias is. +#if ETL_USING_CPP17 + template + inline constexpr bool is_swappable_v = etl::is_swappable::value; + + template + inline constexpr bool is_swappable_with_v = etl::is_swappable_with::value; + + template + inline constexpr bool is_nothrow_swappable_v = etl::is_nothrow_swappable::value; + + template + inline constexpr bool is_nothrow_swappable_with_v = etl::is_nothrow_swappable_with::value; +#endif + #if ETL_USING_CPP11 //********************************* /// Check for the presence of operator() diff --git a/include/etl/u16string.h b/include/etl/u16string.h index 741a61c2..03810b7d 100644 --- a/include/etl/u16string.h +++ b/include/etl/u16string.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* u16string() - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* u16string(const etl::u16string& other) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iu16string. //************************************************************************* u16string(const etl::iu16string& other) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u16string(const etl::iu16string& other, size_type position, size_type length = npos) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the u16string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u16string(const value_type* text) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u16string(const value_type* text, size_type count) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the u16string with. //************************************************************************* u16string(size_type count, value_type c) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template u16string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u16string(std::initializer_list init) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u16string(const etl::u16string_view& view) - : iu16string(reinterpret_cast(&buffer), MAX_SIZE) + : iu16string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/u32string.h b/include/etl/u32string.h index 87327cf8..418bd514 100644 --- a/include/etl/u32string.h +++ b/include/etl/u32string.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* u32string() - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* u32string(const etl::u32string& other) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iu32string. //************************************************************************* u32string(const etl::iu32string& other) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u32string(const etl::iu32string& other, size_type position, size_type length = npos) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the u32string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u32string(const value_type* text) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u32string(const value_type* text, size_type count) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the u32string with. //************************************************************************* u32string(size_type count, value_type c) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template u32string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u32string(std::initializer_list init) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u32string(const etl::u32string_view& view) - : iu32string(reinterpret_cast(&buffer), MAX_SIZE) + : iu32string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/u8string.h b/include/etl/u8string.h index 4561f58c..ffea798f 100644 --- a/include/etl/u8string.h +++ b/include/etl/u8string.h @@ -80,7 +80,7 @@ namespace etl /// Constructor. //************************************************************************* u8string() - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->initialise(); } @@ -90,7 +90,7 @@ namespace etl ///\param other The other u8string. //************************************************************************* u8string(const etl::u8string& other) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(other); } @@ -100,7 +100,7 @@ namespace etl ///\param other The other iu8string. //************************************************************************* u8string(const etl::iu8string& other) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(other); } @@ -112,7 +112,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* u8string(const etl::iu8string& other, size_t position, size_t length = npos) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -124,7 +124,7 @@ namespace etl ///\param text The initial text of the u8string. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR u8string(const value_type* text) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(text); } @@ -135,7 +135,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* u8string(const value_type* text, size_t count) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -146,7 +146,7 @@ namespace etl ///\param value The value to fill the u8string with. //************************************************************************* u8string(size_type count, value_type c) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -160,7 +160,7 @@ namespace etl //************************************************************************* template u8string(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(first, last); } @@ -170,7 +170,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* u8string(std::initializer_list init) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -181,7 +181,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit u8string(const etl::u8string_view& view) - : iu8string(reinterpret_cast(&buffer), MAX_SIZE) + : iu8string(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 504317f7..cba1874f 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -46,10 +46,7 @@ SOFTWARE. #include "iterator.h" #include "type_traits.h" -#if ETL_USING_CPP20 && ETL_USING_STL - #include -#endif - +#include #include namespace etl @@ -102,12 +99,14 @@ namespace etl //************************************************************************* /// Default constructor //************************************************************************* - unaligned_type_common() {} + ETL_CONSTEXPR14 unaligned_type_common() + { + } //************************************************************************* /// Size of the storage. //************************************************************************* - size_t size() const + ETL_CONSTEXPR14 size_t size() const { return Size_; } @@ -115,7 +114,7 @@ namespace etl //************************************************************************* /// Pointer to the beginning of the storage. //************************************************************************* - pointer data() + ETL_CONSTEXPR14 pointer data() { return get_storage(); } @@ -123,7 +122,7 @@ namespace etl //************************************************************************* /// Const pointer to the beginning of the storage. //************************************************************************* - const_pointer data() const + ETL_CONSTEXPR14 const_pointer data() const { return get_storage(); } @@ -245,7 +244,7 @@ namespace etl //************************************************************************* /// Get a pointer to the storage. //************************************************************************* - pointer get_storage() + ETL_CONSTEXPR14 pointer get_storage() { return static_cast(this)->storage; } @@ -253,7 +252,7 @@ namespace etl //************************************************************************* /// Get a const pointer to the storage. //************************************************************************* - const_pointer get_storage() const + ETL_CONSTEXPR14 const_pointer get_storage() const { return static_cast(this)->storage; } @@ -277,7 +276,7 @@ namespace etl protected: //******************************* - unaligned_type_storage() + ETL_CONSTEXPR14 unaligned_type_storage() : storage() { } @@ -326,6 +325,52 @@ namespace etl }; ETL_END_PACKED + //************************************************************************* + /// The shift, in bits, that positions byte 'index' of a value of 'Size' + /// bytes, for the target endianness. + //************************************************************************* + template + ETL_CONSTEXPR size_t byte_shift(size_t index) + { + return ((Endian == ETL_ENDIAN_LITTLE) ? index : (Size - 1U - index)) * 8U; + } + +#if ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + /// Unsigned integer type of a given byte size, used as a constexpr-capable + /// 'proxy' for extracting/inserting the bytes of a floating point value. + //************************************************************************* + template + struct uint_of_size; + + template <> + struct uint_of_size<2U> + { + typedef uint16_t type; + }; + + template <> + struct uint_of_size<4U> + { + typedef uint32_t type; + }; + + template <> + struct uint_of_size<8U> + { + typedef uint64_t type; + }; + + //************************************************************************* + /// Whether the bit_cast fast path may be used for a type of 'Size' bytes. + /// It requires a same-size unsigned integer proxy type to exist. + //************************************************************************* + template + struct use_bit_cast : etl::conditional<(Size == 2U) || (Size == 4U) || (Size == 8U), etl::true_type, etl::false_type> + { + }; +#endif + //************************************************************************* /// Unaligned copy //************************************************************************* @@ -335,6 +380,10 @@ namespace etl //************************************************************************* /// Unaligned copy /// For integrals. + /// Bytes are extracted/inserted directly in the target endianness using + /// shifts, rather than a native-endianness memcpy + conditional reverse. + /// This makes the conversion independent of the host's endianness and + /// usable in a constexpr context (C++14 and later). //************************************************************************* template ETL_PACKED_CLASS(unaligned_copy) @@ -347,44 +396,42 @@ namespace etl //******************************* template - static void copy_value_to_store(const T& value, pointer store) + static ETL_CONSTEXPR14 void copy_value_to_store(const T& value, pointer store) { - memcpy(store, &value, Size_); + // Note: 'etl::unsigned_type' is used rather than 'etl::make_unsigned', as the + // latter is not defined for 'bool'. + typedef typename etl::unsigned_type::type unsigned_t; -#if ETL_HAS_CONSTEXPR_ENDIANNESS - if ETL_IF_CONSTEXPR (Endian_ != etl::endianness::value()) -#else - if (Endian_ != etl::endianness::value()) -#endif + unsigned_t uvalue = static_cast(value); + + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(store, store + Size_); + store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); } } //******************************* template - static void copy_store_to_value(const_pointer store, T & value) + static ETL_CONSTEXPR14 void copy_store_to_value(const_pointer store, T & value) { - memcpy(&value, store, Size_); + typedef typename etl::unsigned_type::type unsigned_t; -#if ETL_HAS_CONSTEXPR_ENDIANNESS - if ETL_IF_CONSTEXPR (Endian_ != etl::endianness::value()) -#else - if (Endian_ != etl::endianness::value()) -#endif + unsigned_t uvalue = unsigned_t(0); + + for (size_t i = 0UL; i < Size_; ++i) { - value = etl::reverse_bytes(value); + uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); } + + value = static_cast(uvalue); } //******************************* - static void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) { - memcpy(dst, src, Size_); - - if (Endian_ != endian_src) + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(dst, dst + Size_); + dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; } } }; @@ -404,8 +451,28 @@ namespace etl typedef typename private_unaligned_type::unaligned_type_storage< Size_>::const_pointer const_pointer; //******************************* + // The bit_cast fast path is only valid when a same-size unsigned integer + // proxy type exists (currently 2, 4 or 8 bytes, e.g. a 16-bit float, float, + // or double). Other sizes (e.g. 80/96/128-bit 'long double') fall back to + // memcpy. + //******************************* +#if ETL_USING_BUILTIN_BIT_CAST template - static void copy_value_to_store(const T& value, pointer store) + static ETL_CONSTEXPR14 void do_copy_value_to_store(const T& value, pointer store, etl::true_type) + { + typedef typename private_unaligned_type::uint_of_size::type uint_t; + + uint_t uvalue = etl::bit_cast(value); + + for (size_t i = 0UL; i < Size_; ++i) + { + store[i] = static_cast(uvalue >> private_unaligned_type::byte_shift(i)); + } + } +#endif + + template + static void do_copy_value_to_store(const T& value, pointer store, etl::false_type) { memcpy(store, &value, Size_); @@ -421,7 +488,41 @@ namespace etl //******************************* template - static void copy_store_to_value(const_pointer store, T & value) + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + void + copy_value_to_store(const T& value, pointer store) + { +#if ETL_USING_BUILTIN_BIT_CAST + typedef typename private_unaligned_type::use_bit_cast::type use_bit_cast_t; +#else + typedef etl::false_type use_bit_cast_t; +#endif + do_copy_value_to_store(value, store, use_bit_cast_t()); + } + + //******************************* +#if ETL_USING_BUILTIN_BIT_CAST + template + static ETL_CONSTEXPR14 void do_copy_store_to_value(const_pointer store, T & value, etl::true_type) + { + typedef typename private_unaligned_type::uint_of_size::type uint_t; + + uint_t uvalue = uint_t(0); + + for (size_t i = 0UL; i < Size_; ++i) + { + uvalue = static_cast(uvalue | (static_cast(store[i]) << private_unaligned_type::byte_shift(i))); + } + + value = etl::bit_cast(uvalue); + } +#endif + + template + static void do_copy_store_to_value(const_pointer store, T & value, etl::false_type) { memcpy(&value, store, Size_); @@ -436,13 +537,32 @@ namespace etl } //******************************* - static void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + template + static +#if ETL_USING_BUILTIN_BIT_CAST + ETL_CONSTEXPR14 +#endif + void + copy_store_to_value(const_pointer store, T & value) { - memcpy(dst, src, Size_); +#if ETL_USING_BUILTIN_BIT_CAST + typedef typename private_unaligned_type::use_bit_cast::type use_bit_cast_t; +#else + typedef etl::false_type use_bit_cast_t; +#endif + do_copy_store_to_value(store, value, use_bit_cast_t()); + } - if (Endian_ != endian_src) + //******************************* + // This is pure byte manipulation (copy + optional reversal), with no + // floating point arithmetic involved, so it is always constexpr-capable + // (C++14 and later), regardless of bit_cast/builtin availability. + //******************************* + static ETL_CONSTEXPR14 void copy_store_to_store(const_pointer src, int endian_src, pointer dst) + { + for (size_t i = 0UL; i < Size_; ++i) { - etl::reverse(dst, dst + Size_); + dst[i] = (Endian_ == endian_src) ? src[i] : src[Size_ - 1U - i]; } } }; @@ -486,7 +606,7 @@ namespace etl //************************************************************************* /// Construct from a value. //************************************************************************* - unaligned_type(T value) + ETL_CONSTEXPR14 unaligned_type(T value) { unaligned_copy::copy_value_to_store(value, this->storage); } @@ -501,6 +621,10 @@ namespace etl //************************************************************************* /// Construct from an address and buffer size. + /// \note 'buffer_size' must be greater than or equal to 'sizeof(T)'. + /// This is a precondition, checked by ETL_ASSERT. If the checks are + /// disabled (e.g. ETL_NO_CHECKS) then passing a smaller buffer size + /// results in a read beyond the end of the buffer. //************************************************************************* unaligned_type(const void* address, size_t buffer_size) { @@ -509,10 +633,38 @@ namespace etl etl::copy_n(reinterpret_cast(address), sizeof(T), this->storage); } + //************************************************************************* + /// Construct from a byte buffer. + /// Unlike the 'const void*' overload above, this does not require a + /// reinterpret_cast, so it is usable in a constexpr context (C++14 and + /// later), allowing compile time decoding of a byte buffer with an + /// explicit endianness. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_type(const unsigned char* address) + { + etl::copy_n(address, sizeof(T), this->storage); + } + + //************************************************************************* + /// Construct from a byte buffer and buffer size. + /// See the note on the 'const unsigned char*' overload above regarding + /// constexpr usability. + /// \note 'buffer_size' must be greater than or equal to 'sizeof(T)'. + /// This is a precondition, checked by ETL_ASSERT. If the checks are + /// disabled (e.g. ETL_NO_CHECKS) then passing a smaller buffer size + /// results in a read beyond the end of the buffer. + //************************************************************************* + ETL_CONSTEXPR14 unaligned_type(const unsigned char* address, size_t buffer_size) + { + ETL_ASSERT(sizeof(T) <= buffer_size, ETL_ERROR(etl::unaligned_type_buffer_size)); + + etl::copy_n(address, sizeof(T), this->storage); + } + //************************************************************************* /// Copy constructor //************************************************************************* - unaligned_type(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian, this->storage); } @@ -521,7 +673,7 @@ namespace etl /// Copy constructor //************************************************************************* template - unaligned_type(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); } @@ -529,7 +681,7 @@ namespace etl //************************************************************************* /// Assignment operator //************************************************************************* - unaligned_type& operator=(T value) + ETL_CONSTEXPR14 unaligned_type& operator=(T value) { unaligned_copy::copy_value_to_store(value, this->storage); @@ -539,7 +691,7 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - unaligned_type& operator=(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type& operator=(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_, this->storage); @@ -550,7 +702,7 @@ namespace etl /// Assignment operator from other endianness. //************************************************************************* template - unaligned_type& operator=(const unaligned_type& other) + ETL_CONSTEXPR14 unaligned_type& operator=(const unaligned_type& other) { unaligned_copy::copy_store_to_store(other.data(), Endian_Other, this->storage); @@ -560,7 +712,7 @@ namespace etl //************************************************************************* /// Conversion operator //************************************************************************* - operator T() const + ETL_CONSTEXPR14 operator T() const { T value = T(); @@ -572,7 +724,7 @@ namespace etl //************************************************************************* /// Get the value. //************************************************************************* - T value() const + ETL_CONSTEXPR14 T value() const { T value = T(); @@ -666,7 +818,7 @@ namespace etl unaligned_type_ext(unaligned_type_ext && other) : private_unaligned_type::unaligned_type_storage_ext(other.storage) { - // If we're constructing from a different endianess then we need to + // If we're constructing from a different endianness then we need to // reverse the data order. if (Endian != Endian_Other) { @@ -728,7 +880,7 @@ namespace etl { this->storage = other.storage; - // If we're assigning from a different endianess then we need to reverse + // If we're assigning from a different endianness then we need to reverse // the data order. if (Endian != Endian_Other) { diff --git a/include/etl/utility.h b/include/etl/utility.h index addd7ca1..6d93aa1f 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -32,6 +32,7 @@ SOFTWARE. #define ETL_UTILITY_INCLUDED #include "platform.h" +#include "limits.h" #include "type_traits.h" #include "private/tuple_element.h" @@ -142,10 +143,228 @@ namespace etl return static_cast::type>(value); } +#if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_integer_comparison_functions) + //*************************************************************************** + // Safe integer comparison functions. + //*************************************************************************** + using std::cmp_equal; + using std::cmp_greater; + using std::cmp_greater_equal; + using std::cmp_less; + using std::cmp_less_equal; + using std::cmp_not_equal; + using std::in_range; +#else + namespace private_utility + { + //*********************************** + /// Determines whether T is one of the standard integer types permitted as + /// an argument to the safe integer comparison functions. The permitted + /// types are the signed and unsigned standard integer types, including + /// signed char and unsigned char. bool, char and the extended character + /// types are excluded, matching the C++ standard's constraints on + /// etl::cmp_* and etl::in_range. + //*********************************** + template + struct is_standard_integer : etl::false_type + { + }; + + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + template <> + struct is_standard_integer : etl::true_type + { + }; + + template + struct is_standard_integer : is_standard_integer + { + }; + template + struct is_standard_integer : is_standard_integer + { + }; + template + struct is_standard_integer : is_standard_integer + { + }; + + //*********************************** + /// Enabled when both T and U are permitted comparison types. + //*********************************** + template + struct enable_if_comparable : etl::enable_if::value && is_standard_integer::value, TReturn> + { + }; + + //*********************************** + /// cmp_less implementation helpers, selected by SFINAE to avoid + /// signed/unsigned comparison warnings in dead code branches. + //*********************************** + + // Both same signedness. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value == etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return t < u; + } + + // T is signed, U is unsigned. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && !etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return (t < T(0)) || (typename etl::make_unsigned::type(t) < u); + } + + // T is unsigned, U is signed. + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_signed::value, bool>::type cmp_less_impl(T t, U u) + ETL_NOEXCEPT + { + return (u >= U(0)) && (t < typename etl::make_unsigned::type(u)); + } + } // namespace private_utility + + //*************************************************************************** + /// Compares the values of two integers. + /// Unlike the built-in comparison operators, negative signed integers always + /// compare less than (and not equal to) unsigned integers. + /// The comparison is safe against value-changing implicit conversions. + /// https://en.cppreference.com/w/cpp/utility/intcmp + ///\ingroup utility + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_equal(T t, U u) ETL_NOEXCEPT + { + return (etl::is_signed::value == etl::is_signed::value) ? (t == u) + : etl::is_signed::value ? ((t >= T(0)) && (typename etl::make_unsigned::type(t) == u)) + : ((u >= U(0)) && (t == typename etl::make_unsigned::type(u))); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_not_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_equal(t, u); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_less(T t, U u) ETL_NOEXCEPT + { + return etl::private_utility::cmp_less_impl(t, u); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_greater(T t, U u) ETL_NOEXCEPT + { + return etl::cmp_less(u, t); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_less_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_less(u, t); + } + + //*************************************************************************** + /// See @ref cmp_equal. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type cmp_greater_equal(T t, U u) ETL_NOEXCEPT + { + return !etl::cmp_less(t, u); + } + + //*************************************************************************** + /// Returns whether the value of t is in the range of values that can be + /// represented by R, that is, whether t can be converted to R without loss + /// of its value. + /// https://en.cppreference.com/w/cpp/utility/in_range + ///\ingroup utility + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename private_utility::enable_if_comparable::type in_range(T t) ETL_NOEXCEPT + { + return etl::cmp_greater_equal(t, etl::numeric_limits::min()) && etl::cmp_less_equal(t, etl::numeric_limits::max()); + } +#endif + // We can't have std::swap and etl::swap templates coexisting in the unit // tests as the compiler will be unable to decide which one to use, due to // ADL. + // These are declared in type_traits.h, so that etl::is_swappable can find + // them, and defined here. #if ETL_NOT_USING_STL && !defined(ETL_IN_UNIT_TEST) + #if ETL_USING_CPP11 + //*************************************************************************** + // swap + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_move_assignable::value>::type swap(T& a, T& b) + ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value&& etl::is_nothrow_move_assignable::value) + { + T temp(ETL_MOVE(a)); + a = ETL_MOVE(b); + b = ETL_MOVE(temp); + } + + template + ETL_CONSTEXPR14 typename etl::enable_if::value>::type swap(T (&a)[Size], T (&b)[Size]) + ETL_NOEXCEPT_IF(private_type_traits::is_nothrow_swappable_helper::value) + { + for (size_t i = 0UL; i < Size; ++i) + { + swap(a[i], b[i]); + } + } + #else //*************************************************************************** // swap template @@ -156,7 +375,7 @@ namespace etl b = ETL_MOVE(temp); } - template < class T, size_t Size > + template ETL_CONSTEXPR14 void swap(T (&a)[Size], T (&b)[Size]) ETL_NOEXCEPT { for (size_t i = 0UL; i < Size; ++i) @@ -164,6 +383,7 @@ namespace etl swap(a[i], b[i]); } } + #endif #endif //*************************************************************************** @@ -1164,6 +1384,30 @@ namespace etl static constexpr T value = Value; }; #endif + + //*************************************************************************** + /// Returns 'value' cast to an unsigned integer. + ///\param value The integer to cast. + ///\return The unsigned integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type to_unsigned(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } + + //*************************************************************************** + /// Returns 'value' cast to a signed integer. + ///\param value The integer to cast. + ///\return The signed integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_signed::type>::type to_signed(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } } // namespace etl #endif diff --git a/include/etl/vector.h b/include/etl/vector.h index 492247be..beac6cdc 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -1212,8 +1212,6 @@ namespace etl typedef pvoidvector base_t; - typedef typename etl::remove_const::type>::type* element_type; - template struct is_compatible_iterator : etl::bool_constant::value_type>::value @@ -1728,7 +1726,10 @@ namespace etl // Convert from pointer to void** static ETL_CONSTEXPR14 void** to_void_pptr(pointer p_buffer) ETL_NOEXCEPT { - return reinterpret_cast(const_cast(p_buffer)); + ETL_STATIC_ASSERT(!etl::is_const::type>::value, + "to_void_pptr must not remove const from the pointed-to element type"); + + return static_cast(const_cast(static_cast(p_buffer))); } }; @@ -1998,10 +1999,14 @@ namespace etl /// Make //************************************************************************* #if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST - template - constexpr auto make_vector(T&&... t) -> etl::vector, sizeof...(T)> + template + constexpr auto make_vector(TValues&&... values) -> etl::vector, sizeof...(TValues)> { - return {etl::forward(t)...}; + // Library Fundamentals TS make_array design: the element type is T when + // supplied explicitly, otherwise the decayed common type of the arguments. + // convert forwards same-type arguments and static_casts the rest. + using TElement = etl::private_make::element_type_t; + return {etl::private_make::convert(etl::forward(values))...}; } #endif diff --git a/include/etl/version.h b/include/etl/version.h index a5e76179..cb9275a1 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -39,8 +39,8 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 20 -#define ETL_VERSION_MINOR 48 -#define ETL_VERSION_PATCH 1 +#define ETL_VERSION_MINOR 49 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION \ ETL_STRING(ETL_VERSION_MAJOR) \ diff --git a/include/etl/wstring.h b/include/etl/wstring.h index 0f9e9567..4d7cebde 100644 --- a/include/etl/wstring.h +++ b/include/etl/wstring.h @@ -77,7 +77,7 @@ namespace etl /// Constructor. //************************************************************************* wstring() - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->initialise(); } @@ -87,7 +87,7 @@ namespace etl ///\param other The other string. //************************************************************************* wstring(const etl::wstring& other) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(other); } @@ -97,7 +97,7 @@ namespace etl ///\param other The other iwstring. //************************************************************************* wstring(const etl::iwstring& other) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(other); } @@ -109,7 +109,7 @@ namespace etl ///\param length The number of characters. Default = npos. //************************************************************************* wstring(const etl::iwstring& other, size_type position, size_type length = npos) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds)); @@ -121,7 +121,7 @@ namespace etl ///\param text The initial text of the wstring. //************************************************************************* ETL_EXPLICIT_STRING_FROM_CHAR wstring(const value_type* text) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(text); } @@ -132,7 +132,7 @@ namespace etl ///\param count The number of characters to copy. //************************************************************************* wstring(const value_type* text, size_type count) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(text, text + count); } @@ -143,7 +143,7 @@ namespace etl ///\param value The value to fill the wstring with. //************************************************************************* wstring(size_type count, value_type c) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->initialise(); this->resize(count, c); @@ -157,7 +157,7 @@ namespace etl //************************************************************************* template wstring(TIterator first, TIterator last, typename etl::enable_if::value, int>::type = 0) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(first, last); } @@ -167,7 +167,7 @@ namespace etl /// Construct from initializer_list. //************************************************************************* wstring(std::initializer_list init) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(init.begin(), init.end()); } @@ -178,7 +178,7 @@ namespace etl ///\param view The string_view. //************************************************************************* explicit wstring(const etl::wstring_view& view) - : iwstring(reinterpret_cast(&buffer), MAX_SIZE) + : iwstring(buffer, MAX_SIZE) { this->assign(view.begin(), view.end()); } diff --git a/library.json b/library.json index f597d53f..78ab105b 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "Embedded Template Library", - "version": "20.48.1", + "version": "20.49.0", "authors": { "name": "John Wellbelove", "email": "john.wellbelove@etlcpp.com" diff --git a/library.properties b/library.properties index 813e2468..815c694d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Embedded Template Library -version=20.48.1 +version=20.49.0 author= John Wellbelove maintainer=John Wellbelove license=MIT diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fbbf091e..e1661909 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -163,6 +163,7 @@ add_executable(etl_tests test_crc8_opensafety.cpp test_crc8_rohc.cpp test_crc8_wcdma.cpp + test_clamped_value.cpp test_cyclic_value.cpp test_debounce.cpp test_delegate.cpp @@ -497,6 +498,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") PRIVATE -fno-omit-frame-pointer -fno-common +# -fmax-errors=10 -pedantic-errors -Wall -Wextra @@ -517,6 +519,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") PRIVATE -fno-omit-frame-pointer -fno-common +# -ferror-limit=10 -pedantic-errors -Wall -Wextra diff --git a/test/maincpp03check.cpp b/test/maincpp03check.cpp index a1745c6e..2c5c9936 100644 --- a/test/maincpp03check.cpp +++ b/test/maincpp03check.cpp @@ -46,6 +46,7 @@ SOFTWARE. #include "etl/callback_timer.h" #include "etl/char_traits.h" #include "etl/checksum.h" +#include "etl/clamped_value.h" #include "etl/combinations.h" #include "etl/compare.h" #include "etl/constant.h" diff --git a/test/meson.build b/test/meson.build index add9b29b..b4bc601d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -151,6 +151,7 @@ etl_test_sources = files( 'test_crc8_opensafety.cpp', 'test_crc8_rohc.cpp', 'test_crc8_wcdma.cpp', + 'test_clamped_value.cpp', 'test_cyclic_value.cpp', 'test_debounce.cpp', 'test_delegate.cpp', diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index 3546cfa3..3c4e8e54 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -202,6 +202,7 @@ target_sources(tests PRIVATE crc8_nrsc5.h.t.cpp crc8_rohc.h.t.cpp crc8_wcdma.h.t.cpp + clamped_value.h.t.cpp cyclic_value.h.t.cpp debounce.h.t.cpp debug_count.h.t.cpp diff --git a/test/syntax_check/clamped_value.h.t.cpp b/test/syntax_check/clamped_value.h.t.cpp new file mode 100644 index 00000000..7651ff8c --- /dev/null +++ b/test/syntax_check/clamped_value.h.t.cpp @@ -0,0 +1,39 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value clamped_double_run_time; +#endif + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE +etl::clamped_value clamped_float_compile_time; +#endif diff --git a/test/test_array.cpp b/test/test_array.cpp index 7a5d0130..1f8b4f9c 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -35,6 +35,9 @@ SOFTWARE. #include #include #include +#if ETL_USING_CPP11 + #include +#endif #include #include "etl/integral_limits.h" @@ -74,7 +77,7 @@ namespace CHECK_EQUAL(data.max_size(), 0); } -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) +#if ETL_USING_CPP17 && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) //************************************************************************* TEST(test_cpp17_deduced_constructor) { @@ -400,6 +403,96 @@ namespace // int i = etl::get<11>(data2); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_get_rvalue) + { + Data data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + const Data data2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + int&& r0 = etl::get<3>(std::move(data1)); + const int&& r1 = etl::get<3>(std::move(data2)); + + CHECK_EQUAL(3, r0); + CHECK_EQUAL(3, r1); + + // The rvalue overloads must be selected and return rvalue references. + CHECK((std::is_same(std::move(data1)))>::value)); + CHECK((std::is_same(std::move(data2)))>::value)); + + // Moving out of an rvalue array element actually moves. + etl::array data3 = {Moveable(1), Moveable(2)}; + + Moveable moved(etl::get<0>(std::move(data3))); + + CHECK_EQUAL(1, moved.value); + CHECK(!data3[0].valid); + } +#endif + + //************************************************************************* + TEST(test_get_constexpr) + { + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 int result = etl::get<3>(data); + CHECK_EQUAL(3, result); + } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_tuple_size) + { + CHECK_EQUAL(SIZE, (etl::tuple_size::value)); + CHECK_EQUAL(SIZE, (std::tuple_size::value)); + + #if ETL_USING_CPP17 + CHECK_EQUAL(SIZE, (etl::tuple_size_v)); + CHECK_EQUAL(SIZE, (std::tuple_size_v)); + #endif + } + + //************************************************************************* + TEST(test_tuple_element) + { + CHECK_TRUE((std::is_same>::value)); + CHECK_TRUE((std::is_same>::value)); + + CHECK_TRUE((std::is_same::type>::value)); + CHECK_TRUE((std::is_same::type>::value)); + } +#endif + +#if ETL_USING_CPP17 + //************************************************************************* + TEST(test_structured_bindings) + { + etl::array data = {1, 2, 3}; + + // Bind by reference and modify. + auto& [a, b, c] = data; + + CHECK_EQUAL(1, a); + CHECK_EQUAL(2, b); + CHECK_EQUAL(3, c); + + a = 10; + b = 20; + c = 30; + + CHECK_EQUAL(10, data[0]); + CHECK_EQUAL(20, data[1]); + CHECK_EQUAL(30, data[2]); + + // Bind by const reference. + const auto& [x, y, z] = data; + + CHECK_EQUAL(10, x); + CHECK_EQUAL(20, y); + CHECK_EQUAL(30, z); + } +#endif + //************************************************************************* TEST(test_assign) { @@ -691,6 +784,16 @@ namespace CHECK(!(greater < data)); } + //************************************************************************* + TEST(test_less_than_constexpr) + { + ETL_CONSTEXPR14 Data lesser = {0, 1, 2, 3, 4, 4, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (lesser < data); + CHECK(result); + } + //************************************************************************* TEST(test_less_than_equal) { @@ -703,6 +806,16 @@ namespace CHECK(!(greater <= data)); } + //************************************************************************* + TEST(test_less_than_equal_constexpr) + { + ETL_CONSTEXPR14 Data lesser = {0, 1, 2, 3, 4, 4, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (lesser <= data); + CHECK(result); + } + //************************************************************************* TEST(test_greater_than) { @@ -715,6 +828,16 @@ namespace CHECK(!(lesser > data)); } + //************************************************************************* + TEST(test_greater_than_constexpr) + { + ETL_CONSTEXPR14 Data greater = {0, 1, 2, 3, 5, 5, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (greater > data); + CHECK(result); + } + //************************************************************************* TEST(test_greater_than_equal) { @@ -728,7 +851,17 @@ namespace } //************************************************************************* -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) + TEST(test_greater_than_equal_constexpr) + { + ETL_CONSTEXPR14 Data greater = {0, 1, 2, 3, 5, 5, 6, 7, 8, 9}; + ETL_CONSTEXPR14 Data data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + ETL_CONSTEXPR14 bool result = (greater >= data); + CHECK(result); + } + + //************************************************************************* +#if ETL_USING_CPP17 && !defined(ETL_TEMPLATE_DEDUCTION_GUIDE_TESTS_DISABLED) TEST(test_array_template_deduction) { etl::array data{char(0), short(1), 2, long(3), 4, 5, 6, 7, 8, 9}; @@ -750,7 +883,7 @@ namespace #endif //************************************************************************* -#if ETL_USING_CPP17 && ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP17 TEST(test_array_template_deduction_for_movable) { etl::array data{Moveable(0), Moveable(1), Moveable(2), Moveable(3), Moveable(4), @@ -773,7 +906,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 TEST(test_make_array) { auto data = etl::make_array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); @@ -795,7 +928,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#if ETL_USING_CPP11 TEST(test_make_array_for_movable) { auto data = etl::make_array(Moveable(0), Moveable(1), Moveable(2), Moveable(3), Moveable(4), Moveable(5), Moveable(6), Moveable(7), @@ -817,6 +950,136 @@ namespace } #endif + //************************************************************************* +#if ETL_USING_CPP11 + // Arguments that are const-qualified lvalues of the element type used to be rejected: + // the old implementation forwarded each argument as T, and forward cannot accept a + // const T lvalue (it requires a non-const T&, and T&& cannot bind to a reference-related + // const lvalue). + TEST(test_make_array_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_array(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 + // The arguments are converted to the element type with static_cast, so narrowing + // initialisers (int literals and int lvalues narrowed to char) must keep working. + // Plain forwarding into the braced initialiser list would reject them. + TEST(test_make_array_from_narrowing_values) + { + static const int static_const_int = 45; + const int local_const_int = 46; + int mutable_int = 47; + + auto data = etl::make_array(0, 1, static_const_int, local_const_int, mutable_int); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(0, data[0]); + CHECK_EQUAL(1, data[1]); + CHECK_EQUAL(45, data[2]); + CHECK_EQUAL(46, data[3]); + CHECK_EQUAL(47, data[4]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 + // With no explicit element type, the element type is deduced as the + // decayed common type of the arguments (Library Fundamentals TS + // make_array design). + TEST(test_make_array_deduced_element_type) + { + static const int static_const_lvalue = 1; + + auto data = etl::make_array(static_const_lvalue, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(1L, data[0]); + CHECK_EQUAL(2L, data[1]); + CHECK_EQUAL(3L, data[2]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 + // An empty array requires the element type to be supplied explicitly. + // etl::make_array() with no element type and no arguments is ill-formed, + // as in the Library Fundamentals TS: there is nothing to deduce the + // element type from. + TEST(test_make_array_empty) + { + auto data = etl::make_array(); + + CHECK((std::is_same, decltype(data)>::value)); + CHECK_TRUE(data.empty()); + CHECK_EQUAL(0U, data.size()); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 + // Arguments that already have the element type are forwarded rather than + // cast. Casting created a prvalue that, before C++17, had to be + // materialised through the move constructor, so a copyable type with a + // deleted move constructor was rejected. + TEST(test_make_array_for_copyable_non_movable) + { + struct NonMovable + { + explicit NonMovable(int v_) + : v(v_) + { + } + NonMovable(const NonMovable&) = default; + NonMovable(NonMovable&&) = delete; + int v; + }; + + NonMovable mutable_lvalue(1); + const NonMovable const_lvalue(2); + + auto data = etl::make_array(mutable_lvalue, const_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(1, data[0].v); + CHECK_EQUAL(2, data[1].v); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP14 + TEST(test_make_array_is_constexpr) + { + static constexpr unsigned char constexpr_value = 0x18U; + + constexpr auto data = etl::make_array(constexpr_value, constexpr_value); + + static_assert(data.size() == 2U, "make_array size"); + static_assert(data[0] == 0x18U, "make_array element"); + + CHECK_EQUAL(0x18U, data[0]); + CHECK_EQUAL(0x18U, data[1]); + } +#endif + #if ETL_USING_CPP14 //************************************************************************* using Array = etl::array; diff --git a/test/test_base64_RFC2152_decoder.cpp b/test/test_base64_RFC2152_decoder.cpp index f1ff542e..e7f1cc3e 100644 --- a/test/test_base64_RFC2152_decoder.cpp +++ b/test/test_base64_RFC2152_decoder.cpp @@ -32,20 +32,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-2152 //***************************************************************************** diff --git a/test/test_base64_RFC2152_encoder.cpp b/test/test_base64_RFC2152_encoder.cpp index 07e5b9a3..cfeb62f9 100644 --- a/test/test_base64_RFC2152_encoder.cpp +++ b/test/test_base64_RFC2152_encoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-2152 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC3501_decoder.cpp b/test/test_base64_RFC3501_decoder.cpp index 7dc5baec..75439d63 100644 --- a/test/test_base64_RFC3501_decoder.cpp +++ b/test/test_base64_RFC3501_decoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-3501 //***************************************************************************** diff --git a/test/test_base64_RFC3501_encoder.cpp b/test/test_base64_RFC3501_encoder.cpp index 57a4f3a1..032057b5 100644 --- a/test/test_base64_RFC3501_encoder.cpp +++ b/test/test_base64_RFC3501_encoder.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-3501 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp b/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp index 16bc0935..c0abb3bd 100644 --- a/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_URL_decoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_URL_decoder_with_padding.cpp b/test/test_base64_RFC4648_URL_decoder_with_padding.cpp index d7cc35d3..d99758ed 100644 --- a/test/test_base64_RFC4648_URL_decoder_with_padding.cpp +++ b/test/test_base64_RFC4648_URL_decoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp b/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp index c1cb2b9c..3c32c94f 100644 --- a/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_URL_encoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_URL_encoder_with_padding.cpp b/test/test_base64_RFC4648_URL_encoder_with_padding.cpp index 4bb21b13..f1327786 100644 --- a/test/test_base64_RFC4648_URL_encoder_with_padding.cpp +++ b/test/test_base64_RFC4648_URL_encoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648-URL with padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_decoder_with_no_padding.cpp b/test/test_base64_RFC4648_decoder_with_no_padding.cpp index 77533716..0e31ee4e 100644 --- a/test/test_base64_RFC4648_decoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_decoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_decoder_with_padding.cpp b/test/test_base64_RFC4648_decoder_with_padding.cpp index f4cd0c4a..54d0f2fc 100644 --- a/test/test_base64_RFC4648_decoder_with_padding.cpp +++ b/test/test_base64_RFC4648_decoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_decoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding //***************************************************************************** diff --git a/test/test_base64_RFC4648_encoder_with_no_padding.cpp b/test/test_base64_RFC4648_encoder_with_no_padding.cpp index d925b4d8..d71b44b1 100644 --- a/test/test_base64_RFC4648_encoder_with_no_padding.cpp +++ b/test/test_base64_RFC4648_encoder_with_no_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with no padding // Using minimum buffer size, with callback. diff --git a/test/test_base64_RFC4648_encoder_with_padding.cpp b/test/test_base64_RFC4648_encoder_with_padding.cpp index f40fc733..92c63c0f 100644 --- a/test/test_base64_RFC4648_encoder_with_padding.cpp +++ b/test/test_base64_RFC4648_encoder_with_padding.cpp @@ -30,20 +30,16 @@ SOFTWARE. #include "etl/base64_encoder.h" +#include "etl/integral_limits.h" #include "etl/string.h" #include "etl/vector.h" #include #include +#include #include #include -#if ETL_USING_STL - #include -#endif - -#include "etl/integral_limits.h" - //***************************************************************************** // Encode/decode using RFC-4648 with padding // Using minimum buffer size, with callback. diff --git a/test/test_byte_stream.cpp b/test/test_byte_stream.cpp index 06b2a453..514dbd81 100644 --- a/test/test_byte_stream.cpp +++ b/test/test_byte_stream.cpp @@ -188,6 +188,59 @@ namespace CHECK(reader_little.get_endianness() == etl::endian::little); } + //************************************************************************* + TEST(byte_stream_writer_construct_from_span) + { + char storage[8]; + + etl::span char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_writer writer_char(char_span, etl::endian::little); + CHECK_EQUAL(char_span.size(), writer_char.capacity()); + + etl::span uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_writer writer_uchar(uchar_span, etl::endian::little); + CHECK_EQUAL(uchar_span.size(), writer_uchar.capacity()); + } + + //************************************************************************* + TEST(byte_stream_reader_construct_from_span) + { + char storage[8] = {0}; + + etl::span char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_char(char_span, etl::endian::little); + CHECK_EQUAL(char_span.size(), reader_char.size_bytes()); + + etl::span const_char_span(storage, storage + ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_const_char(const_char_span, etl::endian::little); + CHECK_EQUAL(const_char_span.size(), reader_const_char.size_bytes()); + + etl::span uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_uchar(uchar_span, etl::endian::little); + CHECK_EQUAL(uchar_span.size(), reader_uchar.size_bytes()); + + etl::span const_uchar_span(reinterpret_cast(storage), ETL_OR_STD17::size(storage)); + etl::byte_stream_reader reader_const_uchar(const_uchar_span, etl::endian::little); + CHECK_EQUAL(const_uchar_span.size(), reader_const_uchar.size_bytes()); + } + + //************************************************************************* + // https://github.com/ETLCPP/etl/issues/1533 + // Passing an etl::span to byte_stream_writer/byte_stream_reader + // must not be ambiguous. + TEST(byte_stream_reader_writer_construct_from_span_uint8_t_no_ambiguity) + { + uint8_t storage[8] = {0}; + + etl::span data(storage, ETL_OR_STD17::size(storage)); + + etl::byte_stream_writer writer(etl::span(data), etl::endian::little); + etl::byte_stream_reader reader(etl::span(data), etl::endian::little); + + CHECK_EQUAL(data.size(), writer.capacity()); + CHECK_EQUAL(data.size(), reader.size_bytes()); + } + //************************************************************************* TEST(write_bool) { diff --git a/test/test_circular_buffer.cpp b/test/test_circular_buffer.cpp index 34452537..cdbd162d 100644 --- a/test/test_circular_buffer.cpp +++ b/test/test_circular_buffer.cpp @@ -67,7 +67,124 @@ namespace CHECK(data.crbegin() == data.crend()); } -#if ETL_USING_STL + //************************************************************************* + TEST(test_constructor_with_size) + { + etl::circular_buffer data(5U); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size) + { + etl::circular_buffer data(SIZE + 3U); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_size_and_value) + { + const Ndc value("value"); + Data data(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size_and_value) + { + const Ndc value("value"); + Data data(SIZE + 3U, value); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_size_and_value) + { + Data data; + data.push(Ndc("old")); + + const Ndc value("value"); + data.assign(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + + data.assign(0U, value); + CHECK(data.empty()); + + data.assign(SIZE + 3U, value); + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_range) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data; + data.push(Ndc("old")); + + data.assign(input.begin(), input.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + data.assign(input.begin(), input.begin()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST(test_assign_self_range) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(input.begin(), input.end()); + + data.assign(data.begin(), data.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + Data::const_iterator first = data.cbegin() + 2; + Data::const_iterator last = data.cend() - 2; + Compare partial_expected(expected.begin() + 2, expected.end() - 2); + + data.assign(first, last); + + CHECK_EQUAL(partial_expected.size(), data.size()); + CHECK(std::equal(partial_expected.begin(), partial_expected.end(), data.begin())); + } + + //************************************************************************* + TEST(test_assign_aliased_value) + { + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4")}; + Data data(input.begin(), input.end()); + + const Ndc expected(data.front()); + data.assign(7U, data.front()); + + CHECK_EQUAL(7U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&expected](const Ndc& item) { return item == expected; })); + } + +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_constructor_from_literal) { diff --git a/test/test_circular_buffer_external_buffer.cpp b/test/test_circular_buffer_external_buffer.cpp index a1374986..425d92b0 100644 --- a/test/test_circular_buffer_external_buffer.cpp +++ b/test/test_circular_buffer_external_buffer.cpp @@ -78,6 +78,131 @@ namespace CHECK(data.crbegin() == data.crend()); } + //************************************************************************* + TEST(test_constructor_with_size) + { + etl::uninitialized_buffer_of buffer1; + etl::circular_buffer_ext data(5U, buffer1.raw, SIZE); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size) + { + etl::uninitialized_buffer_of buffer1; + etl::circular_buffer_ext data(SIZE + 3U, buffer1.raw, SIZE); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [](int value) { return value == 0; })); + } + + //************************************************************************* + TEST(test_constructor_with_size_and_value) + { + Buffer_t buffer1; + const Ndc value("value"); + Data data(5U, value, buffer1.raw, SIZE); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_constructor_with_excess_size_and_value) + { + Buffer_t buffer1; + const Ndc value("value"); + Data data(SIZE + 3U, value, buffer1.raw, SIZE); + + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_size_and_value) + { + Buffer_t buffer1; + Data data(buffer1.raw, SIZE); + data.push(Ndc("old")); + + const Ndc value("value"); + data.assign(5U, value); + + CHECK_EQUAL(5U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + + data.assign(0U, value); + CHECK(data.empty()); + + data.assign(SIZE + 3U, value); + CHECK(data.full()); + CHECK_EQUAL(SIZE, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&value](const Ndc& item) { return item == value; })); + } + + //************************************************************************* + TEST(test_assign_range) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(buffer1.raw, SIZE); + data.push(Ndc("old")); + + data.assign(input.begin(), input.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + data.assign(input.begin(), input.begin()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST(test_assign_self_range) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4"), Ndc("5"), Ndc("6"), + Ndc("7"), Ndc("8"), Ndc("9"), Ndc("10"), Ndc("11"), Ndc("12")}; + Compare expected(input.end() - SIZE, input.end()); + Data data(input.begin(), input.end(), buffer1.raw, SIZE); + + data.assign(data.begin(), data.end()); + + CHECK(data.full()); + CHECK_EQUAL(expected.size(), data.size()); + CHECK(std::equal(expected.begin(), expected.end(), data.begin())); + + Data::const_iterator first = data.cbegin() + 2; + Data::const_iterator last = data.cend() - 2; + Compare partial_expected(expected.begin() + 2, expected.end() - 2); + + data.assign(first, last); + + CHECK_EQUAL(partial_expected.size(), data.size()); + CHECK(std::equal(partial_expected.begin(), partial_expected.end(), data.begin())); + } + + //************************************************************************* + TEST(test_assign_aliased_value) + { + Buffer_t buffer1; + Compare input{Ndc("0"), Ndc("1"), Ndc("2"), Ndc("3"), Ndc("4")}; + Data data(input.begin(), input.end(), buffer1.raw, SIZE); + + const Ndc expected(data.front()); + data.assign(7U, data.front()); + + CHECK_EQUAL(7U, data.size()); + CHECK(std::all_of(data.begin(), data.end(), [&expected](const Ndc& item) { return item == expected; })); + } + //************************************************************************* TEST(test_set_buffer_after_default_constructor) { @@ -99,7 +224,7 @@ namespace CHECK(data.crbegin() == data.crend()); } -#if ETL_USING_STL +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_constructor_from_literal) { diff --git a/test/test_clamped_value.cpp b/test/test_clamped_value.cpp new file mode 100644 index 00000000..cdf81420 --- /dev/null +++ b/test/test_clamped_value.cpp @@ -0,0 +1,535 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/clamped_value.h" + +#include +#include + +namespace +{ + SUITE(test_clamped_value) + { + //************************************************************************* + TEST(test_compile_time_initialisation) + { + etl::clamped_value value; + const etl::clamped_value const_value(value); + int converted_value = value; + int converted_const_value = const_value; + CHECK_EQUAL(2, value.get()); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + CHECK_EQUAL(2, converted_value); + CHECK_EQUAL(2, converted_const_value); + } + + //************************************************************************* + TEST(test_compile_time_bounds_accessors_are_const) + { + const etl::clamped_value value(4); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_compile_time_zero_range) + { + etl::clamped_value value(1); + CHECK_EQUAL(0, value.get()); + CHECK_EQUAL(0, value.min()); + CHECK_EQUAL(0, value.max()); + } +#endif + + //************************************************************************* + TEST(test_run_time_initialisation) + { + etl::clamped_value value(2, 7, 5); + etl::clamped_value default_value; + etl::clamped_value range_value(2, 7); + const etl::clamped_value const_value(value); + int converted_value = value; + int converted_const_value = const_value; + CHECK_EQUAL(5, value.get()); + CHECK_EQUAL(2, value.min()); + CHECK_EQUAL(7, value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), default_value.min()); + CHECK_EQUAL(etl::numeric_limits::max(), default_value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), default_value.get()); + CHECK_EQUAL(2, range_value.get()); + CHECK_EQUAL(5, converted_value); + CHECK_EQUAL(5, converted_const_value); + } + + //************************************************************************* + TEST(test_set_and_assignment_clamp_values) + { + etl::clamped_value compile_time(1); + etl::clamped_value run_time(2, 7, 8); + CHECK_EQUAL(2, compile_time.get()); + CHECK_EQUAL(7, run_time.get()); + compile_time = 8; + run_time.set(1); + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + } + + //************************************************************************* + TEST(test_increment_and_decrement_saturate) + { + etl::clamped_value compile_time(7); + etl::clamped_value run_time(2, 7, 2); + ++compile_time; + --run_time; + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + compile_time.to_min(); + run_time.to_max(); + --compile_time; + ++run_time; + CHECK_EQUAL(2, compile_time.get()); + CHECK_EQUAL(7, run_time.get()); + } + + //************************************************************************* + TEST(test_postfix_operators) + { + etl::clamped_value value(4); + etl::clamped_value previous_increment = value++; + CHECK_EQUAL(4, previous_increment.get()); + CHECK_EQUAL(5, value.get()); + etl::clamped_value previous_decrement = value--; + CHECK_EQUAL(5, previous_decrement.get()); + CHECK_EQUAL(4, value.get()); + } + + //************************************************************************* + TEST(test_advance_saturates) + { + etl::clamped_value value; + value.advance(100); + CHECK_EQUAL(7, value.get()); + value.advance(-100); + CHECK_EQUAL(2, value.get()); + } + + //************************************************************************* + TEST(test_set_run_time_bounds) + { + etl::clamped_value value(2, 7, 6); + value.set(3, 8); + CHECK_EQUAL(3, value.get()); + CHECK_EQUAL(3, value.min()); + CHECK_EQUAL(8, value.max()); + } + + //************************************************************************* + TEST(test_copy_equality_and_swap_compile_time) + { + etl::clamped_value value1(4); + etl::clamped_value value2(value1); + CHECK(value1 == value2); + value2 = 6; + CHECK(value1 != value2); + swap(value1, value2); + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(4, value2.get()); + } + + //************************************************************************* + TEST(test_compile_time_instances_with_different_bounds) + { + etl::clamped_value value1(3); + etl::clamped_value value2(3); + CHECK(value1 == value2); + } + + //************************************************************************* + TEST(test_runtime_equality_uses_current_value) + { + etl::clamped_value equal_value1(2, 7, 3); + etl::clamped_value equal_value2(2, 8, 3); + etl::clamped_value different_value(2, 8, 4); + CHECK(equal_value1 == equal_value2); + CHECK(!(equal_value1 != equal_value2)); + CHECK(equal_value1 != different_value); + } + + //************************************************************************* + TEST(test_copy_assignment_and_self_assignment_run_time) + { + etl::clamped_value value1(2, 7, 5); + etl::clamped_value value2(3, 8, 6); + value1 = value2; + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(3, value1.min()); + CHECK_EQUAL(8, value1.max()); + etl::clamped_value* value1_pointer = &value1; + value1 = *value1_pointer; + CHECK_EQUAL(6, value1.get()); + CHECK_EQUAL(3, value1.min()); + CHECK_EQUAL(8, value1.max()); + } + + //************************************************************************* + TEST(test_unsigned_boundary_values) + { + etl::clamped_value value(2U); + ++value; + CHECK_EQUAL(2U, value.get()); + value.to_min(); + --value; + CHECK_EQUAL(0U, value.get()); + } + + //************************************************************************* + TEST(test_narrow_signed_range_spanning_lowest_value) + { + etl::clamped_value value(-50); + + value += 10; + CHECK_EQUAL(-40, value.get()); + value -= 10; + CHECK_EQUAL(-50, value.get()); + value.advance(INT8_MIN); + CHECK_EQUAL(INT8_MIN, value.get()); + value.advance(INT8_MAX); + CHECK_EQUAL(-10, value.get()); + } + + //************************************************************************* + TEST(test_runtime_postfix_and_advance) + { + etl::clamped_value value(2, 7, 4); + etl::clamped_value previous_increment = value++; + CHECK_EQUAL(4, previous_increment.get()); + CHECK_EQUAL(5, value.get()); + etl::clamped_value previous_decrement = value--; + CHECK_EQUAL(5, previous_decrement.get()); + CHECK_EQUAL(4, value.get()); + value.advance(0); + CHECK_EQUAL(4, value.get()); + value.advance(3); + CHECK_EQUAL(7, value.get()); + value.advance(-3); + CHECK_EQUAL(4, value.get()); + } + + //************************************************************************* + TEST(test_wide_and_negative_ranges) + { + etl::clamped_value negative(-4); + etl::clamped_value wide(0); + etl::clamped_value unsigned_value(2U, 7U, 4U); + + negative.advance(100); + wide.advance(INT64_MAX); + unsigned_value.advance(3); + + CHECK_EQUAL(-2, negative.get()); + CHECK_EQUAL(INT64_C(7), wide.get()); + CHECK_EQUAL(7U, unsigned_value.get()); + } + + //************************************************************************* + TEST(test_compound_and_comparison_operators) + { + etl::clamped_value value(4); + etl::clamped_value other(6); + + value += 10; + CHECK_EQUAL(7, value.get()); + value -= 10; + CHECK_EQUAL(2, value.get()); + CHECK(value < other); + CHECK(value < 6); + CHECK(4 < other); + CHECK(value <= other); + CHECK(value <= 4); + CHECK(4 <= other); + CHECK(other > value); + CHECK(other > 4); + CHECK(6 > value); + CHECK(other >= value); + CHECK(other >= 6); + CHECK(6 >= value); + CHECK(value == 2); + CHECK(2 == value); + CHECK(value != 6); + CHECK(6 != value); + } + + //************************************************************************* + TEST(test_mixed_type_comparisons_are_not_ambiguous) + { + etl::clamped_value narrow(10); + etl::clamped_value value(10); + + CHECK(narrow < 20); + CHECK(value < 20L); + CHECK(20L > value); + CHECK(value <= 20L); + CHECK(20L >= value); + } + + //************************************************************************* + TEST(test_runtime_comparison_operator_overloads) + { + etl::clamped_value value(2, 7, 4); + etl::clamped_value other(2, 8, 6); + + CHECK(value < other); + CHECK(value < 6); + CHECK(4 < other); + CHECK(value <= other); + CHECK(value <= 4); + CHECK(4 <= other); + CHECK(other > value); + CHECK(other > 4); + CHECK(6 > value); + CHECK(other >= value); + CHECK(other >= 6); + CHECK(6 >= value); + } + + //************************************************************************* + TEST(test_advance_extreme_values) + { + etl::clamped_value compile_time(4); + etl::clamped_value run_time(2, 7, 4); + compile_time.advance(INT_MAX); + run_time.advance(INT_MIN); + CHECK_EQUAL(7, compile_time.get()); + CHECK_EQUAL(2, run_time.get()); + } + + //************************************************************************* + TEST(test_subtract_lowest_difference_saturates) + { + etl::clamped_value compile_time(0); + etl::clamped_value run_time(-10, 10, 0); + + compile_time -= etl::clamped_value::difference_limits_type::lowest(); + run_time -= etl::clamped_value::difference_limits_type::lowest(); + + CHECK_EQUAL(10, compile_time.get()); + CHECK_EQUAL(10, run_time.get()); + } + + //************************************************************************* + TEST(test_runtime_compound_assignment_and_swap) + { + etl::clamped_value value1(-4, 8, 2); + etl::clamped_value value2(10, 20, 15); + + value1 += 20; + value2 -= 20; + value1.swap(value2); + + CHECK_EQUAL(10, value1.get()); + CHECK_EQUAL(10, value1.min()); + CHECK_EQUAL(20, value1.max()); + CHECK_EQUAL(8, value2.get()); + CHECK_EQUAL(-4, value2.min()); + CHECK_EQUAL(8, value2.max()); + } + + //************************************************************************* + TEST(test_runtime_reversed_bounds_are_rejected) + { + CHECK_THROW((etl::clamped_value(7, 2)), etl::exception); + CHECK_THROW((etl::clamped_value(7, 2, 4)), etl::exception); + + etl::clamped_value value; + CHECK_THROW(value.set(7, 2), etl::exception); + } + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_compile_time_floating_clamps_and_advances) + { + etl::clamped_value value(4.0f); + CHECK_CLOSE(2.5f, value.get(), 0.0001f); + + value = -4.0f; + CHECK_CLOSE(-1.5f, value.get(), 0.0001f); + + value += 0.75f; + CHECK_CLOSE(-0.75f, value.get(), 0.0001f); + value -= 0.5f; + CHECK_CLOSE(-1.25f, value.get(), 0.0001f); + value.advance(10.0f); + CHECK_CLOSE(2.5f, value.get(), 0.0001f); + } +#endif + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_runtime_floating_clamps_and_advances) + { + etl::clamped_value value(-1.5, 2.5, 4.0); + CHECK_CLOSE(2.5, value.get(), 0.0001); + + value = -4.0; + value += 0.75; + value -= 0.5; + CHECK_CLOSE(-1.25, value.get(), 0.0001); + + value.advance(-10.0); + CHECK_CLOSE(-1.5, value.get(), 0.0001); + } +#endif + +#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_compile_time_floating_increment_saturates_at_fractional_bound) + { + etl::clamped_value compile_time(0.25f); + + ++compile_time; + + CHECK_CLOSE(0.5f, compile_time.get(), 0.0001f); + } +#endif + +#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE + //************************************************************************* + TEST(test_runtime_floating_decrement_saturates_at_fractional_bound) + { + etl::clamped_value value(-0.5f, 0.5f, -0.25f); + + --value; + + CHECK_CLOSE(-0.5f, value.get(), 0.0001f); + } + + //************************************************************************* + TEST(test_runtime_floating_comparison_and_swap) + { + etl::clamped_value value1(-3.0, 3.0, -1.0); + etl::clamped_value value2(4.0, 8.0, 6.0); + + CHECK(value1 < value2); + CHECK(value1 < 1.0); + swap(value1, value2); + + CHECK_CLOSE(6.0, value1.get(), 0.0001); + CHECK_CLOSE(4.0, value1.min(), 0.0001); + CHECK_CLOSE(-1.0, value2.get(), 0.0001); + } + + //************************************************************************* + TEST(test_runtime_floating_infinity_saturates) + { + const double infinity = etl::numeric_limits::infinity(); + etl::clamped_value value(-2.0, 2.0, 0.0); + etl::clamped_value infinite_range(-infinity, infinity, 0.0); + + value += infinity; + CHECK_CLOSE(2.0, value.get(), 0.0001); + value -= infinity; + CHECK_CLOSE(-2.0, value.get(), 0.0001); + + infinite_range = infinity; + CHECK(etl::is_infinity(infinite_range.get())); + } + + //************************************************************************* + TEST(test_floating_finite_overflow_is_prevented) + { + const double maximum = etl::numeric_limits::max(); + etl::clamped_value value(-maximum, maximum, maximum / 2.0); + + value += maximum; + CHECK_EQUAL(maximum, value.get()); + + value = -maximum / 2.0; + value -= maximum; + CHECK_EQUAL(-maximum, value.get()); + + value = -maximum; + value += maximum; + CHECK_EQUAL(0.0, value.get()); + value += maximum; + CHECK_EQUAL(maximum, value.get()); + } + + //************************************************************************* + TEST(test_floating_nan_is_rejected) + { + const double nan = etl::numeric_limits::quiet_NaN(); + CHECK_THROW((etl::clamped_value(nan, 1.0)), etl::exception); + CHECK_THROW((etl::clamped_value(-1.0, nan)), etl::exception); + CHECK_THROW((etl::clamped_value(-1.0, 1.0, nan)), etl::exception); + + etl::clamped_value value(-1.0, 1.0, 0.0); + CHECK_THROW(value.set(nan, 1.0), etl::exception); + CHECK_THROW(value.set(-1.0, nan), etl::exception); + CHECK_THROW(value = nan, etl::exception); + CHECK_THROW(value.advance(nan), etl::exception); + CHECK_THROW(value += nan, etl::exception); + CHECK_THROW(value -= nan, etl::exception); + } + + //************************************************************************* + TEST(test_default_floating_range) + { + etl::clamped_value value; + CHECK_EQUAL(etl::numeric_limits::lowest(), value.min()); + CHECK_EQUAL(etl::numeric_limits::max(), value.max()); + CHECK_EQUAL(etl::numeric_limits::lowest(), value.get()); + } +#endif + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_clamped_value_constexpr_ctor) + { + constexpr etl::clamped_value value; + static_assert(value.get() == 0, "constexpr clamped_value constructor"); + CHECK(true); + } + + #if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE && (ETL_USING_MICROSOFT_COMPILER && (_MSC_VER >= 1950)) + //************************************************************************* + TEST(test_floating_clamped_value_constexpr) + { + constexpr etl::clamped_value value(0.5); + static_assert((value.get() > 0.49) && (value.get() < 0.51), "constexpr floating clamped_value constructor"); + CHECK(true); + } + #endif +#endif + } +} // namespace diff --git a/test/test_const_map.cpp b/test/test_const_map.cpp index fe998a7d..ee8b5aff 100644 --- a/test/test_const_map.cpp +++ b/test/test_const_map.cpp @@ -112,6 +112,9 @@ namespace using mapped_type = Data::mapped_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_map>>::value, + "default compare is not less"); + SUITE(test_const_map) { //************************************************************************* @@ -257,6 +260,27 @@ namespace CHECK_TRUE(data.max_size() == Max_Size); CHECK_FALSE(data.begin() == data.end()); } + + //************************************************************************* + TEST(test_cpp17_deduced_constructor_of_plain_pair) + { + static const etl::const_map data{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + etl::const_map check{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + CHECK_TRUE(data.is_valid()); + CHECK_TRUE(data.size() == Max_Size); + CHECK_FALSE(data.empty()); + CHECK_TRUE(data.full()); + CHECK_TRUE(data.capacity() == Max_Size); + CHECK_TRUE(data.max_size() == Max_Size); + CHECK_FALSE(data.begin() == data.end()); + } #endif //************************************************************************* diff --git a/test/test_const_multimap.cpp b/test/test_const_multimap.cpp index 8c134899..02bd030e 100644 --- a/test/test_const_multimap.cpp +++ b/test/test_const_multimap.cpp @@ -111,6 +111,9 @@ namespace using mapped_type = Data::mapped_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_multimap>>::value, + "default compare is not less"); + SUITE(test_const_multimap) { //************************************************************************* @@ -226,6 +229,28 @@ namespace CHECK_TRUE(data.max_size() == Max_Size); CHECK_FALSE(data.begin() == data.end()); } + + //************************************************************************* + TEST(test_cpp17_deduced_constructor_of_plain_pair) + { + static const etl::const_multimap data{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('G'), 7}, ETL_OR_STD::pair{Key('G'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + etl::const_multimap check{ETL_OR_STD::pair{Key('A'), 0}, ETL_OR_STD::pair{Key('B'), 1}, ETL_OR_STD::pair{Key('C'), 2}, + ETL_OR_STD::pair{Key('D'), 3}, ETL_OR_STD::pair{Key('E'), 4}, ETL_OR_STD::pair{Key('F'), 5}, + ETL_OR_STD::pair{Key('G'), 6}, ETL_OR_STD::pair{Key('H'), 7}, ETL_OR_STD::pair{Key('I'), 8}, + ETL_OR_STD::pair{Key('J'), 9}}; + + CHECK_TRUE(data.is_valid()); + CHECK_TRUE(data.size() == Max_Size); + CHECK_FALSE(data.empty()); + CHECK_TRUE(data.full()); + CHECK_TRUE(data.capacity() == Max_Size); + CHECK_TRUE(data.max_size() == Max_Size); + CHECK_FALSE(data.begin() == data.end()); + } #endif //************************************************************************* diff --git a/test/test_const_multiset.cpp b/test/test_const_multiset.cpp index 62310317..8baf4198 100644 --- a/test/test_const_multiset.cpp +++ b/test/test_const_multiset.cpp @@ -114,6 +114,9 @@ namespace using key_type = Data::key_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_multiset>>::value, + "default compare is not less"); + SUITE(test_const_multiset) { //************************************************************************* diff --git a/test/test_const_set.cpp b/test/test_const_set.cpp index 47f78e0f..0383e852 100644 --- a/test/test_const_set.cpp +++ b/test/test_const_set.cpp @@ -112,6 +112,8 @@ namespace using key_type = Data::key_type; using const_iterator = Data::const_iterator; + static_assert(etl::is_same, etl::iconst_set>>::value, "default compare is not less"); + SUITE(test_const_set) { //************************************************************************* diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index f990e115..60a8965e 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -96,7 +96,11 @@ namespace MoveableOnlyData& operator=(const MoveableOnlyData&) = delete; MoveableOnlyData(MoveableOnlyData&&) = default; MoveableOnlyData& operator=(MoveableOnlyData&&) = default; - int d; + MoveableOnlyData(int _d) + : d{_d} + { + } + int d; }; //***************************************************************************** @@ -583,6 +587,17 @@ namespace CHECK(parameter_correct); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_free_moveableonly_deduced_type) + { + auto d = etl::delegate::create(); + + d({VALUE1}); // deduce argument type from braced initializer list + + CHECK(function_called == FunctionCalled::Free_Moveableonly_Called); + CHECK(parameter_correct); + } + //************************************************************************* #if ETL_USING_CPP17 TEST_FIXTURE(SetupFixture, test_make_delegate_free_moveableonly) @@ -2167,6 +2182,79 @@ namespace #endif } #endif + + #if ETL_USING_CPP17 + //************************************************************************* + // Verify that copy assigning a delegate, and comparing against a default + // constructed one, are usable in a constant expression. + //************************************************************************* + TEST(test_constexpr_copy_assignment_and_clear) + { + using delegate_type = etl::delegate; + + // Copy assignment from a bound delegate. + constexpr auto copy_assigned = []() constexpr + { + const delegate_type source(&free_void); + delegate_type d; + d = source; + return d; + }; + + constexpr delegate_type d1 = copy_assigned(); + + static_assert(d1.is_valid(), "a copy assigned delegate should be valid"); + static_assert(d1 == delegate_type(&free_void), "a copy assigned delegate should compare equal to its source"); + + // Copy assignment from a default constructed delegate, and comparing an + // unbound delegate against another one. Neither may inspect the null stub + // pointer, which the undefined behaviour sanitizer makes non-constant. + constexpr auto cleared = []() constexpr + { + delegate_type d(&free_void); + d = delegate_type(); + return d; + }; + + constexpr delegate_type d2 = cleared(); + + static_assert(!d2.is_valid(), "a delegate assigned from a default constructed one should be invalid"); + static_assert(d2 == delegate_type(), "an unbound delegate should compare equal to a default constructed one"); + static_assert(d2 != d1, "an unbound delegate should not compare equal to a bound one"); + + // The same operations must still behave identically at run time. + delegate_type d3; + d3 = d1; + CHECK_TRUE(d3.is_valid()); + CHECK(d3 == d1); + + d3 = delegate_type(); + CHECK_FALSE(d3.is_valid()); + CHECK(d3 == delegate_type()); + + // Assigning a null function pointer clears the delegate at run time. + // This is deliberately not checked in a constant expression: the null + // test cannot be folded there when the sanitizer instruments it. + delegate_type d4(&free_void); + d4 = static_cast(ETL_NULLPTR); + CHECK_FALSE(d4.is_valid()); + CHECK(d4 == delegate_type()); + } + #endif + + TEST(test_delegate_clear_equals_default_constructed) + { + using delegate_type = etl::delegate; + + delegate_type delegate = {}; + const delegate_type default_constructed; + + CHECK(delegate == default_constructed); + delegate = delegate_type::create(free_int); + CHECK(delegate != default_constructed); + delegate = {}; + CHECK(delegate == default_constructed); + } } } // namespace diff --git a/test/test_deque.cpp b/test/test_deque.cpp index a09f67d5..d03580bd 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2299,6 +2299,72 @@ namespace } #endif + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the element type used to be rejected: + // the old implementation forwarded each argument as T, and forward cannot accept a + // const T lvalue (it requires a non-const T&, and T&& cannot bind to a reference-related + // const lvalue). + TEST(test_make_deque_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_deque(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // The arguments are converted to the element type with static_cast, so narrowing + // initialisers (int literals and int lvalues narrowed to char) must keep working. + // Plain forwarding into the braced initialiser list would reject them. + TEST(test_make_deque_from_narrowing_values) + { + static const int static_const_int = 45; + const int local_const_int = 46; + int mutable_int = 47; + + auto data = etl::make_deque(0, 1, static_const_int, local_const_int, mutable_int); + + using Type = etl::remove_reference_t; + CHECK((std::is_same::value)); + + CHECK_EQUAL(0, data[0]); + CHECK_EQUAL(1, data[1]); + CHECK_EQUAL(45, data[2]); + CHECK_EQUAL(46, data[3]); + CHECK_EQUAL(47, data[4]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // With no explicit element type, the element type is deduced as the + // decayed common type of the arguments (Library Fundamentals TS + // make_array design). + TEST(test_make_deque_deduced_element_type) + { + static const int static_const_lvalue = 1; + + auto data = etl::make_deque(static_const_lvalue, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(1L, data[0]); + CHECK_EQUAL(2L, data[1]); + CHECK_EQUAL(3L, data[2]); + } +#endif + //************************************************************************* TEST(test_fill) { @@ -2323,9 +2389,10 @@ namespace #include "etl/private/diagnostic_uninitialized_push.h" // Verify operator[] returns values matching sequential access - for (size_t i = 0; i < cdata.size(); ++i) + DataNDC::difference_type it_idx = 0; + for (size_t i = 0; i < cdata.size(); ++i, ++it_idx) { - CHECK(cit[i] == cdata[i]); + CHECK(cit[it_idx] == cdata[i]); } // Verify const_iterator operator[] returns const_reference diff --git a/test/test_expected.cpp b/test/test_expected.cpp index 621727b2..d0027f08 100644 --- a/test/test_expected.cpp +++ b/test/test_expected.cpp @@ -120,6 +120,147 @@ namespace std::string e; }; + struct ExplicitValue + { + std::string v; + + ExplicitValue() {} + + explicit ExplicitValue(const std::string& v_) + : v(v_) + { + } + }; + + struct ExplicitError + { + std::string e; + + ExplicitError() = default; + + explicit ExplicitError(const std::string& e_) + : e(e_) + { + } + }; + + struct MoveOnlyValue + { + MoveOnlyValue() + : v(0) + { + } + + explicit MoveOnlyValue(int v_) + : v(v_) + { + } + + MoveOnlyValue(MoveOnlyValue&&) = default; + MoveOnlyValue& operator=(MoveOnlyValue&&) = default; + + MoveOnlyValue(const MoveOnlyValue&) = delete; + MoveOnlyValue& operator=(const MoveOnlyValue&) = delete; + + int v; + }; + + struct FromMoveOnlyValue + { + FromMoveOnlyValue() + : v(0) + { + } + + FromMoveOnlyValue(MoveOnlyValue&& other) + : v(other.v) + { + } + + int v; + }; + + struct NoMoveValue + { + explicit NoMoveValue(int v_) + : v(v_) + { + } + + NoMoveValue(const NoMoveValue&) = default; + NoMoveValue(NoMoveValue&&) = delete; + + int v; + }; + + struct FromInt + { + FromInt() + : v(0) + { + } + + FromInt(int v_) + : v(v_) + { + } + + int v; + }; + + struct ErrorFromExpected + { + ErrorFromExpected() {} + + ErrorFromExpected(int) {} + + ErrorFromExpected(const etl::expected&) {} + }; + + struct ImmobileValue + { + static int destructions; + + explicit ImmobileValue(int v_) + : v(v_) + { + } + + ImmobileValue(const ImmobileValue&) = delete; + ImmobileValue(ImmobileValue&&) = delete; + + ~ImmobileValue() + { + ++destructions; + } + + int v; + }; + + int ImmobileValue::destructions = 0; + + struct ImmobileError + { + static int destructions; + + ImmobileError(int e_) + : e(e_) + { + } + + ImmobileError(const ImmobileError&) = delete; + ImmobileError(ImmobileError&&) = delete; + + ~ImmobileError() + { + ++destructions; + } + + int e; + }; + + int ImmobileError::destructions = 0; + using Expected = etl::expected; using ExpectedV = etl::expected; using ExpectedM = etl::expected; @@ -618,6 +759,20 @@ namespace etl::expected_invalid); } + //************************************************************************* + // See https://github.com/ETLCPP/etl/issues/1532 + TEST(test_dereference_const_and_rvalue_expected) + { + typedef etl::expected Expected1532; + + Expected1532 e = 42; + const Expected1532& ce = e; + + CHECK_EQUAL(42, *e); + CHECK_EQUAL(42, *ce); + CHECK_EQUAL(42, *Expected1532(42)); + } + //************************************************************************* struct value_or_helper { @@ -1637,5 +1792,196 @@ namespace CHECK_FALSE(result.has_value()); CHECK_EQUAL("original_transformed", result.error().e); } + + //************************************************************************* + TEST(test_constructor_from_value_convertible_to_value_type) + { + const Expected result = std::string("converted"); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_constructor_from_expected_with_convertible_types) + { + const etl::expected source(std::string("converted")); + + const Expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_constructor_from_expected_with_convertible_types_carries_error) + { + const etl::expected source(etl::unexpected(std::string("failed"))); + + const Expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_value_type_constructible_from_expected_is_not_unwrapped) + { + struct WrapsExpected + { + WrapsExpected() + : wrapped(false) + { + } + + WrapsExpected(const etl::expected&) + : wrapped(true) + { + } + + explicit WrapsExpected(int) + : wrapped(false) + { + } + + bool wrapped; + }; + + const etl::expected source(7); + + const etl::expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_TRUE(result.value().wrapped); + } + + //************************************************************************* + TEST(test_explicit_value_conversion_is_not_implicit) + { + using ExplicitExpected = etl::expected; + + CHECK_TRUE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_convertible::value)); + + CHECK_TRUE((etl::is_convertible::value)); + } + + //************************************************************************* + TEST(test_explicit_value_conversion_is_available_by_direct_initialisation) + { + const etl::expected result(std::string("converted")); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + + //************************************************************************* + TEST(test_explicit_error_conversion_is_not_implicit) + { + using Source = etl::expected; + using ExplicitExpected = etl::expected; + + CHECK_TRUE((etl::is_constructible::value)); + CHECK_FALSE((etl::is_convertible::value)); + + CHECK_TRUE((etl::is_convertible::value)); + } + + //************************************************************************* + TEST(test_explicit_error_conversion_carries_the_error) + { + const etl::expected source(etl::unexpected(std::string("failed"))); + + const etl::expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_move_constructor_from_expected_with_convertible_types) + { + etl::expected source(MoveOnlyValue(1)); + + const etl::expected result(etl::move(source)); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(1, result.value().v); + } + + //************************************************************************* + TEST(test_move_constructor_from_expected_with_convertible_types_carries_error) + { + etl::expected source(etl::unexpected(Error("failed"))); + + const etl::expected result(etl::move(source)); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("failed", result.error().e); + } + + //************************************************************************* + TEST(test_constructor_from_value_does_not_require_a_move) + { + const etl::expected result(1); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(1, result.value().v); + } + + //************************************************************************* + TEST(test_error_type_constructible_from_expected_is_not_converted) + { + using Source = etl::expected; + + CHECK_FALSE((etl::is_constructible, const Source&>::value)); + CHECK_FALSE((etl::is_constructible, Source&&>::value)); + + CHECK_TRUE((etl::is_constructible, const Source&>::value)); + } + + //************************************************************************* + TEST(test_assign_from_value_convertible_to_value_type) + { + Expected result; + + result = std::string("converted"); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL("converted", result.value().v); + } + //************************************************************************* + TEST(test_conversion_into_an_immobile_value_constructs_in_place) + { + ImmobileValue::destructions = 0; + + { + const etl::expected source(7); + + const etl::expected result(source); + + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(7, result.value().v); + } + + CHECK_EQUAL(1, ImmobileValue::destructions); + } + + //************************************************************************* + TEST(test_conversion_into_an_immobile_error_constructs_in_place) + { + ImmobileError::destructions = 0; + + { + const etl::expected source(etl::unexpected(9)); + + const etl::expected result(source); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL(9, result.error().e); + } + + CHECK_EQUAL(1, ImmobileError::destructions); + } } } // namespace diff --git a/test/test_flat_map.cpp b/test/test_flat_map.cpp index c88bedd9..431c8980 100644 --- a/test/test_flat_map.cpp +++ b/test/test_flat_map.cpp @@ -211,6 +211,8 @@ namespace // return os; //} + static_assert(etl::is_same, etl::iflat_map>>::value, "default compare is not less"); + SUITE(test_flat_map) { NDC N0 = NDC("A"); @@ -1544,6 +1546,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_flat_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_multimap.cpp b/test/test_flat_multimap.cpp index 41962cc2..01c1eba8 100644 --- a/test/test_flat_multimap.cpp +++ b/test/test_flat_multimap.cpp @@ -239,6 +239,8 @@ namespace return true; } + static_assert(etl::is_same, etl::iflat_multimap>>::value, "default compare is not less"); + SUITE(test_flat_multimap) { //************************************************************************* @@ -1300,6 +1302,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_flat_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_multiset.cpp b/test/test_flat_multiset.cpp index 694bb802..a0625d1a 100644 --- a/test/test_flat_multiset.cpp +++ b/test/test_flat_multiset.cpp @@ -241,6 +241,8 @@ namespace std::vector int_data; + static_assert(etl::is_same, etl::iflat_multiset>>::value, "default compare is not less"); + //************************************************************************* struct SetupFixture { @@ -1293,6 +1295,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_flat_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_flat_set.cpp b/test/test_flat_set.cpp index 1355f8c0..f90fde74 100644 --- a/test/test_flat_set.cpp +++ b/test/test_flat_set.cpp @@ -261,6 +261,8 @@ namespace return (lhs.value < rhs.k.value); } + static_assert(etl::is_same, etl::iflat_set>>::value, "default compare is not less"); + //************************************************************************* struct SetupFixture { @@ -1292,6 +1294,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_flat_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_flat_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_format.cpp b/test/test_format.cpp index 6fd2cbf4..bfd4c848 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -218,10 +218,9 @@ namespace { etl::string<100> s; - // mapped to unsigned char - // CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); - // mapped to signed char - // CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); + // int8_t/uint8_t are formatted as integers (issue #1549), not as characters + CHECK_EQUAL("34", test_format(s, "{}", static_cast(34))); + CHECK_EQUAL("-14", test_format(s, "{}", static_cast(-14))); CHECK_EQUAL("6534", test_format(s, "{}", static_cast(6534))); CHECK_EQUAL("-9414", test_format(s, "{}", static_cast(-9414))); CHECK_EQUAL("236534", test_format(s, "{}", static_cast(236534))); @@ -619,45 +618,35 @@ namespace //************************************************************************* TEST(test_format_signed_char) { + // signed char (typically int8_t) is formatted as an integer by default + // (issue #1549), matching std::format. Only {:c} converts it to a character. + // '?' (debug-escape) is NOT a valid presentation type for signed char. etl::string<100> s; - CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast('s'))); CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); - CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); - CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); - CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); - CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); - CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); - CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); - CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + CHECK_EQUAL("a -12 b", test_format(s, "a {} b", static_cast(-12))); } //************************************************************************* TEST(test_format_unsigned_char) { + // unsigned char (typically uint8_t) is formatted as an integer by default + // (issue #1549), matching std::format. Only {:c} converts it to a character. + // '?' (debug-escape) is NOT a valid presentation type for unsigned char. etl::string<100> s; - CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast('s'))); + CHECK_EQUAL("a 115 b", test_format(s, "a {} b", static_cast('s'))); CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast('s'))); - CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast('s'))); - CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast('\t'))); - CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast('\t'))); - CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast('\n'))); - CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast('\r'))); - CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast('"'))); - CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast('\''))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); - CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast('\\'))); CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast('a'))); CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast('a'))); CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast('a'))); + CHECK_EQUAL("a 65 b", test_format(s, "a {} b", static_cast(65))); } //************************************************************************* @@ -961,6 +950,29 @@ namespace CHECK_EQUAL("010", test_format(s, "{:#o}", 8)); } + //************************************************************************* + // Arguments whose count is only known at runtime cannot be held in a + // format_arg_store, so basic_format_args can also be built + // directly over caller owned storage. + TEST(test_vformat_to_runtime_sized_args) + { + etl::string<100> s; + + using iterator_t = etl::back_insert_iterator>; + using arg_t = etl::format_arg; + + etl::array storage = {arg_t(1), arg_t("two"), arg_t(3), arg_t('x')}; + + // Only the first three of the four slots are used. + etl::vformat_to(etl::back_inserter(s), "{} {} {}", etl::format_args(etl::span(storage.data(), 3U))); + CHECK_EQUAL("1 two 3", s); + + // An empty argument list is also valid. + s.clear(); + etl::vformat_to(etl::back_inserter(s), "no args", etl::format_args(etl::span())); + CHECK_EQUAL("no args", s); + } + //************************************************************************* TEST(test_format_custom_type) { diff --git a/test/test_forward_list.cpp b/test/test_forward_list.cpp index 3f805b7f..65d9eb81 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -1445,6 +1445,141 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_forward_list_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_forward_list(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + TEST(test_make_forward_list_common_type_from_mixed_types) + { + auto data = etl::make_forward_list(1, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(1L, *itr++); + CHECK_EQUAL(2L, *itr++); + CHECK_EQUAL(3L, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + struct CopyMoveCounted + { + explicit CopyMoveCounted(int value_) + : value(value_) + { + } + + CopyMoveCounted(const CopyMoveCounted& other) + : value(other.value) + { + ++copy_count; + } + + CopyMoveCounted(CopyMoveCounted&& other) + : value(other.value) + { + ++move_count; + } + + int value; + + static size_t copy_count; + static size_t move_count; + }; + + size_t CopyMoveCounted::copy_count = 0; + size_t CopyMoveCounted::move_count = 0; + + // The arguments must be forwarded into the initializer list, not taken by + // value: a by-value parameter pack would cost an extra move per argument + // (the defect fixed for make_list). Each element is then copied from the + // initializer_list backing array into its node, which is why lvalue + // arguments count two copies. + TEST(test_make_forward_list_argument_copy_and_move_counts) + { + const CopyMoveCounted const_lvalue(1); + CopyMoveCounted mutable_lvalue(2); + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data = etl::make_forward_list(const_lvalue, mutable_lvalue); + + CHECK_EQUAL(4U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + // The exact move counts rely on guaranteed copy elision. Before C++17 the + // returned forward_list may be move-constructed once more, which moves each + // element, so only the copy counts are checked there. + CHECK_EQUAL(0U, CopyMoveCounted::move_count); + #endif + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data2 = etl::make_forward_list(CopyMoveCounted(3)); + + CHECK_EQUAL(1U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + CHECK_EQUAL(1U, CopyMoveCounted::move_count); + #endif + + auto itr = data.begin(); + CHECK_EQUAL(1, (*itr++).value); + CHECK_EQUAL(2, (*itr).value); + CHECK_EQUAL(3, data2.begin()->value); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_forward_list_explicit_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_forward_list(static_const_lvalue, local_const_lvalue, mutable_lvalue, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + CHECK_EQUAL(45, *itr++); + CHECK_EQUAL(46, *itr++); + } +#endif + //************************************************************************* TEST(test_forward_list_exception_types) { diff --git a/test/test_histogram.cpp b/test/test_histogram.cpp index 40f5de20..b0db0263 100644 --- a/test/test_histogram.cpp +++ b/test/test_histogram.cpp @@ -270,7 +270,7 @@ namespace { IntRuntimeOffsetHistogram histogram1(Start, input2.begin(), input2.end()); - IntRuntimeOffsetHistogram histogram2(Start); + IntRuntimeOffsetHistogram histogram2(Start + 1); histogram2 = histogram1; for (int key = Start; key < Start + static_cast(Size); ++key) @@ -300,7 +300,7 @@ namespace IntRuntimeOffsetHistogram histogram1(Start, input2.begin(), input2.end()); IntRuntimeOffsetHistogram histogram_source(Start, input2.begin(), input2.end()); - IntRuntimeOffsetHistogram histogram2(Start); + IntRuntimeOffsetHistogram histogram2(Start + 1); histogram2 = etl::move(histogram_source); for (int key = Start; key < Start + static_cast(Size); ++key) diff --git a/test/test_indirect_vector.cpp b/test/test_indirect_vector.cpp index f0a5d54e..f9973441 100644 --- a/test/test_indirect_vector.cpp +++ b/test/test_indirect_vector.cpp @@ -1621,5 +1621,51 @@ namespace CHECK(std::equal(blank_data.begin(), blank_data.end(), data.begin())); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_indirect_vector_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_indirect_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = typename std::remove_reference::type; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_indirect_vector_explicit_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_indirect_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + CHECK_EQUAL(45, data[3]); + CHECK_EQUAL(46, data[4]); + } +#endif } } // namespace diff --git a/test/test_iterator.cpp b/test/test_iterator.cpp index db018c40..ed808582 100644 --- a/test/test_iterator.cpp +++ b/test/test_iterator.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #include #include @@ -1420,6 +1421,55 @@ namespace CHECK_EQUAL(1, *result); } + //************************************************************************* + // etl::ranges::distance(first, last) + //************************************************************************* + TEST(ranges_distance_iterator_sentinel) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(&data[0], &data[10])); + CHECK_EQUAL(4, etl::ranges::distance(&data[3], &data[7])); + CHECK_EQUAL(0, etl::ranges::distance(&data[3], &data[3])); + + non_random_iterator first(&data[0]); + non_random_sentinel last(&data[6]); + CHECK_EQUAL(6, etl::ranges::distance(first, last)); + } + + //************************************************************************* + // etl::ranges::distance(range) + //************************************************************************* + TEST(ranges_distance_range_with_size_member) + { + std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + + const std::vector& cdata = data; + CHECK_EQUAL(10, etl::ranges::distance(cdata)); + + std::vector empty_data; + CHECK_EQUAL(0, etl::ranges::distance(empty_data)); + } + + //************************************************************************* + TEST(ranges_distance_c_array) + { + int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + CHECK_EQUAL(10, etl::ranges::distance(data)); + } + + //************************************************************************* + TEST(ranges_distance_range_without_size_member) + { + std::forward_list data = {0, 1, 2, 3, 4}; + + CHECK_EQUAL(5, etl::ranges::distance(data)); + CHECK_EQUAL(etl::ranges::distance(data.begin(), data.end()), etl::ranges::distance(data)); + } + TEST(test_is_range) { std::vector vec; diff --git a/test/test_list.cpp b/test/test_list.cpp index 00574f24..45e0d276 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -2272,5 +2272,134 @@ namespace CHECK_EQUAL(ItemNDC("F"), *itr++); } #endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Const-qualified lvalue arguments must be accepted and must not affect the + // deduced element type (the deduced reference types decay via common_type). + TEST(test_make_list_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_list(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + TEST(test_make_list_common_type_from_mixed_types) + { + auto data = etl::make_list(1, 2L, 3); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(1L, *itr++); + CHECK_EQUAL(2L, *itr++); + CHECK_EQUAL(3L, *itr++); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + struct CopyMoveCounted + { + explicit CopyMoveCounted(int value_) + : value(value_) + { + } + + CopyMoveCounted(const CopyMoveCounted& other) + : value(other.value) + { + ++copy_count; + } + + CopyMoveCounted(CopyMoveCounted&& other) + : value(other.value) + { + ++move_count; + } + + int value; + + static size_t copy_count; + static size_t move_count; + }; + + size_t CopyMoveCounted::copy_count = 0; + size_t CopyMoveCounted::move_count = 0; + + // The arguments must be forwarded into the initializer list, not taken by + // value: a by-value parameter pack costs an extra move per argument. + // Each element is then copied from the initializer_list backing array into + // its list node, which is why lvalue arguments count two copies. + TEST(test_make_list_argument_copy_and_move_counts) + { + const CopyMoveCounted const_lvalue(1); + CopyMoveCounted mutable_lvalue(2); + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data = etl::make_list(const_lvalue, mutable_lvalue); + + CHECK_EQUAL(4U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + // The exact move counts rely on guaranteed copy elision. Before C++17 the + // returned list may be move-constructed once more, which moves each element, + // so only the copy counts are checked there. + CHECK_EQUAL(0U, CopyMoveCounted::move_count); + #endif + + CopyMoveCounted::copy_count = 0; + CopyMoveCounted::move_count = 0; + + auto data2 = etl::make_list(CopyMoveCounted(3)); + + CHECK_EQUAL(1U, CopyMoveCounted::copy_count); + #if ETL_USING_CPP17 + CHECK_EQUAL(1U, CopyMoveCounted::move_count); + #endif + + auto itr = data.begin(); + CHECK_EQUAL(1, (*itr++).value); + CHECK_EQUAL(2, (*itr).value); + CHECK_EQUAL(3, data2.begin()->value); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_list_explicit_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_list(static_const_lvalue, local_const_lvalue, mutable_lvalue, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + auto itr = data.begin(); + CHECK_EQUAL(42, *itr++); + CHECK_EQUAL(43, *itr++); + CHECK_EQUAL(44, *itr++); + CHECK_EQUAL(45, *itr++); + CHECK_EQUAL(46, *itr++); + } +#endif } } // namespace diff --git a/test/test_map.cpp b/test/test_map.cpp index c57b5b5e..9ce01897 100644 --- a/test/test_map.cpp +++ b/test/test_map.cpp @@ -1765,6 +1765,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 3c672e42..05171bb0 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -556,7 +556,28 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned) + TEST(test_absolute_unsigned_int8_t) + { + // Edge cases for int8_t : min is -128, which has no positive int8_t + // counterpart, but is representable in uint8_t (128). + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(int8_t(0))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(127))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(-127))); + CHECK_EQUAL(uint8_t(128), etl::absolute_unsigned(int8_t(-128))); // INT8_MIN edge case + + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint8_t) + { + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int16_t) { CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(int16_t(0))); CHECK_EQUAL(uint16_t(32767), etl::absolute_unsigned(int16_t(32767))); @@ -568,23 +589,72 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned_constexpr) + TEST(test_absolute_unsigned_uint16_t) { - constexpr uint16_t absolute1 = etl::absolute(int16_t(0)); - constexpr uint16_t absolute2 = etl::absolute(int16_t(32767)); - constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767)); - // constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // - // Compile error - - CHECK_EQUAL(uint16_t(0), absolute1); - CHECK_EQUAL(uint16_t(32767), absolute2); - CHECK_EQUAL(uint16_t(32767), absolute3); - - constexpr uint16_t absolute5 = etl::absolute(uint16_t(0)); - constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535)); - - CHECK_EQUAL(uint16_t(0), absolute5); - CHECK_EQUAL(uint16_t(65535), absolute6); + CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(uint16_t(0))); + CHECK_EQUAL(uint16_t(65535), etl::absolute_unsigned(uint16_t(65535))); } + + //************************************************************************* + TEST(test_absolute_unsigned_int32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(int32_t(0))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(2147483647))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(-2147483647))); + CHECK_EQUAL(uint32_t(2147483648u), etl::absolute_unsigned(int32_t(-2147483647 - 1))); // INT32_MIN edge case + + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(int64_t(0))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(-9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775808ULL), etl::absolute_unsigned(int64_t(-9223372036854775807LL - 1))); // INT64_MIN edge case + + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_absolute_unsigned_constexpr14) + { + // Unlike etl::absolute, absolute_unsigned can represent INT_MIN safely, + // so this should be usable at compile time even for the minimum value. + constexpr uint16_t r1 = etl::absolute_unsigned(int16_t(0)); + constexpr uint16_t r2 = etl::absolute_unsigned(int16_t(32767)); + constexpr uint16_t r3 = etl::absolute_unsigned(int16_t(-32767)); + constexpr uint16_t r4 = etl::absolute_unsigned(int16_t(-32768)); // No compile error, unlike etl::absolute + + CHECK_EQUAL(uint16_t(0), r1); + CHECK_EQUAL(uint16_t(32767), r2); + CHECK_EQUAL(uint16_t(32767), r3); + CHECK_EQUAL(uint16_t(32768), r4); + + constexpr uint16_t r5 = etl::absolute_unsigned(uint16_t(0)); + constexpr uint16_t r6 = etl::absolute_unsigned(uint16_t(65535)); + + CHECK_EQUAL(uint16_t(0), r5); + CHECK_EQUAL(uint16_t(65535), r6); + } +#endif } } // namespace diff --git a/test/test_memory.cpp b/test/test_memory.cpp index 70f03493..a8fda0fa 100644 --- a/test/test_memory.cpp +++ b/test/test_memory.cpp @@ -138,6 +138,33 @@ namespace } }; +#if ETL_USING_CPP11 + // A trivially copyable type that can be copy constructed but not copy + // assigned. It must use the placement-new uninitialized algorithm path. + struct CopyConstructOnly + { + int value; + + CopyConstructOnly() = default; + + CopyConstructOnly(const CopyConstructOnly&) = default; + CopyConstructOnly& operator=(const CopyConstructOnly&) = delete; + }; + + // A trivially copyable type that can be move constructed but not move + // assigned. It must use the placement-new uninitialized algorithm path. + struct MoveConstructOnly + { + int value; + + MoveConstructOnly() = default; + + MoveConstructOnly(const MoveConstructOnly&) = delete; + MoveConstructOnly(MoveConstructOnly&&) = default; + MoveConstructOnly& operator=(MoveConstructOnly&&) = delete; + }; +#endif + //*********************************** template struct NoDelete @@ -546,6 +573,59 @@ namespace CHECK_EQUAL(0U, count); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_uninitialized_algorithms_use_construction_when_assignment_is_deleted) + { + CopyConstructOnly copy_source[2]; + copy_source[0].value = 1; + copy_source[1].value = 2; + + alignas(CopyConstructOnly) unsigned char copy_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* copy_destination = reinterpret_cast(copy_storage); + + etl::uninitialized_copy(copy_source, copy_source + 2, copy_destination); + CHECK_EQUAL(1, copy_destination[0].value); + CHECK_EQUAL(2, copy_destination[1].value); + + alignas(CopyConstructOnly) unsigned char fill_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* fill_destination = reinterpret_cast(fill_storage); + + etl::uninitialized_fill(fill_destination, fill_destination + 2, copy_source[0]); + CHECK_EQUAL(1, fill_destination[0].value); + CHECK_EQUAL(1, fill_destination[1].value); + + alignas(CopyConstructOnly) unsigned char value_storage[sizeof(CopyConstructOnly) * 2U]; + CopyConstructOnly* value_destination = reinterpret_cast(value_storage); + + etl::uninitialized_value_construct(value_destination, value_destination + 2); + CHECK_EQUAL(0, value_destination[0].value); + CHECK_EQUAL(0, value_destination[1].value); + + MoveConstructOnly move_source[2]; + move_source[0].value = 3; + move_source[1].value = 4; + + alignas(MoveConstructOnly) unsigned char move_storage[sizeof(MoveConstructOnly) * 2U]; + MoveConstructOnly* move_destination = reinterpret_cast(move_storage); + + etl::uninitialized_move(move_source, move_source + 2, move_destination); + CHECK_EQUAL(3, move_destination[0].value); + CHECK_EQUAL(4, move_destination[1].value); + + MoveConstructOnly move_n_source[2]; + move_n_source[0].value = 5; + move_n_source[1].value = 6; + + alignas(MoveConstructOnly) unsigned char move_n_storage[sizeof(MoveConstructOnly) * 2U]; + MoveConstructOnly* move_n_destination = reinterpret_cast(move_n_storage); + + etl::uninitialized_move_n(move_n_source, 2U, move_n_destination); + CHECK_EQUAL(5, move_n_destination[0].value); + CHECK_EQUAL(6, move_n_destination[1].value); + } +#endif + //************************************************************************* TEST(test_uninitialized_default_construct_n_trivial) { diff --git a/test/test_message_router_registry.cpp b/test/test_message_router_registry.cpp index 22da80cb..17e27797 100644 --- a/test/test_message_router_registry.cpp +++ b/test/test_message_router_registry.cpp @@ -187,7 +187,7 @@ namespace CHECK_EQUAL(Registry_Size, registry.max_size()); } -#if ETL_USING_STL +#if ETL_HAS_INITIALIZER_LIST //************************************************************************* TEST(test_initializer_list_construction) { diff --git a/test/test_multimap.cpp b/test/test_multimap.cpp index 86f9af0f..3cc897ae 100644 --- a/test/test_multimap.cpp +++ b/test/test_multimap.cpp @@ -103,6 +103,34 @@ namespace return true; } + template + bool Check_Equal(R1 range1, R2 range2) + { + while (range1.first != range1.second) + { + if (range2.first == range2.second) + { + return false; + } + auto& pair1 = *range1.first; + auto& pair2 = *range2.first; + if ((pair1.first != pair2.first) || (pair1.second != pair2.second)) + { + return false; + } + + ++range1.first; + ++range2.first; + } + + return range2.first == range2.second; + } + template + bool Check_ContainsPair(T1 begin, T1 end, const T2& pair) + { + return std::find_if(begin, end, [&pair](typename T1::reference item) { return (item.first == pair.first) && (item.second == pair.second); }) + != end; + } #include "etl/private/diagnostic_null_dereference_push.h" //************************************************************************* @@ -694,13 +722,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("1"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -718,13 +740,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("1"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -739,13 +755,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("2"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -763,13 +773,7 @@ namespace ETL_OR_STD::pair compare_result = compare_data.equal_range("2"); // Check that both return the same return results - CHECK(compare_result.first->first == data_result.first->first); - CHECK(compare_result.first->second == data_result.first->second); - CHECK(compare_result.second->first == data_result.second->first); - CHECK(compare_result.second->second == data_result.second->second); - - bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); - + const bool isEqual = Check_Equal(data_result, compare_result); CHECK(isEqual); } @@ -1022,47 +1026,47 @@ namespace Compare_Data compare_data(initial_data.begin(), initial_data.end()); Data data(initial_data.begin(), initial_data.end()); - Data::iterator i_data = data.find("0"); - Compare_Data::iterator i_compare = compare_data.find("0"); + Data::iterator i_data = data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("1"); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("2"); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("3"); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("."); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find("A"); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1075,46 +1079,46 @@ namespace CMap compare_data(initial_data.begin(), initial_data.end()); EMap::iterator i_data = data.find(Key("0")); - CMap::iterator i_compare = compare_data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("1")); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("2")); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("3")); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key(".")); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find(Key("A")); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1123,47 +1127,47 @@ namespace const Compare_Data compare_data(initial_data.begin(), initial_data.end()); const Data data(initial_data.begin(), initial_data.end()); - Data::const_iterator i_data = data.find("0"); - Compare_Data::const_iterator i_compare = compare_data.find("0"); + Data::const_iterator i_data = data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("1"); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("2"); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("3"); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find("."); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find("A"); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1176,46 +1180,46 @@ namespace const CMap compare_data(initial_data.begin(), initial_data.end()); EMap::const_iterator i_data = data.find(Key("0")); - CMap::const_iterator i_compare = compare_data.find("0"); + auto i_compare = compare_data.equal_range("0"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("1")); - i_compare = compare_data.find("1"); + i_compare = compare_data.equal_range("1"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("2")); - i_compare = compare_data.find("2"); + i_compare = compare_data.equal_range("2"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key("3")); - i_compare = compare_data.find("3"); + i_compare = compare_data.equal_range("3"); // Check that both return successful return results - CHECK(i_compare->first == i_data->first); - CHECK(i_compare->second == i_data->second); + CHECK(i_data != data.end()); + CHECK(Check_ContainsPair(i_compare.first, i_compare.second, *i_data)); i_data = data.find(Key(".")); - i_compare = compare_data.find("."); + i_compare = compare_data.equal_range("."); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); i_data = data.find(Key("A")); - i_compare = compare_data.find("A"); + i_compare = compare_data.equal_range("A"); // Check that both return successful return results - CHECK(data.end() == i_data); - CHECK(compare_data.end() == i_compare); + CHECK(i_data == data.end()); + CHECK(i_compare.first == i_compare.second); } //************************************************************************* @@ -1685,6 +1689,29 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains_with_transparent_comparator) { diff --git a/test/test_multiset.cpp b/test/test_multiset.cpp index fcbae16a..7a31a65f 100644 --- a/test/test_multiset.cpp +++ b/test/test_multiset.cpp @@ -1608,6 +1608,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_numeric.cpp b/test/test_numeric.cpp index f7407fef..8251a27c 100644 --- a/test/test_numeric.cpp +++ b/test/test_numeric.cpp @@ -135,7 +135,6 @@ namespace CHECK_EQUAL(*c, v); } -#if ETL_USING_STL //************************************************************************* TEST(test_midpoint_std_random_access_iterator) { @@ -163,7 +162,6 @@ namespace CHECK_EQUAL(*c, v); } -#endif //************************************************************************* TEST(test_lerp_floating_point) diff --git a/test/test_optional.cpp b/test/test_optional.cpp index e1e05f9c..905de3cb 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -35,6 +35,12 @@ SOFTWARE. #include #include +// GCC can emit false positive -Wmaybe-uninitialized warnings inside the test data +// types when optional's storage is inspected through inlined, never-taken branches. +// The diagnostic is raised late (after inlining), so the suppression has to cover the +// whole translation unit. It is popped at the end of the file. +#include "etl/private/diagnostic_uninitialized_push.h" + #include "data.h" #include "etl/algorithm.h" #include "etl/optional.h" @@ -1603,5 +1609,351 @@ namespace #endif } #endif + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_transform) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional doubled = engaged.transform([](int i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + + etl::optional from_empty = empty.transform([](int i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional doubled = engaged.transform([](int i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + + etl::optional from_empty = empty.transform([](int i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_change_type) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional text = engaged.transform([](int) { return std::string("forty two"); }); + CHECK_TRUE(text.has_value()); + CHECK_EQUAL("forty two", text.value()); + + etl::optional text_from_empty = empty.transform([](int) { return std::string("forty two"); }); + CHECK_FALSE(text_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_non_pod) + { + etl::optional engaged(Data("transform")); + etl::optional empty; + + etl::optional length = engaged.transform([](const Data& d) { return d.value.size(); }); + CHECK_TRUE(length.has_value()); + CHECK_EQUAL(9U, length.value()); + + etl::optional length_from_empty = empty.transform([](const Data& d) { return d.value.size(); }); + CHECK_FALSE(length_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_transform_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional moved = etl::move(engaged).transform([](DataM&& d) { return DataM(etl::move(d)); }); + CHECK_TRUE(moved.has_value()); + CHECK_TRUE(moved.value().valid); + CHECK_EQUAL(42U, moved.value().value); + CHECK_FALSE(engaged.value().valid); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).transform([](DataM&& d) { return DataM(etl::move(d)); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional incremented = engaged.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = empty.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + + etl::optional rejected = engaged.and_then([](int) { return etl::optional(); }); + CHECK_FALSE(rejected.has_value()); + } + + //************************************************************************* + TEST(test_and_then_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional incremented = engaged.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = empty.and_then([](int i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_change_type) + { + etl::optional engaged(42); + etl::optional empty; + + etl::optional text = engaged.and_then([](int) { return etl::optional("forty two"); }); + CHECK_TRUE(text.has_value()); + CHECK_EQUAL("forty two", text.value()); + + etl::optional text_from_empty = empty.and_then([](int) { return etl::optional("forty two"); }); + CHECK_FALSE(text_from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional value = etl::move(engaged).and_then([](DataM&& d) { return etl::optional(d.value); }); + CHECK_TRUE(value.has_value()); + CHECK_EQUAL(42U, value.value()); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).and_then([](DataM&& d) { return etl::optional(d.value); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else) + { + etl::optional engaged(42); + etl::optional empty; + bool called = false; + + etl::optional from_engaged = engaged.or_else( + [&called]() + { + called = true; + return etl::optional(99); + }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + CHECK_FALSE(called); + + etl::optional from_empty = empty.or_else( + [&called]() + { + called = true; + return etl::optional(99); + }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + CHECK_TRUE(called); + + etl::optional still_empty = empty.or_else([]() { return etl::optional(); }); + CHECK_FALSE(still_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_const) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional from_engaged = engaged.or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + etl::optional from_empty = empty.or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } + + //************************************************************************* + TEST(test_or_else_move) + { + etl::optional engaged(DataM(42U)); + + etl::optional from_engaged = etl::move(engaged).or_else([]() { return etl::optional(DataM(99U)); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42U, from_engaged.value().value); + + etl::optional empty; + etl::optional from_empty = etl::move(empty).or_else([]() { return etl::optional(DataM(99U)); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99U, from_empty.value().value); + } + + //************************************************************************* + TEST(test_monadic_chaining) + { + etl::optional engaged(8); + etl::optional empty; + + etl::optional result = engaged.transform([](int i) { return i * 2; }) + .and_then([](int i) { return etl::optional(i + 1); }) + .or_else([]() { return etl::optional(0); }); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(17, result.value()); + + etl::optional result_from_empty = empty.transform([](int i) { return i * 2; }) + .and_then([](int i) { return etl::optional(i + 1); }) + .or_else([]() { return etl::optional(123); }); + CHECK_TRUE(result_from_empty.has_value()); + CHECK_EQUAL(123, result_from_empty.value()); + } + + //************************************************************************* + TEST(test_is_optional) + { + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + CHECK_TRUE((etl::is_optional>::value)); + + CHECK_FALSE((etl::is_optional::value)); + CHECK_FALSE((etl::is_optional::value)); + CHECK_FALSE((etl::is_optional*>::value)); + CHECK_FALSE((etl::is_optional&>::value)); + + #if ETL_USING_CPP17 + CHECK_TRUE((etl::is_optional_v>)); + CHECK_TRUE((etl::is_optional_v>)); + CHECK_FALSE((etl::is_optional_v)); + #endif + } + + //************************************************************************* + TEST(test_transform_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional doubled = etl::move(engaged).transform([](const int&& i) { return i * 2; }); + CHECK_TRUE(doubled.has_value()); + CHECK_EQUAL(84, doubled.value()); + CHECK_TRUE(engaged.has_value()); + CHECK_EQUAL(42, engaged.value()); + + etl::optional from_empty = etl::move(empty).transform([](const int&& i) { return i * 2; }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_and_then_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional incremented = etl::move(engaged).and_then([](const int&& i) { return etl::optional(i + 1); }); + CHECK_TRUE(incremented.has_value()); + CHECK_EQUAL(43, incremented.value()); + + etl::optional from_empty = etl::move(empty).and_then([](const int&& i) { return etl::optional(i + 1); }); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_const_rvalue) + { + const etl::optional engaged(42); + const etl::optional empty; + + etl::optional from_engaged = etl::move(engaged).or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + etl::optional from_empty = etl::move(empty).or_else([]() { return etl::optional(99); }); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } + + //************************************************************************* + /// Unlike std::optional, a callable returning a reference is accepted. + /// The reference is stripped from the result type and the value is copied. + //************************************************************************* + TEST(test_transform_callable_returning_reference) + { + etl::optional engaged(42); + + auto result = engaged.transform([](int& i) -> int& { return i; }); + + CHECK_TRUE((etl::is_same, decltype(result)>::value)); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(42, result.value()); + + // The result is a copy, not an alias of the source. + result.value() = 1; + CHECK_EQUAL(42, engaged.value()); + } + + //************************************************************************* + TEST(test_and_then_callable_returning_reference) + { + etl::optional engaged(42); + etl::optional empty; + + auto f = [](int i) -> etl::optional& + { + static etl::optional shared; + shared = i + 1; + return shared; + }; + + auto result = engaged.and_then(f); + + CHECK_TRUE((etl::is_same, decltype(result)>::value)); + CHECK_TRUE(result.has_value()); + CHECK_EQUAL(43, result.value()); + + auto from_empty = empty.and_then(f); + CHECK_FALSE(from_empty.has_value()); + } + + //************************************************************************* + TEST(test_or_else_callable_returning_reference) + { + etl::optional engaged(42); + etl::optional empty; + + auto f = []() -> etl::optional& + { + static etl::optional shared(99); + return shared; + }; + + auto from_engaged = engaged.or_else(f); + + CHECK_TRUE((etl::is_same, decltype(from_engaged)>::value)); + CHECK_TRUE(from_engaged.has_value()); + CHECK_EQUAL(42, from_engaged.value()); + + auto from_empty = empty.or_else(f); + CHECK_TRUE(from_empty.has_value()); + CHECK_EQUAL(99, from_empty.value()); + } +#endif } } // namespace + +#include "etl/private/diagnostic_pop.h" diff --git a/test/test_ranges.cpp b/test/test_ranges.cpp index efe64148..f7a668fe 100644 --- a/test/test_ranges.cpp +++ b/test/test_ranges.cpp @@ -41,7 +41,7 @@ SOFTWARE. // Issue #1391: include when available to verify etl::operator| // does not conflict with std::ranges::operator|. -#if ETL_USING_STL && ETL_USING_CPP20 && defined(__has_include) +#if ETL_USING_CPP20 && defined(__has_include) #if __has_include() #include #endif @@ -473,6 +473,20 @@ namespace CHECK_EQUAL(iv3.empty(), true); } + TEST(test_ranges_iota_view_const) + { + const auto iv = etl::ranges::iota_view(1, 7); + + int compare = 1; + for (auto i : iv) + { + CHECK_EQUAL(i, compare); + ++compare; + } + + CHECK_EQUAL(iv[4], 5); + } + TEST(test_ranges_views_iota) { auto iv = etl::ranges::views::iota(1, 7); @@ -540,6 +554,18 @@ namespace CHECK_EQUAL(iv3.empty(), true); } + TEST(test_ranges_repeat_view_const) + { + const auto rv = etl::ranges::repeat_view(1, 7); + + for (auto i : rv) + { + CHECK_EQUAL(i, 1); + } + + CHECK_EQUAL(rv[4], 1); + } + TEST(test_ranges_views_repeat) { auto iv = etl::ranges::views::repeat(1, 7); diff --git a/test/test_set.cpp b/test/test_set.cpp index 863325c0..e23ca03a 100644 --- a/test/test_set.cpp +++ b/test/test_set.cpp @@ -1465,6 +1465,34 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST_FIXTURE(SetupFixture, test_make_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + auto v = *data.begin(); + using Type = decltype(v); + CHECK((std::is_same::value)); + + decltype(data)::const_iterator itr = data.begin(); + + CHECK_EQUAL(42, *itr); + ++itr; + CHECK_EQUAL(43, *itr); + ++itr; + CHECK_EQUAL(44, *itr); + } +#endif + //************************************************************************* TEST_FIXTURE(SetupFixture, test_contains) { diff --git a/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index b19a4032..05f56880 100644 --- a/test/test_span_dynamic_extent.cpp +++ b/test/test_span_dynamic_extent.cpp @@ -983,6 +983,51 @@ namespace CHECK((std::is_same_v>)); } + //************************************************************************* + TEST(test_template_deduction_guide_for_std_vector) + { + std::vector data = {1, 2, 3, 4}; + const std::vector data2 = {1, 2, 3, 4}; + + etl::span span = data; + etl::span span2 = data2; + + CHECK_EQUAL(etl::dynamic_extent, span.extent); + CHECK_EQUAL(ETL_OR_STD17::size(data), span.size()); + CHECK_EQUAL(etl::dynamic_extent, span2.extent); + CHECK_EQUAL(ETL_OR_STD17::size(data2), span2.size()); + + CHECK((std::is_same_v>)); + CHECK((std::is_same_v>)); + } + + //************************************************************************* + TEST(test_template_deduction_guide_for_type_with_data_and_size) + { + struct DataAndSize + { + int buffer[4] = {1, 2, 3, 4}; + int* data() + { + return buffer; + } + size_t size() const + { + return 4U; + } + + using value_type = int; + }; + + DataAndSize data; + + etl::span span = data; + + CHECK_EQUAL(etl::dynamic_extent, span.extent); + CHECK_EQUAL(data.size(), span.size()); + CHECK((std::is_same_v>)); + } + //************************************************************************* TEST(test_template_deduction_guide_for_iterators) { diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index a9314dd2..ad234834 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -542,6 +542,50 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST(test_remove_prefix_empty) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n == size() should result in an empty view + view.remove_prefix(original.size()); + CHECK_EQUAL(view.begin(), view.end()); + CHECK(view.empty()); + } + + //************************************************************************* + TEST(test_remove_prefix_assert) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n > size() should trigger ETL_ASSERT + CHECK_THROW(view.remove_prefix(original.size() + 1), etl::string_view_bounds); + } + + //************************************************************************* + TEST(test_remove_suffix_empty) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n == size() should result in an empty view + view.remove_suffix(original.size()); + CHECK_EQUAL(view.begin(), view.end()); + CHECK(view.empty()); + } + + //************************************************************************* + TEST(test_remove_suffix_assert) + { + std::string original = "Hello World"; + View view(original.c_str()); + + // remove n > size() should trigger ETL_ASSERT + CHECK_THROW(view.remove_suffix(original.size() + 1), etl::string_view_bounds); + } + //************************************************************************* TEST(test_copy) { @@ -1218,5 +1262,23 @@ namespace CHECK_EQUAL(std_pos, etl_pos); } } + + //************************************************************************* + TEST(test_npos_type_and_value) + { + typedef etl::string_view::size_type size_type; + + // npos must have the same type as size_type, not an anonymous enum. + CHECK((etl::is_same::value)); + CHECK_EQUAL(etl::integral_limits::max, etl::string_view::npos); + + // npos must compare equal to the equivalent std::string_view constant. + CHECK_EQUAL(std::string::npos, etl::string_view::npos); + + // substr with the default count must return the whole remaining view. + etl::string_view view("Hello World"); + CHECK_EQUAL(std::string("World"), std::string(view.substr(6).data(), view.substr(6).size())); + CHECK_EQUAL(5U, view.substr(6, etl::string_view::npos).size()); + } } } // namespace diff --git a/test/test_to_arithmetic.cpp b/test/test_to_arithmetic.cpp index 1dadabae..f78895b4 100644 --- a/test/test_to_arithmetic.cpp +++ b/test/test_to_arithmetic.cpp @@ -1039,6 +1039,71 @@ namespace CHECK_EQUAL(83, int(etl::to_arithmetic(ETLText(text.c_str(), text.size()), etl::radix::decimal).value())); } + //************************************************************************* + TEST(test_comparison_operators) + { + const Text text(STR("123")); + const Text bad_text(STR("1X3")); + const Text other_bad_text(STR("2X3")); + const Text overflow_text(STR("99999999999999999999")); + + const etl::to_arithmetic_result result = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result same = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result bad_result = etl::to_arithmetic(bad_text.c_str(), bad_text.size()); + const etl::to_arithmetic_result same_bad_result = etl::to_arithmetic(other_bad_text.c_str(), other_bad_text.size()); + const etl::to_arithmetic_result overflow_result = etl::to_arithmetic(overflow_text.c_str(), overflow_text.size()); + + // result <-> result + CHECK_TRUE(result == same); + CHECK_FALSE(result != same); + CHECK_TRUE(result != bad_result); + CHECK_FALSE(result == bad_result); + + // Two errored results with the same status compare equal. + CHECK_EQUAL(bad_result.error(), same_bad_result.error()); + CHECK_TRUE(bad_result == same_bad_result); + CHECK_FALSE(bad_result != same_bad_result); + + // Two errored results with different statuses do not compare equal. + CHECK(bad_result.error() != overflow_result.error()); + CHECK_TRUE(bad_result != overflow_result); + CHECK_FALSE(bad_result == overflow_result); + + // result <-> value + CHECK_TRUE(result == 123); + CHECK_FALSE(result != 123); + CHECK_TRUE(result != 124); + CHECK_FALSE(result == 124); + + // value <-> result + CHECK_TRUE(123 == result); + CHECK_FALSE(123 != result); + CHECK_TRUE(124 != result); + CHECK_FALSE(124 == result); + + // An errored result never compares equal to a value. + CHECK_FALSE(bad_result == 123); + CHECK_TRUE(bad_result != 123); + CHECK_FALSE(123 == bad_result); + CHECK_TRUE(123 != bad_result); + + // Heterogeneous result <-> value, using a different result value type. + const etl::to_arithmetic_result long_result = etl::to_arithmetic(text.c_str(), text.size()); + const etl::to_arithmetic_result small_result = etl::to_arithmetic(text.c_str(), text.size()); + + CHECK_TRUE(long_result == 123); + CHECK_FALSE(long_result != 123); + CHECK_TRUE(123 == long_result); + CHECK_TRUE(long_result != 124); + CHECK_FALSE(123 != long_result); + CHECK_TRUE(124 != long_result); + + CHECK_TRUE(small_result == 123); + CHECK_FALSE(small_result != 123); + CHECK_TRUE(123 == small_result); + CHECK_TRUE(small_result != 124); + } + //************************************************************************* #if ETL_USING_CPP14 TEST(test_constexpr_integral) diff --git a/test/test_tuple.cpp b/test/test_tuple.cpp index d3816d6f..18b10bdf 100644 --- a/test/test_tuple.cpp +++ b/test/test_tuple.cpp @@ -76,6 +76,32 @@ namespace int i; }; + //********************************* + int apply_sum(int a, int b, int c) + { + return a + b + c; + } + + //********************************* + struct Summer + { + int operator()(int a, int b) const + { + return a + b; + } + }; + + //********************************* + struct Accumulator + { + int total; + + void add(int a, int b) + { + total = a + b; + } + }; + SUITE(test_tuple) { //************************************************************************* @@ -930,5 +956,61 @@ namespace CHECK_EQUAL(1, i); CHECK_EQUAL(std::string("2"), d.value); } + + //************************************************************************* + TEST(test_apply_free_function) + { + etl::tuple t(1, 2, 3); + + CHECK_EQUAL(6, etl::apply(apply_sum, t)); + } + + //************************************************************************* + TEST(test_apply_function_object) + { + etl::tuple t(10, 20); + + CHECK_EQUAL(30, etl::apply(Summer(), t)); + } + + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_apply_lambda) + { + etl::tuple t(2, 3, 4); + + int result = etl::apply([](int a, int b, int c) { return a * b * c; }, t); + + CHECK_EQUAL(24, result); + } +#endif + + //************************************************************************* + TEST(test_apply_member_function) + { + Accumulator acc = {0}; + + etl::apply(&Accumulator::add, etl::make_tuple(&acc, 5, 7)); + + CHECK_EQUAL(12, acc.total); + } + + //************************************************************************* + TEST(test_apply_rvalue_tuple) + { + CHECK_EQUAL(6, etl::apply(apply_sum, etl::make_tuple(1, 2, 3))); + } + + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_apply_empty_tuple) + { + etl::tuple<> t; + + int result = etl::apply([]() { return 42; }, t); + + CHECK_EQUAL(42, result); + } +#endif } } // namespace diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 6756d6d9..844b3511 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -150,6 +150,29 @@ namespace NotDefaultConstructible& operator=(NotDefaultConstructible&&) = delete; }; + // The legacy compiler builtin for trivial construction does not test + // accessibility, unlike is_trivially_constructible. + struct PrivateTrivialDefaultConstructor + { + private: + + PrivateTrivialDefaultConstructor() = default; + }; + + // The legacy compiler builtin for trivial destruction does not test + // accessibility or deletion, unlike is_trivially_destructible. + struct PrivateTrivialDestructor + { + private: + + ~PrivateTrivialDestructor() = default; + }; + + struct DeletedTrivialDestructor + { + ~DeletedTrivialDestructor() = delete; + }; + // A function to test etl::type_identity. template T type_identity_test_add(T first, typename etl::type_identity::type second) @@ -421,8 +444,148 @@ struct etl::is_move_assignable : public etl::true_type }; #endif +#if ETL_USING_CPP11 namespace { + //***************************************************************************** + // Types for the is_swappable tests. + struct Swappable + { + int a; + }; + + struct NonSwappable + { + NonSwappable(const NonSwappable&) = delete; + NonSwappable& operator=(const NonSwappable&) = delete; + }; + + // Detects whether the compiler allows an rvalue to bind to a non-const lvalue + // reference. This is a Microsoft extension, disabled by /permissive-, and it makes + // types such as Swappable swappable as rvalues. + template + void takes_lvalue_reference(T&); + + template + struct rvalue_binds_to_lvalue_reference : etl::false_type + { + }; + + template + struct rvalue_binds_to_lvalue_reference(etl::declval()))> > : etl::true_type + { + }; + + // Types for the is_nothrow_swappable tests. + // Swappable, as it is movable, but the move operations may throw. + struct ThrowingSwappable + { + ThrowingSwappable() {} + ThrowingSwappable(ThrowingSwappable&&) noexcept(false) {} + ThrowingSwappable& operator=(ThrowingSwappable&&) noexcept(false) + { + return *this; + } + }; +} // namespace + +namespace adl +{ + // Not movable, so only the swap found by argument dependent lookup makes + // this type swappable. + struct AdlSwappable + { + AdlSwappable(const AdlSwappable&) = delete; + AdlSwappable& operator=(const AdlSwappable&) = delete; + + int a; + }; + + inline void swap(AdlSwappable&, AdlSwappable&) {} + + // As above, but the swap found by argument dependent lookup cannot throw. + struct NothrowAdlSwappable + { + NothrowAdlSwappable(const NothrowAdlSwappable&) = delete; + NothrowAdlSwappable& operator=(const NothrowAdlSwappable&) = delete; + + int a; + }; + + inline void swap(NothrowAdlSwappable&, NothrowAdlSwappable&) noexcept {} + + // Types for the is_swappable_with tests. + struct SwapLhs + { + int a; + }; + + struct SwapRhs + { + int a; + }; + + inline void swap(SwapLhs&, SwapRhs&) {} + inline void swap(SwapRhs&, SwapLhs&) {} + + // Only swappable in one direction, so is_swappable_with is false. + struct SwapOneWay + { + int a; + }; + + inline void swap(SwapLhs&, SwapOneWay&) {} + + // Swappable only as rvalues. + struct RvalueSwappable + { + int a; + }; + + inline void swap(RvalueSwappable&&, RvalueSwappable&&) {} + + // Types for the is_nothrow_swappable_with tests. + // Swappable with each other, in both directions, without throwing. + struct NothrowSwapLhs + { + int a; + }; + + struct NothrowSwapRhs + { + int a; + }; + + inline void swap(NothrowSwapLhs&, NothrowSwapRhs&) noexcept {} + inline void swap(NothrowSwapRhs&, NothrowSwapLhs&) noexcept {} + + // Swappable with NothrowSwapLhs in both directions, but one of the swaps may throw. + struct HalfNothrowSwap + { + int a; + }; + + inline void swap(NothrowSwapLhs&, HalfNothrowSwap&) noexcept {} + inline void swap(HalfNothrowSwap&, NothrowSwapLhs&) {} +} // namespace adl +#endif + +namespace +{ + //************************************************************************* + // std::is_trivial is deprecated in C++26. + // Use the suggested replacement when compiling to that standard. + //************************************************************************* + template + struct std_is_trivial +#if ETL_USING_CPP26 + : std::integral_constant::value && std::is_trivially_default_constructible::value> +#else + : std::integral_constant::value> +#endif + { + }; + SUITE(test_type_traits) { //************************************************************************* @@ -556,6 +719,989 @@ namespace CHECK(etl::is_pointer::value == std::is_pointer::value); } + //************************************************************************* + TEST(test_is_bounded_array) + { + CHECK(etl::is_bounded_array::value); + CHECK(etl::is_bounded_array::value); + CHECK(etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + CHECK(!etl::is_bounded_array::value); + +#if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); + CHECK(etl::is_bounded_array::value == std::is_bounded_array::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_bounded_array_v); + CHECK(!etl::is_bounded_array_v); + CHECK(!etl::is_bounded_array_v); +#endif + } + + //************************************************************************* + TEST(test_is_unbounded_array) + { + CHECK(etl::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + CHECK(!etl::is_unbounded_array::value); + +#if ETL_USING_CPP20 && defined(__cpp_lib_bounded_array_traits) + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); + CHECK(etl::is_unbounded_array::value == std::is_unbounded_array::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_unbounded_array_v); + CHECK(!etl::is_unbounded_array_v); + CHECK(!etl::is_unbounded_array_v); +#endif + } + + //************************************************************************* + TEST(test_is_null_pointer) + { + CHECK(etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + CHECK(!etl::is_null_pointer::value); + + CHECK(etl::is_null_pointer::value); + CHECK(etl::is_null_pointer::value); + CHECK(etl::is_null_pointer::value); + +#if ETL_USING_CPP14 + CHECK(etl::is_null_pointer::value == std::is_null_pointer::value); + CHECK(etl::is_null_pointer::value == std::is_null_pointer::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_null_pointer_v); + CHECK(!etl::is_null_pointer_v); +#endif + } + + //************************************************************************* + TEST(test_is_standard_layout) + { + struct StandardLayout + { + int a; + int b; + }; + + struct NonStandardLayout + { + virtual void foo() {} + int a; + }; + + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value == std::is_standard_layout::value); + CHECK(etl::is_standard_layout::value); + CHECK(!etl::is_standard_layout::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_standard_layout_v); + CHECK(!etl::is_standard_layout_v); +#endif + } + + //************************************************************************* + TEST(test_is_trivial) + { + struct Trivial + { + int a; + int b; + }; + + struct NonTrivialConstructor + { + NonTrivialConstructor() + : a(1) + { + } + int a; + }; + + struct NonTrivialCopy + { + NonTrivialCopy(const NonTrivialCopy&) {} + int a; + }; + + struct NonTrivialDestructor + { + ~NonTrivialDestructor() {} + int a; + }; + + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + CHECK(etl::is_trivial::value == std_is_trivial::value); + + CHECK(etl::is_trivial::value); + CHECK(etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + CHECK(!etl::is_trivial::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_trivial_v); + CHECK(!etl::is_trivial_v); +#endif + } + + //************************************************************************* +#if ETL_USING_BUILTIN_IS_LAYOUT_COMPATIBLE || (ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible)) + TEST(test_is_layout_compatible) + { + struct A + { + int a; + }; + + struct B + { + const int b; + }; + + struct C + { + int c1; + int c2; + }; + + struct D + { + char d; + }; + + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + CHECK((!etl::is_layout_compatible::value)); + + #if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_layout_compatible) + CHECK((etl::is_layout_compatible::value == std::is_layout_compatible::value)); + CHECK((etl::is_layout_compatible::value == std::is_layout_compatible::value)); + #endif + + #if ETL_USING_CPP17 + CHECK((etl::is_layout_compatible_v)); + CHECK((!etl::is_layout_compatible_v)); + #endif + } +#endif + + //************************************************************************* +#if ETL_USING_BUILTIN_IS_POINTER_INTERCONVERTIBLE_BASE_OF || (ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible)) + TEST(test_is_pointer_interconvertible_base_of) + { + struct Base + { + int b; + }; + + struct Derived : public Base + { + }; + + struct VirtualDerived : virtual public Base + { + }; + + struct Polymorphic + { + virtual ~Polymorphic() {} + + int p; + }; + + struct PolymorphicDerived : public Polymorphic + { + }; + + struct Unrelated + { + int u; + }; + + CHECK((etl::is_pointer_interconvertible_base_of::value)); + CHECK((etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + CHECK((!etl::is_pointer_interconvertible_base_of::value)); + + #if ETL_USING_STL && ETL_USING_CPP20 && defined(__cpp_lib_is_pointer_interconvertible) + CHECK((etl::is_pointer_interconvertible_base_of::value == std::is_pointer_interconvertible_base_of::value)); + CHECK((etl::is_pointer_interconvertible_base_of::value + == std::is_pointer_interconvertible_base_of::value)); + #endif + + #if ETL_USING_CPP17 + CHECK((etl::is_pointer_interconvertible_base_of_v)); + CHECK((!etl::is_pointer_interconvertible_base_of_v)); + #endif + } +#endif + + //************************************************************************* + TEST(test_is_destructible) + { +#if ETL_USING_CPP11 || ETL_USING_BUILTIN_IS_DESTRUCTIBLE + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(!etl::is_destructible::value); +#endif + +#if ETL_USING_CPP11 + struct Destructible + { + int a; + }; + + struct NonDestructible + { + ~NonDestructible() = delete; + }; + + struct ProtectedDestructor + { + protected: + + ~ProtectedDestructor() {} + }; + + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + CHECK(etl::is_destructible::value == std::is_destructible::value); + + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); + CHECK(!etl::is_destructible::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_destructible_v); + CHECK(!etl::is_destructible_v); +#endif + } + + //************************************************************************* + TEST(test_is_nothrow_destructible) + { +#if ETL_USING_CPP11 || ETL_USING_BUILTIN_IS_NOTHROW_DESTRUCTIBLE + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); +#endif + +#if ETL_USING_CPP11 + struct NothrowDestructible + { + int a; + }; + + struct ThrowingDestructible + { + ~ThrowingDestructible() noexcept(false) {} + }; + + struct NotDestructible + { + ~NotDestructible() = delete; + }; + + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value == std::is_nothrow_destructible::value); + + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); + CHECK(!etl::is_nothrow_destructible::value); +#endif + +#if ETL_USING_CPP17 + CHECK(etl::is_nothrow_destructible_v); + CHECK(!etl::is_nothrow_destructible_v); +#endif + } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_is_swappable) + { + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + + // Not movable, so swappable only via the swap found by ADL. + CHECK(etl::is_swappable::value); + CHECK(etl::is_swappable::value); + + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + CHECK(!etl::is_swappable::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + CHECK(etl::is_swappable::value == std::is_swappable::value); + + CHECK(etl::is_swappable_v); + CHECK(!etl::is_swappable_v); + #endif + } + + //************************************************************************* + TEST(test_is_swappable_with) + { + // Note that is_swappable_with does not add references, so the value + // categories of the arguments are those of T and U. + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + + // Heterogeneous swaps, found by argument dependent lookup. + CHECK((etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value)); + + // Swappable in one direction only. + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + + // Swappable as rvalues, via the swap found by ADL. + CHECK((etl::is_swappable_with::value)); + + // Rvalues do not bind to the lvalue references taken by swap, unless the + // compiler allows it as an extension. + CHECK((!etl::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == rvalue_binds_to_lvalue_reference::value)); + + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + CHECK((!etl::is_swappable_with::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value == std::is_swappable_with::value)); + CHECK((etl::is_swappable_with::value + == std::is_swappable_with::value)); + + CHECK((etl::is_swappable_with_v)); + CHECK((!etl::is_swappable_with_v)); + #endif + } + + //************************************************************************* + TEST(test_is_nothrow_swappable) + { + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + + // Not movable, so swappable only via the swap found by ADL, which is noexcept. + CHECK(etl::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value); + + // Swappable, but the swap may throw. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + // Swappable via the swap found by ADL, which is not noexcept. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + // Not swappable at all. + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + CHECK(!etl::is_nothrow_swappable::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + CHECK(etl::is_nothrow_swappable::value == std::is_nothrow_swappable::value); + + CHECK(etl::is_nothrow_swappable_v); + CHECK(!etl::is_nothrow_swappable_v); + #endif + } + + //************************************************************************* + TEST(test_is_nothrow_swappable_with) + { + CHECK((etl::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value)); + + // Swappable with each other, in both directions, without throwing. + CHECK((etl::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value)); + + // Swappable in both directions, but one of the swaps may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Swappable with each other, but the swaps may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Swappable, but the move operations may throw. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Rvalues do not bind to the lvalue references taken by swap. + CHECK((!etl::is_nothrow_swappable_with::value)); + + // Not swappable at all. + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + CHECK((!etl::is_nothrow_swappable_with::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK( + (etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + CHECK( + (etl::is_nothrow_swappable_with::value == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + CHECK((etl::is_nothrow_swappable_with::value + == std::is_nothrow_swappable_with::value)); + + CHECK((etl::is_nothrow_swappable_with_v)); + CHECK((!etl::is_nothrow_swappable_with_v)); + #endif + } +#endif + + //************************************************************************* + TEST(test_is_union) + { + union Union + { + int a; + float b; + }; + + struct NonUnion + { + int a; + int b; + }; + + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value == std::is_union::value); + CHECK(etl::is_union::value); + CHECK(!etl::is_union::value); + CHECK(!etl::is_union::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_union_v); + CHECK(!etl::is_union_v); +#endif + } + + //************************************************************************* + TEST(test_is_empty) + { + struct Empty + { + }; + + struct EmptyDerived : public Empty + { + }; + + struct NotEmpty + { + int a; + }; + + struct Virtual + { + virtual void foo() {} + }; + + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + CHECK(etl::is_empty::value == std::is_empty::value); + + CHECK(etl::is_empty::value); + CHECK(etl::is_empty::value); + CHECK(!etl::is_empty::value); + CHECK(!etl::is_empty::value); + CHECK(!etl::is_empty::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_empty_v); + CHECK(!etl::is_empty_v); +#endif + } + + //************************************************************************* + TEST(test_is_polymorphic) + { + struct NonPolymorphic + { + int a; + }; + + struct Polymorphic + { + virtual ~Polymorphic() {} + }; + + struct PolymorphicDerived : public Polymorphic + { + }; + + struct AbstractPolymorphic + { + virtual void foo() = 0; + }; + + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + CHECK(etl::is_polymorphic::value == std::is_polymorphic::value); + + CHECK(etl::is_polymorphic::value); + CHECK(etl::is_polymorphic::value); + CHECK(etl::is_polymorphic::value); + CHECK(!etl::is_polymorphic::value); + CHECK(!etl::is_polymorphic::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_polymorphic_v); + CHECK(!etl::is_polymorphic_v); +#endif + } + + //************************************************************************* + TEST(test_is_abstract) + { + struct NonAbstract + { + int a; + }; + + struct NonAbstractPolymorphic + { + virtual ~NonAbstractPolymorphic() {} + }; + + struct Abstract + { + virtual ~Abstract() {} + virtual void foo() = 0; + }; + + struct AbstractDerived : public Abstract + { + }; + + struct ConcreteDerived : public Abstract + { + void foo() ETL_OVERRIDE {} + }; + + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + CHECK(etl::is_abstract::value == std::is_abstract::value); + + CHECK(etl::is_abstract::value); + CHECK(etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + CHECK(!etl::is_abstract::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_abstract_v); + CHECK(!etl::is_abstract_v); +#endif + } + + //************************************************************************* +#if ETL_USING_CPP14 + TEST(test_is_final) + { + struct NonFinal + { + int a; + }; + + struct Final final + { + int a; + }; + + struct Base + { + virtual ~Base() {} + }; + + struct FinalDerived final : public Base + { + }; + + struct NonFinalDerived : public Base + { + }; + + union FinalUnion final + { + int a; + }; + + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + CHECK(etl::is_final::value == std::is_final::value); + + CHECK(etl::is_final::value); + CHECK(etl::is_final::value); + CHECK(etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + CHECK(!etl::is_final::value); + + #if ETL_USING_CPP17 + CHECK(etl::is_final_v); + CHECK(!etl::is_final_v); + #endif + } +#endif + + //************************************************************************* +#if ETL_USING_CPP17 + TEST(test_is_aggregate) + { + struct Aggregate + { + int a; + double b; + }; + + struct AggregateDerived : public Aggregate + { + int c; + }; + + struct NonAggregate + { + NonAggregate(int a_) + : a(a_) + { + } + int a; + }; + + struct Virtual + { + virtual ~Virtual() {} + int a; + }; + + union AggregateUnion + { + int a; + double b; + }; + + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + CHECK(etl::is_aggregate::value == std::is_aggregate::value); + + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + CHECK(!etl::is_aggregate::value); + + CHECK(etl::is_aggregate_v); + CHECK(!etl::is_aggregate_v); + } +#endif + + //************************************************************************* + TEST(test_has_virtual_destructor) + { + struct NonVirtualDestructor + { + ~NonVirtualDestructor() {} + int a; + }; + + struct VirtualDestructor + { + virtual ~VirtualDestructor() {} + int a; + }; + + struct DerivedFromVirtualDestructor : public VirtualDestructor + { + int b; + }; + + struct VirtualFunctionOnly + { + virtual void foo() {} + int a; + }; + + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value == std::has_virtual_destructor::value); + + CHECK(etl::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value); + CHECK(etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + CHECK(!etl::has_virtual_destructor::value); + +#if ETL_USING_CPP17 + CHECK(etl::has_virtual_destructor_v); + CHECK(!etl::has_virtual_destructor_v); +#endif + } + + //************************************************************************* +#if ETL_USING_CPP17 + TEST(test_has_unique_object_representations) + { + struct NoPadding + { + int a; + int b; + }; + + struct Padded + { + char a; + int b; + }; + + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value == std::has_unique_object_representations::value); + + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(etl::has_unique_object_representations::value); + CHECK(!etl::has_unique_object_representations::value); + + CHECK(etl::has_unique_object_representations_v); + CHECK(!etl::has_unique_object_representations_v); + } +#endif + + //************************************************************************* + TEST(test_is_scalar) + { + struct Class + { + int a; + void foo() {} + }; + + union Union + { + int a; + float b; + }; + + enum Enum + { + e1 + }; + + // Arithmetic types. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Enumerations. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Pointers. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Pointers to members. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Null pointer. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + // Non-scalars. + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + CHECK(etl::is_scalar::value == std::is_scalar::value); + + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + CHECK(!etl::is_scalar::value); + +#if ETL_USING_CPP17 + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(etl::is_scalar_v); + CHECK(!etl::is_scalar_v); + CHECK(!etl::is_scalar_v); +#endif + } + //************************************************************************* TEST(test_is_reference) { @@ -759,6 +1905,7 @@ namespace CHECK(etl::is_fundamental::value == std::is_fundamental::value); CHECK(etl::is_fundamental::value == std::is_fundamental::value); #if ETL_USING_STL + // etl::is_fundamental does not report std::nullptr_t as fundamental when not using the STL. CHECK(etl::is_fundamental::value == std::is_fundamental::value); #endif } @@ -1352,6 +2499,33 @@ namespace } #undef CHECK_EQUAL_FOR_TYPE + //************************************************************************* + TEST(test_is_scoped_enum) + { + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + + CHECK_TRUE((etl::is_scoped_enum::value)); + CHECK_TRUE((etl::is_scoped_enum::value)); + CHECK_FALSE((etl::is_scoped_enum::value)); + +#if ETL_USING_CPP17 + CHECK_FALSE((etl::is_scoped_enum_v)); + CHECK_TRUE((etl::is_scoped_enum_v)); +#endif + +#if ETL_USING_CPP23 && defined(__cpp_lib_is_scoped_enum) + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); + CHECK_EQUAL((std::is_scoped_enum::value), (etl::is_scoped_enum::value)); +#endif + } + //************************************************************************* TEST(test_integral_constants) { @@ -1700,7 +2874,8 @@ namespace //************************************************************************* TEST(test_is_trivially_constructible) { -#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) +#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) || ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE \ + || ETL_USING_BUILTIN_HAS_TRIVIAL_CONSTRUCTOR #if ETL_USING_CPP17 CHECK((etl::is_trivially_constructible_v) == (std::is_trivially_constructible_v)); CHECK((etl::is_trivially_constructible_v) == (std::is_trivially_constructible_v)); @@ -1710,6 +2885,11 @@ namespace CHECK((etl::is_trivially_constructible::value) == (std::is_trivially_constructible::value)); CHECK((etl::is_trivially_constructible::value) == (std::is_trivially_constructible::value)); #endif + #if ETL_USING_CPP11 + CHECK(!etl::is_trivially_constructible::value); + CHECK((etl::is_trivially_constructible::value) + == (std::is_trivially_constructible::value)); + #endif #endif } @@ -1729,10 +2909,47 @@ namespace #endif } + //************************************************************************* + TEST(test_is_trivially_default_constructible) + { +#if (!(defined(ETL_COMPILER_GCC) && defined(ETL_USE_TYPE_TRAITS_BUILTINS))) + #if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + CHECK((etl::is_trivially_default_constructible::value) == (std::is_trivially_default_constructible::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_default_constructible_v) == (std::is_trivially_default_constructible_v)); + CHECK((etl::is_trivially_default_constructible_v) == (std::is_trivially_default_constructible_v)); + #endif + #endif +#endif + } + + //************************************************************************* + TEST(test_is_trivially_move_constructible) + { +#if (!(defined(ETL_COMPILER_GCC) && defined(ETL_USE_TYPE_TRAITS_BUILTINS))) + #if ETL_USING_CPP11 && (ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS)) + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + CHECK((etl::is_trivially_move_constructible::value) == (std::is_trivially_move_constructible::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_move_constructible_v) == (std::is_trivially_move_constructible_v)); + CHECK((etl::is_trivially_move_constructible_v) == (std::is_trivially_move_constructible_v)); + #endif + #endif +#endif + } + //************************************************************************* TEST(test_is_trivially_destructible) { -#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) +#if ETL_USING_STL || defined(ETL_USE_TYPE_TRAITS_BUILTINS) || defined(ETL_USER_DEFINED_TYPE_TRAITS) || ETL_USING_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE \ + || ETL_USING_BUILTIN_HAS_TRIVIAL_DESTRUCTOR #if ETL_USING_CPP17 CHECK((etl::is_trivially_destructible_v) == (std::is_trivially_destructible_v)); CHECK((etl::is_trivially_destructible_v) == (std::is_trivially_destructible_v)); @@ -1742,6 +2959,31 @@ namespace CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); #endif + #if ETL_USING_CPP11 + CHECK(!etl::is_trivially_destructible::value); + CHECK(!etl::is_trivially_destructible::value); + CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); + CHECK((etl::is_trivially_destructible::value) == (std::is_trivially_destructible::value)); + #endif +#endif + } + + //************************************************************************* + TEST(test_is_trivially_assignable) + { +#if ETL_USING_CPP11 && !defined(ETL_USER_DEFINED_TYPE_TRAITS) \ + && (ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED || ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) == (std::is_trivially_assignable::value)); + CHECK((etl::is_trivially_assignable::value) + == (std::is_trivially_assignable::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_assignable_v) == (std::is_trivially_assignable_v)); + CHECK((etl::is_trivially_assignable_v) == (std::is_trivially_assignable_v)); + #endif #endif } @@ -1763,6 +3005,23 @@ namespace #endif } + //************************************************************************* + TEST(test_is_trivially_move_assignable) + { +#if ETL_USING_CPP11 && !defined(ETL_USER_DEFINED_TYPE_TRAITS) \ + && (ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED || ETL_USING_BUILTIN_IS_TRIVIALLY_ASSIGNABLE) + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + CHECK((etl::is_trivially_move_assignable::value) == (std::is_trivially_move_assignable::value)); + + #if ETL_USING_CPP17 + CHECK((etl::is_trivially_move_assignable_v) == (std::is_trivially_move_assignable_v)); + CHECK((etl::is_trivially_move_assignable_v) == (std::is_trivially_move_assignable_v)); + #endif +#endif + } + //************************************************************************* TEST(test_is_trivially_copyable) { @@ -2268,5 +3527,23 @@ namespace CHECK_TRUE(etl::is_unsigned::value); #endif } + +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_common_type) + { + // Reference types must decay to the same result as std::common_type. + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + CHECK((etl::is_same, char>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + + // Already decayed types. + CHECK((etl::is_same, long>::value)); + CHECK((etl::is_same, std::common_type::type>::value)); + } +#endif } } // namespace diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index 62cf500e..ad1e50c9 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -34,9 +34,69 @@ SOFTWARE. #include "etl/private/diagnostic_useless_cast_push.h" #include +#include namespace { + //*************************************************************************** + /// Representation level helpers. + /// 'memcpy' is used rather than 'etl::bit_cast' so that these work in every + /// configuration, including those without a bit_cast builtin. + //*************************************************************************** + uint32_t bits_of(float value) + { + uint32_t bits = 0U; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + } + + //*************************************************************************** + float float_from_bits(uint32_t bits) + { + float value = 0.0f; + std::memcpy(&value, &bits, sizeof(value)); + return value; + } + + //*************************************************************************** + uint64_t bits_of(double value) + { + uint64_t bits = 0U; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + } + + //*************************************************************************** + double double_from_bits(uint64_t bits) + { + double value = 0.0; + std::memcpy(&value, &bits, sizeof(value)); + return value; + } + +#if ETL_USING_CPP14 && ETL_USING_BUILTIN_BIT_CAST + //*************************************************************************** + /// Encodes 'bits' as a floating point value in the storage of 'TUnaligned', + /// then decodes it back, returning the resulting bit pattern. + /// Used to check that the constexpr bit_cast path is representation + /// preserving. + //*************************************************************************** + template + constexpr TBits round_trip_bits(TBits bits) + { + return etl::bit_cast(TUnaligned(etl::bit_cast(bits)).value()); + } + + //*************************************************************************** + /// The storage byte at 'index' when 'bits' is encoded in 'TUnaligned'. + //*************************************************************************** + template + constexpr unsigned char storage_byte_of(TBits bits, size_t index) + { + return TUnaligned(etl::bit_cast(bits)).data()[index]; + } +#endif + SUITE(test_unaligned_type) { //************************************************************************* @@ -175,6 +235,490 @@ namespace CHECK_THROW(etl::be_uint32_t be_v3(buffer.data(), buffer.size() - 1), etl::unaligned_type_buffer_size); } + //************************************************************************* + /// The 'const unsigned char*' overloads are distinct from the + /// 'const void*' ones above, so check their buffer size contract too. + /// Note that the size check is an ETL_ASSERT, so this only holds when + /// checks are enabled. See the note on the constructor. + //************************************************************************* + TEST(test_construction_from_byte_buffer_size_boundary) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78, 0x9A}; + + // Exactly the required number of bytes. + etl::le_uint32_t le_exact(buffer.data(), sizeof(uint32_t)); + etl::be_uint32_t be_exact(buffer.data(), sizeof(uint32_t)); + + CHECK_EQUAL(uint32_t(0x78563412), le_exact.value()); + CHECK_EQUAL(uint32_t(0x12345678), be_exact.value()); + + // More bytes than required. The surplus is ignored. + etl::le_uint32_t le_larger(buffer.data(), buffer.size()); + etl::be_uint32_t be_larger(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint32_t(0x78563412), le_larger.value()); + CHECK_EQUAL(uint32_t(0x12345678), be_larger.value()); + + // One byte short. + CHECK_THROW(etl::le_uint32_t le_short(buffer.data(), sizeof(uint32_t) - 1U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint32_t be_short(buffer.data(), sizeof(uint32_t) - 1U), etl::unaligned_type_buffer_size); + + // Empty buffer. + CHECK_THROW(etl::le_uint32_t le_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint32_t be_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + + // The widest type is checked too, as it has the largest storage. + CHECK_THROW(etl::le_uint64_t le_wide(buffer.data(), buffer.size()), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint64_t be_wide(buffer.data(), buffer.size()), etl::unaligned_type_buffer_size); + + // Single byte types accept a single byte buffer. + etl::le_uint8_t le_byte(buffer.data(), 1U); + etl::be_uint8_t be_byte(buffer.data(), 1U); + + CHECK_EQUAL(uint8_t(0x12), le_byte.value()); + CHECK_EQUAL(uint8_t(0x12), be_byte.value()); + + CHECK_THROW(etl::le_uint8_t le_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + CHECK_THROW(etl::be_uint8_t be_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size); + } + + //************************************************************************* + // The following tests demonstrate the 'decode' direction: given a raw byte + // buffer (e.g. as received from a file, network socket or memory-mapped + // device), interpret it as an explicitly little/big endian unaligned_type + // and read back the correctly decoded native value. + //************************************************************************* + TEST(test_decode_buffer_uint16) + { + // The same two bytes, interpreted with an explicit endianness. + const std::array buffer = {0x12, 0x34}; + + etl::le_uint16_t le_v(buffer.data(), buffer.size()); + etl::be_uint16_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint16_t(0x3412), le_v.value()); + CHECK_EQUAL(uint16_t(0x1234), be_v.value()); + } + + //************************************************************************* + // 'bool' has no 'make_unsigned' equivalent, so check that it is supported. + TEST(test_bool) + { + etl::unaligned_type le_v(true); + etl::unaligned_type be_v(false); + + CHECK_EQUAL(true, le_v.value()); + CHECK_EQUAL(false, be_v.value()); + + le_v = false; + be_v = true; + + CHECK_EQUAL(false, le_v.value()); + CHECK_EQUAL(true, be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_int32) + { + const std::array buffer = {0x12, 0x34, 0x56, 0x78}; + + etl::le_int32_t le_v(buffer.data(), buffer.size()); + etl::be_int32_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(int32_t(0x78563412), le_v.value()); + CHECK_EQUAL(int32_t(0x12345678), be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_uint64) + { + const std::array buffer = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + + etl::le_uint64_t le_v(buffer.data(), buffer.size()); + etl::be_uint64_t be_v(buffer.data(), buffer.size()); + + CHECK_EQUAL(uint64_t(0xEFCDAB8967452301), le_v.value()); + CHECK_EQUAL(uint64_t(0x0123456789ABCDEF), be_v.value()); + } + + //************************************************************************* + /// The decode tests above cover a subset of the widths only, and no + /// negative or extreme values. These cover every signed and unsigned + /// width at its boundary values, in both directions. + //************************************************************************* + template + void check_boundary_value(TValue value) + { + ETL_STATIC_ASSERT(sizeof(TValue) == TLittle::Size, "Mismatched size"); + ETL_STATIC_ASSERT(sizeof(TValue) == TBig::Size, "Mismatched size"); + + const TLittle le_v(value); + const TBig be_v(value); + + // The two encodings are byte reversals of each other. + for (size_t i = 0U; i < sizeof(TValue); ++i) + { + CHECK_EQUAL(int(le_v.data()[i]), int(be_v.data()[sizeof(TValue) - 1U - i])); + } + + // Decoding the encoded bytes returns the original value. + CHECK_EQUAL(value, TLittle(le_v.data(), sizeof(TValue)).value()); + CHECK_EQUAL(value, TBig(be_v.data(), sizeof(TValue)).value()); + } + + //************************************************************************* + TEST(test_integral_boundary_values) + { + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0); + check_boundary_value(-1); + check_boundary_value(etl::integral_limits::min); + check_boundary_value(etl::integral_limits::max); + + check_boundary_value(0U); + check_boundary_value(etl::integral_limits::max); + } + + //************************************************************************* + /// Explicit byte level checks for the widths that the decode tests above + /// do not cover, using negative values so that the sign bit is exercised. + //************************************************************************* + TEST(test_decode_buffer_negative_values) + { + // -2 == 0xFE, 0xFFFE, 0xFFFFFFFE, 0xFFFFFFFFFFFFFFFE. + const std::array buffer = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + CHECK_EQUAL(int8_t(-2), etl::le_int8_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int8_t(-2), etl::be_int8_t(buffer.data(), buffer.size()).value()); + + CHECK_EQUAL(int16_t(-2), etl::le_int16_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int32_t(-2), etl::le_int32_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int64_t(-2), etl::le_int64_t(buffer.data(), buffer.size()).value()); + + // Big endian reads the same bytes in the opposite order. + CHECK_EQUAL(int16_t(-257), etl::be_int16_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int32_t(-16777217), etl::be_int32_t(buffer.data(), buffer.size()).value()); + CHECK_EQUAL(int64_t(-72057594037927937LL), etl::be_int64_t(buffer.data(), buffer.size()).value()); + } + + //************************************************************************* + TEST(test_decode_buffer_float) + { + // 1.5f in IEEE-754 single precision is 0x3FC00000. + const std::array le_buffer = {0x00, 0x00, 0xC0, 0x3F}; + const std::array be_buffer = {0x3F, 0xC0, 0x00, 0x00}; + + etl::le_float_t le_v(le_buffer.data(), le_buffer.size()); + etl::be_float_t be_v(be_buffer.data(), be_buffer.size()); + + CHECK_EQUAL(1.5f, le_v.value()); + CHECK_EQUAL(1.5f, be_v.value()); + } + + //************************************************************************* + TEST(test_decode_buffer_double) + { + // 1.5 in IEEE-754 double precision is 0x3FF8000000000000. + const std::array le_buffer = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F}; + const std::array be_buffer = {0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + etl::le_double_t le_v(le_buffer.data(), le_buffer.size()); + etl::be_double_t be_v(be_buffer.data(), be_buffer.size()); + + CHECK_EQUAL(1.5, le_v.value()); + CHECK_EQUAL(1.5, be_v.value()); + } + + //************************************************************************* + /// Representation level checks for 'float'. + /// Ordinary values such as 1.5f have a zero mantissa tail, a clear sign + /// bit and a mid range exponent, so they would still pass under several + /// plausible masking or byte ordering faults. These patterns exercise the + /// sign of zero, the infinities, a NaN payload, the subnormal range and + /// the normal range limits. + //************************************************************************* + TEST(test_float_representations) + { + static const uint32_t patterns[] = { + 0x00000000U, // +0.0 + 0x80000000U, // -0.0 + 0x7F800000U, // +infinity + 0xFF800000U, // -infinity + 0x7FC0DEADU, // Quiet NaN with a payload. + 0xFFC0DEADU, // Negative quiet NaN with a payload. + 0x00000001U, // Smallest positive subnormal. + 0x007FFFFFU, // Largest subnormal. + 0x00800000U, // Smallest positive normal. + 0x7F7FFFFFU, // Largest normal. + 0xBFC00000U // -1.5 + }; + + for (size_t i = 0U; i < (sizeof(patterns) / sizeof(patterns[0])); ++i) + { + const uint32_t bits = patterns[i]; + + etl::le_float_t le_v(float_from_bits(bits)); + etl::be_float_t be_v(float_from_bits(bits)); + + // The storage must hold the exact IEEE-754 bytes, in the requested order. + for (size_t b = 0U; b < sizeof(float); ++b) + { + CHECK_EQUAL(int((bits >> (8U * b)) & 0xFFU), int(le_v.data()[b])); + CHECK_EQUAL(int((bits >> (8U * (sizeof(float) - 1U - b))) & 0xFFU), int(be_v.data()[b])); + } + + // The value must survive the round trip bit for bit, including the + // sign of zero and the NaN payload. + CHECK_EQUAL(bits, bits_of(le_v.value())); + CHECK_EQUAL(bits, bits_of(be_v.value())); + + // Decoding the raw bytes must give the same result as encoding them. + etl::le_float_t le_decoded(le_v.data(), le_v.size()); + etl::be_float_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(bits, bits_of(le_decoded.value())); + CHECK_EQUAL(bits, bits_of(be_decoded.value())); + } + } + + //************************************************************************* + /// Representation level checks for 'double'. See the note on the 'float' + /// test above. + //************************************************************************* + TEST(test_double_representations) + { + static const uint64_t patterns[] = { + 0x0000000000000000ULL, // +0.0 + 0x8000000000000000ULL, // -0.0 + 0x7FF0000000000000ULL, // +infinity + 0xFFF0000000000000ULL, // -infinity + 0x7FF80000DEADBEEFULL, // Quiet NaN with a payload. + 0xFFF80000DEADBEEFULL, // Negative quiet NaN with a payload. + 0x0000000000000001ULL, // Smallest positive subnormal. + 0x000FFFFFFFFFFFFFULL, // Largest subnormal. + 0x0010000000000000ULL, // Smallest positive normal. + 0x7FEFFFFFFFFFFFFFULL, // Largest normal. + 0xBFF8000000000000ULL // -1.5 + }; + + for (size_t i = 0U; i < (sizeof(patterns) / sizeof(patterns[0])); ++i) + { + const uint64_t bits = patterns[i]; + + etl::le_double_t le_v(double_from_bits(bits)); + etl::be_double_t be_v(double_from_bits(bits)); + + for (size_t b = 0U; b < sizeof(double); ++b) + { + CHECK_EQUAL(int((bits >> (8U * b)) & 0xFFU), int(le_v.data()[b])); + CHECK_EQUAL(int((bits >> (8U * (sizeof(double) - 1U - b))) & 0xFFU), int(be_v.data()[b])); + } + + CHECK_EQUAL(bits, bits_of(le_v.value())); + CHECK_EQUAL(bits, bits_of(be_v.value())); + + etl::le_double_t le_decoded(le_v.data(), le_v.size()); + etl::be_double_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(bits, bits_of(le_decoded.value())); + CHECK_EQUAL(bits, bits_of(be_decoded.value())); + } + } + + //************************************************************************* + /// 'long double' is typically 80, 96 or 128 bits wide, so it has no same + /// sized unsigned integer proxy and falls back to the non bit_cast, + /// non constexpr path. That fallback still has to work. + //************************************************************************* + TEST(test_long_double_round_trip) + { + const long double value = 1.5L; + + etl::le_long_double_t le_v(value); + etl::be_long_double_t be_v(value); + + CHECK_EQUAL(sizeof(long double), le_v.size()); + CHECK_EQUAL(sizeof(long double), be_v.size()); + + // The little and big endian storage must be byte reversals of each other. + for (size_t i = 0U; i < sizeof(long double); ++i) + { + CHECK_EQUAL(int(le_v.data()[i]), int(be_v.data()[sizeof(long double) - 1U - i])); + } + + CHECK_EQUAL(value, le_v.value()); + CHECK_EQUAL(value, be_v.value()); + + // Decoding the raw bytes must give the same result as encoding them. + etl::le_long_double_t le_decoded(le_v.data(), le_v.size()); + etl::be_long_double_t be_decoded(be_v.data(), be_v.size()); + + CHECK_EQUAL(value, le_decoded.value()); + CHECK_EQUAL(value, be_decoded.value()); + } + +#if ETL_HAS_CONSTEXPR_ENDIANNESS + //************************************************************************* + TEST(test_decode_buffer_host_order) + { + // Decoding a buffer known to be in host order must yield the same + // value regardless of what that host order actually is. + const uint32_t value = 0x12345678U; + + const etl::host_uint32_t encoded(value); + etl::host_uint32_t decoded(encoded.data(), encoded.size()); + + CHECK_EQUAL(value, decoded.value()); + } +#endif + +#if ETL_USING_CPP14 + //************************************************************************* + // Demonstrates that decoding a raw byte buffer with an explicit endianness + // is now usable at compile time (constexpr), via the 'const unsigned char*' + // constructor overloads - not just runtime CHECK_EQUAL. + //************************************************************************* + TEST(test_constexpr_decode_buffer_integral) + { + static ETL_CONSTANT unsigned char le_buffer[4] = {0x78, 0x56, 0x34, 0x12}; + static ETL_CONSTANT unsigned char be_buffer[4] = {0x12, 0x34, 0x56, 0x78}; + + constexpr etl::le_uint32_t le_v(le_buffer, sizeof(le_buffer)); + constexpr etl::be_uint32_t be_v(be_buffer, sizeof(be_buffer)); + + static_assert(le_v.value() == 0x12345678U, "le_uint32_t constexpr decode from buffer + size"); + static_assert(be_v.value() == 0x12345678U, "be_uint32_t constexpr decode from buffer + size"); + + // Buffer-only overload (no explicit size). + constexpr etl::le_uint32_t le_v2(le_buffer); + constexpr etl::be_uint32_t be_v2(be_buffer); + + static_assert(le_v2.value() == 0x12345678U, "le_uint32_t constexpr decode from buffer"); + static_assert(be_v2.value() == 0x12345678U, "be_uint32_t constexpr decode from buffer"); + + // Prove the checks above are genuine compile-time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x12345678U, le_v.value()); + CHECK_EQUAL(0x12345678U, be_v.value()); + } + + #if ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + TEST(test_constexpr_decode_buffer_float) + { + // 1.5f in IEEE-754 single precision is 0x3FC00000. + static ETL_CONSTANT unsigned char le_buffer[4] = {0x00, 0x00, 0xC0, 0x3F}; + static ETL_CONSTANT unsigned char be_buffer[4] = {0x3F, 0xC0, 0x00, 0x00}; + + constexpr etl::le_float_t le_v(le_buffer, sizeof(le_buffer)); + constexpr etl::be_float_t be_v(be_buffer, sizeof(be_buffer)); + + // Bit patterns are compared, rather than the float values, to avoid + // '-Wfloat-equal'. + static_assert(etl::bit_cast(le_v.value()) == 0x3FC00000U, "le_float_t constexpr decode from buffer"); + static_assert(etl::bit_cast(be_v.value()) == 0x3FC00000U, "be_float_t constexpr decode from buffer"); + + CHECK_EQUAL(1.5f, le_v.value()); + CHECK_EQUAL(1.5f, be_v.value()); + } + + //************************************************************************* + /// The constexpr path bit_casts through a same sized unsigned integer. + /// Check, at compile time, that this is exact for the awkward + /// representations too, not just for ordinary values such as 1.5f. + //************************************************************************* + TEST(test_constexpr_float_representations) + { + // Encode, then decode, and require the bit pattern to be unchanged. + static_assert(round_trip_bits(0x00000000U) == 0x00000000U, "le +0.0"); + static_assert(round_trip_bits(0x80000000U) == 0x80000000U, "le -0.0"); + static_assert(round_trip_bits(0x7F800000U) == 0x7F800000U, "le +infinity"); + static_assert(round_trip_bits(0xFF800000U) == 0xFF800000U, "le -infinity"); + static_assert(round_trip_bits(0x7FC0DEADU) == 0x7FC0DEADU, "le NaN payload"); + static_assert(round_trip_bits(0x00000001U) == 0x00000001U, "le smallest subnormal"); + static_assert(round_trip_bits(0x007FFFFFU) == 0x007FFFFFU, "le largest subnormal"); + static_assert(round_trip_bits(0x00800000U) == 0x00800000U, "le smallest normal"); + static_assert(round_trip_bits(0x7F7FFFFFU) == 0x7F7FFFFFU, "le largest normal"); + + static_assert(round_trip_bits(0x80000000U) == 0x80000000U, "be -0.0"); + static_assert(round_trip_bits(0xFF800000U) == 0xFF800000U, "be -infinity"); + static_assert(round_trip_bits(0x7FC0DEADU) == 0x7FC0DEADU, "be NaN payload"); + static_assert(round_trip_bits(0x00000001U) == 0x00000001U, "be smallest subnormal"); + static_assert(round_trip_bits(0x7F7FFFFFU) == 0x7F7FFFFFU, "be largest normal"); + + // The stored bytes must be the IEEE-754 bytes, in the requested order. + static_assert(storage_byte_of(0x7FC0DEADU, 0U) == 0xAD, "le NaN byte 0"); + static_assert(storage_byte_of(0x7FC0DEADU, 1U) == 0xDE, "le NaN byte 1"); + static_assert(storage_byte_of(0x7FC0DEADU, 2U) == 0xC0, "le NaN byte 2"); + static_assert(storage_byte_of(0x7FC0DEADU, 3U) == 0x7F, "le NaN byte 3"); + + static_assert(storage_byte_of(0x7FC0DEADU, 0U) == 0x7F, "be NaN byte 0"); + static_assert(storage_byte_of(0x7FC0DEADU, 1U) == 0xC0, "be NaN byte 1"); + static_assert(storage_byte_of(0x7FC0DEADU, 2U) == 0xDE, "be NaN byte 2"); + static_assert(storage_byte_of(0x7FC0DEADU, 3U) == 0xAD, "be NaN byte 3"); + + // Prove the checks above are genuine compile time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x80000000U, (round_trip_bits(0x80000000U))); + CHECK_EQUAL(0x7FC0DEADU, (round_trip_bits(0x7FC0DEADU))); + } + + //************************************************************************* + /// As 'test_constexpr_float_representations', for the 8 byte proxy. + //************************************************************************* + TEST(test_constexpr_double_representations) + { + static_assert(round_trip_bits(0x0000000000000000ULL) == 0x0000000000000000ULL, "le +0.0"); + static_assert(round_trip_bits(0x8000000000000000ULL) == 0x8000000000000000ULL, "le -0.0"); + static_assert(round_trip_bits(0x7FF0000000000000ULL) == 0x7FF0000000000000ULL, "le +infinity"); + static_assert(round_trip_bits(0xFFF0000000000000ULL) == 0xFFF0000000000000ULL, "le -infinity"); + static_assert(round_trip_bits(0x7FF80000DEADBEEFULL) == 0x7FF80000DEADBEEFULL, "le NaN payload"); + static_assert(round_trip_bits(0x0000000000000001ULL) == 0x0000000000000001ULL, "le smallest subnormal"); + static_assert(round_trip_bits(0x000FFFFFFFFFFFFFULL) == 0x000FFFFFFFFFFFFFULL, "le largest subnormal"); + static_assert(round_trip_bits(0x0010000000000000ULL) == 0x0010000000000000ULL, "le smallest normal"); + static_assert(round_trip_bits(0x7FEFFFFFFFFFFFFFULL) == 0x7FEFFFFFFFFFFFFFULL, "le largest normal"); + + static_assert(round_trip_bits(0x8000000000000000ULL) == 0x8000000000000000ULL, "be -0.0"); + static_assert(round_trip_bits(0xFFF0000000000000ULL) == 0xFFF0000000000000ULL, "be -infinity"); + static_assert(round_trip_bits(0x7FF80000DEADBEEFULL) == 0x7FF80000DEADBEEFULL, "be NaN payload"); + static_assert(round_trip_bits(0x0000000000000001ULL) == 0x0000000000000001ULL, "be smallest subnormal"); + static_assert(round_trip_bits(0x7FEFFFFFFFFFFFFFULL) == 0x7FEFFFFFFFFFFFFFULL, "be largest normal"); + + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 0U) == 0xEF, "le NaN byte 0"); + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 7U) == 0x7F, "le NaN byte 7"); + + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 0U) == 0x7F, "be NaN byte 0"); + static_assert(storage_byte_of(0x7FF80000DEADBEEFULL, 7U) == 0xEF, "be NaN byte 7"); + + CHECK_EQUAL(0x8000000000000000ULL, (round_trip_bits(0x8000000000000000ULL))); + CHECK_EQUAL(0x7FF80000DEADBEEFULL, (round_trip_bits(0x7FF80000DEADBEEFULL))); + } + #endif +#endif + //************************************************************************* TEST(test_endianness) { @@ -1122,6 +1666,74 @@ namespace CHECK_EQUAL(value, uint16_t(be_from_host)); } #endif + +#if ETL_USING_CPP14 + //************************************************************************* + // Demonstrates that integral endian conversion is now usable at compile + // time (constexpr), via genuine static_assert checks - not just runtime + // CHECK_EQUAL. The conversion is evaluated entirely by the compiler. + //************************************************************************* + TEST(test_constexpr_integral_endian_conversion) + { + // Constructing from a value and reading it back via value()/operator T() + // is constexpr for integral types, regardless of endianness. + constexpr etl::le_uint32_t le_v(0x12345678U); + constexpr etl::be_uint32_t be_v(0x12345678U); + + static_assert(le_v.value() == 0x12345678U, "le_uint32_t constexpr value() round trip"); + static_assert(be_v.value() == 0x12345678U, "be_uint32_t constexpr value() round trip"); + + static_assert(uint32_t(le_v) == 0x12345678U, "le_uint32_t constexpr operator T() round trip"); + static_assert(uint32_t(be_v) == 0x12345678U, "be_uint32_t constexpr operator T() round trip"); + + // Verify the underlying byte order is also usable at compile time. + static_assert(le_v.data()[0] == 0x78, "le_uint32_t constexpr byte 0"); + static_assert(le_v.data()[1] == 0x56, "le_uint32_t constexpr byte 1"); + static_assert(le_v.data()[2] == 0x34, "le_uint32_t constexpr byte 2"); + static_assert(le_v.data()[3] == 0x12, "le_uint32_t constexpr byte 3"); + + static_assert(be_v.data()[0] == 0x12, "be_uint32_t constexpr byte 0"); + static_assert(be_v.data()[1] == 0x34, "be_uint32_t constexpr byte 1"); + static_assert(be_v.data()[2] == 0x56, "be_uint32_t constexpr byte 2"); + static_assert(be_v.data()[3] == 0x78, "be_uint32_t constexpr byte 3"); + + // Prove the checks above are genuine compile-time facts, not just + // syntactically valid constexpr, by also exercising them at runtime. + CHECK_EQUAL(0x12345678U, uint32_t(le_v)); + CHECK_EQUAL(0x12345678U, uint32_t(be_v)); + } +#endif + +#if ETL_USING_CPP14 && ETL_USING_BUILTIN_BIT_CAST + //************************************************************************* + // Demonstrates that floating point endian conversion is now usable at + // compile time (constexpr) too, via etl::bit_cast (no std::bit_cast or + // STL dependency required). + //************************************************************************* + TEST(test_constexpr_float_endian_conversion) + { + constexpr etl::le_float_t le_v(1.5f); + constexpr etl::be_float_t be_v(1.5f); + + // Bit patterns are compared, rather than the float values, to avoid + // '-Wfloat-equal'. + static_assert(etl::bit_cast(le_v.value()) == 0x3FC00000U, "le_float_t constexpr value() round trip"); + static_assert(etl::bit_cast(be_v.value()) == 0x3FC00000U, "be_float_t constexpr value() round trip"); + + static_assert(etl::bit_cast(float(le_v)) == 0x3FC00000U, "le_float_t constexpr operator T() round trip"); + static_assert(etl::bit_cast(float(be_v)) == 0x3FC00000U, "be_float_t constexpr operator T() round trip"); + + // LE and BE storage bytes must be the reverse of each other, checked at + // compile time. + static_assert(le_v.data()[0] == be_v.data()[3], "byte 0/3 mismatch"); + static_assert(le_v.data()[1] == be_v.data()[2], "byte 1/2 mismatch"); + static_assert(le_v.data()[2] == be_v.data()[1], "byte 2/1 mismatch"); + static_assert(le_v.data()[3] == be_v.data()[0], "byte 3/0 mismatch"); + + CHECK_EQUAL(1.5f, float(le_v)); + CHECK_EQUAL(1.5f, float(be_v)); + } +#endif } } // namespace diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index 793343eb..57ccd928 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -1484,5 +1484,28 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_map_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_unordered_map(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.at('a')); + CHECK_EQUAL(1, data.at('b')); + CHECK_EQUAL(2, data.at('c')); + } +#endif } } // namespace diff --git a/test/test_unordered_multimap.cpp b/test/test_unordered_multimap.cpp index bf2c2aa6..2207f642 100644 --- a/test/test_unordered_multimap.cpp +++ b/test/test_unordered_multimap.cpp @@ -1243,5 +1243,28 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the pair type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_multimap_from_const_lvalues_of_pair_type) + { + using Pair = ETL_OR_STD::pair; + + static const Pair static_const_lvalue('a', 0); + const Pair local_const_lvalue('b', 1); + Pair mutable_lvalue('c', 2); + + auto data = etl::make_unordered_multimap(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(0, data.find('a')->second); + CHECK_EQUAL(1, data.find('b')->second); + CHECK_EQUAL(2, data.find('c')->second); + } +#endif } } // namespace diff --git a/test/test_unordered_multiset.cpp b/test/test_unordered_multiset.cpp index 9a05aca8..862baa6c 100644 --- a/test/test_unordered_multiset.cpp +++ b/test/test_unordered_multiset.cpp @@ -1113,5 +1113,26 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_multiset_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_unordered_multiset(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(1U, data.count(42)); + CHECK_EQUAL(1U, data.count(43)); + CHECK_EQUAL(1U, data.count(44)); + } +#endif } } // namespace diff --git a/test/test_unordered_set.cpp b/test/test_unordered_set.cpp index da0831c8..cd959a2a 100644 --- a/test/test_unordered_set.cpp +++ b/test/test_unordered_set.cpp @@ -1122,6 +1122,27 @@ namespace CHECK_TRUE(data.contains("FF")); CHECK_FALSE(data.contains(not_inserted)); } + + //************************************************************************* +#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues of the key type must be + // accepted. Guards against the defect fixed for make_array and make_deque, + // which forwarded each argument as the element type rather than as its own + // deduced type and so rejected const lvalues. + TEST(test_make_unordered_set_from_const_lvalues_of_key_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_unordered_set(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + CHECK_EQUAL(3U, data.size()); + CHECK_EQUAL(1U, data.count(42)); + CHECK_EQUAL(1U, data.count(43)); + CHECK_EQUAL(1U, data.count(44)); + } +#endif } #include "etl/private/diagnostic_pop.h" } // namespace diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 5b824aa3..124cff1f 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -785,6 +785,161 @@ namespace } #endif + //********************************* + TEST(test_cmp_equal) + { + // Same signedness. + CHECK_TRUE(etl::cmp_equal(1, 1)); + CHECK_FALSE(etl::cmp_equal(1, 2)); + CHECK_TRUE(etl::cmp_equal(1U, 1U)); + CHECK_FALSE(etl::cmp_equal(1U, 2U)); + + // Mixed signedness where the built-in operator would give the wrong result. + // -1 as unsigned would be a large value, so a naive (a == b) would be false anyway, + // but the classic trap is comparing a negative signed with an equal-bit-pattern unsigned. + CHECK_FALSE(etl::cmp_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_equal(static_cast(-1), -1)); + + // Non-negative signed compared with unsigned. + CHECK_TRUE(etl::cmp_equal(5, 5U)); + CHECK_TRUE(etl::cmp_equal(5U, 5)); + CHECK_FALSE(etl::cmp_equal(-5, 5U)); + CHECK_FALSE(etl::cmp_equal(5U, -5)); + + // Different widths. + CHECK_TRUE(etl::cmp_equal(int8_t(100), int64_t(100))); + CHECK_FALSE(etl::cmp_equal(int8_t(-1), uint64_t(0xFFFFFFFFFFFFFFFFULL))); + } + + //********************************* + TEST(test_cmp_not_equal) + { + CHECK_FALSE(etl::cmp_not_equal(1, 1)); + CHECK_TRUE(etl::cmp_not_equal(1, 2)); + CHECK_TRUE(etl::cmp_not_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_not_equal(5, 5U)); + CHECK_TRUE(etl::cmp_not_equal(-5, 5U)); + } + + //********************************* + TEST(test_cmp_less) + { + // Same signedness. + CHECK_TRUE(etl::cmp_less(1, 2)); + CHECK_FALSE(etl::cmp_less(2, 1)); + CHECK_FALSE(etl::cmp_less(1, 1)); + CHECK_TRUE(etl::cmp_less(1U, 2U)); + CHECK_FALSE(etl::cmp_less(2U, 1U)); + + // A negative signed value is always less than any unsigned value. + CHECK_TRUE(etl::cmp_less(-1, 0U)); + CHECK_TRUE(etl::cmp_less(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_less(static_cast(-1), -1)); + + // Non-negative signed with unsigned. + CHECK_TRUE(etl::cmp_less(5, 6U)); + CHECK_FALSE(etl::cmp_less(6, 5U)); + CHECK_TRUE(etl::cmp_less(5U, 6)); + CHECK_FALSE(etl::cmp_less(6U, 5)); + + // Large unsigned is not less than a small signed. + CHECK_FALSE(etl::cmp_less(0xFFFFFFFFU, 1)); + CHECK_TRUE(etl::cmp_less(1, 0xFFFFFFFFU)); + } + + //********************************* + TEST(test_cmp_greater) + { + CHECK_TRUE(etl::cmp_greater(2, 1)); + CHECK_FALSE(etl::cmp_greater(1, 2)); + CHECK_FALSE(etl::cmp_greater(1, 1)); + + CHECK_FALSE(etl::cmp_greater(-1, 0U)); + CHECK_TRUE(etl::cmp_greater(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_greater(0xFFFFFFFFU, 1)); + CHECK_FALSE(etl::cmp_greater(1, 0xFFFFFFFFU)); + } + + //********************************* + TEST(test_cmp_less_equal) + { + CHECK_TRUE(etl::cmp_less_equal(1, 1)); + CHECK_TRUE(etl::cmp_less_equal(1, 2)); + CHECK_FALSE(etl::cmp_less_equal(2, 1)); + + CHECK_TRUE(etl::cmp_less_equal(-1, static_cast(-1))); + CHECK_FALSE(etl::cmp_less_equal(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_less_equal(5U, 5)); + CHECK_FALSE(etl::cmp_less_equal(6U, 5)); + } + + //********************************* + TEST(test_cmp_greater_equal) + { + CHECK_TRUE(etl::cmp_greater_equal(1, 1)); + CHECK_TRUE(etl::cmp_greater_equal(2, 1)); + CHECK_FALSE(etl::cmp_greater_equal(1, 2)); + + CHECK_FALSE(etl::cmp_greater_equal(-1, static_cast(-1))); + CHECK_TRUE(etl::cmp_greater_equal(static_cast(-1), -1)); + CHECK_TRUE(etl::cmp_greater_equal(5, 5U)); + CHECK_FALSE(etl::cmp_greater_equal(-5, 5U)); + } + + //********************************* + TEST(test_in_range) + { + // Signed target type. + CHECK_TRUE(etl::in_range(0)); + CHECK_TRUE(etl::in_range(127)); + CHECK_TRUE(etl::in_range(-128)); + CHECK_FALSE(etl::in_range(128)); + CHECK_FALSE(etl::in_range(-129)); + CHECK_FALSE(etl::in_range(200)); + + // Unsigned target type. + CHECK_TRUE(etl::in_range(0)); + CHECK_TRUE(etl::in_range(255)); + CHECK_FALSE(etl::in_range(256)); + CHECK_FALSE(etl::in_range(-1)); + + // Unsigned source that would be negative if reinterpreted as signed. + CHECK_FALSE(etl::in_range(static_cast(-1))); + CHECK_TRUE(etl::in_range(static_cast(-1))); + + // Wider target always contains a narrower value. + CHECK_TRUE(etl::in_range(int8_t(-1))); + CHECK_TRUE(etl::in_range(uint32_t(0xFFFFFFFFU))); + } + +#if ETL_USING_CPP14 + //********************************* + TEST(test_cmp_and_in_range_constexpr) + { + constexpr bool ce0 = etl::cmp_equal(1, 1); + constexpr bool ce1 = etl::cmp_not_equal(-1, static_cast(-1)); + constexpr bool ce2 = etl::cmp_less(-1, 0U); + constexpr bool ce3 = etl::cmp_greater(static_cast(-1), -1); + constexpr bool ce4 = etl::cmp_less_equal(-1, static_cast(-1)); + constexpr bool ce5 = etl::cmp_greater_equal(static_cast(-1), -1); + constexpr bool ce6 = etl::in_range(100); + constexpr bool ce7 = etl::in_range(200); + + static_assert(ce0, "cmp_equal constexpr"); + static_assert(ce1, "cmp_not_equal constexpr"); + static_assert(ce2, "cmp_less constexpr"); + static_assert(ce3, "cmp_greater constexpr"); + static_assert(ce4, "cmp_less_equal constexpr"); + static_assert(ce5, "cmp_greater_equal constexpr"); + static_assert(ce6, "in_range true constexpr"); + static_assert(!ce7, "in_range false constexpr"); + + CHECK_TRUE(ce0); + CHECK_TRUE(ce6); + CHECK_FALSE(ce7); + } +#endif + #if ETL_HAS_PACKED //********************************* TEST(test_packed) @@ -1144,5 +1299,239 @@ namespace CHECK(true); } #endif + + //************************************************************************* + TEST(test_to_unsigned_from_int8_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int8_t(0)); + auto uplus = etl::to_unsigned(int8_t(50)); + auto uminus = etl::to_unsigned(int8_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(umin_expected), int(umin)); + CHECK_EQUAL(int(umax_expected), int(umax)); + CHECK_EQUAL(int(uzero_expected), int(uzero)); + CHECK_EQUAL(int(uplus_expected), int(uplus)); + CHECK_EQUAL(int(uminus_expected), int(uminus)); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int16_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int16_t(0)); + auto uplus = etl::to_unsigned(int16_t(50)); + auto uminus = etl::to_unsigned(int16_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int32_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int32_t(0)); + auto uplus = etl::to_unsigned(int32_t(50)); + auto uminus = etl::to_unsigned(int32_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int64_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int64_t(0)); + auto uplus = etl::to_unsigned(int64_t(50)); + auto uminus = etl::to_unsigned(int64_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint8_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint8_t(50)); + auto sminus = etl::to_signed(uint8_t(206)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(206); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(smin_expected), int(smin)); + CHECK_EQUAL(int(smax_expected), int(smax)); + CHECK_EQUAL(int(splus_expected), int(splus)); + CHECK_EQUAL(int(sminus_expected), int(sminus)); + } + + //************************************************************************* + TEST(test_to_signed_from_uint16_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint16_t(50)); + auto sminus = etl::to_signed(uint16_t(65486)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(65486); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint32_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint32_t(50)); + auto sminus = etl::to_signed(uint32_t(4294967246U)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(4294967246U); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint64_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint64_t(50)); + auto sminus = etl::to_signed(uint64_t(18446744073709551566ULL)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(18446744073709551566ULL); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_unsigned_constexpr) + { + const int8_t value = static_cast(-50); + + constexpr auto Size = etl::to_unsigned(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } + + //************************************************************************* + TEST(test_to_signed_constexpr) + { + const uint8_t value = static_cast(50); + + constexpr auto Size = etl::to_signed(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } } } // namespace diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 5b558e05..8e81a606 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -321,6 +321,26 @@ namespace bool copied_to; }; + //********************************************* + // Copy constructible, but copy assignment is deleted. + struct CopyConstructibleNonCopyAssignable + { + CopyConstructibleNonCopyAssignable() = default; + CopyConstructibleNonCopyAssignable(const CopyConstructibleNonCopyAssignable&) = default; + CopyConstructibleNonCopyAssignable& operator=(const CopyConstructibleNonCopyAssignable&) = delete; + }; + + //********************************************* + // Move constructible, but move assignment is deleted. + struct MoveConstructibleNonMoveAssignable + { + MoveConstructibleNonMoveAssignable() = default; + MoveConstructibleNonMoveAssignable(MoveConstructibleNonMoveAssignable&&) = default; + MoveConstructibleNonMoveAssignable& operator=(MoveConstructibleNonMoveAssignable&&) = delete; + MoveConstructibleNonMoveAssignable(const MoveConstructibleNonMoveAssignable&) = delete; + MoveConstructibleNonMoveAssignable& operator=(const MoveConstructibleNonMoveAssignable&) = delete; + }; + //********************************************* // Trivially destructible, but non-copyable and non-movable. // Exercises the trivially-destructible (variadic_union) emplace path. @@ -420,6 +440,109 @@ namespace int value; }; + + //********************************************* + // Non-trivially destructible type whose copy/move ASSIGNMENT has a + // distinguishable effect from copy/move CONSTRUCTION. Used to verify that + // same-type variant assignment dispatches through the alternative's own + // assignment operator (no destroy + reconstruct). Exercises the + // uninitialized_buffer (non-trivial) path. + struct AssignTracker + { + AssignTracker(int value_) + : value(value_) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + AssignTracker(const AssignTracker& other) + : value(other.value) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + AssignTracker(AssignTracker&& other) + : value(other.value) + , constructed(true) + , copy_assigned(false) + , move_assigned(false) + { + } + + ~AssignTracker() {} + + AssignTracker& operator=(const AssignTracker& rhs) + { + value = rhs.value; + copy_assigned = true; + move_assigned = false; + return *this; + } + + AssignTracker& operator=(AssignTracker&& rhs) + { + value = rhs.value; + copy_assigned = false; + move_assigned = true; + return *this; + } + + int value; + bool constructed; + bool copy_assigned; + bool move_assigned; + }; + + //********************************************* + // Trivially destructible variant of AssignTracker (no user destructor) that + // exercises the trivially-destructible (variadic_union) path. + struct TrivialAssignTracker + { + TrivialAssignTracker(int value_) + : value(value_) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker(const TrivialAssignTracker& other) + : value(other.value) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker(TrivialAssignTracker&& other) + : value(other.value) + , copy_assigned(false) + , move_assigned(false) + { + } + + TrivialAssignTracker& operator=(const TrivialAssignTracker& rhs) + { + value = rhs.value; + copy_assigned = true; + move_assigned = false; + return *this; + } + + TrivialAssignTracker& operator=(TrivialAssignTracker&& rhs) + { + value = rhs.value; + copy_assigned = false; + move_assigned = true; + return *this; + } + + int value; + bool copy_assigned; + bool move_assigned; + }; } // namespace // Moved from the top of the file otherwise clang has issues with @@ -560,6 +683,26 @@ struct etl::is_move_constructible : public etl::false_type template <> struct etl::is_move_constructible : public etl::true_type { +}; + +template <> +struct etl::is_copy_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_move_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_copy_constructible : public etl::true_type +{ +}; + +template <> +struct etl::is_move_constructible : public etl::true_type +{ }; #endif @@ -954,6 +1097,71 @@ namespace } #endif + #if ETL_USING_EXCEPTIONS + //************************************************************************* + // A throwing emplace() must leave the variant valueless-after-exception + // (index() == variant_npos) rather than reporting the stale old index. + struct ThrowOnConstruct + { + struct exception + { + }; + + ThrowOnConstruct() = default; + explicit ThrowOnConstruct(int) + { + throw exception(); + } + }; + + TEST(test_emplace_throwing_is_valueless_by_exception) + { + // Trivially destructible suite (variadic_union storage). + { + etl::variant v; + v.emplace(42); + CHECK(!v.valueless_by_exception()); + CHECK_EQUAL(0U, v.index()); + + bool threw = false; + try + { + v.emplace(1); + } + catch (const ThrowOnConstruct::exception&) + { + threw = true; + } + + CHECK(threw); + CHECK(v.valueless_by_exception()); + CHECK_EQUAL(etl::variant_npos, v.index()); + } + + // Non-trivially destructible suite (uninitialized_buffer storage). + { + etl::variant v; + v.emplace("Some Text"); + CHECK(!v.valueless_by_exception()); + CHECK_EQUAL(0U, v.index()); + + bool threw = false; + try + { + v.emplace<1>(1); + } + catch (const ThrowOnConstruct::exception&) + { + threw = true; + } + + CHECK(threw); + CHECK(v.valueless_by_exception()); + CHECK_EQUAL(etl::variant_npos, v.index()); + } + } + #endif + //************************************************************************* TEST(test_copy_constructor) { @@ -999,6 +1207,175 @@ namespace CHECK_EQUAL(variant_1_etl.index(), variant_2_etl.index()); } + //************************************************************************* + // Issue #1512: a variant must only be copy constructible / copy assignable + // when every alternative is copy constructible. Moveable is move-only + // (its copy operations are deleted), so a variant containing it must not + // be copyable, while remaining move constructible / move assignable. + TEST(test_copy_disabled_with_non_copyable_alternative) + { + typedef etl::variant non_copyable_variant; + + CHECK(!etl::is_copy_constructible::value); + CHECK(!etl::is_copy_assignable::value); + CHECK(etl::is_move_constructible::value); + CHECK(etl::is_move_assignable::value); + + // A variant of purely copyable alternatives must remain copyable. + typedef etl::variant copyable_variant; + + CHECK(etl::is_copy_constructible::value); + CHECK(etl::is_copy_assignable::value); + } + + //************************************************************************* + // A non-copy-assignable alternative must disable the variant's copy + // assignment operator, and a non-move-assignable alternative must disable + // the variant's move assignment operator, while leaving the corresponding + // constructors available. + TEST(test_assignment_disabled_with_non_assignable_alternative) + { + typedef etl::variant copy_non_assignable_variant; + + CHECK(etl::is_copy_constructible::value); + CHECK(!etl::is_copy_assignable::value); + + typedef etl::variant move_non_assignable_variant; + + CHECK(etl::is_move_constructible::value); + CHECK(!etl::is_move_assignable::value); + } + + //************************************************************************* + // When source and destination hold the SAME alternative, assignment must + // dispatch through that alternative's own assignment operator rather than + // destroying and reconstructing it. Non-trivially-destructible path. + TEST(test_copy_assign_same_type_uses_alternative_assignment) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + } + + //************************************************************************* + TEST(test_move_assign_same_type_uses_alternative_assignment) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = etl::move(variant_2); + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(!etl::get(variant_1).copy_assigned); + CHECK(etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // When source and destination hold DIFFERENT alternatives, assignment must + // still destroy and reconstruct. + TEST(test_copy_assign_different_type_reconstructs) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 5); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).constructed); + CHECK(!etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // Same as above but for the trivially-destructible (variadic_union) path. + TEST(test_assign_same_type_uses_alternative_assignment_trivial) + { + typedef etl::variant tracker_variant; + + tracker_variant variant_1(etl::in_place_type_t{}, 1); + tracker_variant variant_2(etl::in_place_type_t{}, 2); + + variant_1 = variant_2; + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(2, etl::get(variant_1).value); + CHECK(etl::get(variant_1).copy_assigned); + CHECK(!etl::get(variant_1).move_assigned); + + tracker_variant variant_3(etl::in_place_type_t{}, 3); + variant_1 = etl::move(variant_3); + + CHECK_EQUAL(1U, variant_1.index()); + CHECK_EQUAL(3, etl::get(variant_1).value); + CHECK(etl::get(variant_1).move_assigned); + } + + //************************************************************************* + // Issue #1567: the variant propagates trivial copyability from its + // alternatives, as required by P0602R4. + TEST(test_trivially_copyable) + { + // Fundamental types are trivially copyable in every ETL configuration. + CHECK((std::is_trivially_copyable >::value)); + CHECK((std::is_trivially_copy_constructible >::value)); + CHECK((std::is_trivially_move_constructible >::value)); + CHECK((std::is_trivially_copy_assignable >::value)); + CHECK((std::is_trivially_move_assignable >::value)); + + // An alternative that is not trivially copyable prevents propagation. + CHECK((!std::is_trivially_copyable >::value)); + + // The variant still copies and moves correctly. + typedef etl::variant trivial_variant; + + trivial_variant variant_1(1.5); + trivial_variant variant_2(variant_1); + CHECK_EQUAL(2U, variant_2.index()); + CHECK_EQUAL(1.5, etl::get(variant_2)); + + trivial_variant variant_3; + variant_3 = variant_2; + CHECK_EQUAL(2U, variant_3.index()); + CHECK_EQUAL(1.5, etl::get(variant_3)); + } + + //************************************************************************* + // Issue #1512: even though the copy operations are disabled at compile + // time, a move-only variant must still move construct / move assign + // correctly, preserving the active alternative. + TEST(test_move_only_variant_moves_correctly) + { + typedef etl::variant non_copyable_variant; + + non_copyable_variant variant_1(etl::in_place_type_t{}); + CHECK_EQUAL(1U, variant_1.index()); + + // Move construction preserves the active alternative. + non_copyable_variant variant_2(etl::move(variant_1)); + CHECK_EQUAL(1U, variant_2.index()); + CHECK(etl::get(variant_2).moved_to); + + // Move assignment preserves the active alternative. + non_copyable_variant variant_3; + variant_3 = etl::move(variant_2); + CHECK_EQUAL(1U, variant_3.index()); + CHECK(etl::get(variant_3).moved_to); + } + //************************************************************************* TEST(test_assign_from_value) { diff --git a/test/test_vector.cpp b/test/test_vector.cpp index b9957e5d..32799337 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -1143,9 +1143,12 @@ namespace Compare_Data compare_data(initial_data.begin(), initial_data.end()); Data data(initial_data.begin(), initial_data.end()); - compare_data.erase(compare_data.begin() + 2, compare_data.begin() + 4); + Compare_Data::iterator cdi = compare_data.erase(compare_data.begin() + 2, compare_data.begin() + 4); - data.erase(data.begin() + 2, data.begin() + 4); + Data::iterator di = data.erase(data.begin() + 2, data.begin() + 4); + + CHECK(cdi == compare_data.begin() + 2); + CHECK(di == data.begin() + 2); CHECK_EQUAL(compare_data.size(), data.size()); @@ -1528,8 +1531,12 @@ namespace { auto data = etl::make_vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + // The explicit element type now selects the element type for all + // arguments (Library Fundamentals TS make_array design). Previously it + // pinned only the first argument's deduced type, and the element type + // was still the common type of the arguments - int for this call. using Type = typename std::remove_reference::type; - CHECK((std::is_same::value)); + CHECK((std::is_same::value)); CHECK_EQUAL(0, data[0]); CHECK_EQUAL(1, data[1]); @@ -1564,6 +1571,52 @@ namespace } #endif + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // Arguments that are const-qualified lvalues must be accepted and must not + // affect the deduced element type. Guards against the defect fixed for + // make_array and make_deque, which forwarded each argument as the element + // type rather than as its own deduced type and so rejected const lvalues. + TEST(test_make_vector_from_const_lvalues_of_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue); + + using Type = typename std::remove_reference::type; + CHECK((std::is_same::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + } +#endif + + //************************************************************************* +#if ETL_HAS_INITIALIZER_LIST + // An explicit element type selects the element type and converts every + // argument (Library Fundamentals TS make_array design), accepting + // const-qualified lvalues and narrowing initialisers. + TEST(test_make_vector_explicit_element_type) + { + static const char static_const_lvalue = 42; + const char local_const_lvalue = 43; + char mutable_lvalue = 44; + + auto data = etl::make_vector(static_const_lvalue, local_const_lvalue, mutable_lvalue, 45, 46); + + CHECK((std::is_same, decltype(data)>::value)); + + CHECK_EQUAL(42, data[0]); + CHECK_EQUAL(43, data[1]); + CHECK_EQUAL(44, data[2]); + CHECK_EQUAL(45, data[3]); + CHECK_EQUAL(46, data[4]); + } +#endif + //************************************************************************* TEST(test_fill) { diff --git a/test/test_vector_function_pointer.cpp b/test/test_vector_function_pointer.cpp index 62b18a71..0f95ae4f 100644 --- a/test/test_vector_function_pointer.cpp +++ b/test/test_vector_function_pointer.cpp @@ -301,7 +301,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_function_pointer_external_buffer.cpp b/test/test_vector_function_pointer_external_buffer.cpp index 4a6d5a86..70ef97ec 100644 --- a/test/test_vector_function_pointer_external_buffer.cpp +++ b/test/test_vector_function_pointer_external_buffer.cpp @@ -288,7 +288,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_member_function_pointer.cpp b/test/test_vector_member_function_pointer.cpp index a1828db7..6b8e1512 100644 --- a/test/test_vector_member_function_pointer.cpp +++ b/test/test_vector_member_function_pointer.cpp @@ -311,7 +311,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/test_vector_member_function_pointer_external_buffer.cpp b/test/test_vector_member_function_pointer_external_buffer.cpp index 644fbf92..b94358c3 100644 --- a/test/test_vector_member_function_pointer_external_buffer.cpp +++ b/test/test_vector_member_function_pointer_external_buffer.cpp @@ -316,7 +316,7 @@ namespace CHECK_EQUAL(0U, data.size()); CHECK_EQUAL(initial_data.size(), other_data.size()); - bool is_equal = std::equal(data.begin(), data.end(), other_data.begin()); + bool is_equal = std::equal(initial_data.begin(), initial_data.end(), other_data.begin()); CHECK(is_equal); } diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 503563eb..7a69c37e 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3764,6 +3764,7 @@ + @@ -6303,6 +6304,7 @@ true true + true true @@ -10446,6 +10448,7 @@ + @@ -11804,4 +11807,4 @@ - \ No newline at end of file + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index f20f1a7a..10afee24 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -261,6 +261,9 @@ ETL\Utilities + + ETL\Utilities + ETL\Utilities @@ -2087,6 +2090,9 @@ Tests\Algorithms + + Tests\Algorithms + Tests\Algorithms @@ -2909,6 +2915,9 @@ Tests\Syntax Checks\Source + + Tests\Syntax Checks\Source + Tests\Syntax Checks\Source @@ -4115,4 +4124,4 @@ Resource Files\Images - \ No newline at end of file + diff --git a/version.txt b/version.txt index 09bbfc15..e38061bf 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -20.48.1 +20.49.0