From 5a077e48b9a494700382da384190599e99ff5cb6 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 7 Apr 2026 12:35:53 +0200 Subject: [PATCH] Add C++26 reflection Status (for both STL and Non-STL builds): - Builds and tests with GCC 16 - Clang 22: Reflection not available yet - MSVC: Reflection not available yet --- .devcontainer/gcc16/Dockerfile | 57 + .devcontainer/gcc16/devcontainer.json | 12 + .github/workflows/gcc16-c++26-reflection.yml | 78 + docs/getting-started/macros.md | 1 + docs/types/meta.md | 97 + include/etl/meta.h | 73 + include/etl/private/meta/gcc.h | 1188 +++++++++ include/etl/private/meta/stl.h | 117 + include/etl/type_traits.h | 5 + test/CMakeLists.txt | 13 + test/meson.build | 1 + test/syntax_check/CMakeLists.txt | 1 + test/syntax_check/meta.h.t.cpp | 40 + test/test_meta.cpp | 2391 ++++++++++++++++++ test/vs2022/etl.vcxproj | 1 + test/vs2022/etl.vcxproj.filters | 3 + 16 files changed, 4078 insertions(+) create mode 100644 .devcontainer/gcc16/Dockerfile create mode 100644 .devcontainer/gcc16/devcontainer.json create mode 100644 .github/workflows/gcc16-c++26-reflection.yml create mode 100644 docs/types/meta.md create mode 100644 include/etl/meta.h create mode 100644 include/etl/private/meta/gcc.h create mode 100644 include/etl/private/meta/stl.h create mode 100644 test/syntax_check/meta.h.t.cpp create mode 100644 test/test_meta.cpp diff --git a/.devcontainer/gcc16/Dockerfile b/.devcontainer/gcc16/Dockerfile new file mode 100644 index 00000000..49c10815 --- /dev/null +++ b/.devcontainer/gcc16/Dockerfile @@ -0,0 +1,57 @@ +# Pinned by digest for reproducible builds. +# To update: docker buildx imagetools inspect debian:sid-slim +FROM debian:sid-slim@sha256:ec3fa4e0b2987ae47be353f56854191e300261915470e40165b1c906d22a65db + +# Pin the package archive to a snapshot.debian.org timestamp so that +# `apt-get install` resolves the same package versions on every build. +# This is the snapshot the base image itself was built from - see the comment +# in /etc/apt/sources.list.d/debian.sources of the pinned image above. +ARG DEBIAN_SNAPSHOT=20260918T000000Z + +# snapshot.debian.org serves archived Release files whose Valid-Until has long +# since passed, so that check has to be disabled. +# http is used rather than https because the slim base image has no +# ca-certificates; integrity is guaranteed by the archive's GPG signature. +RUN printf '%s\n' \ + 'Types: deb' \ + "URIs: http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}/" \ + 'Suites: sid' \ + 'Components: main' \ + 'Check-Valid-Until: no' \ + 'Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp' \ + > /etc/apt/sources.list.d/debian.sources + +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + git \ + ninja-build \ + python3 \ + python3-cogapp \ + gcc-16 \ + g++-16 \ + && rm -rf /var/lib/apt/lists/* + +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-16 100 \ + --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-16 \ + --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-16 \ + --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-16 \ + && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-16 100 + +# Create non-root user with stable UID/GID +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=1000 + +RUN groupadd --gid ${USER_GID} ${USERNAME} && \ + useradd --uid ${USER_UID} --gid ${USER_GID} --shell /bin/bash --create-home ${USERNAME} + +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /etl && chown -R ${USERNAME}:${USERNAME} /etl + +WORKDIR /etl + +# Switch to non-root user +USER ${USERNAME} + +CMD ["/bin/bash"] diff --git a/.devcontainer/gcc16/devcontainer.json b/.devcontainer/gcc16/devcontainer.json new file mode 100644 index 00000000..aa753aa9 --- /dev/null +++ b/.devcontainer/gcc16/devcontainer.json @@ -0,0 +1,12 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +// +// GCC 16 is the reference toolchain for the C++26 static reflection support in +// etl/meta.h. It cannot use the shared ../Dockerfile because there is no +// 'gcc:16' base image yet, so gcc-16 is taken from Debian sid instead. +{ + "name": "Gcc 16", + "build": { + "dockerfile": "./Dockerfile" + } +} diff --git a/.github/workflows/gcc16-c++26-reflection.yml b/.github/workflows/gcc16-c++26-reflection.yml new file mode 100644 index 00000000..3fd5c944 --- /dev/null +++ b/.github/workflows/gcc16-c++26-reflection.yml @@ -0,0 +1,78 @@ +name: gcc16-c++26-reflection +on: + push: + branches: [ master, development, pull-request/* ] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - '.devcontainer/**' + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + paths: + - '.github/workflows/**' + - 'include/**' + - 'test/**' + - 'CMakeLists.txt' + - 'cmake/**' + - '.devcontainer/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + + build-gcc16-cpp26-reflection-stl: + name: GCC 16 C++26 Reflection - STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v6.0.3 + with: + persist-credentials: false + + - name: Build Docker image + run: docker build -t etl-gcc16 -f .devcontainer/gcc16/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/etl etl-gcc16 bash -c "\ + cd /etl && \ + export CC=gcc-16 && \ + export CXX=g++-16 && \ + g++-16 --version && \ + cmake -G Ninja -B /tmp/build-stl -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 -DETL_OPTIMISATION=-O0 /etl/test && \ + grep -q 'ETL_GNU_HAS_FREFLECTION:INTERNAL=1' /tmp/build-stl/CMakeCache.txt || (echo 'ERROR: -freflection was not detected - C++26 reflection is not being tested.' && exit 1) && \ + cmake --build /tmp/build-stl -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest --test-dir /tmp/build-stl --output-on-failure" + + build-gcc16-cpp26-reflection-no-stl: + name: GCC 16 C++26 Reflection - No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v6.0.3 + with: + persist-credentials: false + + - name: Build Docker image + run: docker build -t etl-gcc16 -f .devcontainer/gcc16/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/etl etl-gcc16 bash -c "\ + cd /etl && \ + export CC=gcc-16 && \ + export CXX=g++-16 && \ + g++-16 --version && \ + cmake -G Ninja -B /tmp/build-no-stl -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 -DETL_OPTIMISATION=-O0 /etl/test && \ + grep -q 'ETL_GNU_HAS_FREFLECTION:INTERNAL=1' /tmp/build-no-stl/CMakeCache.txt || (echo 'ERROR: -freflection was not detected - C++26 reflection is not being tested.' && exit 1) && \ + cmake --build /tmp/build-no-stl -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest --test-dir /tmp/build-no-stl --output-on-failure" diff --git a/docs/getting-started/macros.md b/docs/getting-started/macros.md index 89a75c3d..aa741f5a 100644 --- a/docs/getting-started/macros.md +++ b/docs/getting-started/macros.md @@ -103,6 +103,7 @@ These may be defined in the project settings or the user created `etl_profile.h` | `ETL_HAS_IDEQUE_REPAIR` | Set to `1` if the repair functionality for `etl::ideque` is enabled, otherwise `0`.| | `ETL_IS_DEBUG_BUILD` | Set to `1` if in a debug build, otherwise `0`.| | `ETL_HAS_POLYMORPHIC_MESSAGES` | Set to `1` if messages are polymorphic, otherwise `0`.| +| `ETL_HAS_REFLECTION` | Defined by `etl/meta.h`. Set to `1` if the compiler supports C++26 static reflection (P2996) and `etl::meta` is available, otherwise `0`.| | `ETL_HAS_ERROR_ON_STRING_TRUNCATION` | Set to `1` if truncated strings are an error, otherwise `0`.| | `ETL_USING_LIBC_WCHAR_H` | These macros will be defined as `0` & `1` depending on whether `ETL_NO_LIBC_WCHAR_H` is defined or not.| | `ETL_NOT_USING_LIBC_WCHAR_H` | Inversion of `ETL_USING_LIBC_WCHAR_H`.| diff --git a/docs/types/meta.md b/docs/types/meta.md new file mode 100644 index 00000000..4909810c --- /dev/null +++ b/docs/types/meta.md @@ -0,0 +1,97 @@ +--- +title: "meta" +--- + +{{< callout type="info">}} + Header: `meta.h` + From: `20.50.0` + Similar to: `std::meta` +{{< /callout >}} + +C++26 static reflection (P2996) in namespace `etl::meta`. + +`etl::meta` mirrors `std::meta`, but is usable in both STL and non-STL builds. +In an STL build it is a thin layer of using-declarations over `std::meta`. +In a non-STL build ETL supplies the minimal declarations that the compiler's +reflection builtins require, so no standard library is needed. + +```cpp +#include "etl/meta.h" +``` + +## Compiler support + +Static reflection is a very new language feature, and at the time of writing +**GCC 16** is the only compiler that implements enough of P2996 to be usable. +Clang (including the Bloomberg `clang-p2996` fork) and MSVC are not supported. + +`etl/meta.h` may always be included. It defines a single macro, +`ETL_HAS_REFLECTION`, as `1` or `0`; when it is `0` the header defines nothing +else. This is the only reflection feature switch in the ETL, and user code +should test it before using anything in `etl::meta`. + +```cpp +#include "etl/meta.h" + +#if ETL_HAS_REFLECTION + static_assert(etl::meta::identifier_of(^^my_type) == "my_type"); +#endif +``` + +GCC requires the `-freflection` option in addition to `-std=c++26`. +The reference toolchain is defined in `.devcontainer/gcc16`. + +## Usage + +Names are used exactly as their `std::meta` equivalents. + +```cpp +struct point +{ + int x; + int y; +}; + +constexpr auto members = + etl::meta::nonstatic_data_members_of(^^point, + etl::meta::access_context::unchecked()); + +static_assert(etl::meta::identifier_of(members[0]) == "x"); +static_assert(etl::meta::type_of(members[0]) == ^^int); +static_assert(etl::meta::size_of(^^point) == 2 * sizeof(int)); +``` + +`identifier_of`, `display_string_of` and `symbol_of` return `std::string_view` +in an STL build and `etl::string_view` in a non-STL build. + +## info_array + +GCC cannot keep the heap allocation of a `std::vector` alive across a +constant evaluation, so a `template for` over the result of `members_of` does +not compile. `etl::meta::info_array` is a fixed-size, allocation-free array of +`info` that can be iterated instead. + +```cpp +constexpr auto ctx = etl::meta::access_context::unchecked(); +constexpr auto count = etl::meta::nonstatic_data_members_of(^^point, ctx).size(); +constexpr auto members = + etl::meta::to_info_array(etl::meta::nonstatic_data_members_of(^^point, ctx)); + +template for (constexpr auto member : members) +{ + // ... +} +``` + +| Member | Description | +|--------|-------------| +| `begin()` | Pointer to the first element. | +| `end()` | Pointer past the last element. | +| `size()` | The number of elements. | +| `operator[](size_t)` | The element at the index. | + +All members are `consteval`. + +## Tests + +`test_meta.cpp` diff --git a/include/etl/meta.h b/include/etl/meta.h new file mode 100644 index 00000000..d39b02da --- /dev/null +++ b/include/etl/meta.h @@ -0,0 +1,73 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_META_INCLUDED +#define ETL_META_INCLUDED + +#include "platform.h" + +//***************************************************************************** +/// \defgroup meta meta +/// C++26 static reflection (P2996). +/// \ingroup etl +//***************************************************************************** + +//***************************************************************************** +// This is the one and only place where reflection support is detected. +// +// GCC 16 is currently the only compiler that implements enough of P2996 for +// etl::meta, and it does so for both STL and non-STL builds. +// See .devcontainer/gcc16 for the reference toolchain. +// +// Clang (including the Bloomberg p2996 fork) and MSVC are not supported yet. +// +// ETL_HAS_REFLECTION is always defined, as 1 or 0. +// When it is 0 this header defines nothing else, so user code can include +// etl/meta.h unconditionally and test ETL_HAS_REFLECTION. +//***************************************************************************** +#if defined(ETL_COMPILER_GCC) && defined(__cpp_impl_reflection) && (__cpp_impl_reflection >= 202506L) + #define ETL_HAS_REFLECTION 1 +#else + #define ETL_HAS_REFLECTION 0 +#endif + +#if ETL_HAS_REFLECTION + #if ETL_USING_STL + // The full standard library is available, so etl::meta is a thin + // using-declaration layer over std::meta. + #include "private/meta/stl.h" + #else + // No standard library: etl::meta supplies the minimal std declarations + // that the reflection builtins require. + #include "private/meta/gcc.h" + #endif +#endif + +#endif // ETL_META_INCLUDED diff --git a/include/etl/private/meta/gcc.h b/include/etl/private/meta/gcc.h new file mode 100644 index 00000000..9a4642da --- /dev/null +++ b/include/etl/private/meta/gcc.h @@ -0,0 +1,1188 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_PRIVATE_META_GCC_H_INCLUDED +#define ETL_PRIVATE_META_GCC_H_INCLUDED + +#include "../../platform.h" +#include "../../initializer_list.h" +#include "../../placement_new.h" +#include "../../string_view.h" + +///\defgroup meta meta +///\ingroup etl + +// =========================================================================== +// Minimal STL shims for ETL_NO_STL mode. +// +// GCC 16's reflection builtins are declared in terms of a handful of types +// that must live in namespace std. In an ETL_NO_STL build we must NOT pull +// in the corresponding standard library headers, so just enough of each type +// is defined here for consteval reflection use - no heap, no RTTI. +// +// Each shim is skipped when the real header has already been included, which +// is detected with that header's own include guard macro. This only tells us +// what has been included SO FAR, so the shims must come after every other +// include in this file; they do, the ETL includes above are the only ones. +// +// std::allocator is deliberately named as such: GCC's constexpr evaluator has +// hardcoded knowledge of std::allocator::allocate and only permits an +// allocation to persist across a constant evaluation when it is made through +// that exact name. A differently named allocator does not work. +// +// std::initializer_list and placement new are not shimmed here: ETL already +// supplies both, for the STL and non-STL configurations alike, via +// initializer_list.h and placement_new.h included above. +// =========================================================================== + +// The shims below construct objects in place during constant evaluation, which +// requires the standard placement new from . ETL's own fallback +// definition in placement_new.h is not usable in a constant expression. +#if !ETL_USING_STD_NEW + #error etl::meta requires the header - placement new must be usable in a constant expression. +#endif + +// --- std::strong_ordering (minimal three-way comparison result) ----------- +#if !defined(_COMPARE) +namespace std +{ + struct strong_ordering + { + signed char _v; + constexpr explicit strong_ordering(signed char v) noexcept + : _v(v) + { + } + + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + static const strong_ordering greater; + + friend constexpr bool operator==(strong_ordering a, strong_ordering b) noexcept + { + return a._v == b._v; + } + friend constexpr bool operator==(strong_ordering a, decltype(nullptr)) noexcept + { + return a._v == 0; + } + friend constexpr bool operator<(strong_ordering a, decltype(nullptr)) noexcept + { + return a._v < 0; + } + friend constexpr bool operator>(strong_ordering a, decltype(nullptr)) noexcept + { + return a._v > 0; + } + friend constexpr bool operator<=(strong_ordering a, decltype(nullptr)) noexcept + { + return a._v <= 0; + } + friend constexpr bool operator>=(strong_ordering a, decltype(nullptr)) noexcept + { + return a._v >= 0; + } + }; + inline constexpr strong_ordering strong_ordering::less{-1}; + inline constexpr strong_ordering strong_ordering::equal{0}; + inline constexpr strong_ordering strong_ordering::equivalent{0}; + inline constexpr strong_ordering strong_ordering::greater{1}; +} // namespace std +#endif // _COMPARE + +// --- std::allocator (constexpr allocation recognised by GCC) ------------- +// GCC's constexpr evaluator has hardcoded knowledge of std::allocator — +// only allocations through it can persist across constant evaluations. +#if !defined(_GLIBCXX_MEMORY) && !defined(_ALLOCATOR_H) && !defined(_MEMORYFWD_H) +namespace std +{ + template + struct allocator + { + using value_type = T; + using size_type = decltype(sizeof(0)); + + constexpr allocator() noexcept = default; + + constexpr T* allocate(size_type n) + { + return static_cast(::operator new(n * sizeof(T))); + } + constexpr void deallocate(T* p, size_type) + { + ::operator delete(p); + } + }; +} // namespace std +#endif // _GLIBCXX_MEMORY + +// --- std::optional (consteval-only shim) --------------------------------- +#if !defined(_GLIBCXX_OPTIONAL) +namespace std +{ + struct nullopt_t + { + constexpr explicit nullopt_t(int) {} + }; + inline constexpr nullopt_t nullopt{0}; + + template + class optional + { + // A union member, not a byte buffer: reinterpret_cast on raw storage is + // not allowed in a constant expression, and this shim is consteval-only. + union storage_type + { + struct empty_type + { + } _M_empty; + T _M_value; + + constexpr storage_type() noexcept + : _M_empty() + { + } + constexpr ~storage_type() {} + }; + + storage_type _M_storage{}; + bool _M_has = false; + + constexpr T* _M_ptr() noexcept + { + return &_M_storage._M_value; + } + constexpr const T* _M_ptr() const noexcept + { + return &_M_storage._M_value; + } + + constexpr void _M_construct(const T& v) + { + ::new (static_cast(&_M_storage._M_value)) T(v); + _M_has = true; + } + constexpr void _M_construct(T&& v) + { + ::new (static_cast(&_M_storage._M_value)) T(static_cast(v)); + _M_has = true; + } + + public: + + using value_type = T; + + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept {} + constexpr optional(const T& v) + { + _M_construct(v); + } + constexpr optional(T&& v) + { + _M_construct(static_cast(v)); + } + + constexpr optional(const optional& other) + { + if (other._M_has) + { + _M_construct(*other); + } + } + + constexpr optional(optional&& other) + { + if (other._M_has) + { + _M_construct(static_cast(*other._M_ptr())); + } + } + + constexpr optional& operator=(const optional& other) + { + if (this != &other) + { + reset(); + if (other._M_has) + { + _M_construct(*other); + } + } + return *this; + } + + constexpr optional& operator=(optional&& other) + { + if (this != &other) + { + reset(); + if (other._M_has) + { + _M_construct(static_cast(*other._M_ptr())); + } + } + return *this; + } + + constexpr ~optional() + { + reset(); + } + + constexpr void reset() noexcept + { + if (_M_has) + { + _M_ptr()->~T(); + _M_has = false; + } + } + + constexpr bool has_value() const noexcept + { + return _M_has; + } + constexpr explicit operator bool() const noexcept + { + return _M_has; + } + constexpr T& operator*() + { + return *_M_ptr(); + } + constexpr const T& operator*() const + { + return *_M_ptr(); + } + constexpr T* operator->() + { + return _M_ptr(); + } + constexpr const T* operator->() const + { + return _M_ptr(); + } + }; +} // namespace std +#endif // _GLIBCXX_OPTIONAL + +// --- std::vector (consteval-capable shim using std::allocator) ----------- +#if !defined(_GLIBCXX_VECTOR) +namespace std +{ + template > + class vector + { + Alloc _M_alloc{}; + T* _M_begin = nullptr; + decltype(sizeof(0)) _M_size = 0; + decltype(sizeof(0)) _M_cap = 0; + + constexpr void _M_reserve_exact(decltype(sizeof(0)) n) + { + _M_begin = n ? _M_alloc.allocate(n) : nullptr; + _M_cap = n; + _M_size = 0; + } + + constexpr void _M_clear() noexcept + { + for (decltype(sizeof(0)) i = 0; i < _M_size; ++i) + { + (_M_begin + i)->~T(); + } + if (_M_begin) + { + _M_alloc.deallocate(_M_begin, _M_cap); + } + _M_begin = nullptr; + _M_size = 0; + _M_cap = 0; + } + + public: + + using value_type = T; + using size_type = decltype(sizeof(0)); + using iterator = T*; + using const_iterator = const T*; + + constexpr vector() noexcept = default; + constexpr vector(std::initializer_list il) + { + _M_reserve_exact(il.size()); + for (auto& e : il) + { + ::new (static_cast(_M_begin + _M_size++)) T(e); + } + } + + constexpr vector(const vector& other) + { + _M_reserve_exact(other._M_size); + for (size_type i = 0; i < other._M_size; ++i) + { + ::new (static_cast(_M_begin + _M_size++)) T(other._M_begin[i]); + } + } + + constexpr vector(vector&& other) noexcept + : _M_begin(other._M_begin) + , _M_size(other._M_size) + , _M_cap(other._M_cap) + { + other._M_begin = nullptr; + other._M_size = 0; + other._M_cap = 0; + } + + constexpr vector& operator=(const vector& other) + { + if (this != &other) + { + _M_clear(); + _M_reserve_exact(other._M_size); + for (size_type i = 0; i < other._M_size; ++i) + { + ::new (static_cast(_M_begin + _M_size++)) T(other._M_begin[i]); + } + } + return *this; + } + + constexpr vector& operator=(vector&& other) noexcept + { + if (this != &other) + { + _M_clear(); + _M_begin = other._M_begin; + _M_size = other._M_size; + _M_cap = other._M_cap; + other._M_begin = nullptr; + other._M_size = 0; + other._M_cap = 0; + } + return *this; + } + + constexpr ~vector() + { + _M_clear(); + } + + constexpr size_type size() const noexcept + { + return _M_size; + } + constexpr bool empty() const noexcept + { + return _M_size == 0; + } + constexpr T* begin() noexcept + { + return _M_begin; + } + constexpr const T* begin() const noexcept + { + return _M_begin; + } + constexpr T* end() noexcept + { + return _M_begin + _M_size; + } + constexpr const T* end() const noexcept + { + return _M_begin + _M_size; + } + constexpr T& operator[](size_type i) + { + return _M_begin[i]; + } + constexpr const T& operator[](size_type i) const + { + return _M_begin[i]; + } + constexpr void push_back(const T& v) + { + if (_M_size == _M_cap) + { + size_type nc = _M_cap ? _M_cap * 2 : 4; + T* nb = _M_alloc.allocate(nc); + for (size_type i = 0; i < _M_size; ++i) + { + ::new (static_cast(nb + i)) T(static_cast(_M_begin[i])); + (_M_begin + i)->~T(); + } + if (_M_begin) + _M_alloc.deallocate(_M_begin, _M_cap); + _M_begin = nb; + _M_cap = nc; + } + ::new (static_cast(_M_begin + _M_size++)) T(v); + } + }; +} // namespace std +#endif // _GLIBCXX_VECTOR + +// --- std::basic_string / std::string / std::u8string (consteval shim) ---- +#if !defined(_GLIBCXX_STRING) +namespace std +{ + template + class basic_string + { + std::allocator _M_alloc{}; + CharT* _M_data = nullptr; + decltype(sizeof(0)) _M_size = 0; + + constexpr void _M_assign(const CharT* s, decltype(sizeof(0)) n) + { + _M_size = n; + _M_data = _M_alloc.allocate(_M_size + 1); + for (decltype(sizeof(0)) i = 0; i < _M_size; ++i) + { + _M_data[i] = s[i]; + } + _M_data[_M_size] = CharT(); + } + + constexpr void _M_release() noexcept + { + if (_M_data) + { + _M_alloc.deallocate(_M_data, _M_size + 1); + } + _M_data = nullptr; + _M_size = 0; + } + + constexpr void _M_copy_from(const basic_string& other) + { + if (other._M_data) + { + _M_assign(other._M_data, other._M_size); + } + } + + public: + + using value_type = CharT; + using size_type = decltype(sizeof(0)); + + constexpr basic_string() noexcept = default; + + constexpr basic_string(const CharT* s) + { + size_type n = 0; + while (s[n]) ++n; + _M_assign(s, n); + } + + constexpr basic_string(const basic_string& other) + { + _M_copy_from(other); + } + + constexpr basic_string(basic_string&& other) noexcept + : _M_data(other._M_data) + , _M_size(other._M_size) + { + other._M_data = nullptr; + other._M_size = 0; + } + + constexpr basic_string& operator=(const basic_string& other) + { + if (this != &other) + { + _M_release(); + _M_copy_from(other); + } + return *this; + } + + constexpr basic_string& operator=(basic_string&& other) noexcept + { + if (this != &other) + { + _M_release(); + _M_data = other._M_data; + _M_size = other._M_size; + other._M_data = nullptr; + other._M_size = 0; + } + return *this; + } + + constexpr ~basic_string() + { + _M_release(); + } + + constexpr const CharT* data() const noexcept + { + return _M_data; + } + constexpr size_type size() const noexcept + { + return _M_size; + } + constexpr bool empty() const noexcept + { + return _M_size == 0; + } + }; + + using string = basic_string; + using u8string = basic_string; +} // namespace std +#endif // _GLIBCXX_STRING + +// The u8string typedef is missing when the real predates char8_t. +#if defined(_GLIBCXX_STRING) && !defined(__cpp_lib_char8_t) +namespace std +{ + using u8string = basic_string; +} +#endif + +// --- std::constructible_from concept shim -------------------------------- +#if !defined(_GLIBCXX_CONCEPTS) +namespace std +{ + template + concept constructible_from = __is_constructible(T, Args...); +} +#endif // _GLIBCXX_CONCEPTS + +// =========================================================================== +// GCC 16 NO_STL reflection implementation. +// +// GCC 16's reflection builtins are only recognised when declared inside +// namespace std::meta. We declare them there with ETL-friendly return +// types (etl::string_view instead of std::string_view, std::vector +// for list-returning builtins), then using-import every name into +// etl::meta so user code sees etl::meta::foo(). +// =========================================================================== + +namespace std +{ + namespace meta + { + using info = decltype(^^int); + + //************************************************************************* + /// Simple aggregate that GCC can aggregate-initialize for string results. + /// etl::string_view has constructors so it's not an aggregate — GCC ICEs. + /// GCC's builtins aggregate-initialize this as { pointer, length }. + //************************************************************************* + struct __string_result + { + const char* _data = nullptr; + unsigned long _size = 0; + }; + + //************************************************************************* + /// Access control context. + //************************************************************************* + struct access_context + { + private: + + consteval access_context(info scope, info dc) noexcept + : _M_scope{scope} + , _M_designating_class{dc} + { + } + + public: + + access_context() = delete; + consteval access_context(const access_context&) = default; + consteval access_context(access_context&&) = default; + + consteval info scope() const + { + return _M_scope; + } + consteval info designating_class() const + { + return _M_designating_class; + } + + static consteval access_context current() noexcept; + static consteval access_context unprivileged() noexcept + { + return access_context{^^::, info{}}; + } + static consteval access_context unchecked() noexcept + { + return access_context{info{}, info{}}; + } + consteval access_context via(info cls) const + { + return access_context{_M_scope, cls}; + } + + info _M_scope; + info _M_designating_class; + }; + + //************************************************************************* + /// Options for data_member_spec (simplified, no std::optional/string). + //************************************************************************* + struct data_member_options + { + struct _Name + { + template + requires std::constructible_from + consteval _Name(_Tp&& __n) + : _M_is_u8(true) + , _M_u8s(static_cast<_Tp&&>(__n)) + { + } + + template + requires std::constructible_from + consteval _Name(_Tp&& __n) + : _M_is_u8(false) + , _M_s(static_cast<_Tp&&>(__n)) + { + } + + private: + + bool _M_is_u8; + std::u8string _M_u8s; + std::string _M_s; + info _M_unused = {}; + }; + + std::optional<_Name> name; + std::optional alignment = {}; + std::optional bit_width = {}; + bool no_unique_address = false; + std::vector annotations = {}; + }; + + // --- Naming & display ------------------------------------------------ + consteval bool has_identifier(info); + consteval __string_result identifier_of(info); + consteval __string_result display_string_of(info); + + //************************************************************************* + /// Exception type that GCC reflection builtins expect to exist. + //************************************************************************* + class exception + { + public: + + consteval exception() noexcept + : _M_what("") + { + } + consteval exception(const char* w) noexcept + : _M_what(w) + { + } + consteval exception(const exception&) noexcept = default; + consteval const char* what() const noexcept + { + return _M_what; + } + const char* _M_what; + }; + + // TODO: query builtins + + // --- Core queries ---------------------------------------------------- + consteval info type_of(info); + consteval info object_of(info); + consteval info constant_of(info); + consteval bool has_parent(info); + consteval info parent_of(info); + consteval info dealias(info); + consteval bool has_template_arguments(info); + consteval info template_of(info); + consteval std::vector template_arguments_of(info); + consteval std::vector parameters_of(info); + consteval info variable_of(info); + consteval info return_type_of(info); + + // TODO: predicate builtins + + // --- Access predicates ----------------------------------------------- + consteval bool is_accessible(info, access_context); + consteval bool is_public(info); + consteval bool is_protected(info); + consteval bool is_private(info); + + // --- Virtual / override / final -------------------------------------- + consteval bool is_virtual(info); + consteval bool is_pure_virtual(info); + consteval bool is_override(info); + consteval bool is_final(info); + + // --- Function / member qualifiers ------------------------------------ + consteval bool is_deleted(info); + consteval bool is_defaulted(info); + consteval bool is_explicit(info); + consteval bool is_noexcept(info); + consteval bool is_bit_field(info); + consteval bool is_enumerator(info); + consteval bool is_const(info); + consteval bool is_volatile(info); + consteval bool is_mutable_member(info); + consteval bool is_lvalue_reference_qualified(info); + consteval bool is_rvalue_reference_qualified(info); + consteval bool is_user_declared(info); + consteval bool is_user_provided(info); + + // --- Storage duration / linkage -------------------------------------- + consteval bool has_static_storage_duration(info); + consteval bool has_thread_storage_duration(info); + consteval bool has_automatic_storage_duration(info); + consteval bool has_internal_linkage(info); + consteval bool has_module_linkage(info); + consteval bool has_external_linkage(info); + consteval bool has_c_language_linkage(info); + consteval bool has_linkage(info); + + // --- Entity kind predicates ------------------------------------------ + consteval bool is_type(info); + consteval bool is_namespace(info); + consteval bool is_namespace_alias(info); + consteval bool is_function(info); + consteval bool is_variable(info); + consteval bool is_type_alias(info); + consteval bool is_complete_type(info); + consteval bool is_enumerable_type(info); + + // --- Function kind predicates ---------------------------------------- + consteval bool is_conversion_function(info); + consteval bool is_operator_function(info); + consteval bool is_literal_operator(info); + consteval bool is_special_member_function(info); + consteval bool is_constructor(info); + consteval bool is_default_constructor(info); + consteval bool is_copy_constructor(info); + consteval bool is_move_constructor(info); + consteval bool is_assignment(info); + consteval bool is_copy_assignment(info); + consteval bool is_move_assignment(info); + consteval bool is_destructor(info); + + // --- Parameter predicates (P3096) ------------------------------------ + consteval bool is_function_parameter(info); + consteval bool is_explicit_object_parameter(info); + consteval bool has_default_argument(info); + consteval bool has_ellipsis_parameter(info); + + // --- Template predicates --------------------------------------------- + consteval bool is_template(info); + consteval bool is_function_template(info); + consteval bool is_variable_template(info); + consteval bool is_class_template(info); + consteval bool is_alias_template(info); + consteval bool is_conversion_function_template(info); + consteval bool is_operator_function_template(info); + consteval bool is_literal_operator_template(info); + consteval bool is_constructor_template(info); + consteval bool is_concept(info); + + // --- Value / object / membership predicates -------------------------- + consteval bool is_value(info); + consteval bool is_object(info); + consteval bool is_structured_binding(info); + consteval bool is_class_member(info); + consteval bool is_namespace_member(info); + consteval bool is_nonstatic_data_member(info); + consteval bool is_static_member(info); + consteval bool is_base(info); + consteval bool has_default_member_initializer(info); + + // --- Accessibility queries ------------------------------------------- + consteval bool has_inaccessible_nonstatic_data_members(info, access_context); + consteval bool has_inaccessible_bases(info, access_context); + consteval bool has_inaccessible_subobjects(info, access_context); + + // TODO: member query builtins + + // --- Member / base / enumerator queries ------------------------------ + consteval std::vector members_of(info, access_context); + consteval std::vector bases_of(info, access_context); + consteval std::vector static_data_members_of(info, access_context); + consteval std::vector nonstatic_data_members_of(info, access_context); + consteval std::vector subobjects_of(info, access_context); + consteval std::vector enumerators_of(info); + + // TODO: type-trait mirrors + + // --- Primary type categories ----------------------------------------- + consteval bool is_void_type(info); + consteval bool is_null_pointer_type(info); + consteval bool is_integral_type(info); + consteval bool is_floating_point_type(info); + consteval bool is_array_type(info); + consteval bool is_pointer_type(info); + consteval bool is_lvalue_reference_type(info); + consteval bool is_rvalue_reference_type(info); + consteval bool is_member_object_pointer_type(info); + consteval bool is_member_function_pointer_type(info); + consteval bool is_enum_type(info); + consteval bool is_union_type(info); + consteval bool is_class_type(info); + consteval bool is_function_type(info); + consteval bool is_reflection_type(info); + + // --- Composite type categories --------------------------------------- + consteval bool is_reference_type(info); + consteval bool is_arithmetic_type(info); + consteval bool is_fundamental_type(info); + consteval bool is_object_type(info); + consteval bool is_scalar_type(info); + consteval bool is_compound_type(info); + consteval bool is_member_pointer_type(info); + + // --- Type properties ------------------------------------------------- + consteval bool is_const_type(info); + consteval bool is_volatile_type(info); + consteval bool is_trivially_copyable_type(info); + consteval bool is_standard_layout_type(info); + consteval bool is_empty_type(info); + consteval bool is_polymorphic_type(info); + consteval bool is_abstract_type(info); + consteval bool is_final_type(info); + consteval bool is_aggregate_type(info); + consteval bool is_signed_type(info); + consteval bool is_unsigned_type(info); + consteval bool is_bounded_array_type(info); + consteval bool is_unbounded_array_type(info); + consteval bool is_scoped_enum_type(info); + consteval bool is_structural_type(info); + + // --- Constructible / assignable / destructible ----------------------- + template > + consteval bool is_constructible_type(info, Rg&&); + consteval bool is_default_constructible_type(info); + consteval bool is_copy_constructible_type(info); + consteval bool is_move_constructible_type(info); + consteval bool is_assignable_type(info, info); + consteval bool is_copy_assignable_type(info); + consteval bool is_move_assignable_type(info); + consteval bool is_swappable_with_type(info, info); + consteval bool is_swappable_type(info); + consteval bool is_destructible_type(info); + + template > + consteval bool is_trivially_constructible_type(info, Rg&&); + consteval bool is_trivially_default_constructible_type(info); + consteval bool is_trivially_copy_constructible_type(info); + consteval bool is_trivially_move_constructible_type(info); + consteval bool is_trivially_assignable_type(info, info); + consteval bool is_trivially_copy_assignable_type(info); + consteval bool is_trivially_move_assignable_type(info); + consteval bool is_trivially_destructible_type(info); + + template > + consteval bool is_nothrow_constructible_type(info, Rg&&); + consteval bool is_nothrow_default_constructible_type(info); + consteval bool is_nothrow_copy_constructible_type(info); + consteval bool is_nothrow_move_constructible_type(info); + consteval bool is_nothrow_assignable_type(info, info); + consteval bool is_nothrow_copy_assignable_type(info); + consteval bool is_nothrow_move_assignable_type(info); + consteval bool is_nothrow_swappable_with_type(info, info); + consteval bool is_nothrow_swappable_type(info); + consteval bool is_nothrow_destructible_type(info); + + consteval bool is_implicit_lifetime_type(info); + consteval bool has_virtual_destructor(info); + consteval bool has_unique_object_representations(info); + + // --- Property queries ------------------------------------------------ + consteval size_t rank(info); + consteval size_t extent(info, unsigned = 0); + + // --- Type relations -------------------------------------------------- + consteval bool is_same_type(info, info); + consteval bool is_base_of_type(info, info); + consteval bool is_convertible_type(info, info); + consteval bool is_nothrow_convertible_type(info, info); + consteval bool is_layout_compatible_type(info, info); + + // TODO: transforms + layout + extract + + // --- Type transformations -------------------------------------------- + consteval info add_const(info); + consteval info add_volatile(info); + consteval info add_cv(info); + consteval info remove_const(info); + consteval info remove_volatile(info); + consteval info remove_cv(info); + consteval info add_lvalue_reference(info); + consteval info add_rvalue_reference(info); + consteval info remove_reference(info); + consteval info add_pointer(info); + consteval info remove_pointer(info); + consteval info remove_cvref(info); + consteval info decay(info); + consteval info make_signed(info); + consteval info make_unsigned(info); + consteval info remove_extent(info); + consteval info remove_all_extents(info); + consteval info underlying_type(info); + consteval info unwrap_reference(info); + consteval info unwrap_ref_decay(info); + consteval std::strong_ordering type_order(info, info); + template > + consteval info common_type(Rg&&); + + // --- Layout queries -------------------------------------------------- + struct member_offset + { + ptrdiff_t bytes; + ptrdiff_t bits; + constexpr ptrdiff_t total_bits() const + { + return bytes * __CHAR_BIT__ + bits; + } + friend constexpr bool operator==(const member_offset& a, const member_offset& b) noexcept + { + return a.bytes == b.bytes && a.bits == b.bits; + } + friend constexpr bool operator!=(const member_offset& a, const member_offset& b) noexcept + { + return !(a == b); + } + friend constexpr bool operator<(const member_offset& a, const member_offset& b) noexcept + { + return a.total_bits() < b.total_bits(); + } + }; + consteval member_offset offset_of(info); + consteval size_t size_of(info); + consteval size_t alignment_of(info); + consteval size_t bit_size_of(info); + + // --- Value extraction / injection ------------------------------------ + template + consteval T extract(info); + template + consteval info reflect_constant(T); + template + consteval info reflect_object(T&); + template + consteval info reflect_function(T&); + + // --- Substitution ---------------------------------------------------- + template > + consteval bool can_substitute(info, Rg&&); + template > + consteval info substitute(info, Rg&&); + + // --- reflect_invoke -------------------------------------------------- + // Not yet implemented in GCC 16 trunk. + + // --- define_aggregate / data_member_spec ----------------------------- + consteval info data_member_spec(info, data_member_options); + consteval bool is_data_member_spec(info); + template > + consteval info define_aggregate(info, Rg&&); + + // --- Extra predicates ------------------------------------------------ + // is_structural_type is supported by GCC 16 and is declared with the + // other type properties above. is_access_specified is not yet + // implemented in GCC 16. + + // --- Operator queries (declared here so the builtin is in std::meta) - + enum class operators + { + op_new = 1, + op_delete, + op_array_new, + op_array_delete, + op_co_await, + op_parentheses, + op_square_brackets, + op_arrow, + op_arrow_star, + op_tilde, + op_exclamation, + op_plus, + op_minus, + op_star, + op_slash, + op_percent, + op_caret, + op_ampersand, + op_equals, + op_pipe, + op_plus_equals, + op_minus_equals, + op_star_equals, + op_slash_equals, + op_percent_equals, + op_caret_equals, + op_ampersand_equals, + op_pipe_equals, + op_equals_equals, + op_exclamation_equals, + op_less, + op_greater, + op_less_equals, + op_greater_equals, + op_spaceship, + op_ampersand_ampersand, + op_pipe_pipe, + op_less_less, + op_greater_greater, + op_less_less_equals, + op_greater_greater_equals, + op_plus_plus, + op_minus_minus, + op_comma + }; + consteval operators operator_of(info); + consteval __string_result symbol_of(operators); + + } // namespace meta +} // namespace std + +namespace etl +{ + namespace meta + { + // Import everything from std::meta into etl::meta + using namespace std::meta; + + // Also pull in the operators enumerators for unqualified use + using enum std::meta::operators; + + // Wrap string-returning builtins to return etl::string_view + // (std::meta versions return __string_result aggregate because + // etl::string_view is not an aggregate and GCC ICEs on it) + namespace private_meta + { + //********************************************************************* + /// GCC initialises the string-returning builtins' result from a single + /// NUL-terminated 'const char*', exactly as it would initialise a + /// std::string_view from a character pointer. Only __string_result's + /// first member is written, so the length must be recovered here. + //********************************************************************* + consteval etl::string_view to_string_view(std::meta::__string_result s) + { + if (s._data == nullptr) + { + // Avoid pointer arithmetic on nullptr, which is ill-formed in a + // constant expression. + return etl::string_view(); + } + + unsigned long length = s._size; + + if (length == 0) + { + while (s._data[length] != '\0') + { + ++length; + } + } + + return etl::string_view(s._data, length); + } + } // namespace private_meta + + consteval etl::string_view identifier_of(info r) + { + return private_meta::to_string_view(std::meta::identifier_of(r)); + } + + consteval etl::string_view display_string_of(info r) + { + return private_meta::to_string_view(std::meta::display_string_of(r)); + } + + consteval etl::string_view symbol_of(operators op) + { + return private_meta::to_string_view(std::meta::symbol_of(op)); + } + + //************************************************************************* + /// info_array — fixed-size, zero-allocation array of info values. + /// + /// GCC 16 limitation: template-for over a std::vector fails + /// because the vector's heap allocation cannot persist in a constant + /// expression. The workaround is to copy the elements into a plain + /// aggregate array (no allocation) and iterate that instead. + /// + /// Usage pattern: + /// constexpr auto N = std::meta::members_of(^^T, ctx).size(); + /// constexpr auto arr = etl::meta::to_info_array( + /// std::meta::members_of(^^T, ctx)); + /// template for (constexpr auto m : arr) { ... } + //************************************************************************* + template + struct info_array + { + info elems[N > 0 ? N : 1]{}; + consteval const info* begin() const + { + return elems; + } + consteval const info* end() const + { + return elems + N; + } + consteval size_t size() const + { + return N; + } + consteval const info& operator[](size_t i) const + { + return elems[i]; + } + }; + + template + consteval info_array to_info_array(const std::vector& v) + { + info_array a{}; + for (size_t i = 0; i < N; ++i) a.elems[i] = v[i]; + return a; + } + + } // namespace meta +} // namespace etl + +#endif // ETL_PRIVATE_META_GCC_H_INCLUDED diff --git a/include/etl/private/meta/stl.h b/include/etl/private/meta/stl.h new file mode 100644 index 00000000..107db1a7 --- /dev/null +++ b/include/etl/private/meta/stl.h @@ -0,0 +1,117 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_PRIVATE_META_STL_H_INCLUDED +#define ETL_PRIVATE_META_STL_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include // Workaround for GCC 16: depends on but does not include it. + +///\defgroup meta meta +///\ingroup etl + +namespace etl +{ + namespace meta + { + //************************************************************************* + // Standard path, used when the full standard library is available. + // + // A using-directive is sufficient here: qualified lookup for + // etl::meta::foo also searches the namespaces nominated by the + // using-directives of etl::meta ([namespace.qual]), so every std::meta + // name - including the function templates such as extract<> - is + // reachable as etl::meta::name. This mirrors the non-STL implementation + // in gcc.h, which uses the same mechanism. + //************************************************************************* + using namespace std::meta; + + // Enumerators of std::meta::operators (op_equals_equals, etc.) are scoped, + // so they need a using-enum-declaration rather than the using-directive. + using enum std::meta::operators; + + //************************************************************************* + /// info_array — fixed-size, zero-allocation array of info values. + /// + /// GCC limitation: template-for over a std::vector fails because the + /// vector's heap allocation cannot persist in a constant expression. The + /// workaround is to copy the elements into a plain aggregate array (no + /// allocation) and iterate that instead. + /// + /// Usage pattern: + /// constexpr auto N = std::meta::members_of(^^T, ctx).size(); + /// constexpr auto arr = etl::meta::to_info_array( + /// std::meta::members_of(^^T, ctx)); + /// template for (constexpr auto m : arr) { ... } + //************************************************************************* + template + struct info_array + { + info elems[N > 0 ? N : 1]{}; + consteval const info* begin() const + { + return elems; + } + consteval const info* end() const + { + return elems + N; + } + consteval size_t size() const + { + return N; + } + consteval const info& operator[](size_t i) const + { + return elems[i]; + } + }; + + template + consteval info_array to_info_array(const std::vector& v) + { + info_array a{}; + for (size_t i = 0; i < N; ++i) + { + a.elems[i] = v[i]; + } + return a; + } + + } // namespace meta +} // namespace etl + +#endif // ETL_PRIVATE_META_STL_H_INCLUDED diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 827e6be0..c66efa9f 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -3808,9 +3808,14 @@ namespace etl template struct is_nothrow_relocatable { + #if ETL_USING_CPP11 static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value || (etl::is_nothrow_move_constructible::type>::value && etl::is_nothrow_destructible::type>::value); + #else + // No move semantics: only trivially relocatable types are known to be nothrow relocatable. + static ETL_CONSTANT bool value = etl::is_trivially_relocatable::value; + #endif }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a03b5c5c..446bb693 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.10) project(etl_unit_tests LANGUAGES CXX) +include(CheckCXXCompilerFlag) + #include(FetchContent) #FetchContent_Declare( # googletest @@ -232,6 +234,7 @@ add_executable(etl_tests test_map.cpp test_math.cpp test_math_functions.cpp + test_meta.cpp test_mean.cpp test_memory.cpp test_mem_cast.cpp @@ -513,6 +516,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") ${EXTRA_COMPILE_OPTIONS} ) target_link_options(etl_tests PRIVATE ${EXTRA_LINK_OPTIONS}) + + if (ETL_CXX_STANDARD MATCHES "26") + # -freflection is only accepted when compiling as C++26, so probe with that standard. + set(CMAKE_REQUIRED_FLAGS "-std=c++26") + check_cxx_compiler_flag(-freflection ETL_GNU_HAS_FREFLECTION) + unset(CMAKE_REQUIRED_FLAGS) + if (ETL_GNU_HAS_FREFLECTION) + target_compile_options(etl_tests PRIVATE -freflection) + endif () + endif () endif () if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") diff --git a/test/meson.build b/test/meson.build index 16890071..14805d6b 100644 --- a/test/meson.build +++ b/test/meson.build @@ -234,6 +234,7 @@ etl_test_sources = files( 'test_message_timer_atomic.cpp', 'test_message_timer_interrupt.cpp', 'test_message_timer_locked.cpp', + 'test_meta.cpp', 'test_multi_array.cpp', 'test_multi_range.cpp', 'test_multi_span.cpp', diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index a8964e31..2e2a993b 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -289,6 +289,7 @@ target_sources(tests PRIVATE message_timer_interrupt.h.t.cpp message_timer_locked.h.t.cpp message_types.h.t.cpp + meta.h.t.cpp multimap.h.t.cpp multiset.h.t.cpp multi_array.h.t.cpp diff --git a/test/syntax_check/meta.h.t.cpp b/test/syntax_check/meta.h.t.cpp new file mode 100644 index 00000000..f7b5b754 --- /dev/null +++ b/test/syntax_check/meta.h.t.cpp @@ -0,0 +1,40 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +// etl/meta.h must be includable unconditionally, on every compiler and every +// language standard. Where reflection is unavailable it defines nothing +// except ETL_HAS_REFLECTION, which is always defined as either 1 or 0. +#if !defined(ETL_HAS_REFLECTION) + #error ETL_HAS_REFLECTION is not defined by etl/meta.h +#endif + +#if (ETL_HAS_REFLECTION != 0) && (ETL_HAS_REFLECTION != 1) + #error ETL_HAS_REFLECTION must be defined as 0 or 1 +#endif diff --git a/test/test_meta.cpp b/test/test_meta.cpp new file mode 100644 index 00000000..143bf199 --- /dev/null +++ b/test/test_meta.cpp @@ -0,0 +1,2391 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/meta.h" + +#if ETL_HAS_REFLECTION + + //--------------------------------------------------------------------------- + // Helper macros: evaluate consteval-only expressions at compile time, + // then pass the plain bool/value result to the runtime CHECK framework. + // GCC 16's std::meta::info is a consteval-only type and cannot appear + // in runtime expressions such as those generated by CHECK(). + //--------------------------------------------------------------------------- + #define CONSTEXPR_CHECK(expr) \ + do { \ + constexpr bool _etl_chk_ = (expr); \ + CHECK(_etl_chk_); \ + } while (0) + + #define CONSTEXPR_CHECK_FALSE(expr) \ + do { \ + constexpr bool _etl_chk_ = (expr); \ + CHECK(!_etl_chk_); \ + } while (0) + + #define CONSTEXPR_CHECK_EQUAL(expected, expr) \ + do { \ + constexpr auto _etl_val_ = (expr); \ + CHECK_EQUAL(expected, _etl_val_); \ + } while (0) + + //--------------------------------------------------------------------------- + // In an STL build identifier_of / symbol_of return std::string_view. + // In the non-STL build they return etl::string_view. + // Use this alias so the test code is compatible with both. + //--------------------------------------------------------------------------- + #if ETL_USING_STL +using meta_string_view = std::string_view; + #else +using meta_string_view = etl::string_view; + #endif + +// A struct with external linkage (defined outside anonymous namespace). +struct ExternalLinkageStruct +{ + int val; +}; + +namespace +{ + //--------------------------------------------------------------------------- + // Simple aggregate with public fields (naming, member queries, layout). + //--------------------------------------------------------------------------- + struct SimpleStruct + { + int x; + double y; + }; + + //--------------------------------------------------------------------------- + // A union type (is_union_type). + //--------------------------------------------------------------------------- + union TestUnion + { + int i; + float f; + }; + + //--------------------------------------------------------------------------- + // Struct with mixed access (access predicates, inaccessible-member queries). + //--------------------------------------------------------------------------- + struct MixedAccess + { + public: + + int pub; + + protected: + + int prot; + + private: + + [[maybe_unused]] + int priv; + }; + + //--------------------------------------------------------------------------- + // Struct with a mutable member, a const member, and a volatile member. + //--------------------------------------------------------------------------- + struct CVQualifiedMembers + { + mutable int m; + const int c = 0; + volatile int v; + }; + + //--------------------------------------------------------------------------- + // Bit-field struct (is_bit_field, bit_size_of, bit_offset_of). + //--------------------------------------------------------------------------- + struct BitFields + { + unsigned a : 3; + unsigned b : 5; + }; + + //--------------------------------------------------------------------------- + // Scoped and unscoped enumerations (enumerators_of, is_scoped_enum_type). + //--------------------------------------------------------------------------- + enum class Color + { + Red, + Green, + Blue + }; + enum Direction + { + North, + South, + East, + West + }; + + //--------------------------------------------------------------------------- + // Base / Derived hierarchy (bases_of, is_base, is_virtual, is_override). + //--------------------------------------------------------------------------- + struct Base + { + virtual ~Base() = default; + virtual void vfunc() {} + int base_data = 0; + }; + + struct Derived : public Base + { + void vfunc() override {} + int derived_data = 0; + }; + + //--------------------------------------------------------------------------- + // Class with a pure-virtual function (is_pure_virtual, is_abstract_type). + //--------------------------------------------------------------------------- + struct AbstractBase + { + virtual ~AbstractBase() = default; + virtual void pure() = 0; + }; + + //--------------------------------------------------------------------------- + // Final class (is_final, is_final_type). + //--------------------------------------------------------------------------- + struct FinalClass final : Derived + { + }; + + //--------------------------------------------------------------------------- + // Class with explicit / deleted / defaulted / noexcept special members. + //--------------------------------------------------------------------------- + struct SpecialMembers + { + SpecialMembers() = default; + SpecialMembers(const SpecialMembers&) = delete; + SpecialMembers(SpecialMembers&&) noexcept = default; + SpecialMembers& operator=(const SpecialMembers&) = default; + SpecialMembers& operator=(SpecialMembers&&) noexcept = default; + ~SpecialMembers() = default; + + explicit SpecialMembers(int) {} + }; + + //--------------------------------------------------------------------------- + // Class with operator overloads (operator_of, symbol_of, is_operator_function). + //--------------------------------------------------------------------------- + struct Operators + { + bool operator==(const Operators&) const = default; + Operators operator+(const Operators&) const + { + return {}; + } + int operator()(int a) const + { + return a; + } + }; + + //--------------------------------------------------------------------------- + // Class with a static data member (static_data_members_of, has_static_storage_duration). + //--------------------------------------------------------------------------- + struct WithStatic + { + [[maybe_unused]] + static inline int s_count = 0; + int value = 0; + }; + + //--------------------------------------------------------------------------- + // Type alias (is_type_alias, dealias). + //--------------------------------------------------------------------------- + using IntAlias = int; + + //--------------------------------------------------------------------------- + // A simple class template (is_class_template, template_of, template_arguments_of, substitute). + //--------------------------------------------------------------------------- + template + struct FixedArray + { + T data[N]; + }; + + using IntArray5 = FixedArray; + + //--------------------------------------------------------------------------- + // A constexpr function (reflect_function) and a constexpr variable (reflect_object). + //--------------------------------------------------------------------------- + constexpr int global_const = 42; + + constexpr int add(int a, int b) + { + return a + b; + } + + //--------------------------------------------------------------------------- + // A function with default arguments and variadic-style overload (parameter reflection). + //--------------------------------------------------------------------------- + [[maybe_unused]] + int with_default(int a, int b = 10) + { + return a + b; + } + + //--------------------------------------------------------------------------- + // An empty struct (is_empty_type, is_aggregate_type). + //--------------------------------------------------------------------------- + struct Empty + { + }; + + //--------------------------------------------------------------------------- + // A polymorphic non-abstract class (is_polymorphic_type but not is_abstract_type). + //--------------------------------------------------------------------------- + struct Polymorphic + { + virtual ~Polymorphic() = default; + virtual void foo() {} + }; + + //--------------------------------------------------------------------------- + // A trivially-copyable, non-polymorphic POD-like struct. + //--------------------------------------------------------------------------- + struct Trivial + { + int a; + int b; + }; + + //--------------------------------------------------------------------------- + // Class with ref-qualified member functions (is_lvalue/rvalue_reference_qualified). + //--------------------------------------------------------------------------- + struct RefQualified + { + void lref() & {} + void rref() && {} + void none() {} + }; + + //--------------------------------------------------------------------------- + // Class with a conversion operator (is_conversion_function). + //--------------------------------------------------------------------------- + struct WithConversion + { + operator int() const + { + return 0; + } + }; + + //--------------------------------------------------------------------------- + // Class with a deducing-this (explicit object parameter) member. + //--------------------------------------------------------------------------- + struct WithDeducingThis + { + int value = 0; + int get(this WithDeducingThis const& self) + { + return self.value; + } + }; + + //--------------------------------------------------------------------------- + // A thread_local variable (has_thread_storage_duration). + //--------------------------------------------------------------------------- + [[maybe_unused]] + thread_local int tls_var = 0; + + //--------------------------------------------------------------------------- + // A constexpr helper for is_structural_type — structural types can be used + // as non-type template parameters. + //--------------------------------------------------------------------------- + struct Structural + { + int x; + // Structural: all members public, no virtual, no user-declared ctors. + }; + + //--------------------------------------------------------------------------- + // A namespace-level variable with internal linkage. + //--------------------------------------------------------------------------- + [[maybe_unused]] + static int internal_var = 0; + + SUITE(test_meta) + { + //========================================================================= + // Naming / identification + //========================================================================= + + //************************************************************************* + TEST(test_identifier_of_type) + { + // identifier_of on a reflected type should return its unqualified name. + constexpr auto name = etl::meta::identifier_of(^^SimpleStruct); + CONSTEXPR_CHECK(name == "SimpleStruct"); + } + + //************************************************************************* + TEST(test_identifier_of_member) + { + // identifier_of on a reflected data member should return the field name. + constexpr auto name_x = + etl::meta::identifier_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]); + constexpr auto name_y = + etl::meta::identifier_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]); + CHECK(meta_string_view("x") == name_x); + CHECK(meta_string_view("y") == name_y); + } + + //************************************************************************* + TEST(test_identifier_of_enum_type) + { + // identifier_of on a reflected enumeration type. + constexpr auto name = etl::meta::identifier_of(^^Color); + CONSTEXPR_CHECK(name == "Color"); + } + + //************************************************************************* + TEST(test_identifier_of_enumerator) + { + // identifier_of on individual enumerators. + CONSTEXPR_CHECK(meta_string_view("Red") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[0])); + CONSTEXPR_CHECK(meta_string_view("Green") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[1])); + CONSTEXPR_CHECK(meta_string_view("Blue") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[2])); + } + + //************************************************************************* + TEST(test_has_identifier) + { + // Named entities have identifiers; built-in types may not. + CONSTEXPR_CHECK(etl::meta::has_identifier(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::has_identifier(^^Color)); + } + + //************************************************************************* + TEST(test_operator_of_and_symbol_of) + { + // Find operator== in Operators and verify operator_of / symbol_of. + // Locate the operator== member. + constexpr auto it = []() consteval + { + auto members = etl::meta::members_of(^^Operators, etl::meta::access_context::unchecked()); + for (auto m : members) + { + if (etl::meta::is_operator_function(m) && etl::meta::operator_of(m) == etl::meta::op_equals_equals) + return m; + } + return etl::meta::info{}; + }(); + + CONSTEXPR_CHECK(it != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::operator_of(it) == etl::meta::op_equals_equals); + CONSTEXPR_CHECK(meta_string_view("==") == etl::meta::symbol_of(etl::meta::op_equals_equals)); + } + + //************************************************************************* + TEST(test_symbol_of_various_operators) + { + // Spot-check symbol_of for a few operator enumerators. + CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_plus) == "+"); + CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_minus) == "-"); + CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_parentheses) == "()"); + CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_spaceship) == "<=>"); + CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_square_brackets) == "[]"); + } + + //========================================================================= + // Core entity queries + //========================================================================= + + //************************************************************************* + TEST(test_type_of_data_member) + { + // type_of on the first member of SimpleStruct should reflect int. + constexpr auto t = etl::meta::type_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]); + CONSTEXPR_CHECK(t == ^^int); + } + + //************************************************************************* + TEST(test_type_of_second_member) + { + // type_of on the second member of SimpleStruct should reflect double. + constexpr auto t = etl::meta::type_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]); + CONSTEXPR_CHECK(t == ^^double); + } + + //************************************************************************* + TEST(test_parent_of_data_member) + { + // parent_of a data member should be the containing class. + constexpr auto parent = etl::meta::parent_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]); + CONSTEXPR_CHECK(parent == ^^SimpleStruct); + } + + //************************************************************************* + TEST(test_dealias) + { + // dealias on a type alias should yield the underlying type. + constexpr auto dealiased = etl::meta::dealias(^^IntAlias); + CONSTEXPR_CHECK(dealiased == ^^int); + } + + //************************************************************************* + TEST(test_constant_of) + { + // constant_of on a reflected constexpr variable should yield a value reflection. + constexpr auto val = etl::meta::extract(etl::meta::constant_of(^^global_const)); + CHECK_EQUAL(42, val); + } + + //************************************************************************* + TEST(test_template_of) + { + // template_of on an instantiation should return the primary template. + // IntArray5 is a type alias, so dealias first to get the instantiation. + constexpr auto tmpl = etl::meta::template_of(etl::meta::dealias(^^IntArray5)); + CONSTEXPR_CHECK(tmpl == ^^FixedArray); + } + + //************************************************************************* + TEST(test_template_arguments_of) + { + // template_arguments_of on FixedArray should give two arguments. + constexpr auto result = []() consteval + { + auto args = etl::meta::template_arguments_of(etl::meta::dealias(^^IntArray5)); + struct R + { + size_t sz; + bool arg0_is_int; + size_t arg1; + }; + return R{args.size(), args[0] == ^^int, etl::meta::extract(args[1])}; + }(); + CHECK_EQUAL(size_t(2), result.sz); + // First argument is the type int. + CONSTEXPR_CHECK(result.arg0_is_int); + // Second argument is the value 5. + CHECK_EQUAL(size_t(5), result.arg1); + } + + //========================================================================= + // 4a: Access specifier predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_public) + { + // The first nonstatic data member of MixedAccess (pub) is public. + constexpr auto m0 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[0]; + CONSTEXPR_CHECK(etl::meta::is_public(m0)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_protected(m0)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_private(m0)); + } + + //************************************************************************* + TEST(test_is_protected) + { + // The second nonstatic data member of MixedAccess (prot) is protected. + constexpr auto m1 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[1]; + CONSTEXPR_CHECK_FALSE(etl::meta::is_public(m1)); + CONSTEXPR_CHECK(etl::meta::is_protected(m1)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_private(m1)); + } + + //************************************************************************* + TEST(test_is_private) + { + // The third nonstatic data member of MixedAccess (priv) is private. + constexpr auto m2 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[2]; + CONSTEXPR_CHECK_FALSE(etl::meta::is_public(m2)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_protected(m2)); + CONSTEXPR_CHECK(etl::meta::is_private(m2)); + } + + //========================================================================= + // 4b: Virtual / override predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_virtual) + { + // Base::vfunc is virtual. + constexpr auto vfunc = []() consteval + { + auto members = etl::meta::members_of(^^Base, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "vfunc") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(vfunc != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_virtual(vfunc)); + } + + //************************************************************************* + TEST(test_is_pure_virtual) + { + // AbstractBase::pure is pure-virtual. + constexpr auto pure_fn = []() consteval + { + auto members = etl::meta::members_of(^^AbstractBase, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "pure") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(pure_fn != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_pure_virtual(pure_fn)); + CONSTEXPR_CHECK(etl::meta::is_virtual(pure_fn)); + } + + //************************************************************************* + TEST(test_is_override) + { + // Derived::vfunc is an override. + constexpr auto vfunc = []() consteval + { + auto members = etl::meta::members_of(^^Derived, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "vfunc") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(vfunc != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_override(vfunc)); + CONSTEXPR_CHECK(etl::meta::is_virtual(vfunc)); + } + + //************************************************************************* + TEST(test_non_virtual_member_is_not_virtual) + { + // Base::base_data is not virtual. + // base_data is a data member, so is_virtual should be false. + constexpr auto base_data = []() consteval + { + auto members = etl::meta::nonstatic_data_members_of(^^Base, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::identifier_of(m) == "base_data") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(base_data != etl::meta::info{}); + CONSTEXPR_CHECK_FALSE(etl::meta::is_virtual(base_data)); + } + + //========================================================================= + // 4c: Function property predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_defaulted) + { + // SpecialMembers default constructor is defaulted. + constexpr auto def_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_default_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(def_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_defaulted(def_ctor)); + } + + //************************************************************************* + TEST(test_is_deleted) + { + // SpecialMembers copy constructor is deleted. + constexpr auto copy_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_copy_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(copy_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_deleted(copy_ctor)); + } + + //************************************************************************* + TEST(test_is_explicit) + { + // SpecialMembers(int) is explicit. + constexpr auto int_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_constructor(m) && !etl::meta::is_default_constructor(m) && !etl::meta::is_copy_constructor(m) + && !etl::meta::is_move_constructor(m) && etl::meta::is_explicit(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(int_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_explicit(int_ctor)); + } + + //************************************************************************* + TEST(test_is_noexcept) + { + // SpecialMembers move constructor is noexcept. + constexpr auto move_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_move_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(move_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_noexcept(move_ctor)); + } + + //========================================================================= + // 4d: Data member / misc predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_bit_field) + { + // BitFields::a and ::b are bit-fields. + CONSTEXPR_CHECK(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[0])); + CONSTEXPR_CHECK(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[1])); + } + + //************************************************************************* + TEST(test_non_bitfield_is_not_bit_field) + { + // SimpleStruct::x is not a bit-field. + CONSTEXPR_CHECK_FALSE(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //************************************************************************* + TEST(test_is_enumerator) + { + // Each element of enumerators_of(^^Color) should satisfy is_enumerator. + constexpr bool all_enumerators = []() consteval + { + auto enums = etl::meta::enumerators_of(^^Color); + for (auto e : enums) + if (!etl::meta::is_enumerator(e)) + return false; + return true; + }(); + CHECK(all_enumerators); + } + + //************************************************************************* + TEST(test_non_enumerator_is_not_enumerator) + { + // A data member is not an enumerator. + CONSTEXPR_CHECK_FALSE( + etl::meta::is_enumerator(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //************************************************************************* + TEST(test_is_final) + { + // FinalClass is final. + CONSTEXPR_CHECK(etl::meta::is_final(^^FinalClass)); + // Base is not final. + CONSTEXPR_CHECK_FALSE(etl::meta::is_final(^^Base)); + } + + //========================================================================= + // 4e: CV / ref qualification predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_mutable_member) + { + // CVQualifiedMembers::m is mutable. + // members[0] = m (mutable), members[1] = c (const), members[2] = v (volatile) + constexpr auto m0 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[0]; + constexpr auto m1 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[1]; + constexpr auto m2 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[2]; + CONSTEXPR_CHECK(etl::meta::is_mutable_member(m0)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_mutable_member(m1)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_mutable_member(m2)); + } + + //************************************************************************* + TEST(test_is_const_on_type) + { + // is_const on ^^const int should be true. + CONSTEXPR_CHECK(etl::meta::is_const(^^const int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_const(^^int)); + } + + //************************************************************************* + TEST(test_is_volatile_on_type) + { + // is_volatile on ^^volatile int should be true. + CONSTEXPR_CHECK(etl::meta::is_volatile(^^volatile int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_volatile(^^int)); + } + + //========================================================================= + // 4f: Storage duration & linkage + //========================================================================= + + //************************************************************************* + TEST(test_has_static_storage_duration) + { + // WithStatic::s_count has static storage duration. + constexpr auto result = []() consteval + { + auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + bool has_static; + }; + return R{statics.size(), statics.size() >= 1 && etl::meta::has_static_storage_duration(statics[0])}; + }(); + CHECK(result.sz >= size_t(1)); + CHECK(result.has_static); + } + + //************************************************************************* + TEST(test_has_internal_linkage) + { + // internal_var (declared static at namespace scope) has internal linkage. + CONSTEXPR_CHECK(etl::meta::has_internal_linkage(^^internal_var)); + CONSTEXPR_CHECK(etl::meta::has_linkage(^^internal_var)); + } + + //************************************************************************* + TEST(test_has_external_linkage) + { + // ExternalLinkageStruct is defined outside anonymous namespace + // so it has external linkage. + CONSTEXPR_CHECK(etl::meta::has_external_linkage(^^ExternalLinkageStruct)); + } + + //************************************************************************* + TEST(test_has_linkage) + { + // Types and namespace-scope variables have linkage. + CONSTEXPR_CHECK(etl::meta::has_linkage(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::has_linkage(^^internal_var)); + } + + //========================================================================= + // 4g: Membership predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_class_member) + { + // SimpleStruct::x is a class member. + CONSTEXPR_CHECK(etl::meta::is_class_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //************************************************************************* + TEST(test_is_namespace_member) + { + // global_const is a namespace member (it lives at namespace scope). + CONSTEXPR_CHECK(etl::meta::is_namespace_member(^^global_const)); + // A data member is not a namespace member. + CONSTEXPR_CHECK_FALSE( + etl::meta::is_namespace_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //************************************************************************* + TEST(test_is_nonstatic_data_member) + { + // SimpleStruct::x is a non-static data member. + CONSTEXPR_CHECK( + etl::meta::is_nonstatic_data_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //************************************************************************* + TEST(test_is_static_member) + { + // WithStatic::s_count is a static member. + constexpr auto result = []() consteval + { + auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + bool is_static; + }; + return R{statics.size(), statics.size() >= 1 && etl::meta::is_static_member(statics[0])}; + }(); + CHECK(result.sz >= size_t(1)); + CHECK(result.is_static); + } + + //************************************************************************* + TEST(test_is_base) + { + // The base-class specifier in Derived should satisfy is_base. + constexpr auto result = []() consteval + { + auto bases = etl::meta::bases_of(^^Derived, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + bool is_base_val; + }; + return R{bases.size(), bases.size() == 1 && etl::meta::is_base(bases[0])}; + }(); + CHECK_EQUAL(size_t(1), result.sz); + CHECK(result.is_base_val); + } + + //========================================================================= + // 4h: Entity kind predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_type) + { + CONSTEXPR_CHECK(etl::meta::is_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_type(^^Color)); + // A variable is not a type. + CONSTEXPR_CHECK_FALSE(etl::meta::is_type(^^global_const)); + } + + //************************************************************************* + TEST(test_is_namespace) + { + // The global namespace is a namespace. + CONSTEXPR_CHECK(etl::meta::is_namespace(^^::)); + // A type is not a namespace. + CONSTEXPR_CHECK_FALSE(etl::meta::is_namespace(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_function) + { + CONSTEXPR_CHECK(etl::meta::is_function(^^add)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_function(^^global_const)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_function(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_variable) + { + CONSTEXPR_CHECK(etl::meta::is_variable(^^global_const)); + CONSTEXPR_CHECK(etl::meta::is_variable(^^internal_var)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_variable(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_type_alias) + { + CONSTEXPR_CHECK(etl::meta::is_type_alias(^^IntAlias)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_type_alias(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_type_alias(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_complete_type) + { + CONSTEXPR_CHECK(etl::meta::is_complete_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_complete_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_complete_type(^^Color)); + } + + //************************************************************************* + TEST(test_is_enumerable_type) + { + // Enums with a definition are enumerable. + CONSTEXPR_CHECK(etl::meta::is_enumerable_type(^^Color)); + CONSTEXPR_CHECK(etl::meta::is_enumerable_type(^^Direction)); + // A non-enum, non-class scalar is not enumerable. + CONSTEXPR_CHECK_FALSE(etl::meta::is_enumerable_type(^^int)); + } + + //========================================================================= + // 4i: Template predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_template) + { + // FixedArray is a template; its instantiation (dealiased IntArray5) is not. + CONSTEXPR_CHECK(etl::meta::is_template(^^FixedArray)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_template(etl::meta::dealias(^^IntArray5))); + } + + //************************************************************************* + TEST(test_is_class_template) + { + CONSTEXPR_CHECK(etl::meta::is_class_template(^^FixedArray)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_class_template(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_has_template_arguments) + { + // An instantiation has template arguments; the primary template does not. + // IntArray5 is a type alias, so dealias to get the instantiation. + CONSTEXPR_CHECK(etl::meta::has_template_arguments(etl::meta::dealias(^^IntArray5))); + CONSTEXPR_CHECK_FALSE(etl::meta::has_template_arguments(^^SimpleStruct)); + } + + //========================================================================= + // 4j: Special member function predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_constructor) + { + // SpecialMembers has constructors; find at least one. + constexpr bool found = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_constructor(m)) + return true; + return false; + }(); + CHECK(found); + } + + //************************************************************************* + TEST(test_is_default_constructor) + { + constexpr auto def_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_default_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(def_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_special_member_function(def_ctor)); + } + + //************************************************************************* + TEST(test_is_copy_constructor) + { + constexpr auto copy_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_copy_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(copy_ctor != etl::meta::info{}); + // This one is deleted. + CONSTEXPR_CHECK(etl::meta::is_deleted(copy_ctor)); + } + + //************************************************************************* + TEST(test_is_move_constructor) + { + constexpr auto move_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_move_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(move_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_noexcept(move_ctor)); + CONSTEXPR_CHECK(etl::meta::is_defaulted(move_ctor)); + } + + //************************************************************************* + TEST(test_is_destructor) + { + constexpr auto dtor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_destructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(dtor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_special_member_function(dtor)); + CONSTEXPR_CHECK(etl::meta::is_defaulted(dtor)); + } + + //************************************************************************* + TEST(test_is_copy_assignment) + { + constexpr auto copy_assign = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_copy_assignment(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(copy_assign != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_assignment(copy_assign)); + CONSTEXPR_CHECK(etl::meta::is_defaulted(copy_assign)); + } + + //************************************************************************* + TEST(test_is_move_assignment) + { + constexpr auto move_assign = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_move_assignment(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(move_assign != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_assignment(move_assign)); + CONSTEXPR_CHECK(etl::meta::is_noexcept(move_assign)); + } + + //************************************************************************* + TEST(test_is_user_provided) + { + // SpecialMembers(int) is user-provided (has a body). + constexpr auto int_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_constructor(m) && !etl::meta::is_default_constructor(m) && !etl::meta::is_copy_constructor(m) + && !etl::meta::is_move_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(int_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_user_provided(int_ctor)); + } + + //************************************************************************* + TEST(test_is_user_declared) + { + // All special members of SpecialMembers are user-declared. + constexpr auto def_ctor = []() consteval + { + auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_default_constructor(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(def_ctor != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_user_declared(def_ctor)); + } + + //========================================================================= + // 4k: Value / object predicates + //========================================================================= + + //************************************************************************* + TEST(test_is_value) + { + // A reflected constant value is a value. + constexpr auto val = etl::meta::reflect_constant(42); + CONSTEXPR_CHECK(etl::meta::is_value(val)); + // A type is not a value. + CONSTEXPR_CHECK_FALSE(etl::meta::is_value(^^int)); + } + + //************************************************************************* + TEST(test_is_object) + { + // In GCC 16, ^^variable reflects the variable, not the object. + // Use object_of to get the object reflection. + CONSTEXPR_CHECK(etl::meta::is_object(etl::meta::object_of(^^global_const))); + // A type is not an object. + CONSTEXPR_CHECK_FALSE(etl::meta::is_object(^^int)); + // A function is not an object. + CONSTEXPR_CHECK_FALSE(etl::meta::is_object(^^add)); + } + + //************************************************************************* + TEST(test_has_default_member_initializer) + { + // Base::base_data has a default member initializer (= 0). + constexpr auto base_data = []() consteval + { + auto members = etl::meta::nonstatic_data_members_of(^^Base, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::identifier_of(m) == "base_data") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(base_data != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::has_default_member_initializer(base_data)); + + // SimpleStruct::x has no default member initializer. + CONSTEXPR_CHECK_FALSE( + etl::meta::has_default_member_initializer(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0])); + } + + //========================================================================= + // Access context & member queries + //========================================================================= + + //************************************************************************* + TEST(test_access_context_unchecked) + { + // unchecked() grants access to all members, including private ones. + constexpr auto sz = []() consteval + { + auto members = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked()); + return members.size(); + }(); + CHECK_EQUAL(size_t(3), sz); + } + + //************************************************************************* + TEST(test_access_context_unprivileged) + { + // unprivileged() only sees public members. + constexpr auto result = []() consteval + { + auto members = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unprivileged()); + struct R + { + size_t sz; + meta_string_view name; + }; + return R{members.size(), members.size() >= 1 ? etl::meta::identifier_of(members[0]) : ""}; + }(); + CHECK_EQUAL(size_t(1), result.sz); + CHECK(meta_string_view("pub") == result.name); + } + + //************************************************************************* + TEST(test_is_accessible_public_member) + { + constexpr auto result = []() consteval + { + auto all = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked()); + struct R + { + bool pub_accessible; + bool priv_accessible; + }; + return R{etl::meta::is_accessible(all[0], etl::meta::access_context::unprivileged()), + etl::meta::is_accessible(all[2], etl::meta::access_context::unprivileged())}; + }(); + // pub is accessible from unprivileged context. + CHECK(result.pub_accessible); + // priv is not accessible from unprivileged context. + CHECK(!result.priv_accessible); + } + + //************************************************************************* + TEST(test_members_of) + { + // members_of returns all members (functions, types, data) of a class. + constexpr auto sz = []() consteval + { + auto members = etl::meta::members_of(^^SimpleStruct, etl::meta::access_context::unchecked()); + return members.size(); + }(); + // SimpleStruct has at least x and y. + CHECK(sz >= size_t(2)); + } + + //************************************************************************* + TEST(test_bases_of) + { + // Derived has one base (Base). + constexpr auto sz = []() consteval + { + auto bases = etl::meta::bases_of(^^Derived, etl::meta::access_context::unchecked()); + return bases.size(); + }(); + CHECK_EQUAL(size_t(1), sz); + } + + //************************************************************************* + TEST(test_bases_of_no_bases) + { + // SimpleStruct has no base classes. + constexpr auto sz = []() consteval + { + auto bases = etl::meta::bases_of(^^SimpleStruct, etl::meta::access_context::unchecked()); + return bases.size(); + }(); + CHECK_EQUAL(size_t(0), sz); + } + + //************************************************************************* + TEST(test_static_data_members_of) + { + // WithStatic has one static data member (s_count). + constexpr auto result = []() consteval + { + auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + meta_string_view name; + }; + return R{statics.size(), statics.size() >= 1 ? etl::meta::identifier_of(statics[0]) : ""}; + }(); + CHECK_EQUAL(size_t(1), result.sz); + CHECK(meta_string_view("s_count") == result.name); + } + + //************************************************************************* + TEST(test_nonstatic_data_members_of) + { + // WithStatic has one non-static data member (value). + constexpr auto result = []() consteval + { + auto nonstatics = etl::meta::nonstatic_data_members_of(^^WithStatic, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + meta_string_view name; + }; + return R{nonstatics.size(), nonstatics.size() >= 1 ? etl::meta::identifier_of(nonstatics[0]) : ""}; + }(); + CHECK_EQUAL(size_t(1), result.sz); + CHECK(meta_string_view("value") == result.name); + } + + //************************************************************************* + TEST(test_enumerators_of) + { + // Color has 3 enumerators, Direction has 4. + constexpr auto color_sz = []() consteval + { + return etl::meta::enumerators_of(^^Color).size(); + }(); + CHECK_EQUAL(size_t(3), color_sz); + + constexpr auto dir_sz = []() consteval + { + return etl::meta::enumerators_of(^^Direction).size(); + }(); + CHECK_EQUAL(size_t(4), dir_sz); + } + + //************************************************************************* + TEST(test_has_inaccessible_nonstatic_data_members) + { + // MixedAccess has inaccessible members from unprivileged context. + CONSTEXPR_CHECK(etl::meta::has_inaccessible_nonstatic_data_members(^^MixedAccess, etl::meta::access_context::unprivileged())); + // SimpleStruct has no inaccessible members (all public). + CONSTEXPR_CHECK_FALSE(etl::meta::has_inaccessible_nonstatic_data_members(^^SimpleStruct, etl::meta::access_context::unprivileged())); + } + + //************************************************************************* + TEST(test_has_inaccessible_bases) + { + // Derived's base (Base) is public, so no inaccessible bases. + CONSTEXPR_CHECK_FALSE(etl::meta::has_inaccessible_bases(^^Derived, etl::meta::access_context::unprivileged())); + } + + //========================================================================= + // substitute / can_substitute / extract + //========================================================================= + + //************************************************************************* + TEST(test_substitute) + { + // substitute(^^FixedArray, {^^double, reflect_constant(size_t(10))}) + // should produce FixedArray. + constexpr auto result = etl::meta::substitute(^^FixedArray, {^^double, etl::meta::reflect_constant(size_t(10))}); + CONSTEXPR_CHECK(etl::meta::is_type(result)); + CONSTEXPR_CHECK(etl::meta::is_complete_type(result)); + // Verify the template arguments round-trip. + constexpr auto args_check = [&]() consteval + { + auto args = etl::meta::template_arguments_of(result); + struct R + { + bool arg0_is_double; + size_t arg1; + }; + return R{args[0] == ^^double, etl::meta::extract(args[1])}; + }(); + CONSTEXPR_CHECK(args_check.arg0_is_double); + CHECK_EQUAL(size_t(10), args_check.arg1); + } + + //************************************************************************* + TEST(test_can_substitute_valid) + { + // FixedArray is valid. + CONSTEXPR_CHECK(etl::meta::can_substitute(^^FixedArray, {^^int, etl::meta::reflect_constant(size_t(3))})); + } + + //************************************************************************* + TEST(test_extract_int) + { + // Extract an int from a reflected constant. + constexpr auto r = etl::meta::reflect_constant(123); + constexpr int val = etl::meta::extract(r); + CHECK_EQUAL(123, val); + } + + //************************************************************************* + TEST(test_extract_bool) + { + constexpr auto r = etl::meta::reflect_constant(true); + constexpr bool val = etl::meta::extract(r); + CHECK(val); + } + + //========================================================================= + // 17a: Primary type categories + // (These tests require substitute, which is STL-only.) + //========================================================================= + + //************************************************************************* + TEST(test_is_void_type) + { + CONSTEXPR_CHECK(etl::meta::is_void_type(^^void)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_void_type(^^int)); + } + + //************************************************************************* + TEST(test_is_null_pointer_type) + { + CONSTEXPR_CHECK(etl::meta::is_null_pointer_type(^^decltype(nullptr))); + CONSTEXPR_CHECK_FALSE(etl::meta::is_null_pointer_type(^^int)); + } + + //************************************************************************* + TEST(test_is_integral_type) + { + CONSTEXPR_CHECK(etl::meta::is_integral_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_integral_type(^^bool)); + CONSTEXPR_CHECK(etl::meta::is_integral_type(^^char)); + CONSTEXPR_CHECK(etl::meta::is_integral_type(^^unsigned long long)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_integral_type(^^double)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_integral_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_floating_point_type) + { + CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^float)); + CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^double)); + CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^long double)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_floating_point_type(^^int)); + } + + //************************************************************************* + TEST(test_is_array_type) + { + CONSTEXPR_CHECK(etl::meta::is_array_type(^^int[5])); + CONSTEXPR_CHECK(etl::meta::is_array_type(^^int[])); + CONSTEXPR_CHECK_FALSE(etl::meta::is_array_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_array_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_pointer_type) + { + CONSTEXPR_CHECK(etl::meta::is_pointer_type(^^int*)); + CONSTEXPR_CHECK(etl::meta::is_pointer_type(^^const char*)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_pointer_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_pointer_type(^^int&)); + } + + //************************************************************************* + TEST(test_is_lvalue_reference_type) + { + CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_type(^^int&)); + CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_type(^^const int&)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_type(^^int&&)); + } + + //************************************************************************* + TEST(test_is_rvalue_reference_type) + { + CONSTEXPR_CHECK(etl::meta::is_rvalue_reference_type(^^int&&)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_type(^^int&)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_type(^^int)); + } + + //************************************************************************* + TEST(test_is_member_object_pointer_type) + { + CONSTEXPR_CHECK(etl::meta::is_member_object_pointer_type(^^int SimpleStruct::*)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_member_object_pointer_type(^^int*)); + } + + //************************************************************************* + TEST(test_is_member_function_pointer_type) + { + CONSTEXPR_CHECK(etl::meta::is_member_function_pointer_type(^^void(Base::*)())); + CONSTEXPR_CHECK_FALSE(etl::meta::is_member_function_pointer_type(^^int SimpleStruct::*)); + } + + //************************************************************************* + TEST(test_is_enum_type) + { + CONSTEXPR_CHECK(etl::meta::is_enum_type(^^Color)); + CONSTEXPR_CHECK(etl::meta::is_enum_type(^^Direction)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_enum_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_enum_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_union_type) + { + CONSTEXPR_CHECK(etl::meta::is_union_type(^^TestUnion)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_union_type(^^SimpleStruct)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_union_type(^^int)); + } + + //************************************************************************* + TEST(test_is_class_type) + { + CONSTEXPR_CHECK(etl::meta::is_class_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_class_type(^^Base)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_class_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_class_type(^^Color)); + } + + //************************************************************************* + TEST(test_is_function_type) + { + CONSTEXPR_CHECK(etl::meta::is_function_type(^^int(int, int))); + CONSTEXPR_CHECK_FALSE(etl::meta::is_function_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_function_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_reflection_type) + { + // ^^etl::meta::info doesn't work when info is a using-declaration + // (a using-declarator cannot be reflected). + // In the std path etl::meta::info is a using-declaration for + // std::meta::info, so reflect the std name there. + // etl::meta::info is a using-declaration for std::meta::info and a + // using-declarator cannot be reflected, so reflect the std name. + CONSTEXPR_CHECK(etl::meta::is_reflection_type(^^std::meta::info)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_reflection_type(^^int)); + } + + //========================================================================= + // 17b: Composite type categories + //========================================================================= + + //************************************************************************* + TEST(test_is_reference_type) + { + CONSTEXPR_CHECK(etl::meta::is_reference_type(^^int&)); + CONSTEXPR_CHECK(etl::meta::is_reference_type(^^int&&)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_reference_type(^^int)); + } + + //************************************************************************* + TEST(test_is_arithmetic_type) + { + CONSTEXPR_CHECK(etl::meta::is_arithmetic_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_arithmetic_type(^^double)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_arithmetic_type(^^SimpleStruct)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_arithmetic_type(^^int*)); + } + + //************************************************************************* + TEST(test_is_fundamental_type) + { + CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^void)); + CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^decltype(nullptr))); + CONSTEXPR_CHECK_FALSE(etl::meta::is_fundamental_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_object_type) + { + CONSTEXPR_CHECK(etl::meta::is_object_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_object_type(^^SimpleStruct)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_object_type(^^void)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_object_type(^^int&)); + } + + //************************************************************************* + TEST(test_is_scalar_type) + { + CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^int*)); + CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^Color)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_scalar_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_compound_type) + { + CONSTEXPR_CHECK(etl::meta::is_compound_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_compound_type(^^int*)); + CONSTEXPR_CHECK(etl::meta::is_compound_type(^^int&)); + CONSTEXPR_CHECK(etl::meta::is_compound_type(^^Color)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_compound_type(^^int)); + } + + //************************************************************************* + TEST(test_is_member_pointer_type) + { + CONSTEXPR_CHECK(etl::meta::is_member_pointer_type(^^int SimpleStruct::*)); + CONSTEXPR_CHECK(etl::meta::is_member_pointer_type(^^void(Base::*)())); + CONSTEXPR_CHECK_FALSE(etl::meta::is_member_pointer_type(^^int*)); + } + + //========================================================================= + // 17c: Type properties + //========================================================================= + + //************************************************************************* + TEST(test_is_const_type) + { + CONSTEXPR_CHECK(etl::meta::is_const_type(^^const int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_const_type(^^int)); + } + + //************************************************************************* + TEST(test_is_volatile_type) + { + CONSTEXPR_CHECK(etl::meta::is_volatile_type(^^volatile int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_volatile_type(^^int)); + } + + //************************************************************************* + TEST(test_is_trivially_copyable_type) + { + CONSTEXPR_CHECK(etl::meta::is_trivially_copyable_type(^^Trivial)); + CONSTEXPR_CHECK(etl::meta::is_trivially_copyable_type(^^int)); + // Polymorphic types are not trivially copyable. + CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_copyable_type(^^Polymorphic)); + } + + //************************************************************************* + TEST(test_is_empty_type) + { + CONSTEXPR_CHECK(etl::meta::is_empty_type(^^Empty)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_empty_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_polymorphic_type) + { + CONSTEXPR_CHECK(etl::meta::is_polymorphic_type(^^Polymorphic)); + CONSTEXPR_CHECK(etl::meta::is_polymorphic_type(^^Base)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_polymorphic_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_abstract_type) + { + CONSTEXPR_CHECK(etl::meta::is_abstract_type(^^AbstractBase)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_abstract_type(^^Base)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_abstract_type(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_is_final_type) + { + CONSTEXPR_CHECK(etl::meta::is_final_type(^^FinalClass)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_final_type(^^Base)); + } + + //************************************************************************* + TEST(test_is_aggregate_type) + { + CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^Empty)); + CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^Trivial)); + // SpecialMembers has user-declared constructors, so it's not aggregate. + CONSTEXPR_CHECK_FALSE(etl::meta::is_aggregate_type(^^SpecialMembers)); + } + + //************************************************************************* + TEST(test_is_signed_type) + { + CONSTEXPR_CHECK(etl::meta::is_signed_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_signed_type(^^double)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_signed_type(^^unsigned int)); + } + + //************************************************************************* + TEST(test_is_unsigned_type) + { + CONSTEXPR_CHECK(etl::meta::is_unsigned_type(^^unsigned int)); + CONSTEXPR_CHECK(etl::meta::is_unsigned_type(^^bool)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_unsigned_type(^^int)); + } + + //************************************************************************* + TEST(test_is_bounded_array_type) + { + CONSTEXPR_CHECK(etl::meta::is_bounded_array_type(^^int[5])); + CONSTEXPR_CHECK_FALSE(etl::meta::is_bounded_array_type(^^int[])); + CONSTEXPR_CHECK_FALSE(etl::meta::is_bounded_array_type(^^int)); + } + + //************************************************************************* + TEST(test_is_unbounded_array_type) + { + CONSTEXPR_CHECK(etl::meta::is_unbounded_array_type(^^int[])); + CONSTEXPR_CHECK_FALSE(etl::meta::is_unbounded_array_type(^^int[5])); + CONSTEXPR_CHECK_FALSE(etl::meta::is_unbounded_array_type(^^int)); + } + + //************************************************************************* + TEST(test_is_scoped_enum_type) + { + CONSTEXPR_CHECK(etl::meta::is_scoped_enum_type(^^Color)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_scoped_enum_type(^^Direction)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_scoped_enum_type(^^int)); + } + + //========================================================================= + // 17d: Constructible / assignable / destructible + //========================================================================= + + //************************************************************************* + TEST(test_is_default_constructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_default_constructible_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_default_constructible_type(^^int)); + } + + //************************************************************************* + TEST(test_is_copy_constructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_copy_constructible_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_copy_constructible_type(^^int)); + // SpecialMembers has a deleted copy constructor. + CONSTEXPR_CHECK_FALSE(etl::meta::is_copy_constructible_type(^^SpecialMembers)); + } + + //************************************************************************* + TEST(test_is_move_constructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^int)); + CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^SpecialMembers)); + } + + //************************************************************************* + TEST(test_is_constructible_type) + { + // int is constructible from int. + CONSTEXPR_CHECK(etl::meta::is_constructible_type(^^int, {^^int})); + // SpecialMembers is constructible from int (explicit ctor). + CONSTEXPR_CHECK(etl::meta::is_constructible_type(^^SpecialMembers, {^^int})); + } + + //************************************************************************* + TEST(test_is_assignable_type) + { + CONSTEXPR_CHECK(etl::meta::is_assignable_type(^^int&, ^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_assignable_type(^^int, ^^int)); + } + + //************************************************************************* + TEST(test_is_copy_assignable_type) + { + CONSTEXPR_CHECK(etl::meta::is_copy_assignable_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_copy_assignable_type(^^int)); + } + + //************************************************************************* + TEST(test_is_move_assignable_type) + { + CONSTEXPR_CHECK(etl::meta::is_move_assignable_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_move_assignable_type(^^SpecialMembers)); + } + + //************************************************************************* + TEST(test_is_destructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_destructible_type(^^SimpleStruct)); + CONSTEXPR_CHECK(etl::meta::is_destructible_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_destructible_type(^^void)); + } + + //************************************************************************* + TEST(test_is_trivially_copy_constructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_trivially_copy_constructible_type(^^Trivial)); + CONSTEXPR_CHECK(etl::meta::is_trivially_copy_constructible_type(^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_copy_constructible_type(^^Polymorphic)); + } + + //************************************************************************* + TEST(test_is_trivially_copy_assignable_type) + { + CONSTEXPR_CHECK(etl::meta::is_trivially_copy_assignable_type(^^Trivial)); + CONSTEXPR_CHECK(etl::meta::is_trivially_copy_assignable_type(^^int)); + } + + //************************************************************************* + TEST(test_is_trivially_destructible_type) + { + CONSTEXPR_CHECK(etl::meta::is_trivially_destructible_type(^^Trivial)); + CONSTEXPR_CHECK(etl::meta::is_trivially_destructible_type(^^int)); + // Polymorphic has a virtual destructor, so not trivially destructible. + CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_destructible_type(^^Polymorphic)); + } + + //========================================================================= + // 17e: Type property queries (rank, extent) + //========================================================================= + + //************************************************************************* + TEST(test_rank) + { + CONSTEXPR_CHECK_EQUAL(size_t(0), etl::meta::rank(^^int)); + CONSTEXPR_CHECK_EQUAL(size_t(1), etl::meta::rank(^^int[5])); + CONSTEXPR_CHECK_EQUAL(size_t(2), etl::meta::rank(^^int[3][4])); + } + + //************************************************************************* + TEST(test_extent) + { + CONSTEXPR_CHECK_EQUAL(size_t(5), etl::meta::extent(^^int[5])); + CONSTEXPR_CHECK_EQUAL(size_t(3), etl::meta::extent(^^int[3][4], 0)); + CONSTEXPR_CHECK_EQUAL(size_t(4), etl::meta::extent(^^int[3][4], 1)); + // Unbounded first dimension has extent 0. + CONSTEXPR_CHECK_EQUAL(size_t(0), etl::meta::extent(^^int[])); + } + + //========================================================================= + // 17f: Type relations + //========================================================================= + + //************************************************************************* + TEST(test_is_same_type) + { + CONSTEXPR_CHECK(etl::meta::is_same_type(^^int, ^^int)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_same_type(^^int, ^^double)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_same_type(^^int, ^^const int)); + } + + //************************************************************************* + TEST(test_is_base_of_type) + { + CONSTEXPR_CHECK(etl::meta::is_base_of_type(^^Base, ^^Derived)); + CONSTEXPR_CHECK(etl::meta::is_base_of_type(^^Base, ^^FinalClass)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_base_of_type(^^Derived, ^^Base)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_base_of_type(^^SimpleStruct, ^^Derived)); + } + + //************************************************************************* + TEST(test_is_convertible_type) + { + CONSTEXPR_CHECK(etl::meta::is_convertible_type(^^int, ^^double)); + CONSTEXPR_CHECK(etl::meta::is_convertible_type(^^Derived*, ^^Base*)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_convertible_type(^^Base*, ^^Derived*)); + } + + //************************************************************************* + TEST(test_has_virtual_destructor) + { + CONSTEXPR_CHECK(etl::meta::has_virtual_destructor(^^Base)); + CONSTEXPR_CHECK(etl::meta::has_virtual_destructor(^^Polymorphic)); + CONSTEXPR_CHECK_FALSE(etl::meta::has_virtual_destructor(^^SimpleStruct)); + CONSTEXPR_CHECK_FALSE(etl::meta::has_virtual_destructor(^^Trivial)); + } + + //========================================================================= + // Type transformations + //========================================================================= + + //************************************************************************* + TEST(test_remove_const) + { + CONSTEXPR_CHECK(etl::meta::remove_const(^^const int) == ^^int); + // Removing const from non-const is a no-op. + CONSTEXPR_CHECK(etl::meta::remove_const(^^int) == ^^int); + } + + //************************************************************************* + TEST(test_remove_volatile) + { + CONSTEXPR_CHECK(etl::meta::remove_volatile(^^volatile int) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_volatile(^^int) == ^^int); + } + + //************************************************************************* + TEST(test_remove_cv) + { + CONSTEXPR_CHECK(etl::meta::remove_cv(^^const volatile int) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_cv(^^const int) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_cv(^^volatile int) == ^^int); + } + + //************************************************************************* + TEST(test_add_const) + { + CONSTEXPR_CHECK(etl::meta::add_const(^^int) == ^^const int); + // Adding const to already-const is idempotent. + CONSTEXPR_CHECK(etl::meta::add_const(^^const int) == ^^const int); + } + + //************************************************************************* + TEST(test_add_volatile) + { + CONSTEXPR_CHECK(etl::meta::add_volatile(^^int) == ^^volatile int); + } + + //************************************************************************* + TEST(test_add_cv) + { + CONSTEXPR_CHECK(etl::meta::add_cv(^^int) == ^^const volatile int); + } + + //************************************************************************* + TEST(test_remove_reference) + { + CONSTEXPR_CHECK(etl::meta::remove_reference(^^int&) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_reference(^^int&&) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_reference(^^int) == ^^int); + } + + //************************************************************************* + TEST(test_add_lvalue_reference) + { + CONSTEXPR_CHECK(etl::meta::add_lvalue_reference(^^int) == ^^int&); + } + + //************************************************************************* + TEST(test_add_rvalue_reference) + { + CONSTEXPR_CHECK(etl::meta::add_rvalue_reference(^^int) == ^^int&&); + } + + //************************************************************************* + TEST(test_make_signed) + { + CONSTEXPR_CHECK(etl::meta::make_signed(^^unsigned int) == ^^int); + } + + //************************************************************************* + TEST(test_make_unsigned) + { + CONSTEXPR_CHECK(etl::meta::make_unsigned(^^int) == ^^unsigned int); + } + + //************************************************************************* + TEST(test_remove_extent) + { + // remove_extent(int[5]) -> int + CONSTEXPR_CHECK(etl::meta::remove_extent(^^int[5]) == ^^int); + // remove_extent(int[3][4]) -> int[4] + CONSTEXPR_CHECK(etl::meta::remove_extent(^^int[3][4]) == ^^int[4]); + } + + //************************************************************************* + TEST(test_remove_all_extents) + { + CONSTEXPR_CHECK(etl::meta::remove_all_extents(^^int[3][4]) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_all_extents(^^int[5]) == ^^int); + } + + //************************************************************************* + TEST(test_remove_pointer) + { + CONSTEXPR_CHECK(etl::meta::remove_pointer(^^int*) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_pointer(^^const int*) == ^^const int); + // Non-pointer is unchanged. + CONSTEXPR_CHECK(etl::meta::remove_pointer(^^int) == ^^int); + } + + //************************************************************************* + TEST(test_add_pointer) + { + CONSTEXPR_CHECK(etl::meta::add_pointer(^^int) == ^^int*); + CONSTEXPR_CHECK(etl::meta::add_pointer(^^const int) == ^^const int*); + } + + //************************************************************************* + TEST(test_remove_cvref) + { + CONSTEXPR_CHECK(etl::meta::remove_cvref(^^const int&) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_cvref(^^volatile int&&) == ^^int); + CONSTEXPR_CHECK(etl::meta::remove_cvref(^^const volatile int) == ^^int); + } + + //************************************************************************* + TEST(test_decay) + { + // Array decays to pointer. + CONSTEXPR_CHECK(etl::meta::decay(^^int[5]) == ^^int*); + // Function decays to function pointer. + CONSTEXPR_CHECK(etl::meta::decay(^^int(int)) == ^^int (*)(int)); + // CV-ref is stripped. + CONSTEXPR_CHECK(etl::meta::decay(^^const int&) == ^^int); + } + + //************************************************************************* + TEST(test_common_type) + { + // common_type(int, double) -> double + constexpr auto ct = etl::meta::common_type({^^int, ^^double}); + CONSTEXPR_CHECK(ct == ^^double); + } + + //************************************************************************* + TEST(test_underlying_type) + { + // Color is an enum class; its underlying type is int by default. + constexpr auto ut = etl::meta::underlying_type(^^Color); + CONSTEXPR_CHECK(etl::meta::is_integral_type(ut)); + } + + //========================================================================= + // Data layout + //========================================================================= + + //************************************************************************* + TEST(test_size_of_type) + { + CONSTEXPR_CHECK_EQUAL(sizeof(int), etl::meta::size_of(^^int)); + CONSTEXPR_CHECK_EQUAL(sizeof(double), etl::meta::size_of(^^double)); + CONSTEXPR_CHECK_EQUAL(sizeof(SimpleStruct), etl::meta::size_of(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_alignment_of_type) + { + CONSTEXPR_CHECK_EQUAL(alignof(int), etl::meta::alignment_of(^^int)); + CONSTEXPR_CHECK_EQUAL(alignof(double), etl::meta::alignment_of(^^double)); + CONSTEXPR_CHECK_EQUAL(alignof(SimpleStruct), etl::meta::alignment_of(^^SimpleStruct)); + } + + //************************************************************************* + TEST(test_bit_size_of_bit_field) + { + // BitFields::a is 3 bits, BitFields::b is 5 bits. + CONSTEXPR_CHECK_EQUAL(size_t(3), + etl::meta::bit_size_of(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[0])); + CONSTEXPR_CHECK_EQUAL(size_t(5), + etl::meta::bit_size_of(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[1])); + } + + //************************************************************************* + TEST(test_offset_of) + { + // The first member of SimpleStruct should be at offset {0, 0}. + constexpr auto off = etl::meta::offset_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]); + CHECK_EQUAL(ptrdiff_t(0), off.bytes); + CHECK_EQUAL(ptrdiff_t(0), off.bits); + } + + //************************************************************************* + TEST(test_offset_of_second_member) + { + // The second member of SimpleStruct (double y) should be at a non-zero offset. + constexpr auto off = etl::meta::offset_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]); + // double typically follows int with padding; offset should be > 0. + CHECK(off.bytes > ptrdiff_t(0)); + } + + //************************************************************************* + TEST(test_member_offset_total_bits) + { + etl::meta::member_offset off{2, 3}; + CHECK_EQUAL(ptrdiff_t(2 * __CHAR_BIT__ + 3), off.total_bits()); + } + + //************************************************************************* + TEST(test_member_offset_comparison) + { + etl::meta::member_offset a{0, 0}; + etl::meta::member_offset b{1, 0}; + CHECK(a < b); + CHECK(a == a); + CHECK(!(a == b)); + } + + //========================================================================= + // reflect_constant / reflect_object / reflect_function + //========================================================================= + + //************************************************************************* + TEST(test_reflect_constant_int) + { + constexpr auto r = etl::meta::reflect_constant(42); + constexpr int val = etl::meta::extract(r); + CHECK_EQUAL(42, val); + } + + //************************************************************************* + TEST(test_reflect_constant_char) + { + constexpr auto r = etl::meta::reflect_constant('A'); + constexpr char val = etl::meta::extract(r); + CHECK_EQUAL('A', val); + } + + //************************************************************************* + TEST(test_reflect_constant_negative) + { + constexpr auto r = etl::meta::reflect_constant(-7); + constexpr int val = etl::meta::extract(r); + CHECK_EQUAL(-7, val); + } + + //************************************************************************* + TEST(test_reflect_object) + { + // reflect_object on global_const should give a reflection of the object. + constexpr auto r = etl::meta::reflect_object(global_const); + CONSTEXPR_CHECK(etl::meta::is_object(r)); + } + + //************************************************************************* + TEST(test_reflect_function) + { + // reflect_function on add should give a reflection of the function. + constexpr auto r = etl::meta::reflect_function(add); + CONSTEXPR_CHECK(etl::meta::is_function(r)); + } + + //========================================================================= + // define_aggregate / data_member_spec + //========================================================================= + + // GCC warns about missing field initializers in designated-init of + // data_member_options, but omitted fields are intentionally defaulted. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" + + //************************************************************************* + TEST(test_data_member_spec_basic) + { + // Create a data member spec for an int named "value". + constexpr auto spec = etl::meta::data_member_spec(^^int, {.name = "value"}); + CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec)); + } + + //************************************************************************* + TEST(test_data_member_spec_unnamed) + { + // Create a data member spec without an explicit name (using bit-width). + // GCC 16 uses .bit_width; Bloomberg clang-p2996 uses .width. + constexpr auto spec = etl::meta::data_member_spec(^^unsigned, {.bit_width = 8}); + CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec)); + } + + //************************************************************************* + TEST(test_data_member_spec_with_no_unique_address) + { + // Create a data member spec with no_unique_address. + constexpr auto spec = etl::meta::data_member_spec(^^int, {.name = "val", .no_unique_address = true}); + CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec)); + } + + //************************************************************************* + TEST(test_data_member_spec_bit_field) + { + // Create a bit-field data member spec. + constexpr auto spec = etl::meta::data_member_spec(^^unsigned, {.name = "bits", .bit_width = 4}); + CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec)); + } + + //************************************************************************* + TEST(test_define_aggregate) + { + // Define a new aggregate type with two members at compile time. + // define_aggregate must be called from a consteval { } block in GCC 16. + struct NewType; + consteval + { + etl::meta::define_aggregate(^^NewType, + {etl::meta::data_member_spec(^^int, {.name = "a"}), etl::meta::data_member_spec(^^double, {.name = "b"})}); + } + // Now NewType is complete; query it in a consteval lambda. + constexpr auto result = []() consteval + { + auto new_type = ^^NewType; + bool is_type_ok = etl::meta::is_type(new_type); + bool is_complete_ok = etl::meta::is_complete_type(new_type); + + auto members = etl::meta::nonstatic_data_members_of(new_type, etl::meta::access_context::unchecked()); + struct R + { + size_t sz; + meta_string_view name0; + meta_string_view name1; + bool type0_is_int; + bool type1_is_double; + bool is_type; + bool is_complete; + }; + return R{members.size(), + etl::meta::identifier_of(members[0]), + etl::meta::identifier_of(members[1]), + etl::meta::type_of(members[0]) == ^^int, + etl::meta::type_of(members[1]) == ^^double, + is_type_ok, + is_complete_ok}; + }(); + CHECK(result.is_type); + CHECK(result.is_complete); + CHECK_EQUAL(size_t(2), result.sz); + CHECK(meta_string_view("a") == result.name0); + CHECK(meta_string_view("b") == result.name1); + CHECK(result.type0_is_int); + CHECK(result.type1_is_double); + } + + #pragma GCC diagnostic pop + + //========================================================================= + // Parameter reflection + //========================================================================= + + //************************************************************************* + TEST(test_parameters_of) + { + // add(int, int) has 2 parameters. + constexpr auto sz = []() consteval + { + return etl::meta::parameters_of(^^add).size(); + }(); + CHECK_EQUAL(size_t(2), sz); + } + + //************************************************************************* + TEST(test_parameter_names) + { + // Parameters of add are named "a" and "b". + constexpr auto result = []() consteval + { + auto params = etl::meta::parameters_of(^^add); + struct R + { + meta_string_view name0; + meta_string_view name1; + }; + return R{etl::meta::identifier_of(params[0]), etl::meta::identifier_of(params[1])}; + }(); + CHECK(meta_string_view("a") == result.name0); + CHECK(meta_string_view("b") == result.name1); + } + + //************************************************************************* + TEST(test_is_function_parameter) + { + constexpr auto result = []() consteval + { + auto params = etl::meta::parameters_of(^^add); + struct R + { + bool p0; + bool p1; + }; + return R{etl::meta::is_function_parameter(params[0]), etl::meta::is_function_parameter(params[1])}; + }(); + CHECK(result.p0); + CHECK(result.p1); + } + + //************************************************************************* + TEST(test_return_type_of) + { + // add returns int. + constexpr auto ret = etl::meta::return_type_of(^^add); + CONSTEXPR_CHECK(ret == ^^int); + } + + //************************************************************************* + TEST(test_has_default_argument) + { + // with_default(int a, int b = 10): second param has a default. + constexpr auto result = []() consteval + { + auto params = etl::meta::parameters_of(^^with_default); + struct R + { + bool first_has_default; + bool second_has_default; + }; + return R{etl::meta::has_default_argument(params[0]), etl::meta::has_default_argument(params[1])}; + }(); + CHECK(!result.first_has_default); + CHECK(result.second_has_default); + } + + //************************************************************************* + + //========================================================================= + // Edge-case tests — remaining API gaps + //========================================================================= + + //************************************************************************* + TEST(test_is_lvalue_reference_qualified) + { + // RefQualified::lref is &-qualified. + constexpr auto lref_fn = []() consteval + { + auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "lref") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(lref_fn != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_qualified(lref_fn)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_qualified(lref_fn)); + } + + //************************************************************************* + TEST(test_is_rvalue_reference_qualified) + { + // RefQualified::rref is &&-qualified. + constexpr auto rref_fn = []() consteval + { + auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "rref") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(rref_fn != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_rvalue_reference_qualified(rref_fn)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_qualified(rref_fn)); + } + + //************************************************************************* + TEST(test_no_ref_qualification) + { + // RefQualified::none has neither ref-qualification. + constexpr auto none_fn = []() consteval + { + auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "none") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(none_fn != etl::meta::info{}); + CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_qualified(none_fn)); + CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_qualified(none_fn)); + } + + //************************************************************************* + TEST(test_has_thread_storage_duration) + { + CONSTEXPR_CHECK(etl::meta::has_thread_storage_duration(^^tls_var)); + CONSTEXPR_CHECK_FALSE(etl::meta::has_static_storage_duration(^^tls_var)); + } + + //************************************************************************* + TEST(test_has_automatic_storage_duration) + { + // A local variable inside a consteval context has automatic storage. + // We test indirectly: namespace-scope variables do NOT have automatic storage. + CONSTEXPR_CHECK_FALSE(etl::meta::has_automatic_storage_duration(^^global_const)); + CONSTEXPR_CHECK_FALSE(etl::meta::has_automatic_storage_duration(^^internal_var)); + } + + //************************************************************************* + //************************************************************************* + TEST(test_is_structural_type) + { + // Structural (all-public, no virtual, trivially destructible) is structural. + CONSTEXPR_CHECK(etl::meta::is_structural_type(^^Structural)); + CONSTEXPR_CHECK(etl::meta::is_structural_type(^^int)); + // A class with non-public non-static data members is never structural. + CONSTEXPR_CHECK_FALSE(etl::meta::is_structural_type(^^MixedAccess)); + } + + //************************************************************************* + TEST(test_object_of) + { + // object_of on a reflected variable returns the object it designates. + constexpr auto obj = etl::meta::object_of(^^global_const); + CONSTEXPR_CHECK(etl::meta::is_object(obj)); + } + + //************************************************************************* + TEST(test_is_conversion_function) + { + // WithConversion has a conversion operator. + constexpr auto conv = []() consteval + { + auto members = etl::meta::members_of(^^WithConversion, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::is_conversion_function(m)) + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(conv != etl::meta::info{}); + CONSTEXPR_CHECK(etl::meta::is_conversion_function(conv)); + } + + //************************************************************************* + TEST(test_is_trivially_constructible_type_multi_arg) + { + // Trivial is trivially constructible from (Trivial const&). + CONSTEXPR_CHECK(etl::meta::is_trivially_constructible_type(^^Trivial, {^^const Trivial& })); + // Trivial is trivially constructible from (Trivial&&). + CONSTEXPR_CHECK(etl::meta::is_trivially_constructible_type(^^Trivial, {^^Trivial&& })); + } + + //************************************************************************* + + //************************************************************************* + TEST(test_is_explicit_object_parameter) + { + // WithDeducingThis::get has an explicit object parameter (deducing this). + constexpr auto get_fn = []() consteval + { + auto members = etl::meta::members_of(^^WithDeducingThis, etl::meta::access_context::unchecked()); + for (auto m : members) + if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "get") + return m; + return etl::meta::info{}; + }(); + CONSTEXPR_CHECK(get_fn != etl::meta::info{}); + + constexpr auto result = [&]() consteval + { + auto params = etl::meta::parameters_of(get_fn); + struct R + { + size_t sz; + bool is_explicit; + }; + return R{params.size(), params.size() >= 1 && etl::meta::is_explicit_object_parameter(params[0])}; + }(); + CHECK(result.sz >= size_t(1)); + CHECK(result.is_explicit); + } + } +} // namespace + +#endif // ETL_HAS_REFLECTION diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index e7bb1ff5..bd6391bb 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -10447,6 +10447,7 @@ + ../../../unittest-cpp diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 954b7de1..1ac2027d 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1664,6 +1664,9 @@ Tests\Messaging + + Tests\Types + Tests\Messaging