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/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml index 648e0bcb..1521793d 100644 --- a/.github/workflows/bazel-gcc-c++23-no-stl.yml +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -44,7 +44,7 @@ jobs: - 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 6ca727ef..39c0a0a4 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -50,7 +50,6 @@ jobs: 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)" @@ -79,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)" @@ -101,7 +99,6 @@ jobs: 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)" diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index 3ddb0cfa..e7909302 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -50,7 +50,6 @@ jobs: 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)" @@ -79,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)" @@ -101,7 +99,6 @@ jobs: 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)" diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index ccec532b..54b05957 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -50,7 +50,6 @@ jobs: 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)" @@ -79,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)" @@ -101,7 +99,6 @@ jobs: 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)" diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index 0d3fa3f7..eb6d6bf5 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -62,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)" @@ -91,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)" @@ -120,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)" @@ -149,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)" @@ -171,7 +167,6 @@ jobs: 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)" @@ -193,7 +188,6 @@ jobs: 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)" @@ -215,7 +209,6 @@ jobs: 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)" @@ -237,7 +230,6 @@ jobs: 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)" diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index d25a346c..97ba5af3 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -57,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: @@ -79,8 +86,7 @@ jobs: 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)" @@ -101,8 +107,7 @@ jobs: 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)" @@ -123,8 +128,7 @@ jobs: 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)" @@ -145,8 +149,7 @@ jobs: 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)" @@ -167,8 +170,7 @@ jobs: 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)" @@ -189,8 +191,7 @@ jobs: 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)" @@ -211,8 +212,7 @@ jobs: 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)" diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml index e118c66a..5905289a 100644 --- a/.github/workflows/clang-c++26.yml +++ b/.github/workflows/clang-c++26.yml @@ -55,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) && \ @@ -77,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) && \ @@ -99,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) && \ @@ -121,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) && \ @@ -143,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) && \ @@ -163,7 +158,6 @@ jobs: 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)" @@ -185,7 +179,6 @@ jobs: 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)" @@ -207,7 +200,6 @@ jobs: 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)" @@ -229,7 +221,6 @@ jobs: 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/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index 4d33a909..71a5809e 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -49,7 +49,6 @@ jobs: - 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 ./ @@ -72,7 +71,6 @@ jobs: - 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 bc7cca67..0cb6dc2c 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -48,7 +48,6 @@ jobs: - 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 ./ @@ -70,7 +69,6 @@ jobs: - 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 ./ diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index 1378fc69..4968a87d 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -48,7 +48,6 @@ jobs: - 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 ./ @@ -70,7 +69,6 @@ jobs: - 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 6e004456..68be97d8 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -48,7 +48,6 @@ jobs: - 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 ./ @@ -70,7 +69,6 @@ jobs: - 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 ./ @@ -92,7 +90,6 @@ jobs: - 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 ./ @@ -114,7 +111,6 @@ jobs: - 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 ./ diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index a7e29e56..65cf7dc4 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -48,7 +48,6 @@ jobs: - 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 ./ @@ -70,7 +69,6 @@ jobs: - 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 ./ @@ -92,7 +90,6 @@ jobs: - 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 ./ @@ -114,7 +111,6 @@ jobs: - 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 63677261..38506641 100644 --- a/.github/workflows/gcc-c++26.yml +++ b/.github/workflows/gcc-c++26.yml @@ -50,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 ./ && \ @@ -72,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 ./ && \ @@ -94,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 ./ && \ @@ -116,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/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/docs/containers/arrays/array.md b/docs/containers/arrays/array.md index fcf8af32..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) -> etl::array +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 diff --git a/docs/containers/lists/forward-list.md b/docs/containers/lists/forward-list.md index 442d6b51..f51acc9e 100644 --- a/docs/containers/lists/forward-list.md +++ b/docs/containers/lists/forward-list.md @@ -65,15 +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(T&&... t) -> etl::forward_list, sizeof...(T)> +template +constexpr auto make_forward_list(TValues&&... values) -> etl::forward_list ``` -The element type is deduced as the common type of the arguments. +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::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 0ae64e39..598b900b 100644 --- a/docs/containers/lists/list.md +++ b/docs/containers/lists/list.md @@ -69,15 +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(T&&... t) -> etl::list, sizeof...(T)> +template +constexpr auto make_list(TValues&&... values) -> etl::list ``` -The element type is deduced as the common type of the arguments. +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::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 cc3ea886..0231aac5 100644 --- a/docs/containers/maps/flat-map.md +++ b/docs/containers/maps/flat-map.md @@ -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 58bda46b..4fede72c 100644 --- a/docs/containers/maps/flat-multimap.md +++ b/docs/containers/maps/flat-multimap.md @@ -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/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 f326e308..b0bb1ad1 100644 --- a/docs/containers/queues & stacks/deque.md +++ b/docs/containers/queues & stacks/deque.md @@ -34,13 +34,20 @@ Defines data as an `deque` of `int`, of length `10`, containing the supplied dat ## Make template **C++11 and above** ```cpp -template -constexpr auto make_deque(TValues&&... values) -> etl::deque +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 f4200dad..13add134 100644 --- a/docs/containers/sets/const-multiset.md +++ b/docs/containers/sets/const-multiset.md @@ -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 100549f7..dbb3b3ad 100644 --- a/docs/containers/sets/const-set.md +++ b/docs/containers/sets/const-set.md @@ -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 d88e4a17..29bd454d 100644 --- a/docs/containers/sets/flat-multiset.md +++ b/docs/containers/sets/flat-multiset.md @@ -235,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. --- @@ -493,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 e5c08610..4216b30b 100644 --- a/docs/containers/sets/flat-set.md +++ b/docs/containers/sets/flat-set.md @@ -235,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`) --- @@ -248,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. --- @@ -288,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. --- @@ -509,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/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/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/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/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/types/type-traits.md b/docs/types/type-traits.md index 721fdfc3..dc95aca1 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.50.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.50.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.50.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.50.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.50.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.50.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.50.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/expected.md b/docs/utilities/expected.md index f6cbcd63..8443a782 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 @@ -421,6 +421,7 @@ value_type& value() const Get the value. Undefined if has_value() returns false. Not valid for void specialisation. +Before: C++11 --- @@ -536,13 +537,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 +608,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/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/include/etl/array.h b/include/etl/array.h index 1bb92cea..c1d7d6e4 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1052,17 +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)> { - // Forward each argument as its own deduced type, then convert explicitly to T. - // Forwarding as T (the old behaviour) made the call ill-formed for any const-qualified - // lvalue of type T: forward requires a non-const T&, and a T&& cannot bind to a - // reference-related const lvalue. The explicit conversion also keeps narrowing - // initialisers (e.g. make_array(0, 1)) working, which plain forwarding into the - // braced initialiser list would reject. - return {static_cast(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 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/const_map.h b/include/etl/const_map.h index 051e6303..747cc5ed 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -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_} diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index 589fe331..824abf77 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_} diff --git a/include/etl/const_multiset.h b/include/etl/const_multiset.h index 6184f9e7..28e9567d 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_} diff --git a/include/etl/const_set.h b/include/etl/const_set.h index df7f0989..e734de43 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -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_} diff --git a/include/etl/deque.h b/include/etl/deque.h index a35eda34..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,16 +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)> { - // Forward each argument as its own deduced type, then convert explicitly to T. - // Forwarding as T (the old behaviour) made the call ill-formed for any const-qualified - // lvalue of type T: forward requires a non-const T&, and a T&& cannot bind to a - // reference-related const lvalue. The explicit conversion also keeps narrowing - // initialisers (e.g. make_deque(0, 1)) working, which plain forwarding into the - // braced initialiser list would reject. - return {static_cast(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 1feb1487..49b99aad 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -1435,6 +1435,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 +1465,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 30152dc9..8820ac6f 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -1516,7 +1516,7 @@ namespace etl } // for integers - template + template void format_num(OutputIt& it, T value, const format_spec_t& spec) { size_t width = 0; 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/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 7be34fcc..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/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 dc1ada2f..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 @@ -95,7 +96,16 @@ namespace etl 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)); + } } //********************************* @@ -112,7 +122,14 @@ namespace etl //********************************* 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; 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 eeb4b5c7..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 @@ -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/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/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index eafbde77..c351da9c 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -492,7 +492,7 @@ namespace etl //************************************************************************* /// Assignment //************************************************************************* - delegate& operator=(const delegate& rhs) = default; + ETL_CONSTEXPR14 delegate& operator=(const delegate& rhs) = default; //************************************************************************* /// Create from Lambda or Functor. diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index b2b8771e..aaedb064 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -94,6 +94,15 @@ namespace etl 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) @@ -105,6 +114,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) { @@ -156,6 +166,14 @@ namespace etl 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)); @@ -183,6 +201,7 @@ namespace etl } static void move_assign_impl(char*, const char*, etl::false_type) {} + #include "diagnostic_pop.h" }; //******************************************* @@ -555,113 +574,94 @@ 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(const variant_base& other) noexcept(etl::conjunction...>::value) + variant_trivially_destructible_base(const variant_trivially_destructible_base& other) noexcept( + etl::conjunction...>::value) : data() , type_id(other.type_id) { - copy_construct_from(other, is_all_trivially_copyable{}); + copy_construct_from(other); } - variant_base(variant_base&& other) noexcept(etl::conjunction...>::value) + variant_trivially_destructible_base(variant_trivially_destructible_base&& other) noexcept( + etl::conjunction...>::value) : data() , type_id(other.type_id) { - move_construct_from(other, is_all_trivially_copyable{}); + move_construct_from(other); } - variant_base& operator=(const variant_base& other) noexcept(etl::conjunction...>::value - && etl::conjunction...>::value) + 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, is_all_trivially_copyable{}); + copy_assign_from(other); } return *this; } - variant_base& operator=(variant_base&& other) noexcept(etl::conjunction...>::value - && etl::conjunction...>::value) + variant_trivially_destructible_base& + operator=(variant_trivially_destructible_base&& other) noexcept(etl::conjunction...>::value + && etl::conjunction...>::value) { if (this != &other) { - move_assign_from(other, is_all_trivially_copyable{}); + move_assign_from(other); } return *this; } - ~variant_base() = default; + ~variant_trivially_destructible_base() = default; private: - // All alternatives trivially copyable => a raw-byte copy correctly - // reproduces both copy and move construction. - typedef etl::integral_constant...>::value> is_all_trivially_copyable; - //******************************************* - // Trivially copyable fast path: raw-byte copy. + // 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_base& other, etl::true_type) noexcept - { - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void move_construct_from(const variant_base& other, etl::true_type) noexcept - { - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void copy_assign_from(const variant_base& other, etl::true_type) noexcept - { - type_id = other.type_id; - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - void move_assign_from(const variant_base& other, etl::true_type) noexcept - { - type_id = other.type_id; - memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); - } - - //******************************************* - // Non-trivial path: dispatch to the active alternative's own - // copy/move constructor. 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_base& other, etl::false_type) + void copy_construct_from(const variant_trivially_destructible_base& other) { if (other.type_id != variant_npos) { @@ -669,7 +669,7 @@ namespace etl } } - void move_construct_from(variant_base& other, etl::false_type) + void move_construct_from(variant_trivially_destructible_base& other) { if (other.type_id != variant_npos) { @@ -677,7 +677,7 @@ namespace etl } } - void copy_assign_from(const variant_base& other, etl::false_type) + void copy_assign_from(const variant_trivially_destructible_base& other) { if ((type_id != variant_npos) && (type_id == other.type_id)) { @@ -698,7 +698,7 @@ namespace etl } } - void move_assign_from(variant_base& other, etl::false_type) + void move_assign_from(variant_trivially_destructible_base& other) { if ((type_id != variant_npos) && (type_id == other.type_id)) { @@ -720,6 +720,76 @@ namespace etl } }; + //*************************************************************************** + /// 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? //******************************************* diff --git a/include/etl/profiles/determine_builtin_support.h b/include/etl/profiles/determine_builtin_support.h index deadec8c..ad681fb5 100644 --- a/include/etl/profiles/determine_builtin_support.h +++ b/include/etl/profiles/determine_builtin_support.h @@ -53,14 +53,74 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 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_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 @@ -120,14 +180,74 @@ SOFTWARE. #define ETL_USING_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE (__has_builtin(__has_trivial_constructor) || __has_builtin(__is_trivially_constructible)) #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)) #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 @@ -136,6 +256,10 @@ 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 @@ -183,6 +307,50 @@ SOFTWARE. #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. @@ -212,14 +380,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 @@ -260,6 +488,16 @@ 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 @@ -282,10 +520,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); diff --git a/include/etl/signal.h b/include/etl/signal.h index 695bd0c1..eda5dff5 100644 --- a/include/etl/signal.h +++ b/include/etl/signal.h @@ -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/string_view.h b/include/etl/string_view.h index 1178e4c4..81446bcf 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -425,18 +425,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; } //************************************************************************* 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/type_traits.h b/include/etl/type_traits.h index 6ecf9451..ea9823a7 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> @@ -3528,6 +4365,20 @@ namespace etl { }; + //********************************************* + // is_trivially_default_constructible + template + struct is_trivially_default_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + + //********************************************* + // is_trivially_move_constructible + template + struct is_trivially_move_constructible : public etl::bool_constant::value || etl::is_pointer::value> + { + }; + //********************************************* // is_trivially_destructible template @@ -3535,10 +4386,40 @@ namespace etl { }; + //********************************************* + // 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 { }; @@ -3655,12 +4536,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; @@ -3751,6 +4644,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 //*************************************************************************** @@ -3956,6 +4889,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 @@ -4090,6 +5040,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/utility.h b/include/etl/utility.h index 3c7da3a5..f11ccca8 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -340,7 +340,31 @@ namespace etl // 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 @@ -351,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) @@ -359,6 +383,7 @@ namespace etl swap(a[i], b[i]); } } + #endif #endif //*************************************************************************** diff --git a/include/etl/vector.h b/include/etl/vector.h index 2c5b3713..beac6cdc 100644 --- a/include/etl/vector.h +++ b/include/etl/vector.h @@ -1999,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/test/test_array.cpp b/test/test_array.cpp index 8c8fa168..1f8b4f9c 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -77,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) { @@ -861,7 +861,7 @@ namespace } //************************************************************************* -#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_array_template_deduction) { etl::array data{char(0), short(1), 2, long(3), 4, 5, 6, 7, 8, 9}; @@ -883,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), @@ -906,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); @@ -928,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), @@ -951,7 +951,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#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 @@ -974,7 +974,7 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST +#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. @@ -998,7 +998,74 @@ namespace #endif //************************************************************************* -#if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP14 +#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; diff --git a/test/test_circular_buffer.cpp b/test/test_circular_buffer.cpp index 2bbf9feb..cdbd162d 100644 --- a/test/test_circular_buffer.cpp +++ b/test/test_circular_buffer.cpp @@ -67,6 +67,123 @@ namespace CHECK(data.crbegin() == data.crend()); } + //************************************************************************* + 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 cd0c40cd..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) { diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index c4e870bd..60a8965e 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -2183,6 +2183,65 @@ namespace } #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; diff --git a/test/test_deque.cpp b/test/test_deque.cpp index 85365284..d03580bd 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2346,6 +2346,25 @@ namespace } #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) { @@ -2370,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_forward_list.cpp b/test/test_forward_list.cpp index 8687bf82..65d9eb81 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -1556,6 +1556,30 @@ namespace } #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_indirect_vector.cpp b/test/test_indirect_vector.cpp index 2d8033cd..f9973441 100644 --- a/test/test_indirect_vector.cpp +++ b/test/test_indirect_vector.cpp @@ -1644,5 +1644,28 @@ namespace 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 767166e7..45e0d276 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -2377,5 +2377,29 @@ namespace 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_multimap.cpp b/test/test_multimap.cpp index 4273932b..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); } //************************************************************************* 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_string_view.cpp b/test/test_string_view.cpp index a9314dd2..1d016160 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) { 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_type_traits.cpp b/test/test_type_traits.cpp index 531557de..0c031435 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -421,8 +421,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 +696,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) { @@ -1353,6 +2476,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) { @@ -1730,6 +2880,42 @@ 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) { @@ -1746,6 +2932,25 @@ namespace #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 + } + //************************************************************************* TEST(test_is_trivially_copy_assignable) { @@ -1764,6 +2969,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) { diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 0680f79a..8e81a606 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -1324,6 +1324,35 @@ namespace 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 diff --git a/test/test_vector.cpp b/test/test_vector.cpp index 0340c4cb..32799337 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -1531,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]); @@ -1590,6 +1594,29 @@ namespace } #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) {