diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 00000000..22cc390f --- /dev/null +++ b/.bazelrc @@ -0,0 +1,26 @@ +# Bazel settings for ETL +build --enable_bzlmod +build --cxxopt=-std=c++17 + +# Cross-compilation: build flags + QEMU (mirrors .devcontainer/run-tests.sh) +# Each config sets CC + tool env vars so Bazel's auto-configured toolchain +# finds the full cross-tool suite, and --run_under for QEMU execution. +build:armhf --repo_env=CC=arm-linux-gnueabihf-gcc --repo_env=AR=arm-linux-gnueabihf-ar --repo_env=LD=arm-linux-gnueabihf-ld --repo_env=NM=arm-linux-gnueabihf-nm --repo_env=STRIP=arm-linux-gnueabihf-strip --repo_env=OBJDUMP=arm-linux-gnueabihf-objdump +build:armhf --cxxopt=-std=c++23 --copt=-DETL_NO_STL --copt=-O0 +test:armhf --run_under=/usr/bin/qemu-arm-static + +build:i386 --repo_env=CC=i686-linux-gnu-gcc --repo_env=AR=i686-linux-gnu-ar --repo_env=LD=i686-linux-gnu-ld --repo_env=NM=i686-linux-gnu-nm --repo_env=STRIP=i686-linux-gnu-strip --repo_env=OBJDUMP=i686-linux-gnu-objdump +build:i386 --cxxopt=-std=c++23 --copt=-DETL_NO_STL --copt=-O0 +test:i386 --run_under=/usr/bin/qemu-i386-static + +build:powerpc --repo_env=CC=powerpc-linux-gnu-gcc --repo_env=AR=powerpc-linux-gnu-ar --repo_env=LD=powerpc-linux-gnu-ld --repo_env=NM=powerpc-linux-gnu-nm --repo_env=STRIP=powerpc-linux-gnu-strip --repo_env=OBJDUMP=powerpc-linux-gnu-objdump +build:powerpc --cxxopt=-std=c++23 --copt=-DETL_NO_STL --copt=-O0 +test:powerpc --run_under=/usr/bin/qemu-ppc + +build:riscv64 --repo_env=CC=riscv64-linux-gnu-gcc --repo_env=AR=riscv64-linux-gnu-ar --repo_env=LD=riscv64-linux-gnu-ld --repo_env=NM=riscv64-linux-gnu-nm --repo_env=STRIP=riscv64-linux-gnu-strip --repo_env=OBJDUMP=riscv64-linux-gnu-objdump +build:riscv64 --cxxopt=-std=c++23 --copt=-DETL_NO_STL --copt=-O0 +test:riscv64 --run_under=/usr/bin/qemu-riscv64-static + +build:s390x --repo_env=CC=s390x-linux-gnu-gcc --repo_env=AR=s390x-linux-gnu-ar --repo_env=LD=s390x-linux-gnu-ld --repo_env=NM=s390x-linux-gnu-nm --repo_env=STRIP=s390x-linux-gnu-strip --repo_env=OBJDUMP=s390x-linux-gnu-objdump +build:s390x --cxxopt=-std=c++23 --copt=-DETL_NO_STL --copt=-O0 +test:s390x --run_under=/usr/bin/qemu-s390x-static diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000..f9c71a52 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +8.5.1 diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..59753925 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,182 @@ +--- +# TODO: Enable these checks in smaller steps +# cppcoreguidelines-*, +# portability-* if needed +# readability-*, -readability-magic-numbers, +# misc-*, -misc-no-recursion, +# modernize-*, -modernize-use-trailing-return-type, +# performance-*, + +Checks: >- + cert-*, -cert-dcl37-c, -cert-dcl51-cpp, + clang-analyzer-*, + bugprone-*, + -bugprone-easily-swappable-parameters, + cppcoreguidelines-pro-type-vararg, + cppcoreguidelines-pro-type-reinterpret-cast, + llvm-*,-llvm-header-guard,-llvm-include-order, + google-readability-casting +ExtraArgs: ['-Wno-unknown-warning-option'] +HeaderFileExtensions: ['h', 'hpp'] +HeaderFilterRegex: '.*include/etl/.*' +ImplementationFileExtensions: ['cpp'] +UseColor: true +# TODO: Enable these when readability check is enabled +# CheckOptions : +# - key: readability-identifier-naming.AbstractClassCase +# value: snake_case +# - key: readability-identifier-naming.AbstractClassPrefix +# value: I +# - key: readability-identifier-naming.AbstractClassSuffix +# value: '' +# - key: readability-identifier-naming.ClassCase +# value: snake_case +# - key: readability-identifier-naming.ClassPrefix +# value: '' +# - key: readability-identifier-naming.ClassSuffix +# value: '' +# - key: readability-identifier-naming.GlobalConstantCase +# value: UPPER_CASE +# - key: readability-identifier-naming.GlobalConstantPrefix +# value: '' +# - key: readability-identifier-naming.GlobalConstantSuffix +# value: '' +# - key: readability-identifier-naming.ConstantCase +# value: snake_case +# - key: readability-identifier-naming.ConstantPrefix +# value: '' +# - key: readability-identifier-naming.ConstantSuffix +# value: '' +# - key: readability-identifier-naming.ConstantMemberCase +# value: snake_case +# - key: readability-identifier-naming.ConstantMemberPrefix +# value: k +# - key: readability-identifier-naming.ConstantMemberSuffix +# value: '' +# - key: readability-identifier-naming.StaticConstantCase +# value: snake_case +# - key: readability-identifier-naming.StaticConstantPrefix +# value: k +# - key: readability-identifier-naming.StaticConstantSuffix +# value: '' +# - key: readability-identifier-naming.EnumCase +# value: snake_case +# - key: readability-identifier-naming.EnumPrefix +# value: '' +# - key: readability-identifier-naming.EnumSuffix +# value: '' +# - key: readability-identifier-naming.EnumConstantCase +# value: snake_case +# - key: readability-identifier-naming.EnumConstantPrefix +# value: '' +# - key: readability-identifier-naming.EnumConstantSuffix +# value: '' +# - key: readability-identifier-naming.GlobalVariableCase +# value: snake_case +# - key: readability-identifier-naming.GlobalVariablePrefix +# value: g +# - key: readability-identifier-naming.GlobalVariableSuffix +# value: '' +# - key: readability-identifier-naming.LocalVariableCase +# value: snake_case +# - key: readability-identifier-naming.LocalVariablePrefix +# value: '' +# - key: readability-identifier-naming.LocalVariableSuffix +# value: '' +# - key: readability-identifier-naming.StructCase +# value: aNy_CasE +# - key: readability-identifier-naming.StructPrefix +# value: '' +# - key: readability-identifier-naming.StructSuffix +# value: '' +# - key: readability-identifier-naming.FunctionCase +# value: snake_case +# - key: readability-identifier-naming.FunctionPrefix +# value: '' +# - key: readability-identifier-naming.FunctionSuffix +# value: '' +# - key: readability-identifier-naming.MethodCase +# value: snake_case +# - key: readability-identifier-naming.MethodPrefix +# value: '' +# - key: readability-identifier-naming.MethodSuffix +# value: '' +# - key: readability-identifier-naming.ParameterCase +# value: snake_case +# - key: readability-identifier-naming.PrivateMethodCase +# value: snake_case +# - key: readability-identifier-naming.PrivateMethodPrefix +# value: '' +# - key: readability-identifier-naming.PrivateMethodSuffix +# value: '' +# - key: readability-identifier-naming.PublicMethodCase +# value: snake_case +# - key: readability-identifier-naming.PublicMethodPrefix +# value: '' +# - key: readability-identifier-naming.PublicMethodSuffix +# value: '' +# - key: readability-identifier-naming.MemberCase +# value: snake_case +# - key: readability-identifier-naming.MemberPrefix +# value: _ +# - key: readability-identifier-naming.MemberSuffix +# value: '' +# - key: readability-identifier-naming.PrivateMemberCase +# value: snake_case +# - key: readability-identifier-naming.PrivateMemberPrefix +# value: _ +# - key: readability-identifier-naming.PrivateMemberSuffix +# value: '' +# - key: readability-identifier-naming.PublicMemberCase +# value: snake_case +# - key: readability-identifier-naming.PublicMemberPrefix +# value: '' +# - key: readability-identifier-naming.PublicMemberSuffix +# value: '' +# - key: readability-identifier-naming.NamespaceCase +# value: lower_case +# - key: readability-identifier-naming.NamespacePrefix +# value: '' +# - key: readability-identifier-naming.NamespaceSuffix +# value: '' +# - key: readability-identifier-naming.InlineNamespaceCase +# value: lower_case +# - key: readability-identifier-naming.InlineNamespacePrefix +# value: '' +# - key: readability-identifier-naming.InlineNamespaceSuffix +# value: '' +# - key: readability-identifier-length.IgnoredParameterNames +# value: ^(n|id|a|b|x|y)$ +# - key: readability-identifier-length.IgnoredLoopCounterNames +# value: ^[ijkxy_]$ +# - key: readability-identifier-naming.GlobalFunctionCase +# value: snake_case +# - key: readability-identifier-naming.GlobalFunctionPrefix +# value: '' +# - key: readability-identifier-naming.GlobalFunctionSuffix +# value: '' +# - key: readability-identifier-naming.TemplateParameterCase +# value: CamelCase +# - key: readability-identifier-naming.TemplateParameterPrefix +# value: '' +# - key: readability-identifier-naming.TemplateParameterSuffix +# value: '' +# - key: readability-identifier-naming.TemplateTemplateParameterCase +# value: CamelCase +# - key: readability-identifier-naming.TemplateTemplateParameterPrefix +# value: 'TPL' +# - key: readability-identifier-naming.TemplateTemplateParameterSuffix +# value: '' +# - key: readability-identifier-naming.TypeTemplateParameterCase +# value: CamelCase +# - key: readability-identifier-naming.TypeTemplateParameterPrefix +# value: 'T' +# - key: readability-identifier-naming.TypeTemplateParameterSuffix +# value: '' +# - key: readability-identifier-naming.ValueTemplateParameterCase +# value: UPPER_CASE +# - key: readability-identifier-naming.ValueTemplateParameterPrefix +# value: '' +# - key: readability-identifier-naming.ValueTemplateParameterSuffix +# value: '' +... diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f51b1a16..2e08080a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -36,15 +36,32 @@ RUN set -eux \ && apt-get -y install --no-install-recommends \ python3-full \ python3-pip \ + python3-cogapp \ git \ wget \ cmake \ - && rm -rf /var/lib/apt/lists/* \ - && if pip help install | grep -q '\-\-break-system-packages'; then \ - pip install --no-cache-dir --break-system-packages cogapp; \ - else \ - pip install --no-cache-dir cogapp; \ - fi + clang-format \ + clang-format-18 \ + lcov \ + && rm -rf /var/lib/apt/lists/* + +RUN set -eux; \ + VERSION="2.4.1"; \ + case "$(uname -m)" in \ + x86_64) ARCH="amd64"; SHA256="bdaa2c0fbee03e5c2f99e605d9419386ce5d558440baac2017398faada839e04" ;; \ + aarch64) ARCH="arm64"; SHA256="0a09e1f04a0f8a86fd4e709552613f5d82adf6bc72f0a4b5e217670894e79fbf" ;; \ + *) echo "Unsupported architecture: $(uname -m)"; exit 1 ;; \ + esac; \ + wget -O treefmt.tar.gz "https://github.com/numtide/treefmt/releases/download/v${VERSION}/treefmt_${VERSION}_linux_${ARCH}.tar.gz" \ + && echo "${SHA256} treefmt.tar.gz" | sha256sum -c \ + && tar xzf treefmt.tar.gz treefmt \ + && install -m 755 treefmt /usr/bin/treefmt \ + && rm treefmt.tar.gz treefmt + +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel RUN set -eux \ && echo "Pip version: " \ diff --git a/.devcontainer/armhf/Dockerfile b/.devcontainer/armhf/Dockerfile new file mode 100644 index 00000000..a6f866df --- /dev/null +++ b/.devcontainer/armhf/Dockerfile @@ -0,0 +1,61 @@ +# armhf Test Environment for ETL +# Uses QEMU user-mode emulation to run armhf binaries on x64 host +FROM debian:trixie + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install QEMU user-mode emulation and armhf cross-compilation tools +RUN dpkg --add-architecture armhf && \ + apt-get update && apt-get install -y --no-install-recommends \ + qemu-user-static \ + qemu-user \ + binfmt-support \ + gcc-arm-linux-gnueabihf \ + g++-arm-linux-gnueabihf \ + cmake \ + make \ + ninja-build \ + git \ + wget \ + ca-certificates \ + file \ + libc6:armhf \ + libstdc++6:armhf \ + libatomic1:armhf \ + && rm -rf /var/lib/apt/lists/* + +# 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} + +# Set working directory +WORKDIR /workspaces/etl + +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel + +# Verify QEMU and cross-compilation setup +RUN echo "=== Host Architecture ===" && \ + uname -m && \ + echo "" && \ + echo "=== armhf Cross Compiler ===" && \ + arm-linux-gnueabihf-gcc --version && \ + echo "" && \ + echo "=== QEMU arm ===" && \ + qemu-arm-static --version | head -n1 + +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /workspaces/etl && chown -R ${USERNAME}:${USERNAME} /workspaces + +# Switch to non-root user +USER ${USERNAME} + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/armhf/devcontainer.json b/.devcontainer/armhf/devcontainer.json new file mode 100644 index 00000000..a1dc285e --- /dev/null +++ b/.devcontainer/armhf/devcontainer.json @@ -0,0 +1,29 @@ +// 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 +{ + "name": "armhf (Debian)", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + "settings": { + "cmake.sourceDirectory": "${workspaceFolder}/test", + "cmake.configureArgs": [ + "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/armhf/toolchain-armhf.cmake", + "-DBUILD_TESTS=ON", + "-DNO_STL=OFF", + "-DETL_CXX_STANDARD=23" + ], + "cmake.buildDirectory": "${workspaceFolder}/build-armhf", + "cmake.generator": "Ninja" + } + } + }, + "remoteUser": "root" +} diff --git a/.devcontainer/armhf/toolchain-armhf.cmake b/.devcontainer/armhf/toolchain-armhf.cmake new file mode 100644 index 00000000..a7f9f00a --- /dev/null +++ b/.devcontainer/armhf/toolchain-armhf.cmake @@ -0,0 +1,21 @@ +# CMake toolchain file for armhf cross-compilation +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +# Specify the cross compiler +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_AR arm-linux-gnueabihf-ar) +set(CMAKE_RANLIB arm-linux-gnueabihf-ranlib) +set(CMAKE_STRIP arm-linux-gnueabihf-strip) + +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# For libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Set QEMU for running tests +set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-arm-static CACHE FILEPATH "Path to the emulator for cross-compiled binaries") diff --git a/.devcontainer/context/reinstall-cmake.sh b/.devcontainer/context/reinstall-cmake.sh deleted file mode 100644 index 280d8145..00000000 --- a/.devcontainer/context/reinstall-cmake.sh +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env bash - -# For more detailed debugging, uncomment the next line -# set -x - -# Explicitly set CMAKE_VERSION from first argument, default to "none" -CMAKE_VERSION=${1:-"none"} - -# --- Configuration --- -DOWNLOAD_ATTEMPTS=3 -DOWNLOAD_RETRY_DELAY=5 # seconds - -# --- Global Variables --- -TMP_DIR="" # Initialize TMP_DIR, will be set by mktemp -# Filenames used within TMP_DIR -CMAKE_INSTALLER_SCRIPT_LOCAL_NAME="cmake-installer.sh" -CMAKE_CHECKSUM_FILE_LOCAL_NAME="cmake-checksums.txt" -# This will be the actual name of the CMake binary, derived from version and arch -# It's important for matching against the checksum file. -DERIVED_CMAKE_BINARY_FILENAME="" - -# --- Cleanup Function --- -# This trap will execute on EXIT, ERR, SIGINT, SIGTERM -# It's crucial for debugging to see the state of TMP_DIR if things go wrong. -cleanup() { - # $? is the exit code of the last command before the trap was triggered - # or the argument to exit if the script called exit explicitly. - LAST_EXIT_CODE=$? - echo # Newline for readability - - # Only proceed with detailed cleanup if TMP_DIR was actually created - if [[ -n "${TMP_DIR}" && -d "${TMP_DIR}" ]]; then - echo "--- Cleanup: Temporary Directory Inspector (${TMP_DIR}) ---" - echo "Listing contents of TMP_DIR:" - ls -la "${TMP_DIR}" - - # Check and display checksum file content - if [[ -f "${TMP_DIR}/${CMAKE_CHECKSUM_FILE_LOCAL_NAME}" ]]; then - echo "--- Content of downloaded checksum file (${CMAKE_CHECKSUM_FILE_LOCAL_NAME}) ---" - cat "${TMP_DIR}/${CMAKE_CHECKSUM_FILE_LOCAL_NAME}" - echo "--- End of checksum file ---" - else - echo "Checksum file (${CMAKE_CHECKSUM_FILE_LOCAL_NAME}) not found in TMP_DIR." - fi - - # Check and display head of (potentially) installer script - # Useful to see if it's an HTML error page - if [[ -f "${TMP_DIR}/${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}" ]]; then - echo "--- First 10 lines of downloaded installer script (${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}) ---" - head -n 10 "${TMP_DIR}/${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}" - echo "--- End of installer script head ---" - elif [[ -f "${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}" ]]; then - # If it was renamed - echo "--- First 10 lines of downloaded installer script (${DERIVED_CMAKE_BINARY_FILENAME}) ---" - head -n 10 "${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}" - echo "--- End of installer script head ---" - else - echo "Installer script not found in TMP_DIR (checked for ${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME} and ${DERIVED_CMAKE_BINARY_FILENAME})." - fi - - echo "Attempting to remove temporary directory: ${TMP_DIR}" - rm -Rf "${TMP_DIR}" - echo "Temporary directory removed." - echo "--- End of Cleanup ---" - else - echo "--- Cleanup: TMP_DIR was not set or not a directory, no temp files to inspect or clean. ---" - fi - - # Ensure the script exits with the LAST_EXIT_CODE observed by the trap - # unless it was 0 and the script is exiting due to an explicit non-zero exit. - # The 'exit' command within the trap will override the script's natural exit code. - # So, if the script was going to exit 0, but cleanup had an issue, this could change it. - # However, for debugging an exit code 8, we want to preserve the code that *caused* the trap. - echo "Script finished with exit code: ${LAST_EXIT_CODE}." - exit "${LAST_EXIT_CODE}" -} -trap cleanup EXIT ERR SIGINT SIGTERM - -# Immediately turn on `set -e` after trap setup -set -e - -# --- Helper Functions --- -# Function to download a file with retries and basic validation -download_file() { - local url="$1" - local output_filename="$2" - local attempts_left=$DOWNLOAD_ATTEMPTS - local wget_exit_code=0 - - while [ $attempts_left -gt 0 ]; do - echo "Downloading: ${url}" - echo "Saving to: ${TMP_DIR}/${output_filename}" - echo "Attempt $((DOWNLOAD_ATTEMPTS - attempts_left + 1)) of ${DOWNLOAD_ATTEMPTS}..." - - # Use wget with: - # -O: specify output file - # --timeout: connection/read timeout - # --tries: number of retries (wget's own retry, distinct from this loop) - # --quiet: suppress normal output, but errors still go to stderr - # --show-progress: if not quiet, shows a progress bar (optional) - # Using -q for less verbose logs, but on failure, we need to know. - wget -O "${TMP_DIR}/${output_filename}" --timeout=30 --tries=1 "${url}" - wget_exit_code=$? - - if [ ${wget_exit_code} -eq 0 ]; then - echo "Download command successful for ${output_filename}." - if [ -s "${TMP_DIR}/${output_filename}" ]; then # -s: file exists and has a size greater than 0 - # Basic check for common HTML error page indicators - # This is a heuristic and might not catch all error pages. - if head -n 5 "${TMP_DIR}/${output_filename}" | grep -Eiq ' /dev/null; then - echo "wget not found. Attempting to install wget via apt-get..." - if command -v apt-get > /dev/null; then - apt-get update -y - apt-get install -y --no-install-recommends wget - echo "wget installed." - else - echo "ERROR: apt-get not found. Cannot install wget. Please install wget manually." - exit 1 - fi -else - echo "wget is available." -fi - -# 2. (Optional) Remove existing CMake installed via apt -echo "Attempting to remove any existing CMake installed via apt..." -if command -v apt-get > /dev/null; then - if dpkg -s cmake &> /dev/null; then # Check if cmake package is actually installed - apt-get -y purge --auto-remove cmake - echo "cmake package purged." - else - echo "cmake package not found via dpkg, skipping purge." - fi -else - echo "apt-get not found, skipping removal of CMake via apt." -fi - -# 3. Create installation and temporary directories -echo "Creating CMake installation directory /opt/cmake..." -mkdir -p /opt/cmake - -TMP_DIR=$(mktemp -d -t cmake-install-XXXXXXXXXX) -echo "Temporary directory created: ${TMP_DIR}" -# Crucial: subsequent operations needing temp files should happen in or relative to TMP_DIR -# We will cd into TMP_DIR later, or use full paths like ${TMP_DIR}/filename - -# 4. Determine system architecture -echo "Determining system architecture..." -architecture=$(dpkg --print-architecture) -case "${architecture}" in - arm64) ARCH="aarch64" ;; - amd64) ARCH="x86_64" ;; - *) - echo "ERROR: Unsupported architecture '${architecture}' reported by dpkg." - exit 1 - ;; -esac -echo "Detected architecture: ${architecture} (mapped to CMake arch: ${ARCH})" - -# 5. Define CMake download URLs and the filename expected by checksum -DERIVED_CMAKE_BINARY_FILENAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" -CMAKE_BINARY_URL="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${DERIVED_CMAKE_BINARY_FILENAME}" -CMAKE_CHECKSUM_FILE_URL="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-SHA-256.txt" - -# 6. Download CMake binary and checksum file -echo "--- Downloading Files ---" -if ! download_file "${CMAKE_BINARY_URL}" "${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}"; then - echo "ERROR: Failed to download CMake binary installer. See messages above." - exit 1 -fi - -if ! download_file "${CMAKE_CHECKSUM_FILE_URL}" "${CMAKE_CHECKSUM_FILE_LOCAL_NAME}"; then - echo "ERROR: Failed to download CMake checksum file. See messages above." - exit 1 -fi -echo "Downloads complete." -echo "--- End of Downloading Files ---" -echo # Newline for readability - -# Before checksum, rename the downloaded installer to its derived name, -# as the checksum file refers to this specific name. -echo "Renaming downloaded installer from '${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}' to '${DERIVED_CMAKE_BINARY_FILENAME}' for checksum verification." -mv "${TMP_DIR}/${CMAKE_INSTALLER_SCRIPT_LOCAL_NAME}" "${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}" -if [ ! -f "${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}" ]; then - echo "ERROR: Failed to rename installer script for checksum. File '${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}' does not exist after move." - exit 1 -fi - -# 7. Verify checksum -echo "--- Verifying Checksum ---" -echo "Checksum file is: ${TMP_DIR}/${CMAKE_CHECKSUM_FILE_LOCAL_NAME}" -echo "Binary file to check is: ${TMP_DIR}/${DERIVED_CMAKE_BINARY_FILENAME}" - -# Ensure the checksum file actually contains an entry for our binary -# This is important because the SHA-256.txt file contains checksums for *all* release assets -echo "Checking if checksum file contains entry for '${DERIVED_CMAKE_BINARY_FILENAME}'..." -if ! grep -q "${DERIVED_CMAKE_BINARY_FILENAME}" "${TMP_DIR}/${CMAKE_CHECKSUM_FILE_LOCAL_NAME}"; then - echo "ERROR: The downloaded checksum file '${CMAKE_CHECKSUM_FILE_LOCAL_NAME}' does NOT contain an entry for '${DERIVED_CMAKE_BINARY_FILENAME}'." - echo "This strongly suggests that the CMAKE_VERSION ('${CMAKE_VERSION}') or ARCH ('${ARCH}') is incorrect, or the specified version does not provide a .sh installer for this architecture." - echo "Please verify the version and available files at https://github.com/Kitware/CMake/releases/tag/v${CMAKE_VERSION}" - exit 1 -fi -echo "Checksum file contains an entry for '${DERIVED_CMAKE_BINARY_FILENAME}'." - -# Perform the checksum. We need to be in the directory where the files are. -echo "Changing directory to ${TMP_DIR} for checksum verification." -cd "${TMP_DIR}" # <<<<<<< IMPORTANT: sha256sum -c needs to find files - -echo "Verifying checksum of '${DERIVED_CMAKE_BINARY_FILENAME}' using '${CMAKE_CHECKSUM_FILE_LOCAL_NAME}'..." -# The --ignore-missing flag is good, as the .txt file has many checksums. -# The --strict flag would cause it to error if there are improperly formatted lines. -# We rely on the grep check above to ensure our specific file is mentioned. -if sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_FILE_LOCAL_NAME}"; then - echo "Checksum verification successful for '${DERIVED_CMAKE_BINARY_FILENAME}'." -else - SHA_EXIT_CODE=$? - echo "ERROR: Checksum verification FAILED for '${DERIVED_CMAKE_BINARY_FILENAME}' with exit code ${SHA_EXIT_CODE}." - # Cleanup trap will show file contents. - exit 1 # Critical failure -fi -echo "--- End of Verifying Checksum ---" -echo # Newline for readability - -# 8. Install CMake -echo "--- Installing CMake ---" -echo "Making the CMake installer script '${DERIVED_CMAKE_BINARY_FILENAME}' executable..." -chmod +x "${DERIVED_CMAKE_BINARY_FILENAME}" # Still in TMP_DIR - -echo "Executing CMake installer script: ./${DERIVED_CMAKE_BINARY_FILENAME} --prefix=/opt/cmake --skip-license" -# Execute the script. If this script exits with 8, this is our culprit. -if ./"${DERIVED_CMAKE_BINARY_FILENAME}" --prefix=/opt/cmake --skip-license; then - echo "CMake installer script executed successfully." -else - INSTALLER_EXIT_CODE=$? - echo "ERROR: CMake installer script FAILED with exit code ${INSTALLER_EXIT_CODE}." - # This is the most likely place for an exit code 8 if downloads and checksums were okay. - # The trap will handle cleanup. The script will exit with INSTALLER_EXIT_CODE due to the trap. - exit ${INSTALLER_EXIT_CODE} # Explicitly exit with the installer's code -fi -echo "--- End of Installing CMake ---" -echo # Newline for readability - -# 9. Create symlinks -echo "Creating symbolic links for cmake and ctest in /usr/local/bin/..." -ln -sf /opt/cmake/bin/cmake /usr/local/bin/cmake -ln -sf /opt/cmake/bin/ctest /usr/local/bin/ctest -echo "Symbolic links created." - -echo # Newline for readability -echo "SUCCESS: CMake ${CMAKE_VERSION} installation and setup complete." - -# The script will exit with 0 here. The trap will run, see $? is 0, and then exit 0. -exit 0 \ No newline at end of file diff --git a/.devcontainer/i386/Dockerfile b/.devcontainer/i386/Dockerfile new file mode 100644 index 00000000..b879ee00 --- /dev/null +++ b/.devcontainer/i386/Dockerfile @@ -0,0 +1,61 @@ +# i386 Test Environment for ETL +# Uses QEMU user-mode emulation to run i386 binaries on x64 host +FROM debian:trixie + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install QEMU user-mode emulation and i386 cross-compilation tools +RUN dpkg --add-architecture i386 && \ + apt-get update && apt-get install -y --no-install-recommends \ + qemu-user-static \ + qemu-user \ + binfmt-support \ + gcc-i686-linux-gnu \ + g++-i686-linux-gnu \ + cmake \ + make \ + ninja-build \ + git \ + wget \ + ca-certificates \ + file \ + libc6:i386 \ + libstdc++6:i386 \ + libatomic1:i386 \ + && rm -rf /var/lib/apt/lists/* + +# 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} + +# Set working directory +WORKDIR /workspaces/etl + +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel + +# Verify QEMU and cross-compilation setup +RUN echo "=== Host Architecture ===" && \ + uname -m && \ + echo "" && \ + echo "=== i386 Cross Compiler ===" && \ + i686-linux-gnu-gcc --version && \ + echo "" && \ + echo "=== QEMU i386 ===" && \ + qemu-i386-static --version | head -n1 + +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /workspaces/etl && chown -R ${USERNAME}:${USERNAME} /workspaces + +# Switch to non-root user +USER ${USERNAME} + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/i386/devcontainer.json b/.devcontainer/i386/devcontainer.json new file mode 100644 index 00000000..84e28a8e --- /dev/null +++ b/.devcontainer/i386/devcontainer.json @@ -0,0 +1,29 @@ +// 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 +{ + "name": "i386 (Debian)", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + "settings": { + "cmake.sourceDirectory": "${workspaceFolder}/test", + "cmake.configureArgs": [ + "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/i386/toolchain-i386.cmake", + "-DBUILD_TESTS=ON", + "-DNO_STL=OFF", + "-DETL_CXX_STANDARD=23" + ], + "cmake.buildDirectory": "${workspaceFolder}/build-i386", + "cmake.generator": "Ninja" + } + } + }, + "remoteUser": "root" +} diff --git a/.devcontainer/i386/toolchain-i386.cmake b/.devcontainer/i386/toolchain-i386.cmake new file mode 100644 index 00000000..502e2285 --- /dev/null +++ b/.devcontainer/i386/toolchain-i386.cmake @@ -0,0 +1,21 @@ +# CMake toolchain file for i386 cross-compilation +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR i386) + +# Specify the cross compiler +set(CMAKE_C_COMPILER i686-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER i686-linux-gnu-g++) +set(CMAKE_AR i686-linux-gnu-ar) +set(CMAKE_RANLIB i686-linux-gnu-ranlib) +set(CMAKE_STRIP i686-linux-gnu-strip) + +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# For libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Set QEMU for running tests +set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-i386-static CACHE FILEPATH "Path to the emulator for cross-compiled binaries") diff --git a/.devcontainer/powerpc/Dockerfile b/.devcontainer/powerpc/Dockerfile new file mode 100644 index 00000000..a2442782 --- /dev/null +++ b/.devcontainer/powerpc/Dockerfile @@ -0,0 +1,85 @@ +# powerpc Test Environment for ETL +# Uses QEMU user-mode emulation to run powerpc binaries on x64 host +FROM debian:sid-20260406 + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install QEMU user-mode emulation and powerpc cross-compilation tools +RUN dpkg --add-architecture powerpc && \ + apt-get update && apt-get install -y --no-install-recommends \ + binfmt-support \ + gpg \ + ca-certificates \ + cmake \ + make \ + ninja-build \ + git \ + wget \ + file \ + debian-ports-archive-keyring \ + && rm -rf /var/lib/apt/lists/* + +RUN cat < /etc/apt/sources.list.d/debian.sources +Types: deb +URIs: http://snapshot.debian.org/archive/debian/20260406T000000Z +Suites: sid +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp +EOF + +RUN cat < /etc/apt/sources.list.d/powerpc.sources +Types: deb +URIs: http://snapshot.debian.org/archive/debian-ports/20260406T000000Z +Suites: sid +Components: main +Architectures: powerpc +Signed-By: /usr/share/keyrings/debian-ports-archive-keyring.gpg +EOF + +RUN echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid + +RUN apt-get update && apt-get install -y --no-install-recommends \ + qemu-user-static \ + qemu-user \ + gcc-powerpc-linux-gnu \ + g++-powerpc-linux-gnu \ + libc6:powerpc \ + libstdc++6:powerpc \ + libatomic1:powerpc \ + && rm -rf /var/lib/apt/lists/* + +# 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} + +# Set working directory +WORKDIR /workspaces/etl + +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel + +# Verify QEMU and cross-compilation setup +RUN echo "=== Host Architecture ===" && \ + uname -m && \ + echo "" && \ + echo "=== powerpc Cross Compiler ===" && \ + powerpc-linux-gnu-gcc --version && \ + echo "" && \ + echo "=== QEMU powerpc ===" && \ + qemu-ppc-static --version | head -n1 + +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /workspaces/etl && chown -R ${USERNAME}:${USERNAME} /workspaces + +# Switch to non-root user +USER ${USERNAME} + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/powerpc/devcontainer.json b/.devcontainer/powerpc/devcontainer.json new file mode 100644 index 00000000..1d7a9a05 --- /dev/null +++ b/.devcontainer/powerpc/devcontainer.json @@ -0,0 +1,29 @@ +// 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 +{ + "name": "powerpc (Debian)", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + "settings": { + "cmake.sourceDirectory": "${workspaceFolder}/test", + "cmake.configureArgs": [ + "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/powerpc/toolchain-powerpc.cmake", + "-DBUILD_TESTS=ON", + "-DNO_STL=ON", + "-DETL_CXX_STANDARD=23" + ], + "cmake.buildDirectory": "${workspaceFolder}/build-powerpc", + "cmake.generator": "Ninja" + } + } + }, + "remoteUser": "root" +} diff --git a/.devcontainer/powerpc/toolchain-powerpc.cmake b/.devcontainer/powerpc/toolchain-powerpc.cmake new file mode 100644 index 00000000..1afea7da --- /dev/null +++ b/.devcontainer/powerpc/toolchain-powerpc.cmake @@ -0,0 +1,21 @@ +# CMake toolchain file for powerpc cross-compilation +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR powerpc) + +# Specify the cross compiler +set(CMAKE_C_COMPILER powerpc-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER powerpc-linux-gnu-g++) +set(CMAKE_AR powerpc-linux-gnu-ar) +set(CMAKE_RANLIB powerpc-linux-gnu-ranlib) +set(CMAKE_STRIP powerpc-linux-gnu-strip) + +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# For libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Set QEMU for running tests +set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-ppc CACHE FILEPATH "Path to the emulator for cross-compiled binaries") diff --git a/.devcontainer/riscv64/Dockerfile b/.devcontainer/riscv64/Dockerfile new file mode 100644 index 00000000..90a95a4c --- /dev/null +++ b/.devcontainer/riscv64/Dockerfile @@ -0,0 +1,61 @@ +# riscv64 Test Environment for ETL +# Uses QEMU user-mode emulation to run riscv64 binaries on x64 host +FROM debian:trixie + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install QEMU user-mode emulation and riscv64 cross-compilation tools +RUN dpkg --add-architecture riscv64 && \ + apt-get update && apt-get install -y --no-install-recommends \ + qemu-user-static \ + qemu-user \ + binfmt-support \ + gcc-riscv64-linux-gnu \ + g++-riscv64-linux-gnu \ + cmake \ + make \ + ninja-build \ + git \ + wget \ + ca-certificates \ + file \ + libc6:riscv64 \ + libstdc++6:riscv64 \ + libatomic1:riscv64 \ + && rm -rf /var/lib/apt/lists/* + +# 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} + +# Set working directory +WORKDIR /workspaces/etl + +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel + +# Verify QEMU and cross-compilation setup +RUN echo "=== Host Architecture ===" && \ + uname -m && \ + echo "" && \ + echo "=== riscv64 Cross Compiler ===" && \ + riscv64-linux-gnu-gcc --version && \ + echo "" && \ + echo "=== QEMU riscv64 ===" && \ + qemu-riscv64-static --version | head -n1 + +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /workspaces/etl && chown -R ${USERNAME}:${USERNAME} /workspaces + +# Switch to non-root user +USER ${USERNAME} + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/riscv64/devcontainer.json b/.devcontainer/riscv64/devcontainer.json new file mode 100644 index 00000000..0e673f24 --- /dev/null +++ b/.devcontainer/riscv64/devcontainer.json @@ -0,0 +1,29 @@ +// 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 +{ + "name": "riscv64 (Debian)", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + "settings": { + "cmake.sourceDirectory": "${workspaceFolder}/test", + "cmake.configureArgs": [ + "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/riscv64/toolchain-riscv64.cmake", + "-DBUILD_TESTS=ON", + "-DNO_STL=OFF", + "-DETL_CXX_STANDARD=23" + ], + "cmake.buildDirectory": "${workspaceFolder}/build-riscv64", + "cmake.generator": "Ninja" + } + } + }, + "remoteUser": "root" +} diff --git a/.devcontainer/riscv64/toolchain-riscv64.cmake b/.devcontainer/riscv64/toolchain-riscv64.cmake new file mode 100644 index 00000000..adc1c7f3 --- /dev/null +++ b/.devcontainer/riscv64/toolchain-riscv64.cmake @@ -0,0 +1,20 @@ +# CMake toolchain file for riscv64 cross-compilation +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR riscv64) + +# Specify the cross compiler +set(CMAKE_C_COMPILER riscv64-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER riscv64-linux-gnu-g++) +set(CMAKE_AR riscv64-linux-gnu-ar) +set(CMAKE_RANLIB riscv64-linux-gnu-ranlib) +set(CMAKE_STRIP riscv64-linux-gnu-strip) +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# For libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Set QEMU for running tests +set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-riscv64-static CACHE FILEPATH "Path to the emulator for cross-compiled binaries") diff --git a/.devcontainer/run-tests.sh b/.devcontainer/run-tests.sh new file mode 100755 index 00000000..9618a15b --- /dev/null +++ b/.devcontainer/run-tests.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Run tests inside the separately created docker container for different hardware architecture +# +# Strategy: +# * Create docker image +# * Enter image +# * Cross build tests +# * Run tests via QEMU +# + +set -e + +usage() +{ + echo "Usage: run-tests.sh " + echo "Architecture: armhf|i386|powerpc|riscv64|s390x" + echo "(run from project root)" +} + +ARCHLIST="armhf i386 powerpc riscv64 s390x" + +if [[ " $ARCHLIST " =~ " $1 " ]] ; then + ARCH=$1 +else + echo "Unsupported architecture: $1" + usage + exit 1 +fi + +if [ "$2" = "" ] ; then + echo "Creating docker image..." + docker build -t $ARCH .devcontainer/$ARCH + + echo "Entering container..." + docker run -it --rm -v "$PWD":/workspaces/etl -w /workspaces/etl $ARCH /bin/bash .devcontainer/run-tests.sh $ARCH inside_container + +elif [ "$2" = "inside_container" ] ; then + echo "Cross building tests..." + mkdir -p build-$ARCH + cd build-$ARCH + cmake -DCMAKE_TOOLCHAIN_FILE=../.devcontainer/$ARCH/toolchain-$ARCH.cmake \ + -DBUILD_TESTS=ON -DNO_STL=ON -DETL_CXX_STANDARD=23 \ + -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_OPTIMISATION=-O0 -DETL_ENABLE_SANITIZER=OFF -DETL_MESSAGES_ARE_NOT_VIRTUAL=OFF \ + ../test + export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) + cmake --build . + + echo "Running tests via CTest (using QEMU emulator from toolchain)..." + ctest -V --output-on-failure + echo "Tests successful." +else + echo "Invalid second argument: $2" + usage + exit 1 +fi diff --git a/.devcontainer/s390x/Dockerfile b/.devcontainer/s390x/Dockerfile index 21a6289c..536f2de4 100644 --- a/.devcontainer/s390x/Dockerfile +++ b/.devcontainer/s390x/Dockerfile @@ -7,7 +7,7 @@ ENV DEBIAN_FRONTEND=noninteractive # Install QEMU user-mode emulation and s390x cross-compilation tools RUN dpkg --add-architecture s390x && \ - apt-get update && apt-get install -y --no-install-recommends\ + apt-get update && apt-get install -y --no-install-recommends \ qemu-user-static \ qemu-user \ binfmt-support \ @@ -18,14 +18,29 @@ RUN dpkg --add-architecture s390x && \ ninja-build \ git \ wget \ + ca-certificates \ file \ libc6:s390x \ libstdc++6:s390x \ + libatomic1:s390x \ && rm -rf /var/lib/apt/lists/* +# 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} + # Set working directory WORKDIR /workspaces/etl +# Install Bazelisk as 'bazel' +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && \ + wget -qO /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-${ARCH} && \ + chmod +x /usr/local/bin/bazel + # Verify QEMU and cross-compilation setup RUN echo "=== Host Architecture ===" && \ uname -m && \ @@ -36,5 +51,11 @@ RUN echo "=== Host Architecture ===" && \ echo "=== QEMU s390x ===" && \ qemu-s390x-static --version | head -n1 +# Ensure workspace directory ownership for non-root user +RUN mkdir -p /workspaces/etl && chown -R ${USERNAME}:${USERNAME} /workspaces + +# Switch to non-root user +USER ${USERNAME} + # Default command CMD ["/bin/bash"] diff --git a/.devcontainer/s390x/devcontainer.json b/.devcontainer/s390x/devcontainer.json index 8b7f8235..535a0799 100644 --- a/.devcontainer/s390x/devcontainer.json +++ b/.devcontainer/s390x/devcontainer.json @@ -18,7 +18,7 @@ "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/s390x/toolchain-s390x.cmake", "-DBUILD_TESTS=ON", "-DNO_STL=OFF", - "-DETL_CXX_STANDARD=17" + "-DETL_CXX_STANDARD=23" ], "cmake.buildDirectory": "${workspaceFolder}/build-s390x", "cmake.generator": "Ninja" diff --git a/.devcontainer/ubuntu-26.04/Dockerfile b/.devcontainer/ubuntu-26.04/Dockerfile new file mode 100644 index 00000000..ddd21706 --- /dev/null +++ b/.devcontainer/ubuntu-26.04/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:26.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + git \ + ninja-build \ + python3 \ + python3-pip \ + clang \ + docker.io \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/bash vscode + +WORKDIR /etl + +CMD ["/bin/bash"] diff --git a/.github/workflows/bazel-gcc-c++23-no-stl.yml b/.github/workflows/bazel-gcc-c++23-no-stl.yml new file mode 100644 index 00000000..b41765a2 --- /dev/null +++ b/.github/workflows/bazel-gcc-c++23-no-stl.yml @@ -0,0 +1,22 @@ +name: bazel-gcc-c++23-no-stl +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-bazel-gcc-cpp23-no-stl: + name: Bazel GCC C++23 Linux - No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: bazel build //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL + + - name: Run tests + run: bazel test //test:etl_tests --cxxopt=-std=c++23 --copt=-DETL_NO_STL --test_output=all diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index d511f5b7..6b73b7c1 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -25,7 +25,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index d06778b1..830832b5 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -25,7 +25,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index 9fb4611f..6ae2f4b9 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -25,7 +25,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index c68f9870..e246a795 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -32,7 +32,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -61,7 +61,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -90,7 +90,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -112,7 +112,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -134,7 +134,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -156,7 +156,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -178,7 +178,7 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index ffa88744..0c2d8c9b 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -13,26 +13,19 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 - # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - - name: Install newer Clang - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x ./llvm.sh - sudo ./llvm.sh 17 - - name: Build run: | - export CC=clang-17 - export CXX=clang++-17 + export CC=clang + export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=OFF -DETL_CXX_STANDARD=23 ./ - clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -42,26 +35,19 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 - # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - - name: Install newer Clang - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x ./llvm.sh - sudo ./llvm.sh 17 - - name: Build run: | - export CC=clang-17 - export CXX=clang++-17 + export CC=clang + export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ - clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -71,26 +57,19 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 - # Temporary fix. See https://github.com/actions/runner-images/issues/8659 - - name: Install newer Clang - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x ./llvm.sh - sudo ./llvm.sh 17 - - name: Build run: | - export CC=clang-17 - export CXX=clang++-17 + export CC=clang + export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ - clang-17 --version - make -j $(getconf _NPROCESSORS_ONLN) + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -110,9 +89,9 @@ jobs: export CC=clang export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=OFF -DETL_CXX_STANDARD=23 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -132,9 +111,9 @@ jobs: export CC=clang export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=OFF -DETL_CXX_STANDARD=23 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -154,9 +133,9 @@ jobs: export CC=clang export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -176,9 +155,9 @@ jobs: export CC=clang export CXX=clang++ export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 - cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++26.yml b/.github/workflows/clang-c++26.yml new file mode 100644 index 00000000..35d127bf --- /dev/null +++ b/.github/workflows/clang-c++26.yml @@ -0,0 +1,185 @@ +name: clang-c++26 +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-clang-cpp26-linux-stl: + name: Clang C++26 Linux - STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ + clang --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-clang-cpp26-linux-no-stl: + name: Clang C++26 Linux - No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ + clang --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-clang-cpp26-linux-stl-force-cpp03: + name: Clang C++26 Linux - STL - Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ + clang --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-clang-cpp26-linux-no-stl-force-cpp03: + name: Clang C++26 Linux - No STL - Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ + clang --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-clang-cpp26-osx-stl: + name: Clang C++26 OSX - STL + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-26] + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: | + export CC=clang + export CXX=clang++ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" + + - name: Run tests + run: ./test/etl_tests -v + + build-clang-cpp26-osx-no-stl: + name: Clang C++26 OSX - No STL + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-26] + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: | + export CC=clang + export CXX=clang++ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" + + - name: Run tests + run: ./test/etl_tests -v + + build-clang-cpp26-osx-stl-force-cpp03: + name: Clang C++26 OSX - STL - Force C++03 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-26] + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: | + export CC=clang + export CXX=clang++ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 + cmake -D BUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" + + - name: Run tests + run: ./test/etl_tests -v + + build-clang-cpp26-osx-no-stl-force-cpp03: + name: Clang C++26 OSX - No STL - Force C++03 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-26] + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: | + export CC=clang + export CXX=clang++ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 + cmake -D BUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ + clang --version + make -j "$(getconf _NPROCESSORS_ONLN)" + + - name: Run tests + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-syntax-checks.yml b/.github/workflows/clang-syntax-checks.yml index 700b2087..2c6000e7 100644 --- a/.github/workflows/clang-syntax-checks.yml +++ b/.github/workflows/clang-syntax-checks.yml @@ -23,10 +23,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=03 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp03-linux-No-STL: - name: Syntax Check - Clang C++03 Linux No STL + name: Syntax Check - Clang C++03 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -41,7 +41,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=03 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp11-linux-STL: name: Syntax Check - Clang C++11 Linux STL @@ -59,10 +59,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp11-linux-No-STL: - name: Syntax Check - Clang C++11 Linux No STL + name: Syntax Check - Clang C++11 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -77,7 +77,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp11-linux-STL-Force-CPP03: name: Syntax Check - Clang C++11 Linux STL Force C++03 @@ -95,7 +95,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=11 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp11-linux-No-STL-Force-CPP03: name: Syntax Check - Clang C++11 Linux No STL Force C++03 @@ -113,7 +113,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=11 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp14-linux-STL: name: Syntax Check - Clang C++14 Linux STL @@ -131,10 +131,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp14-linux-No-STL: - name: Syntax Check - Clang C++14 Linux No STL + name: Syntax Check - Clang C++14 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -149,7 +149,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp14-linux-STL-Force-CPP03: name: Syntax Check - Clang C++14 Linux STL Force C++03 @@ -167,7 +167,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=14 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp14-linux-No-STL-Force-CPP03: name: Syntax Check - Clang C++14 Linux No STL Force C++03 @@ -185,7 +185,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=14 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp17-linux-STL: name: Syntax Check - Clang C++17 Linux STL @@ -203,10 +203,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp17-linux-No-STL: - name: Syntax Check - Clang C++17 Linux No STL + name: Syntax Check - Clang C++17 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -221,7 +221,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp17-linux-STL-Force-CPP03: name: Syntax Check - Clang C++17 Linux STL Force C++03 @@ -239,7 +239,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp17-linux-No-STL-Force-CPP03: name: Syntax Check - Clang C++17 Linux No STL Force C++03 @@ -257,7 +257,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp20-linux-STL: name: Syntax Check - Clang C++20 Linux STL @@ -275,10 +275,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp20-linux-No-STL: - name: Syntax Check - Clang C++20 Linux No STL + name: Syntax Check - Clang C++20 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -293,7 +293,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp20-linux-STL-Force-CPP03: name: Syntax Check - Clang C++20 Linux STL Force C++03 @@ -311,7 +311,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp20-linux-No-STL-Force-CPP03: name: Syntax Check - Clang C++20 Linux No STL Force C++03 @@ -329,7 +329,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp23-linux-STL: name: Syntax Check - Clang C++23 Linux STL @@ -347,10 +347,10 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp23-linux-No-STL: - name: Syntax Check - Clang C++23 Linux No STL + name: Syntax Check - Clang C++23 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -365,7 +365,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp23-linux-STL-Force-CPP03: name: Syntax Check - Clang C++23 Linux STL Force C++03 @@ -383,7 +383,7 @@ jobs: export CXX=clang++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-clang-cpp23-linux-No-STL-Force-CPP03: name: Syntax Check - Clang C++23 Linux No STL Force C++03 @@ -401,4 +401,84 @@ jobs: export CXX=clang++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 ./test/syntax_check clang --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" + + build-clang-cpp26-linux-STL: + name: Syntax Check - Clang C++26 Linux STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + cmake -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 ./test/syntax_check && \ + clang++ --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-clang-cpp26-linux-No-STL: + name: Syntax Check - Clang C++26 Linux No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + cmake -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 ./test/syntax_check && \ + clang++ --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-clang-cpp26-linux-STL-Force-CPP03: + name: Syntax Check - Clang C++26 Linux STL Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./test/syntax_check && \ + clang++ --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-clang-cpp26-linux-No-STL-Force-CPP03: + name: Syntax Check - Clang C++26 Linux No STL Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=clang && \ + export CXX=clang++ && \ + cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./test/syntax_check && \ + clang++ --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml new file mode 100644 index 00000000..214d30b2 --- /dev/null +++ b/.github/workflows/clang-tidy.yaml @@ -0,0 +1,29 @@ +name: clang-tidy + +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + clang-tidy: + name: clang-tidy + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends clang clang-tidy + clang --version + clang-tidy --version + run-clang-tidy --version || true + + - name: Run clang-tidy + run: | + test/run-clang-tidy.sh diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 79f9553e..5c05ebef 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -65,8 +65,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 - -# GitHub Repository settings -# -> Settings -> Pages -# -> Source: gh actions + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index 571c6d6e..c9706aeb 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -26,7 +26,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -49,7 +49,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index f623af08..a3277b66 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -25,7 +25,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index fc0ddd97..dbeef7fa 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -25,7 +25,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index 5965e05e..be4e6fb2 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -25,7 +25,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -47,7 +47,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -69,7 +69,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -91,7 +91,7 @@ jobs: export CXX=g++ cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/gcc-c++23-armhf.yml b/.github/workflows/gcc-c++23-armhf.yml new file mode 100644 index 00000000..1bd2912b --- /dev/null +++ b/.github/workflows/gcc-c++23-armhf.yml @@ -0,0 +1,29 @@ +name: gcc-c++23-armhf +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp23-linux-no-stl-armhf: + name: GCC C++23 Linux - No STL - armhf + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-armhf -f .devcontainer/armhf/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-armhf bash -c "\ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=.devcontainer/armhf/toolchain-armhf.cmake \ + -DEXTRA_TESTING_FLAGS=-v \ + ./ && \ + cmake --build . -- -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest -V" diff --git a/.github/workflows/gcc-c++23-i386.yml b/.github/workflows/gcc-c++23-i386.yml new file mode 100644 index 00000000..31bfc331 --- /dev/null +++ b/.github/workflows/gcc-c++23-i386.yml @@ -0,0 +1,29 @@ +name: gcc-c++23-i386 +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp23-linux-no-stl-i386: + name: GCC C++23 Linux - No STL - i386 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-i386 -f .devcontainer/i386/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-i386 bash -c "\ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=.devcontainer/i386/toolchain-i386.cmake \ + -DEXTRA_TESTING_FLAGS=-v \ + ./ && \ + cmake --build . -- -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest -V" diff --git a/.github/workflows/gcc-c++23-powerpc.yml b/.github/workflows/gcc-c++23-powerpc.yml new file mode 100644 index 00000000..7494ae55 --- /dev/null +++ b/.github/workflows/gcc-c++23-powerpc.yml @@ -0,0 +1,29 @@ +name: gcc-c++23-powerpc +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp23-linux-no-stl-powerpc: + name: GCC C++23 Linux - No STL - powerpc + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-powerpc -f .devcontainer/powerpc/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-powerpc bash -c "\ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=.devcontainer/powerpc/toolchain-powerpc.cmake \ + -DEXTRA_TESTING_FLAGS=-v \ + ./ && \ + cmake --build . -- -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest -V" diff --git a/.github/workflows/gcc-c++23-riscv64.yml b/.github/workflows/gcc-c++23-riscv64.yml new file mode 100644 index 00000000..695056a8 --- /dev/null +++ b/.github/workflows/gcc-c++23-riscv64.yml @@ -0,0 +1,29 @@ +name: gcc-c++23-riscv64 +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp23-linux-no-stl-riscv64: + name: GCC C++23 Linux - No STL - riscv64 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-riscv64 -f .devcontainer/riscv64/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-riscv64 bash -c "\ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=.devcontainer/riscv64/toolchain-riscv64.cmake \ + -DEXTRA_TESTING_FLAGS=-v \ + ./ && \ + cmake --build . -- -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest -V" diff --git a/.github/workflows/gcc-c++23-s390x.yml b/.github/workflows/gcc-c++23-s390x.yml new file mode 100644 index 00000000..f6a15aca --- /dev/null +++ b/.github/workflows/gcc-c++23-s390x.yml @@ -0,0 +1,29 @@ +name: gcc-c++23-s390x +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp23-linux-no-stl-s390x: + name: GCC C++23 Linux - No STL - s390x + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-s390x -f .devcontainer/s390x/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-s390x bash -c "\ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=.devcontainer/s390x/toolchain-s390x.cmake \ + -DEXTRA_TESTING_FLAGS=-v \ + ./ && \ + cmake --build . -- -j \$(getconf _NPROCESSORS_ONLN) && \ + ctest -V" diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index f91924bd..9ca6baf0 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 @@ -23,9 +23,9 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ - cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=OFF -DETL_CXX_STANDARD=23 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -35,7 +35,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 @@ -45,9 +45,9 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ - cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=OFF -DETL_CXX_STANDARD=23 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -57,7 +57,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 @@ -67,9 +67,9 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ - cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v @@ -79,7 +79,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04] + os: [ubuntu-24.04] steps: - uses: actions/checkout@v4 @@ -89,9 +89,9 @@ jobs: export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 export CC=gcc export CXX=g++ - cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03=ON -DETL_CXX_STANDARD=23 ./ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 -DETL_OPTIMISATION=-O3 ./ gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" - name: Run tests run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/gcc-c++26.yml b/.github/workflows/gcc-c++26.yml new file mode 100644 index 00000000..b7ff42d3 --- /dev/null +++ b/.github/workflows/gcc-c++26.yml @@ -0,0 +1,97 @@ +name: gcc-c++26 +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-gcc-cpp26-linux-stl: + name: GCC C++26 Linux - STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-gcc-cpp26-linux-no-stl: + name: GCC C++26 Linux - No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=26 ./ && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-gcc-cpp26-linux-stl-force-cpp03: + name: GCC C++26 Linux - STL - Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" + + build-gcc-cpp26-linux-no-stl-force-cpp03: + name: GCC C++26 Linux - No STL - Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build and run tests + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export ASAN_OPTIONS=alloc_dealloc_mismatch=0,detect_leaks=0 && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./ && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN) && \ + ./test/etl_tests -v" diff --git a/.github/workflows/gcc-syntax-checks.yml b/.github/workflows/gcc-syntax-checks.yml index 5f3753b1..b6b77bad 100644 --- a/.github/workflows/gcc-syntax-checks.yml +++ b/.github/workflows/gcc-syntax-checks.yml @@ -23,10 +23,10 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=03 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp03-linux-No-STL: - name: Syntax Check - GCC C++03 Linux No STL + name: Syntax Check - GCC C++03 Linux No STL runs-on: ${{ matrix.os }} strategy: matrix: @@ -41,7 +41,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=03 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp11-linux-STL: name: Syntax Check - GCC C++11 Linux STL @@ -59,7 +59,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp11-linux-No-STL: name: Syntax Check - GCC C++11 Linux No STL @@ -77,7 +77,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=11 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp11-linux-STL-Force-CPP03: name: Syntax Check - GCC C++11 Linux STL Force C++03 @@ -95,7 +95,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=11 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp11-linux-No-STL-Force-CPP03: name: Syntax Check - GCC C++11 Linux No STL Force C++03 @@ -113,7 +113,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=11 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp14-linux-STL: name: Syntax Check - GCC C++14 Linux STL @@ -131,7 +131,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp14-linux-No-STL: name: Syntax Check - GCC C++14 Linux No STL @@ -149,7 +149,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=14 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp14-linux-STL-Force-CPP03: name: Syntax Check - GCC C++14 Linux STL Force C++03 @@ -167,7 +167,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=14 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp14-linux-No-STL-Force-CPP03: name: Syntax Check - GCC C++14 Linux No STL Force C++03 @@ -185,7 +185,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=14 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp17-linux-STL: name: Syntax Check - GCC C++17 Linux STL @@ -203,7 +203,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp17-linux-No-STL: name: Syntax Check - GCC C++17 Linux No STL @@ -221,7 +221,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp17-linux-STL-Force-CPP03: name: Syntax Check - GCC C++17 Linux STL Force C++03 @@ -239,7 +239,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp17-linux-No-STL-Force-CPP03: name: Syntax Check - GCC C++17 Linux No STL Force C++03 @@ -257,7 +257,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp20-linux-STL: name: Syntax Check - GCC C++20 Linux STL @@ -275,7 +275,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp20-linux-No-STL: name: Syntax Check - GCC C++20 Linux No STL @@ -293,7 +293,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=20 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp20-linux-STL-Force-CPP03: name: Syntax Check - GCC C++20 Linux STL Force C++03 @@ -311,7 +311,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp20-linux-No-STL-Force-CPP03: name: Syntax Check - GCC C++20 Linux No STL Force C++03 @@ -329,7 +329,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=20 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp23-linux-STL: name: Syntax Check - GCC C++23 Linux STL @@ -347,7 +347,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp23-linux-No-STL: name: Syntax Check - GCC C++23 Linux No STL @@ -365,7 +365,7 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=23 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp23-linux-STL-Force-CPP03: name: Syntax Check - GCC C++23 Linux STL Force C++03 @@ -383,7 +383,7 @@ jobs: export CXX=g++ cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) + make -j "$(getconf _NPROCESSORS_ONLN)" build-gcc-cpp23-linux-No-STL-Force-CPP03: name: Syntax Check - GCC C++23 Linux No STL Force C++03 @@ -401,4 +401,84 @@ jobs: export CXX=g++ cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=23 ./test/syntax_check gcc --version - make -j $(getconf _NPROCESSORS_ONLN) \ No newline at end of file + make -j "$(getconf _NPROCESSORS_ONLN)" + + build-gcc-cpp26-linux-STL: + name: Syntax Check - GCC C++26 Linux STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -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 ./test/syntax_check && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-gcc-cpp26-linux-No-STL: + name: Syntax Check - GCC C++26 Linux No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -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 ./test/syntax_check && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-gcc-cpp26-linux-STL-Force-CPP03: + name: Syntax Check - GCC C++26 Linux STL Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./test/syntax_check && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" + + build-gcc-cpp26-linux-No-STL-Force-CPP03: + name: Syntax Check - GCC C++26 Linux No STL Force C++03 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t etl-ubuntu-2604 -f .devcontainer/ubuntu-26.04/Dockerfile . + + - name: Build + run: | + docker run --rm --user root -v ${{ github.workspace }}:/workspaces/etl etl-ubuntu-2604 bash -c "\ + cd /workspaces/etl && \ + export CC=gcc && \ + export CXX=g++ && \ + cmake -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=26 ./test/syntax_check && \ + gcc --version && \ + make -j \$(getconf _NPROCESSORS_ONLN)" diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index 032ffd1e..1829c342 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -14,12 +14,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Generate + - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y python3-cogapp - cd include/etl/generators && bash generate.bat - - name: Check Generated Headers For Changes + - name: Run generator_test.py run: | - git diff --exit-code + cd scripts && python3 generator_test.py diff --git a/.github/workflows/meson-gcc-c++23-no-stl.yml b/.github/workflows/meson-gcc-c++23-no-stl.yml new file mode 100644 index 00000000..d8767317 --- /dev/null +++ b/.github/workflows/meson-gcc-c++23-no-stl.yml @@ -0,0 +1,31 @@ +name: meson-gcc-c++23-no-stl +on: + push: + branches: [ master, development, pull-request/* ] + pull_request: + branches: [ master, development, pull-request/* ] + types: [opened, synchronize, reopened] + +jobs: + + build-meson-gcc-cpp23-no-stl: + name: Meson GCC C++23 Linux - No STL + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v6 + + - name: Install Meson + run: sudo apt-get install -y meson + + - name: Configure + run: | + export CC=gcc + export CXX=g++ + meson setup builddir -Duse_stl=false -Dcpp_std=c++23 + + - name: Build + run: meson compile -C builddir + + - name: Run tests + run: meson test -C builddir -v diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 00000000..3bdb072e --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "etl", + hdrs = glob(["include/**/*.h"]), + includes = ["include"], +) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000..a52b73e8 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,7 @@ +module( + name = "etl", + version = "0.0.0", +) + +bazel_dep(name = "platforms", version = "0.0.11") +bazel_dep(name = "rules_cc", version = "0.1.1") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 00000000..c6bcd5ce --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,205 @@ +{ + "lockFileVersion": 24, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.1/source.json": "b6500ffcd7b48cd72c29bb67bcac781e12701cc0d6d55d266a652583cfcdab01", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" + ], + "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "1.9.23" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" + ], + "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", + "strip_version": "1.9.23-1.0.20" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint" + ], + "executable": true + } + }, + "rules_android": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", + "strip_prefix": "rules_android-0.1.1", + "urls": [ + "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + } + }, + "facts": {} +} diff --git a/README.md b/README.md index 869455a0..1af9c518 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![GitHub release (latest by date)](https://img.shields.io/github/v/release/jwellbelove/etl) [![Release date](https://img.shields.io/github/release-date/jwellbelove/etl?color=%231182c3)](https://img.shields.io/github/release-date/jwellbelove/etl?color=%231182c3) -[![Standard](https://img.shields.io/badge/c%2B%2B-98/03/11/14/17/20/23-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) +[![Standard](https://img.shields.io/badge/c%2B%2B-98/03/11/14/17/20/23/26-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) ![GitHub contributors](https://img.shields.io/github/contributors-anon/ETLCPP/etl) ![GitHub forks](https://img.shields.io/github/forks/ETLCPP/etl?style=flat) @@ -15,6 +15,7 @@ ![CI](https://github.com/ETLCPP/etl/actions/workflows/gcc-c++17.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/gcc-c++20.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/gcc-c++23.yml/badge.svg?branch=master) +![CI](https://github.com/ETLCPP/etl/actions/workflows/gcc-c++26.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/gcc-syntax-checks.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++11.yml/badge.svg?branch=master) @@ -22,6 +23,7 @@ ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++17.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++20.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++23.yml/badge.svg?branch=master) +![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-c++26.yml/badge.svg?branch=master) ![CI](https://github.com/ETLCPP/etl/actions/workflows/clang-syntax-checks.yml/badge.svg?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3c14cd918ccf40008d0bcd7b083d5946)](https://www.codacy.com/manual/jwellbelove/etl?utm_source=github.com&utm_medium=referral&utm_content=ETLCPP/etl&utm_campaign=Badge_Grade) @@ -49,7 +51,7 @@ Its design goals include: - Offering APIs that closely resemble those of the STL, enabling familiar and consistent usage. - Maintaining compatibility with C++98 while implementing many features introduced in later standards -(C++11/14/17/20/23) where possible. +(C++11/14/17/20/23/26) where possible. - Ensuring deterministic behavior, which is critical in real-time and resource-constrained environments. diff --git a/arduino/Embedded_Template_Library.h b/arduino/Embedded_Template_Library.h index aed4c865..64542c84 100644 --- a/arduino/Embedded_Template_Library.h +++ b/arduino/Embedded_Template_Library.h @@ -18,6 +18,22 @@ #define ARDUINO_BOARD "Teensy 3.5" #elif defined(__MK66FX1M0__) #define ARDUINO_BOARD "Teensy 3.6" + #elif defined(ARDUINO_TEENSY40) + #define ARDUINO_BOARD "Teensy 4.0" + #elif defined(ARDUINO_TEENSY41) + #define ARDUINO_BOARD "Teensy 4.1" + #elif defined(ARDUINO_TEENSY_MICROMOD) + #define ARDUINO_BOARD "Teensy MicroMod" + #else + #define ARDUINO_BOARD "Unknown" + #endif + +#elif defined(CORE_ARDUINO_PICO) + + #if defined(PICO_RP2040) + #define ARDUINO_BOARD "RP2040" + #elif defined(PICO_RP2350) + #define ARDUINO_BOARD "RP2350" #else #define ARDUINO_BOARD "Unknown" #endif diff --git a/docs/bazel.md b/docs/bazel.md new file mode 100644 index 00000000..7ab5168d --- /dev/null +++ b/docs/bazel.md @@ -0,0 +1,295 @@ +# Building ETL with Bazel + +ETL provides first-class [Bazel](https://bazel.build/) support, both for developing ETL itself and for consuming it as a dependency in your own projects. + +## Prerequisites + +- [Bazelisk](https://github.com/bazelbuild/bazelisk) (recommended) or [Bazel](https://bazel.build/install) 7.0 or later (with Bzlmod support) + +[Bazelisk](https://github.com/bazelbuild/bazelisk) is a launcher that automatically downloads and runs the Bazel version specified in the `.bazelversion` file at the project root. This ensures all contributors use a consistent Bazel version. Simply install Bazelisk and use `bazel` as usual — it transparently delegates to the correct version. + +## Syntax Checks + +To validate that every ETL header is well-formed and compiles on its own (equivalent to `test/run-syntax-checks.sh` for CMake): + +```sh +bazel build //test/syntax_check:syntax_check +``` + +This compiles a set of minimal `.t.cpp` files, each of which includes a single ETL header, with strict warning flags enabled. + +## Cleaning Build Artifacts + +To remove all build outputs and symlinks (`bazel-bin`, `bazel-out`, `bazel-etl`, etc.): + +```sh +bazel clean +``` + +For a full cleanup including the external dependency cache: + +```sh +bazel clean --expunge +``` + +## Running Unit Tests + +To run the full test suite: + +```sh +bazel test //test:etl_tests +``` + +You can also pass standard Bazel flags: + +```sh +# Run with verbose test output +bazel test //test:etl_tests --test_output=all + +# Run tests matching a filter (UnitTest++ subset) +bazel test //test:etl_tests --test_arg= +``` + +## Using ETL in Your Project + +### With Bzlmod (recommended, Bazel 7+) + +Add ETL as a dependency in your project's `MODULE.bazel`: + +```python +bazel_dep(name = "etl", version = "20.47.1") + +git_override( + module_name = "etl", + remote = "https://github.com/ETLCPP/etl.git", + tag = "20.47.1", # or a specific commit +) +``` + +Then depend on it in your `BUILD.bazel`: + +```python +cc_library( + name = "my_library", + srcs = ["my_library.cpp"], + hdrs = ["my_library.h"], + deps = ["@etl//:etl"], +) +``` + +### With WORKSPACE (legacy) + +In your `WORKSPACE` file: + +```python +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "etl", + remote = "https://github.com/ETLCPP/etl.git", + tag = "20.47.1", +) +``` + +Then use `deps = ["@etl//:etl"]` in your targets as shown above. + +## Project Structure + +| File | Purpose | +|---|---| +| `MODULE.bazel` | Module definition and dependencies | +| `BUILD.bazel` | Exposes ETL as a `cc_library` | +| `.bazelversion` | Bazel version for Bazelisk | +| `.bazelrc` | Default Bazel settings | +| `test/BUILD.bazel` | Unit test target | +| `test/syntax_check/BUILD.bazel` | Header syntax check target | +| `test/UnitTest++/BUILD.bazel` | Vendored UnitTest++ framework | + +## Cross-Compilation + +Bazel supports cross-compilation through its [platforms](https://bazel.build/extending/platforms) and [toolchains](https://bazel.build/extending/toolchains) system. Since ETL is a header-only library, there is nothing to cross-compile for the library itself. However, when building tests or consuming ETL in an application targeting a different architecture, you need to define a platform and register an appropriate C++ toolchain. + +Example platform definition (e.g. in a `platforms/BUILD.bazel`): + +```python +platform( + name = "linux_arm64", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) +``` + +Then build with: + +```sh +bazel build //:etl --platforms=//platforms:linux_arm64 +``` + +> **Note:** You must also have a C++ toolchain registered that supports the target platform. +> See the [Bazel toolchains documentation](https://bazel.build/extending/toolchains) for details. + +### Running Cross-Compiled Tests under QEMU + +Cross-compiled test binaries cannot run natively on the host. Pre-defined configurations in `.bazelrc` select the correct cross-compiler via `--repo_env=CC`, set the build flags to match `.devcontainer/run-tests.sh` (C++23, No-STL, `-O0`), and use `--run_under` to execute the resulting binary under the appropriate QEMU emulator: + +```sh +# Cross-build and run tests for ARM (armhf) +bazel test //test:etl_tests --config=armhf + +# Other architectures +bazel test //test:etl_tests --config=i386 +bazel test //test:etl_tests --config=powerpc +bazel test //test:etl_tests --config=riscv64 +bazel test //test:etl_tests --config=s390x +``` + +These configs are designed to run inside the Docker containers under `.devcontainer/`, which provide the cross-compiler toolchains and QEMU binaries. Each config sets `CC`, `AR`, `LD`, `NM`, `STRIP`, and `OBJDUMP` via `--repo_env` so that Bazel's auto-configured toolchain finds the complete prefixed cross-tool suite. + +You can also use `--run_under` directly for custom setups: + +```sh +bazel test //test:etl_tests --run_under=/usr/bin/qemu-arm-static +``` + +## Compiler and Build Configuration + +Unlike CMake where options like `ETL_CXX_STANDARD` and `CMAKE_CXX_COMPILER` are set at configure time, Bazel uses command-line flags and `.bazelrc` configurations. + +### C++ Standard Version + +Use `--cxxopt` to pass the desired standard flag: + +```sh +# C++17 (default in .bazelrc) +bazel test //test:etl_tests --cxxopt=-std=c++17 + +# C++20 +bazel test //test:etl_tests --cxxopt=-std=c++20 + +# C++23 +bazel test //test:etl_tests --cxxopt=-std=c++23 + +# C++14 +bazel test //test:etl_tests --cxxopt=-std=c++14 +``` + +### Optimization Level + +Use `--compilation_mode` (shorthand `-c`) for standard profiles, or `--copt` for explicit flags: + +```sh +# Debug (default) — no optimization, debug symbols +bazel test //test:etl_tests -c dbg + +# Optimized — O2 with NDEBUG +bazel test //test:etl_tests -c opt + +# Fast build — no optimization, no debug symbols +bazel test //test:etl_tests -c fastbuild + +# Custom optimization level +bazel test //test:etl_tests --copt=-O3 +bazel test //test:etl_tests --copt=-O1 +bazel test //test:etl_tests --copt=-Os +``` + +### Selecting the Compiler (GCC vs Clang) + +Bazel uses the system's default `CC` environment variable. Override it to switch compilers: + +```sh +# Use Clang +bazel test //test:etl_tests --repo_env=CC=clang + +# Use a specific GCC version +bazel test //test:etl_tests --repo_env=CC=gcc-13 + +# Use a specific Clang version +bazel test //test:etl_tests --repo_env=CC=clang-18 +``` + +> **Note:** Bazel's auto-configured toolchain infers the C++ compiler from `CC` automatically +> (e.g. `CC=gcc-13` → `g++-13` for C++ compilation). There is no need to set `CXX` separately. + +### Combining Options + +Flags can be combined freely: + +```sh +# Clang, C++20, optimized +bazel test //test:etl_tests --repo_env=CC=clang --cxxopt=-std=c++20 -c opt + +# GCC 13, C++23, debug +bazel test //test:etl_tests --repo_env=CC=gcc-13 --cxxopt=-std=c++23 -c dbg +``` + +### STL vs. No-STL Mode + +ETL can operate without the standard library, which is common on bare-metal embedded targets. Use `--copt` to define `ETL_NO_STL`: + +```sh +# Build and test without STL +bazel test //test:etl_tests --copt=-DETL_NO_STL + +# Build with STL (default, no flag needed) +bazel test //test:etl_tests +``` + +When `ETL_NO_STL` is defined, ETL provides its own implementations of containers, algorithms, and utilities instead of delegating to ``, ``, etc. + +### Type Traits Configuration + +ETL supports three type traits strategies, controlled via preprocessor defines: + +| Mode | Define | Description | +|---|---|---| +| **STL type traits** | *(default)* | Uses `` from the standard library | +| **Compiler builtins** | `ETL_USE_TYPE_TRAITS_BUILTINS` | Uses compiler intrinsics (`__is_trivially_copyable`, etc.) — useful when STL headers are unavailable or incomplete | +| **User-defined** | `ETL_USER_DEFINED_TYPE_TRAITS` | Uses ETL's own type traits implementations | + +```sh +# Use compiler built-in type traits +bazel test //test:etl_tests --copt=-DETL_USE_TYPE_TRAITS_BUILTINS + +# Use ETL's own user-defined type traits +bazel test //test:etl_tests --copt=-DETL_USER_DEFINED_TYPE_TRAITS +``` + +These are mutually exclusive — define at most one. If neither is defined and STL is available, ETL uses ``. + +### Other Configuration Defines + +Additional defines can be passed the same way via `--copt=-D...`: + +| Define | Description | +|---|---| +| `ETL_FORCE_TEST_CPP03_IMPLEMENTATION` | Force C++03 code paths even when a newer standard is available | +| `ETL_MESSAGES_ARE_NOT_VIRTUAL` | Use non-virtual message types | + +```sh +# Force C++03 implementation paths +bazel test //test:etl_tests --copt=-DETL_FORCE_TEST_CPP03_IMPLEMENTATION +``` + +### Using `.bazelrc` Presets + +To avoid retyping flags, add configurations to `.bazelrc`: + +``` +# .bazelrc + +# Named configurations +build:clang --repo_env=CC=clang +build:gcc13 --repo_env=CC=gcc-13 +build:c++20 --cxxopt=-std=c++20 +build:c++23 --cxxopt=-std=c++23 +build:release --compilation_mode=opt +``` + +Then use them with `--config`: + +```sh +bazel test //test:etl_tests --config=clang --config=c++20 --config=release +``` diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 00000000..2dc8d25d --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,289 @@ +# Docker for Development + +## Overview + +The ETL repository ships a set of Docker-based development environments under +`.devcontainer/`. They give every contributor an identical, reproducible toolchain +regardless of host operating system. Three flavours are provided: + +| Flavour | Path | Purpose | +|---|---|---| +| **Default** | `.devcontainer/` | Day-to-day development (Microsoft C++ dev-container base image) | +| **Compiler-specific** | `.devcontainer/gcc09/` … `.devcontainer/gcc15/`, `.devcontainer/clang7/` … `.devcontainer/clang21/` | Test against a specific GCC or Clang version | +| **s390x big-endian** | `.devcontainer/s390x/` | Cross-compile and run tests on an s390x target via QEMU | + +All containers include CMake, Make, Git, Python 3, cogapp (the code generator used +by ETL), clang-format 18, and treefmt. + +## Prerequisites + +- **Docker** (or Docker Desktop) – any recent version that supports `docker build` + and `docker run`. +- **VS Code** with the + [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + extension (optional, but recommended for the smoothest experience). +- **Git** – to clone the repository. + +## Quick Start + +### Using the helper script + +The fastest way to get a shell inside the default container from the project root: + +```bash +./scripts/run-docker.sh +``` + +This script performs two steps: + +1. **Builds** the image from `.devcontainer/Dockerfile` and tags it `etl`. +2. **Runs** an interactive container that bind-mounts the repository at + `/home/vscode/etl` so that edits made inside the container are visible on + the host (and vice versa). + +You are dropped into a Bash shell as the `vscode` user with the working +directory set to the repository root. + +### Using VS Code Dev Containers + +1. Open the repository folder in VS Code. +2. When prompted, click **Reopen in Container** – or run the command + *Dev Containers: Reopen in Container* from the command palette. +3. VS Code reads `.devcontainer/devcontainer.json`, builds the image, and + attaches to the running container automatically. + +To open a **specific compiler variant** instead, run +*Dev Containers: Open Folder in Container…* and pick the sub-folder (e.g. +`.devcontainer/gcc14/`), or use the command palette action +*Dev Containers: Open Named Container Configuration…* and select the desired +name (e.g. "Gcc 14", "Clang 18"). + +## Default Development Container + +The file `.devcontainer/Dockerfile` is a multi-purpose image used by the +default configuration **and** by every compiler-specific variant (they simply +override the `BASE_IMAGE_NAME` build argument). + +### Base image + +```text +mcr.microsoft.com/devcontainers/cpp:2 +``` + +The exact digest is pinned in `devcontainer.json` so that builds are +reproducible even if the upstream tag is updated. + +### Installed tools + +The Dockerfile installs the following on top of the base image: + +| Tool | Purpose | +|---|---| +| `python3`, `pip` | Runtime for helper scripts | +| `python3-cogapp` / `cogapp` | ETL code generator | +| `git` | Version control | +| `wget` | Downloading additional tooling | +| `cmake`, `make` | Build system | +| `clang-format` (v18) | Source formatting (see [source-formatting.md](source-formatting.md)) | +| `treefmt` (v2.4.1) | Single-command formatting wrapper | + +### Reproducible builds with Debian snapshots + +The default configuration sets the build argument +`DEBIAN_SNAPSHOT=20260223T000000Z`. When this value is not `"none"`, the +Dockerfile rewrites the APT sources to point at +`snapshot.debian.org/archive/debian/`, ensuring that every +contributor installs identical package versions. Compiler-specific variants +that are based on upstream `gcc:*` or `silkeh/clang:*` images set +`DEBIAN_SNAPSHOT=none` because those images manage their own package sources. + +## Compiler-Specific Containers + +Each sub-folder under `.devcontainer/` contains a `devcontainer.json` that +reuses the **same** `Dockerfile` (`../Dockerfile`) but overrides the +`BASE_IMAGE_NAME` build argument to select a different compiler. + +### GCC variants + +| Folder | Base image | Name | +|---|---|---| +| `gcc09/` | `gcc:9` | Gcc 09 | +| `gcc10/` | `gcc:10` | Gcc 10 | +| `gcc11/` | `gcc:11` | Gcc 11 | +| `gcc12/` | `gcc:12` | Gcc 12 | +| `gcc13/` | `gcc:13` | Gcc 13 | +| `gcc14/` | `gcc:14` | Gcc 14 | +| `gcc15/` | `gcc:15` | Gcc 15 | + +### Clang variants + +| Folder | Base image | Name | +|---|---|---| +| `clang7/` | `silkeh/clang:7` | Clang 7 | +| `clang8/` | `silkeh/clang:8` | Clang 8 | +| `clang9/` | `silkeh/clang:9` | Clang 9 | +| `clang10/` | `silkeh/clang:10` | Clang 10 | +| `clang11/` | `silkeh/clang:11` | Clang 11 | +| `clang12/` | `silkeh/clang:12` | Clang 12 | +| `clang13/` | `silkeh/clang:13` | Clang 13 | +| `clang14/` | `silkeh/clang:14` | Clang 14 | +| `clang15/` | `silkeh/clang:15` | Clang 15 | +| `clang16/` | `silkeh/clang:16` | Clang 16 | +| `clang17/` | `silkeh/clang:17` | Clang 17 | +| `clang18/` | `silkeh/clang:18` | Clang 18 | +| `clang19/` | `silkeh/clang:19` | Clang 19 | +| `clang20/` | `silkeh/clang:20` | Clang 20 | +| `clang21/` | `silkeh/clang:21` | Clang 21 | + +All compiler-specific variants set `DEBIAN_SNAPSHOT` to `"none"` because they +rely on the upstream image's own package sources. + +## s390x Big-Endian Cross-Compilation + +The `s390x` container lives in `.devcontainer/s390x/` and has its **own** +Dockerfile (it does not reuse the default one). It is based on +`debian:trixie` and installs: + +- QEMU user-mode emulation (`qemu-user-static`, `qemu-user`, `binfmt-support`) +- s390x cross-compilation toolchain (`gcc-s390x-linux-gnu`, + `g++-s390x-linux-gnu`) +- CMake, Make, Ninja, Git, wget + +### Container setup + +Open `.devcontainer/s390x/` as a Dev Container in VS Code, or build manually: + +```bash +docker build -t etl-s390x .devcontainer/s390x +docker run -it --rm -v .:/workspaces/etl -w /workspaces/etl etl-s390x +``` + +### CMake toolchain + +A CMake toolchain file is provided at +`.devcontainer/s390x/toolchain-s390x.cmake`. It sets: + +- `CMAKE_SYSTEM_PROCESSOR` to `s390x` +- Cross-compilers `s390x-linux-gnu-gcc` / `g++` +- `CMAKE_CROSSCOMPILING_EMULATOR` to `/usr/bin/qemu-s390x-static` + +The VS Code Dev Container configuration already passes this toolchain file +via `cmake.configureArgs`, so CMake Tools picks it up automatically. + +### Running tests under QEMU + +Because the toolchain file sets `CMAKE_CROSSCOMPILING_EMULATOR`, CTest +automatically invokes `qemu-s390x-static` when running test binaries. +No extra flags are needed: + +```bash +cmake -S test -B build-s390x \ + -DCMAKE_TOOLCHAIN_FILE=.devcontainer/s390x/toolchain-s390x.cmake \ + -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_CXX_STANDARD=17 -G Ninja +cmake --build build-s390x +ctest --test-dir build-s390x +``` + +## Building and Running Tests + +Once inside any container (default, compiler-specific, or s390x) you can +build and run the ETL test suite. + +### Quick CMake workflow + +```bash +# Configure – build tests with C++17 +cmake -S test -B build -DBUILD_TESTS=ON -DETL_CXX_STANDARD=17 + +# Build +cmake --build build -j $(nproc) + +# Run tests +ctest --test-dir build +``` + +Change `DETL_CXX_STANDARD` to `11`, `14`, `17`, `20`, or `23` as needed. +Add `-DNO_STL=ON` to build without the standard library. + +### Using the run-tests script + +The repository also provides a convenience script in `test/`: + +```bash +cd test +./run-tests.sh [optimisation] [threads] [sanitizer] [compiler] [verbose] +``` + +| Argument | Values | Default | +|---|---|---| +| C++ standard | `11`, `14`, `17`, `20`, `23` | *(required)* | +| Optimisation | `0`, `1`, `2`, `3` | `0` | +| Threads | any integer | `4` | +| Sanitizer | `s` (enable) / `n` (disable) | `n` | +| Compiler | `gcc` / `clang` | all | +| Verbose | `v` (enable) / `n` (disable) | `n` | + +Example – run C++17 tests at `-O2` with 8 threads using GCC: + +```bash +./run-tests.sh 17 2 8 n gcc n +``` + +## Formatting Inside the Container + +The default container ships with **clang-format 18** and **treefmt**. +See [source-formatting.md](source-formatting.md) for the full formatting guide. + +Quick reference: + +```bash +# Format all tracked C/C++ files with treefmt +treefmt + +# Or use clang-format directly via the wrapper +./scripts/clang-format-wrapper -i include/etl/*.h +``` + +The wrapper script `scripts/clang-format-wrapper` resolves the correct +clang-format binary (prefers `clang-format-18`, falls back to `clang-format` +after checking the major version). + +## Customisation + +To add extra packages or tools to the default container, edit +`.devcontainer/Dockerfile`. The image follows a straightforward +`apt-get install` pattern, so adding a new package is as simple as appending +it to the existing `apt-get` line. + +To create a new compiler variant: + +1. Create a folder under `.devcontainer/` (e.g. `.devcontainer/gcc16/`). +2. Add a `devcontainer.json` that references `"../Dockerfile"` and sets + `BASE_IMAGE_NAME` to the desired image (e.g. `gcc:16`). +3. Set `DEBIAN_SNAPSHOT` to `"none"` for upstream compiler images. + +Example: + +```jsonc +{ + "name": "Gcc 16", + "build": { + "dockerfile": "../Dockerfile", + "args": { + "BASE_IMAGE_NAME": "gcc:16", + "DEBIAN_SNAPSHOT": "none" + }, + "context": "../context" + } +} +``` + +## Troubleshooting + +| Symptom | Cause / Fix | +|---|---| +| `apt-get` fails with *"Release file … is not valid yet"* | The Debian snapshot timestamp is in the future relative to the build host clock. Either update `DEBIAN_SNAPSHOT` in `devcontainer.json` or set it to `"none"`. | +| `clang-format` reports the wrong version | The wrapper expects version **18**. Make sure the image installs `clang-format` (or `clang-format-18`) and that the binary is on `PATH`. | +| Permission errors on mounted files | The `run-docker.sh` script runs as user `vscode`. Ensure your host UID matches, or adjust the `--user` flag. | +| s390x tests crash immediately | Verify that `qemu-user-static` is installed and that `binfmt-support` is active. On some hosts you may need to register binfmt handlers with `docker run --privileged --rm tonistiigi/binfmt --install all`. | +| Build is very slow the first time | Docker is downloading and building the image from scratch. Subsequent builds use the layer cache and are much faster. | diff --git a/docs/format.md b/docs/format.md new file mode 100644 index 00000000..02b4edda --- /dev/null +++ b/docs/format.md @@ -0,0 +1,389 @@ +# ETL Format & Print + +## 1. Overview + +ETL provides text formatting facilities modelled on C++20 `std::format` and C++23 +`std::print`. They allow type-safe, positional formatting of values into strings +or directly to a character output device — without heap allocation. + +**Minimum language standard:** C++11 (`ETL_USING_CPP11`). + +**Headers:** + +| Header | Provides | +|---|---| +| `etl/format.h` | `etl::format_to`, `etl::format_to_n`, `etl::formatted_size` | +| `etl/print.h` | `etl::print`, `etl::println` (includes `etl/format.h`) | + +## 2. `etl::format_to` + +### Generic output-iterator overload + +```cpp +template +OutputIt format_to(OutputIt out, format_string fmt, Args&&... args); +``` + +Formats `args` according to the format string `fmt` and writes the result through +the output iterator `out`. Returns an iterator past the last character written. + +`OutputIt` can be any output iterator whose dereferenced type is assignable from +`char`, for example `etl::istring::iterator` or +`etl::back_insert_iterator`. + +```cpp +etl::string<100> s; + +// Using a raw iterator — you must resize the string yourself +etl::istring::iterator result = etl::format_to(s.begin(), "{0} {1}", 34, 56); +s.uninitialized_resize(static_cast(result - s.begin())); +// s == "34 56" + +// Using a back_insert_iterator — string grows automatically +s.clear(); +etl::back_insert_iterator it(s); +etl::format_to(it, "{} {}", 65, 34); +// s == "65 34" +``` + +### `etl::istring&` overload (ETL-specific) + +```cpp +template +etl::istring::iterator format_to(etl::istring& out, + format_string fmt, + Args&&... args); +``` + +Convenience overload that writes into an `etl::istring` (or any derived +`etl::string`). The string is automatically resized to the number of +characters written, up to `out.max_size()`. Returns an iterator past the +last character written. + +```cpp +etl::string<100> s; +etl::format_to(s, "Hello, {}!", "world"); +// s == "Hello, world!" +``` + +### `etl::format_to_n` + +```cpp +template +OutputIt format_to_n(OutputIt out, size_t n, + format_string fmt, Args&&... args); +``` + +Like `format_to`, but writes **at most** `n` characters. Characters beyond the +limit are silently discarded. + +```cpp +etl::string<10> s = "abcdefghij"; +etl::format_to_n(s.begin(), 3, "xy{}", 123); +// s == "xy1defghij" (only 3 chars written) +``` + +## 3. `etl::formatted_size` + +```cpp +template +size_t formatted_size(format_string fmt, Args&&... args); +``` + +Returns the total number of characters that `format_to` would produce, without +actually writing anything. Useful for pre-computing buffer sizes. + +```cpp +size_t n; +n = etl::formatted_size(""); // 0 +n = etl::formatted_size("{}", ""); // 0 +n = etl::formatted_size("xyz{}", 12); // 5 +n = etl::formatted_size("{}", "abc"); // 3 +``` + +## 4. `etl::print` and `etl::println` + +Declared in `etl/print.h`. + +### `etl::print` + +```cpp +template +void print(etl::format_string fmt, Args&&... args); +``` + +Formats the arguments and outputs each character by calling `etl_putchar()`. + +### `etl::println` + +```cpp +// With arguments — prints formatted text followed by '\n' +template +void println(etl::format_string fmt, Args&&... args); + +// Without arguments — prints a bare newline +void println(); +``` + +### Implementing `etl_putchar` + +`etl/print.h` declares (but does not define) the following C-linkage function: + +```cpp +extern "C" void etl_putchar(int c); +``` + +You **must** provide a definition in your project. The `int` parameter follows +the convention of the standard `putchar()` and carries a single `char` value. + +Typical implementations forward to a UART, a debug probe, `putchar`, or any +other single-character output sink: + +```cpp +// Example: forward to standard putchar +extern "C" void etl_putchar(int c) +{ + putchar(c); +} +``` + +### Example + +```cpp +etl::print("x = {}, y = {}\n", 10, 20); // "x = 10, y = 20\n" +etl::println("Hello, {}!", "world"); // "Hello, world!\n" +etl::println(); // "\n" +``` + +## 5. Format String Syntax + +A format string is ordinary text with **replacement fields** delimited by braces: + +``` +"literal text {} more text {1:>10} end" +``` + +### Replacement field grammar + +``` +replacement_field ::= '{' [arg_id] [':' format_spec] '}' +arg_id ::= integer // e.g. 0, 1, 2 … +format_spec ::= [[fill]align] [sign] ['#'] ['0'] [width] ['.' precision] ['L'] [type] +``` + +| Component | Syntax | Description | +|---|---|---| +| **Argument index** | `{0}`, `{1}`, … | Manual positional indexing. Cannot be mixed with automatic indexing. | +| **Automatic index** | `{}` | Uses the next argument in order. Cannot be mixed with manual indexing. | +| **Fill character** | any character except `{` or `}` | Used together with an alignment specifier. Default is space (` `). | +| **Alignment** | `<` left, `>` right, `^` center | Aligns the formatted value within the given *width*. | +| **Sign** | `+` always, `-` negative only (default), ` ` space for positive | Controls sign display for numeric types. | +| **`#` (alt form)** | `#` | Adds `0x`/`0X` for hex, `0b`/`0B` for binary, `0` for octal. | +| **`0` (zero-pad)** | `0` | Pads the number with leading zeros (after sign/prefix). | +| **Width** | integer, or `{}` / `{n}` | Minimum field width. Supports nested replacement fields for dynamic width. | +| **Precision** | `.` integer, or `.{}` / `.{n}` | For strings: maximum characters to output. For floats: number of decimal digits. Supports nested replacement fields. | +| **`L`** | `L` | Locale-specific flag (parsed but currently ignored). | +| **Type** | see [Presentation Types](#7-presentation-types-per-argument-kind) | Selects the output representation. | + +### Examples + +```cpp +etl::format_to(s, "{:>10}", 42); // " 42" +etl::format_to(s, "{:*^10}", 42); // "****42****" +etl::format_to(s, "{:+05d}", 67); // "+00067" +etl::format_to(s, "{:#x}", 0x3f4); // "0x3f4" +etl::format_to(s, "{:.3s}", "abcdef"); // "abc" +etl::format_to(s, "{1} {0}", 1, 2); // "2 1" +``` + +## 6. Supported Argument Types + +The core set of formattable types (matching `std::basic_format_arg`): + +| Category | Types | +|---|---| +| Boolean | `bool` | +| Character | `char` | +| Signed integer | `int`, `long long int` | +| Unsigned integer | `unsigned int`, `unsigned long long int` | +| Floating-point *(opt-in)* | `float`, `double`, `long double` — requires `ETL_USING_FORMAT_FLOATING_POINT` | +| String | `const char*`, `etl::string_view` | +| Pointer | `const void*` | + +### Implicit conversions + +Types not listed above are converted automatically before formatting: + +| Source type | Stored as | +|---|---| +| `short` | `int` | +| `unsigned short`, `uint16_t` | `unsigned int` | +| `long int` | `int` or `long long int` (platform-dependent) | +| `unsigned long int`, `size_t` | `unsigned int` or `unsigned long long int` | +| `int8_t` (`signed char`) | `char` | +| `uint8_t` (`unsigned char`) | `char` | +| `int16_t` | `int` | +| `uint32_t` | `unsigned int` | +| `int32_t` | `int` | +| `etl::string` | `etl::string_view` (lifetime of the temporary is guaranteed) | +| any pointer `T*` | `const void*` | + +## 7. Presentation Types per Argument Kind + +### Integers (`int`, `unsigned int`, `long long int`, `unsigned long long int`) + +| Type | Meaning | Example | +|---|---|---| +| `d` *(default)* | Decimal | `134` → `"134"` | +| `x` | Lowercase hexadecimal | `0x3f4` → `"3f4"` | +| `X` | Uppercase hexadecimal | `0x3f4` → `"3F4"` | +| `o` | Octal | `034` → `"34"` | +| `b` | Lowercase binary | `0b1010` → `"1010"` | +| `B` | Uppercase binary | `0b1010` → `"1010"` | +| `c` | Character (value as char) | `67` → `"C"` | + +With `#`: prefixes `0x`/`0X`, `0b`/`0B`, or leading `0` for octal. + +### Characters (`char`, `signed char`, `unsigned char`) + +| Type | Meaning | Example | +|---|---|---| +| `c` *(default)* | Character itself | `'s'` → `"s"` | +| `?` | Debug / escaped | `'\n'` → `"'\\n'"` | +| `d` | Decimal code point | `'a'` → `"97"` | +| `x` / `X` | Hex code point | `'a'` → `"61"` | + +### Booleans (`bool`) + +| Type | Meaning | Example | +|---|---|---| +| *(default)* | `false` / `true` | `true` → `"true"` | +| `s` | Same as default | `true` → `"true"` | +| `d` | `0` / `1` | `true` → `"1"` | +| `x` / `X` | Hex `0` / `1` | `true` → `"1"` | +| `o` | Octal (with `#`: `01`) | `true` → `"01"` | + +### Strings (`const char*`, `etl::string_view`, `etl::string`) + +| Type | Meaning | Example | +|---|---|---| +| `s` *(default)* | String output | `"data1"` → `"data1"` | +| `?` | Debug / escaped | `"data1\n"` → `"\"data1\\n\""` | + +Width and precision apply: width sets the minimum field width; precision (`.N`) +truncates the string to at most *N* characters. + +```cpp +etl::format_to(s, "{:>10s}", "data1"); // " data1" +etl::format_to(s, "{:.3s}", "abcdef"); // "abc" +etl::format_to(s, ".{:^8.3s}!", "data1"); // ". dat !" +``` + +### Pointers (`const void*`) + +| Type | Meaning | Example | +|---|---|---| +| `p` *(default)* | Lowercase hex with `0x` prefix | `nullptr` → `"0x0"` | +| `P` | Uppercase hex with `0X` prefix | `nullptr` → `"0X0"` | + +### Floating-point (`float`, `double`, `long double`) + +Requires `ETL_USING_FORMAT_FLOATING_POINT`. + +| Type | Meaning | Example | +|---|---|---| +| *(default)* | Shortest representation | `1.5f` → `"1.5"` | +| `e` / `E` | Scientific notation | `1.0f` → `"1.000000e+00"` | +| `f` / `F` | Fixed-point notation | `1.125f` → `"1.125000"` | +| `g` / `G` | General (fixed or scientific) | `1e10f` → `"1.000000e+10"` | +| `a` / `A` | Hexadecimal floating-point | `1.5f` → `"0x1.8p+0"` | + +`nan`, `inf` (lowercase for `e`/`f`/`g`/`a`, uppercase for `E`/`F`/`G`/`A`). + +## 8. Escape Sequences and Literal Braces + +### Literal braces + +Because `{` and `}` delimit replacement fields, they must be escaped by +doubling: + +| Input | Output | +|---|---| +| `{{` | `{` | +| `}}` | `}` | + +```cpp +etl::format_to(s, "abc{{def"); // "abc{def" +etl::format_to(s, "}}abc"); // "}abc" +``` + +### Debug / escaped presentation (`?`) + +The `?` type specifier produces a debug representation: + +- **Characters** are wrapped in single quotes with C-style escape sequences: + + | Character | Output | + |---|---| + | `\t` | `'\\t'` | + | `\n` | `'\\n'` | + | `\r` | `'\\r'` | + | `"` | `'\\\"'` | + | `'` | `'\\''` | + | `\\` | `'\\\\'` | + +- **Strings** are wrapped in double quotes with the same escape sequences: + + ```cpp + etl::format_to(s, "{:?}", "data1\n"); // "\"data1\\n\"" + ``` + +## 9. Error Handling + +Invalid format strings cause an `etl::bad_format_string_exception` (derived from +`etl::format_exception`, which is derived from `etl::exception`). + +Common error conditions: + +| Condition | Example | +|---|---| +| Missing closing brace | `"a{b"` | +| Unescaped `}` without matching `{` | `"a}b"` | +| Invalid characters inside `{}` | `"a{b}"` | +| Argument index out of range | `"{1}"` with only one argument | +| Mixing manual and automatic indexing | `"{0} {}"` | +| Invalid type specifier for the argument | `"{:d}"` on a `string_view` | +| Double colon in format spec | `"{::}"` | +| Precision on an integer | `"{:+#05.5X}"` on an `int` | + +```cpp +etl::string<100> s; +// These all throw etl::bad_format_string_exception: +etl::format_to(s, "a{b}", 1); // bad index spec +etl::format_to(s, "a{b", 1); // closing brace missing +etl::format_to(s, "a}b"); // unescaped } +etl::format_to(s, "{:d}", sv); // invalid type for string_view +``` + +> **Note:** On C++20 and later, format strings are validated at compile time +> via `consteval`. The checks cover syntax (balanced braces, valid format spec +> grammar, index bounds, no mixing of automatic and manual indexing) as well as +> type/specifier compatibility (e.g. `{:d}` is rejected for string arguments). +> A malformed format string produces a compile error whose diagnostic mentions +> `please_note_this_is_error_message_format_string_syntax_error`. +> +> On C++11–C++17, the same checks run at runtime and throw +> `etl::bad_format_string_exception`. + +## 10. Differences from `std::format` + +| Area | `std::format` (C++20/23) | ETL | +|---|---|---| +| **Output target** | Returns `std::string` | Writes through an output iterator or into `etl::istring&` — no heap allocation. | +| **`etl::istring&` overload** | Not available | `format_to(etl::istring&, ...)` automatically resizes the string. | +| **`print` / `println` output** | Writes to `FILE*` / `stdout` | Writes character-by-character via user-defined `etl_putchar(int)`. | +| **Floating-point support** | Always available | Opt-in via `ETL_USING_FORMAT_FLOATING_POINT`. | +| **User-defined formatters** | `std::formatter` specialisations | Not yet supported. | +| **Locale** | `L` flag uses `std::locale` | `L` flag is parsed but has no effect. | +| **Compile-time validation** | Enforced via `consteval` on C++20 | Enforced via `consteval` on C++20 (syntax and type/specifier compatibility); validates at run time and throws `etl::bad_format_string_exception` on C++11–C++17. | +| **`format_to_n` return type** | `std::format_to_n_result` | Returns the underlying `OutputIt` directly. | diff --git a/docs/generators.md b/docs/generators.md new file mode 100644 index 00000000..d44a38b6 --- /dev/null +++ b/docs/generators.md @@ -0,0 +1,190 @@ +# Code Generation for Pre-C++11 Support + +ETL supports C++03 (also referred to as C++98) environments where variadic +templates, `constexpr`, and other modern features are unavailable. To +provide equivalent functionality, certain headers are **generated** using +[Cog](https://nedbatchelder.com/code/cog/), a Python-based code generation +tool that embeds Python snippets inside source files. + +This document explains how the code generation system works and how to +regenerate the headers if you modify a generator template. + +--- + +## Overview + +| Directory | Contents | +|---|---| +| `include/etl/generators/` | Generator templates (`*_generator.h`) and batch scripts | +| `include/etl/private/` | Generated output (`*_cpp03.h`) committed to the repository | +| `scripts/generator_test.py` | CI script that verifies generators match committed files | + +The generator templates contain embedded Python code (delimited by `[[[cog` +and `]]]`) that produces the repetitive C++03 boilerplate. Cog processes +these templates and writes the expanded output to `include/etl/private/`. + +--- + +## Generated Headers + +The following C++03 compatibility headers are generated: + +| Generator | Output | Purpose | +|---|---|---| +| `fsm_fwd_decl_cpp03_generator.h` | `fsm_fwd_decl_cpp03.h` | FSM forward declarations | +| `fsm_friend_decl_cpp03_generator.h` | `fsm_friend_decl_cpp03.h` | FSM friend declarations | +| `fsm_cpp03_generator.h` | `fsm_cpp03.h` | Finite state machine implementation | +| `message_router_cpp03_generator.h` | `message_router_cpp03.h` | Message router | +| `message_packet_cpp03_generator.h` | `message_packet_cpp03.h` | Message packet | +| `largest_type_cpp03_generator.h` | `largest_type_cpp03.h` | Largest type metafunction | +| `largest_alignment_cpp03_generator.h` | `largest_alignment_cpp03.h` | Largest alignment metafunction | +| `largest_cpp03_generator.h` | `largest_cpp03.h` | Largest type/size utilities | +| `smallest_cpp03_generator.h` | `smallest_cpp03.h` | Smallest type/size utilities | +| `type_traits_cpp03_generator.h` | `type_traits_cpp03.h` | Type traits (`is_one_of`, etc.) | +| `type_lookup_cpp03_generator.h` | `type_lookup_cpp03.h` | Type lookup metafunction | +| `type_select_cpp03_generator.h` | `type_select_cpp03.h` | Type selection metafunction | +| `variant_pool_cpp03_generator.h` | `variant_pool_cpp03.h` | Variant pool | + +--- + +## Generator Parameters + +Cog variables control how many template parameter overloads are generated: + +| Variable | Default | Used by | +|---|---|---| +| `Handlers` | 16 | FSM and message router generators | +| `NTypes` | 16 | Type utility generators (largest, smallest, lookup, select, variant pool) | +| `IsOneOf` | 16 | Type traits generator (`is_one_of`) | + +These defaults produce overloads supporting up to 16 types or handlers, +which is sufficient for most embedded applications while keeping compile +times reasonable. + +--- + +## Prerequisites + +* **Python 3** +* **cogapp** – install via: + + ```bash + pip install cogapp + ``` + +--- + +## Regenerating Headers + +### Using the batch scripts (Windows) + +Each generator has a corresponding `.bat` file in `include/etl/generators/`: + +```bat +cd include/etl/generators +generate.bat # Regenerate all headers +generate_fsm.bat # Regenerate FSM headers only +generate_smallest.bat # Regenerate smallest_cpp03.h only +# etc. +``` + +### Manual invocation + +Run Cog directly from the `include/etl/generators/` directory: + +```bash +cd include/etl/generators + +# Example: regenerate smallest_cpp03.h +python3 -m cogapp -d -e -o../private/smallest_cpp03.h -DNTypes=16 smallest_cpp03_generator.h + +# Example: regenerate fsm_cpp03.h +python3 -m cogapp -d -e -o../private/fsm_cpp03.h -DHandlers=16 fsm_cpp03_generator.h +``` + +Cog options used: + +| Option | Meaning | +|---|---| +| `-d` | Delete the generator markers from output | +| `-e` | Warn if the input file has no generator markers | +| `-o` | Write output to the specified file | +| `-D=` | Define a Cog variable | + +### Regenerating all headers + +The `generate.bat` script regenerates every header: + +```bash +cd include/etl/generators +./generate.bat # Windows +# or run the commands manually on Linux/macOS +``` + +On Linux/macOS you can run the commands from `generate.bat` directly in +your shell (they are standard `python3 -m cogapp` invocations). + +--- + +## Verifying Generators + +After modifying a generator template, verify the output matches the +committed file: + +```bash +python3 scripts/generator_test.py +``` + +This script: + +1. Runs Cog on every `*_generator.h` file. +2. Compares each output against the corresponding file in + `include/etl/private/`. +3. Reports success or failure. + +The `generator.yml` GitHub Actions workflow runs this automatically on +every push and pull request. + +--- + +## How Generators Work + +A generator template contains standard C++ code interspersed with Cog +directives. For example, from `smallest_cpp03_generator.h`: + +```cpp +/*[[[cog +import cog +cog.outl("template " % int(NTypes)) +]]]*/ +// Generated code appears here after running Cog +/*[[[end]]]*/ +``` + +When Cog processes this file with `-DNTypes=16`, the Python code executes +and outputs the expanded template parameter list supporting 16 types. + +--- + +## Adding a New Generator + +1. Create `include/etl/generators/_cpp03_generator.h` with Cog + directives. +2. Add a corresponding entry to `generate.bat`. +3. Run `generate.bat` (or the equivalent Cog command) to produce + `include/etl/private/_cpp03.h`. +4. Commit both the generator and the generated output. +5. Verify with `python3 scripts/generator_test.py`. + +--- + +## Troubleshooting + +| Problem | Solution | +|---|---| +| `ModuleNotFoundError: No module named 'cogapp'` | Install Cog: `pip install cogapp` | +| Generator output differs from committed file | Regenerate and commit the updated output | +| Need more than 16 types/handlers | Change `-DNTypes=` or `-DHandlers=` and regenerate | diff --git a/docs/meson.md b/docs/meson.md new file mode 100644 index 00000000..3fb318d3 --- /dev/null +++ b/docs/meson.md @@ -0,0 +1,116 @@ +# Building ETL with Meson + +## Prerequisites + +- [Meson](https://mesonbuild.com/) >= 0.57.0 +- A C++17 compiler (GCC, Clang, or MSVC) +- [Ninja](https://ninja-build.org/) (default Meson backend) + +UnitTest++ is fetched automatically as a Meson subproject — no manual dependency installation is needed. + +## Quick Start + +```bash +# Configure (from the project root) +meson setup builddir + +# Build +meson compile -C builddir + +# Run tests +meson test -C builddir +``` + +## Build Options + +### ETL project options + +| Option | Type | Default | Description | +|---------------------|------|---------|-------------------------------------------------------------------| +| `use_stl` | bool | `true` | Build with STL support. When `false`, defines `ETL_NO_STL`. | +| `enable_sanitizer` | bool | `false` | Enable AddressSanitizer and UndefinedBehaviorSanitizer (GCC/Clang only). | + +### Meson built-in options + +| Option | Type | Default | Description | +|-------------|--------|----------|----------------------------------------------------------| +| `cpp_std` | string | `c++17` | C++ standard to compile with (e.g. `c++20`, `c++23`). | +| `buildtype` | string | `debug` | Build type: `plain`, `debug`, `debugoptimized`, `release`, `minsize`. | +| `werror` | bool | `false` | Treat compiler warnings as errors. | + +These are handled by Meson directly — no `get_option()` call is needed in the build files. + +### Examples + +```bash +# No STL, C++23 +meson setup builddir -Duse_stl=false -Dcpp_std=c++23 + +# Release build with sanitizers +meson setup builddir -Dbuildtype=release -Denable_sanitizer=true + +# Override the C++ standard on an existing build directory +meson configure builddir -Dcpp_std=c++20 +``` + +## Selecting a Compiler + +The compiler is chosen at configure time via environment variables: + +```bash +# GCC +CC=gcc CXX=g++ meson setup builddir + +# Clang +CC=clang CXX=clang++ meson setup builddir + +# Specific versions +CC=gcc-14 CXX=g++-14 meson setup builddir +CC=clang-18 CXX=clang++-18 meson setup builddir +``` + +To switch compilers on an existing build directory, wipe it first: + +```bash +CC=clang CXX=clang++ meson setup --wipe builddir +``` + +Or use separate directories per compiler: + +```bash +CC=gcc CXX=g++ meson setup build-gcc +CC=clang CXX=clang++ meson setup build-clang +``` + +## Running Tests + +```bash +# Run all tests +meson test -C builddir + +# Verbose output (shows individual test results) +meson test -C builddir -v + +# Run the test binary directly +./builddir/test/etl_unit_tests +``` + +## Sanitizers + +On GCC and Clang, AddressSanitizer and UndefinedBehaviorSanitizer can be enabled via the `enable_sanitizer` option: + +```bash +meson setup builddir -Denable_sanitizer=true +``` + +Note: UBSan may prevent certain `constexpr` evaluations involving function pointers from compiling (e.g. in the closure tests). This matches the CMake build, where sanitizers are also opt-in via `ETL_ENABLE_SANITIZER=ON`. + +## Using ETL as a Subproject + +ETL can be consumed as a Meson subproject. In your project's `subprojects/` directory, create an `etl.wrap` file, then use: + +```meson +etl_dep = dependency('etl', fallback: ['etl', 'etl_dep']) +``` + +When built as a subproject, the ETL test suite is not compiled. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 00000000..c6773f6f --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,412 @@ +# Testing ETL + +This document describes how to build and run the ETL test suite locally, +inside Dev Containers, and in CI. + +## Table of Contents + +1. [Prerequisites](#prerequisites) +2. [Running Tests Locally (`test/run-tests.sh`)](#running-tests-locally) +3. [Syntax Checks (`test/run-syntax-checks.sh`)](#syntax-checks) +4. [Cross-Architecture Testing (`.devcontainer/run-tests.sh`)](#cross-architecture-testing) +5. [Dev Containers for Native Compilers](#dev-containers-for-native-compilers) +6. [CMake Options Reference](#cmake-options-reference) +7. [CI Checks (GitHub Actions)](#ci-checks-github-actions) +8. [Appveyor (Windows / MSVC)](#appveyor-windows--msvc) +9. [Code Coverage](#code-coverage) +10. [Generator Tests (`scripts/generator_test.py`)](#generator-tests) + +--- + +## Prerequisites + +* **CMake** ≥ 3.10 +* **GCC** and/or **Clang** (any version supported by the project) +* **Make** or **Ninja** (build backend) +* **Docker** (only needed for cross-architecture testing via `.devcontainer/run-tests.sh`) +* **QEMU user-mode** (installed automatically inside the cross-arch Docker images) + +The project is header-only, so there is no library to compile – the build +step compiles the test binary `etl_tests` which links against a bundled copy +of **UnitTest++**. + +--- + +## Running Tests Locally + +The main entry point for local testing is **`test/run-tests.sh`**. It +iterates over a matrix of compiler / configuration combinations, creates a +temporary `build-make` directory for each one, runs CMake + Make + CTest, +and reports coloured pass/fail output (also appended to `log.txt`). + +### Synopsis + +```bash +cd test +./run-tests.sh [Optimisation] [Threads] [Sanitizer] [Compiler] [Verbose] +``` + +| Argument | Values | Default | +|---|---|---| +| C++ Standard | `11`, `14`, `17`, `20`, `23`, or `all` | *(required)* | +| Optimisation | `0`, `1`, `2`, `3` | `0` | +| Threads | any positive integer | `4` | +| Sanitizer | `s` (enable) / `n` (disable) | `n` (disabled) | +| Compiler | `gcc`, `clang` | all compilers | +| Verbose | `v` (enable) / `n` (disable) | `n` (disabled) | + +### Examples + +```bash +# Run all C++17 tests with GCC only, optimisation -O0, 8 threads +./run-tests.sh 17 0 8 n gcc + +# Run every standard with both compilers, sanitizers enabled, verbose +./run-tests.sh all 0 4 s "" v +``` + +### What the script does + +For every selected C++ standard the script loops over a built-in list of +configurations (STL / No STL / Force C++03 / Non-virtual messages / …) for +each selected compiler. For every combination it: + +1. Creates a fresh `build-make` directory inside the configuration's source + subdirectory. +2. Invokes `cmake` with the appropriate `-D` flags (see + [CMake Options Reference](#cmake-options-reference)). +3. Builds via `cmake --build .` (parallel level controlled by + `CMAKE_BUILD_PARALLEL_LEVEL`). +4. Runs `ctest -V`. +5. Reports success or failure and removes the build directory. + +The script exits immediately on the first compilation or test failure. + +### Test configurations exercised + +| Compiler | Configuration | +|---|---| +| GCC | STL | +| GCC | STL – Non-virtual messages | +| GCC | STL – Force C++03 | +| GCC | No STL | +| GCC | No STL – Force C++03 | +| GCC | No STL – Builtin mem functions | +| Clang | STL | +| Clang | STL – Force C++03 | +| Clang | No STL | +| Clang | No STL – Force C++03 | +| Clang | No STL – Builtin mem functions | +| GCC / Clang | Initializer list test | +| GCC / Clang | Error macros – log_errors, exceptions, log_errors_and_exceptions, assert_function | + +--- + +## Syntax Checks + +The script **`test/run-syntax-checks.sh`** performs compilation-only syntax +checks across multiple C++ standards and configurations. Unlike +`run-tests.sh`, it **does not run the test binary** – it only verifies that +the code compiles successfully. This is useful for quickly validating that +header changes do not introduce compilation errors across the supported +standard/configuration matrix. + +### Synopsis + +```bash +cd test +./run-syntax-checks.sh [Threads] [Compiler] +``` + +| Argument | Values | Default | +|---|---|---| +| C++ Standard | `03`, `11`, `14`, `17`, `20`, `23`, or `a` (all) | *(required)* | +| Threads | any positive integer | `4` | +| Compiler | `gcc`, `clang` | all compilers | + +### Examples + +```bash +# Check C++17 syntax with GCC only, using 8 threads +./run-syntax-checks.sh 17 8 gcc + +# Check all standards with both compilers +./run-syntax-checks.sh a +``` + +### What the script does + +The script operates from the `test/syntax_check` directory and iterates over +the selected C++ standard(s). For each standard and compiler combination it: + +1. Creates fresh build directories (`bgcc` / `bclang`). +2. Invokes `cmake` with the appropriate `-D` flags for the configuration. +3. Builds via `cmake --build`. +4. Reports compilation success or failure (logged to `log.txt`). + +The script exits immediately on the first compilation failure. + +### Configurations checked per standard + +For each C++ standard the following configurations are compiled: + +| Compiler | Configuration | +|---|---| +| GCC | STL | +| GCC | No STL | +| GCC | STL – Built-in traits | +| GCC | No STL – Built-in traits | +| Clang | STL | +| Clang | No STL | +| Clang | STL – Built-in traits | +| Clang | No STL – Built-in traits | + +--- + +## Cross-Architecture Testing + +**`.devcontainer/run-tests.sh`** builds and runs the test suite for +non-x86_64 architectures using Docker and QEMU user-mode emulation. It is +designed to be run **from the project root**. + +### Supported architectures + +| Argument | Target | Endianness | QEMU binary | +|---|---|---|---| +| `armhf` | ARM hard-float (32-bit) | Little | `qemu-arm-static` | +| `i386` | x86 32-bit | Little | `qemu-i386-static` | +| `powerpc` | PowerPC 32-bit | Big | `qemu-ppc` | +| `riscv64` | RISC-V 64-bit | Little | `qemu-riscv64-static` | +| `s390x` | IBM Z (64-bit) | Big | `qemu-s390x-static` | + +### Synopsis + +```bash +# From the project root +.devcontainer/run-tests.sh +``` + +### How it works + +The script has two phases controlled by a second (internal) argument: + +1. **Outside the container** (no second argument): + * Builds a Docker image from `.devcontainer//Dockerfile`. + * Starts a container, bind-mounting the project at `/workspaces/etl`. + * Re-invokes itself *inside* the container with the `inside_container` + flag. + +2. **Inside the container** (`inside_container`): + * Creates `build-` and runs CMake with the appropriate cross- + compilation toolchain file + (`.devcontainer//toolchain-.cmake`). + * Builds with `cmake --build .` using all available cores. + * Runs the test suite via `ctest --output-on-failure`. + +The toolchain files set `CMAKE_CROSSCOMPILING_EMULATOR` so that CTest can +run the binary transparently through QEMU. + +### Example + +```bash +# Build & run the armhf test suite +.devcontainer/run-tests.sh armhf +``` + +The cross-arch containers build with the following fixed settings: + +* C++23, No STL, sanitizer off, optimisation -O0. + +--- + +## Dev Containers for Native Compilers + +The `.devcontainer/` directory also provides Dev Container definitions for a +wide range of **native** (x86_64) compiler versions. These are intended for +use with **VS Code Dev Containers** or **GitHub Codespaces**. + +| Directory | Compiler | +|---|---| +| `gcc09` – `gcc15` | GCC 9 through 15 | +| `clang7` – `clang21` | Clang 7 through 21 | + +Each subdirectory contains a `devcontainer.json` that references the shared +`Dockerfile` (`.devcontainer/Dockerfile`) and passes the appropriate base +Docker image via the `BASE_IMAGE_NAME` build argument (e.g. `gcc:15`). + +The default Dev Container (`.devcontainer/devcontainer.json`) uses the +Microsoft C++ dev-container base image. + +To use one of these containers: + +1. Open the repository in VS Code. +2. **Ctrl+Shift+P → Dev Containers: Reopen in Container** and select the + desired configuration (e.g. *Gcc 15*). +3. Use `test/run-tests.sh` inside the container as described above. + +--- + +## CMake Options Reference + +When invoking CMake for the test suite (source directory is `test/`), the +following `-D` options control the build: + +| Option | Type | Description | +|---|---|---| +| `BUILD_TESTS` | `BOOL` | Must be `ON` to compile the test binary. | +| `NO_STL` | `BOOL` | Build without the C++ Standard Library. | +| `ETL_CXX_STANDARD` | `STRING` | C++ standard: `11`, `14`, `17`, `20`, `23`. | +| `ETL_OPTIMISATION` | `STRING` | Compiler optimisation flag, e.g. `-O0`. | +| `ETL_ENABLE_SANITIZER` | `BOOL` | Enable address / undefined-behaviour sanitizers. | +| `ETL_USE_TYPE_TRAITS_BUILTINS` | `BOOL` | Use compiler built-in type traits. | +| `ETL_USER_DEFINED_TYPE_TRAITS` | `BOOL` | Use user-defined type traits. | +| `ETL_FORCE_TEST_CPP03_IMPLEMENTATION` | `BOOL` | Force the C++03 code paths even on newer standards. | +| `ETL_MESSAGES_ARE_NOT_VIRTUAL` | `BOOL` | Use non-virtual message types. | +| `ETL_USE_BUILTIN_MEM_FUNCTIONS` | `BOOL` | Use built-in memory functions in No-STL mode. | +| `CMAKE_TOOLCHAIN_FILE` | `PATH` | Toolchain file for cross-compilation. | + +### Minimal manual build example + +```bash +cd test +mkdir build && cd build +cmake -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_CXX_STANDARD=20 .. +cmake --build . -j$(nproc) +ctest -V +``` + +--- + +## CI Checks (GitHub Actions) + +Every push or pull request to `master`, `development`, or `pull-request/*` +branches triggers a comprehensive set of GitHub Actions workflows defined in +`.github/workflows/`. + +### Workflow matrix + +| Workflow file | Compiler | Standard | Notes | +|---|---|---|---| +| `gcc-c++11.yml` | GCC | C++11 | STL, No STL, Force C++03 | +| `gcc-c++14.yml` | GCC | C++14 | STL, No STL, Force C++03 | +| `gcc-c++17.yml` | GCC | C++17 | STL, No STL, Force C++03 | +| `gcc-c++20.yml` | GCC | C++20 | STL, No STL, Force C++03 | +| `gcc-c++23.yml` | GCC | C++23 | STL, No STL, Force C++03 | +| `clang-c++11.yml` | Clang | C++11 | STL, No STL, Force C++03 | +| `clang-c++14.yml` | Clang | C++14 | STL, No STL, Force C++03 | +| `clang-c++17.yml` | Clang | C++17 | STL, No STL, Force C++03 | +| `clang-c++20.yml` | Clang | C++20 | STL, No STL, Force C++03 | +| `clang-c++23.yml` | Clang | C++23 | STL, No STL, Force C++03 | +| `gcc-syntax-checks.yml` | GCC | C++03 – C++23 | Compilation-only syntax checks (no tests run) | +| `clang-syntax-checks.yml` | Clang | C++03 – C++23 | Compilation-only syntax checks (no tests run) | +| `msvc.yml` | MSVC 2022 | C++17 | Windows, STL & No STL | +| `gcc-c++23-armhf.yml` | GCC cross | C++23 | armhf via QEMU | +| `gcc-c++23-i386.yml` | GCC cross | C++23 | i386 via QEMU | +| `gcc-c++23-powerpc.yml` | GCC cross | C++23 | powerpc via QEMU | +| `gcc-c++23-riscv64.yml` | GCC cross | C++23 | RISC-V 64 via QEMU | +| `gcc-c++23-s390x.yml` | GCC cross | C++23 | s390x via QEMU | +| `coverage.yml` | GCC | — | Generates lcov coverage report, deploys to GitHub Pages | +| `generator.yml` | — | — | Runs the code generator | +| `platformio-update.yml` | — | — | PlatformIO registry update | + +### Typical CI job structure + +Each compiler/standard workflow follows the same pattern: + +1. **Checkout** – `actions/checkout@v4`. +2. **Build** – set `CC`/`CXX`, call `cmake` with the appropriate `-D` flags, + then `make -j`. +3. **Run tests** – execute `./test/etl_tests -v` (or `ctest -V` for cross- + arch jobs). + +The cross-architecture CI jobs additionally install a cross-compiler +toolchain and QEMU inside a `debian:trixie` container, use the matching +toolchain file from `.devcontainer//`, and run tests via CTest (which +delegates to QEMU through `CMAKE_CROSSCOMPILING_EMULATOR`). + +### Branches tested + +* `master` +* `development` +* `pull-request/*` + +All workflows run on both `push` and `pull_request` events (types: opened, +synchronize, reopened). + +--- + +## Appveyor (Windows / MSVC) + +The `appveyor.yml` at the repository root provides additional Windows CI +using **Visual Studio 2022**. It builds the `master` branch only. + +Configurations tested: + +* Debug MSVC C++14 +* Debug MSVC C++14 – No STL +* Debug MSVC C++17 +* Debug MSVC C++17 – No STL +* Debug MSVC C++20 +* Debug MSVC C++20 – No STL + +The build uses the VS 2022 solution file at `test/vs2022/etl.vcxproj`. + +--- + +## Code Coverage + +The `coverage.yml` GitHub Actions workflow generates an **lcov** coverage +report: + +1. Runs `test/run-coverage.sh` which builds and tests with GCC coverage + flags. +2. Uploads the HTML report as a build artifact (retained for 30 days). +3. On pushes to `master`, deploys the report to **GitHub Pages**. + +To generate coverage locally: + +```bash +cd test +./run-coverage.sh +# Open test/build-coverage/coverage/index.html +``` + +--- + +## Generator Tests + +The script **`scripts/generator_test.py`** verifies that the code generators +in `include/etl/generators/` produce output matching the checked-in header +files in `include/etl/private/`. + +ETL uses [Cog](https://nedbatchelder.com/code/cog/) to generate certain +repetitive header files (e.g. `delegate.h`, `message_packet.h`). The +generator templates live in `include/etl/generators/*_generator.h` and the +generated output is committed to `include/etl/private/*.h`. This test +ensures the two stay in sync. + +### Prerequisites + +* **Python 3** +* **cogapp** – install via `pip install cogapp` + +### Synopsis + +```bash +python3 scripts/generator_test.py +``` + +### What the script does + +1. Iterates over every `*_generator.h` file in `include/etl/generators/`. +2. Runs Cog on each generator, outputting to `build/generator_tmp/`. +3. Compares each generated file against the corresponding file in + `include/etl/private/`. +4. Reports success if all files match, or failure if any differ. + +The script exits with code `0` on success and `1` on failure. + +### CI integration + +The `generator.yml` GitHub Actions workflow runs this script automatically +on pushes and pull requests to verify generator consistency. diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 63bfe13b..91252319 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -51,7 +51,7 @@ SOFTWARE. #include "type_traits.h" #include "utility.h" -#include +#include #include #include "private/minmax_push.h" @@ -91,26 +91,6 @@ namespace etl template ETL_CONSTEXPR14 void insertion_sort(TIterator first, TIterator last, TCompare compare); - class algorithm_exception : public etl::exception - { - public: - - algorithm_exception(string_type reason_, string_type file_name_, numeric_type line_number_) - : exception(reason_, file_name_, line_number_) - { - } - }; - - class algorithm_error : public algorithm_exception - { - public: - - algorithm_error(string_type file_name_, numeric_type line_number_) - : algorithm_exception(ETL_ERROR_TEXT("algorithm:error", ETL_ALGORITHM_FILE_ID"A"), file_name_, line_number_) - { - } - }; - } // namespace etl //***************************************************************************** @@ -558,7 +538,8 @@ namespace etl { while (first != last) { - *first = value; + // This cast is necessary because the signedness can differ + *first = static_cast::value_type>(value); ++first; } } @@ -898,6 +879,7 @@ namespace etl { TDistance parent = (value_index - 1) / 2; +#include "etl/private/diagnostic_array_bounds_push.h" while ((value_index > top_index) && compare(first[parent], value)) { first[value_index] = ETL_MOVE(first[parent]); @@ -906,6 +888,7 @@ namespace etl } first[value_index] = ETL_MOVE(value); +#include "etl/private/diagnostic_pop.h" } // Adjust Heap Helper @@ -1358,7 +1341,9 @@ namespace etl value_type temp(ETL_MOVE(*first)); // Move the rest. +#include "etl/private/diagnostic_stringop_overread_push.h" TIterator result = etl::move(etl::next(first), last, first); +#include "etl/private/diagnostic_pop.h" // Restore the first item in its rotated position. *result = ETL_MOVE(temp); @@ -2786,7 +2771,9 @@ namespace etl d_size_type d_size = etl::distance(o_begin, o_end); min_size_type min_size = etl::min(s_size, d_size); + #include "etl/private/diagnostic_null_dereference_push.h" return etl::move(i_begin, i_begin + min_size, o_begin); + #include "etl/private/diagnostic_pop.h" } //*************************************************************************** @@ -5860,12 +5847,14 @@ namespace etl } } + #include "etl/private/diagnostic_array_bounds_push.h" // Sort the heap to produce a sorted output range for (auto heap_end = heap_size - 1; heap_end > 0; --heap_end) { etl::iter_swap(result_first, result_first + heap_end); sift_down(result_first, decltype(heap_size){0}, heap_end, comp, proj2); } + #include "etl/private/diagnostic_pop.h" return {etl::move(in_last), etl::move(r)}; } @@ -6202,6 +6191,16 @@ namespace etl I left_partition = stable_partition_impl(first, middle, etl::ref(pred), etl::ref(proj), len / 2); I right_partition = stable_partition_impl(middle, last, etl::ref(pred), etl::ref(proj), len - len / 2); + if (left_partition == middle) + { + return right_partition; + } + + if (middle == right_partition) + { + return left_partition; + } + return etl::rotate(left_partition, middle, right_partition); } }; diff --git a/include/etl/array.h b/include/etl/array.h index 2051807b..ffa3a307 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -35,10 +35,7 @@ SOFTWARE. #include "algorithm.h" #include "error_handler.h" #include "exception.h" -#include "functional.h" -#include "initializer_list.h" #include "iterator.h" -#include "nth_type.h" #include "parameter_type.h" #include "static_assert.h" #include "type_traits.h" diff --git a/include/etl/atomic/atomic_gcc_sync.h b/include/etl/atomic/atomic_gcc_sync.h index 087489aa..425d8707 100644 --- a/include/etl/atomic/atomic_gcc_sync.h +++ b/include/etl/atomic/atomic_gcc_sync.h @@ -341,6 +341,68 @@ namespace etl return __atomic_fetch_xor(&value, v, order); } + // Fetch max + T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + T old = load(order); + + while (v > old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + T old = load(order); + + while (v > old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + // Fetch min + T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + T old = load(order); + + while (v < old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + T old = load(order); + + while (v < old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + // Exchange T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) { @@ -1234,6 +1296,72 @@ namespace etl return __sync_fetch_and_xor(&value, v); } + // Fetch max + T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + (void)order; + T old = load(order); + + while (v > old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_max(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + (void)order; + T old = load(order); + + while (v > old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + // Fetch min + T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + (void)order; + T old = load(order); + + while (v < old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_min(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + (void)order; + T old = load(order); + + while (v < old) + { + if (compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + // Exchange T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) { diff --git a/include/etl/atomic/atomic_std.h b/include/etl/atomic/atomic_std.h index ee765a70..06fcef6c 100644 --- a/include/etl/atomic/atomic_std.h +++ b/include/etl/atomic/atomic_std.h @@ -40,11 +40,98 @@ namespace etl { //*************************************************************************** // ETL Atomic type for compilers that support std::atomic. - // etl::atomic is a simple wrapper around std::atomic. + // etl::atomic is a wrapper around std::atomic that adds fetch_max and + // fetch_min if the standard library does not yet provide them. //*************************************************************************** +#if ETL_HAS_STD_ATOMIC_MIN_MAX + // The standard library already provides fetch_max/fetch_min. template using atomic = std::atomic; +#else + template + class atomic : public std::atomic + { + using base_type = std::atomic; + + public: + + atomic() ETL_NOEXCEPT = default; + + ETL_CONSTEXPR atomic(T desired) ETL_NOEXCEPT + : base_type(desired) + { + } + + atomic(const atomic&) = delete; + atomic& operator=(const atomic&) = delete; + atomic& operator=(const atomic&) volatile = delete; + + using base_type::operator=; + + // Fetch max + T fetch_max(T v, std::memory_order order = std::memory_order_seq_cst) + { + T old = this->load(order); + + while (v > old) + { + if (this->compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_max(T v, std::memory_order order = std::memory_order_seq_cst) volatile + { + T old = this->load(order); + + while (v > old) + { + if (this->compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + // Fetch min + T fetch_min(T v, std::memory_order order = std::memory_order_seq_cst) + { + T old = this->load(order); + + while (v < old) + { + if (this->compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + + T fetch_min(T v, std::memory_order order = std::memory_order_seq_cst) volatile + { + T old = this->load(order); + + while (v < old) + { + if (this->compare_exchange_weak(old, v, order)) + { + break; + } + } + + return old; + } + }; +#endif using memory_order = std::memory_order; @@ -55,66 +142,66 @@ namespace etl static ETL_CONSTANT etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; static ETL_CONSTANT etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; - using atomic_bool = std::atomic; - using atomic_char = std::atomic; - using atomic_schar = std::atomic; - using atomic_uchar = std::atomic; - using atomic_short = std::atomic; - using atomic_ushort = std::atomic; - using atomic_int = std::atomic; - using atomic_uint = std::atomic; - using atomic_long = std::atomic; - using atomic_ulong = std::atomic; - using atomic_llong = std::atomic; - using atomic_ullong = std::atomic; - using atomic_wchar_t = std::atomic; + using atomic_bool = etl::atomic; + using atomic_char = etl::atomic; + using atomic_schar = etl::atomic; + using atomic_uchar = etl::atomic; + using atomic_short = etl::atomic; + using atomic_ushort = etl::atomic; + using atomic_int = etl::atomic; + using atomic_uint = etl::atomic; + using atomic_long = etl::atomic; + using atomic_ulong = etl::atomic; + using atomic_llong = etl::atomic; + using atomic_ullong = etl::atomic; + using atomic_wchar_t = etl::atomic; #if ETL_HAS_NATIVE_CHAR8_T - using atomic_char8_t = std::atomic; + using atomic_char8_t = etl::atomic; #endif #if ETL_HAS_NATIVE_CHAR16_T - using atomic_char16_t = std::atomic; + using atomic_char16_t = etl::atomic; #endif #if ETL_HAS_NATIVE_CHAR32_T - using atomic_char32_t = std::atomic; + using atomic_char32_t = etl::atomic; #endif #if ETL_USING_8BIT_TYPES - using atomic_uint8_t = std::atomic; - using atomic_int8_t = std::atomic; + using atomic_uint8_t = etl::atomic; + using atomic_int8_t = etl::atomic; #endif - using atomic_uint16_t = std::atomic; - using atomic_int16_t = std::atomic; - using atomic_uint32_t = std::atomic; - using atomic_int32_t = std::atomic; + using atomic_uint16_t = etl::atomic; + using atomic_int16_t = etl::atomic; + using atomic_uint32_t = etl::atomic; + using atomic_int32_t = etl::atomic; #if ETL_USING_64BIT_TYPES - using atomic_uint64_t = std::atomic; - using atomic_int64_t = std::atomic; + using atomic_uint64_t = etl::atomic; + using atomic_int64_t = etl::atomic; #endif - using atomic_int_least8_t = std::atomic; - using atomic_uint_least8_t = std::atomic; - using atomic_int_least16_t = std::atomic; - using atomic_uint_least16_t = std::atomic; - using atomic_int_least32_t = std::atomic; - using atomic_uint_least32_t = std::atomic; + using atomic_int_least8_t = etl::atomic; + using atomic_uint_least8_t = etl::atomic; + using atomic_int_least16_t = etl::atomic; + using atomic_uint_least16_t = etl::atomic; + using atomic_int_least32_t = etl::atomic; + using atomic_uint_least32_t = etl::atomic; #if ETL_USING_64BIT_TYPES - using atomic_int_least64_t = std::atomic; - using atomic_uint_least64_t = std::atomic; + using atomic_int_least64_t = etl::atomic; + using atomic_uint_least64_t = etl::atomic; #endif - using atomic_int_fast8_t = std::atomic; - using atomic_uint_fast8_t = std::atomic; - using atomic_int_fast16_t = std::atomic; - using atomic_uint_fast16_t = std::atomic; - using atomic_int_fast32_t = std::atomic; - using atomic_uint_fast32_t = std::atomic; + using atomic_int_fast8_t = etl::atomic; + using atomic_uint_fast8_t = etl::atomic; + using atomic_int_fast16_t = etl::atomic; + using atomic_uint_fast16_t = etl::atomic; + using atomic_int_fast32_t = etl::atomic; + using atomic_uint_fast32_t = etl::atomic; #if ETL_USING_64BIT_TYPES - using atomic_int_fast64_t = std::atomic; - using atomic_uint_fast64_t = std::atomic; + using atomic_int_fast64_t = etl::atomic; + using atomic_uint_fast64_t = etl::atomic; #endif - using atomic_intptr_t = std::atomic; - using atomic_uintptr_t = std::atomic; - using atomic_size_t = std::atomic; - using atomic_ptrdiff_t = std::atomic; - using atomic_intmax_t = std::atomic; - using atomic_uintmax_t = std::atomic; + using atomic_intptr_t = etl::atomic; + using atomic_uintptr_t = etl::atomic; + using atomic_size_t = etl::atomic; + using atomic_ptrdiff_t = etl::atomic; + using atomic_intmax_t = etl::atomic; + using atomic_uintmax_t = etl::atomic; } // namespace etl #endif diff --git a/include/etl/base64.h b/include/etl/base64.h index 4ce98b3c..03c73648 100644 --- a/include/etl/base64.h +++ b/include/etl/base64.h @@ -31,11 +31,8 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "integral_limits.h" -#include "static_assert.h" #include "type_traits.h" -#include - /************************************************************************************************************************************************************************** * See https://en.wikipedia.org/wiki/Base64 * diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index f80d0ec6..5c585d85 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -34,7 +34,6 @@ SOFTWARE. ///\ingroup string #include "platform.h" -#include "static_assert.h" #include "type_traits.h" #include "utility.h" diff --git a/include/etl/basic_string.h b/include/etl/basic_string.h index 1a38cc99..59db587a 100644 --- a/include/etl/basic_string.h +++ b/include/etl/basic_string.h @@ -39,7 +39,6 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "flags.h" -#include "functional.h" #include "integral_limits.h" #include "iterator.h" #include "memory.h" @@ -93,20 +92,6 @@ namespace etl } }; - //*************************************************************************** - ///\ingroup string - /// String empty exception. - //*************************************************************************** - class string_empty : public etl::string_exception - { - public: - - string_empty(string_type file_name_, numeric_type line_number_) - : string_exception(ETL_ERROR_TEXT("string:empty", ETL_BASIC_STRING_FILE_ID"A"), file_name_, line_number_) - { - } - }; - //*************************************************************************** ///\ingroup string /// String out of bounds exception. @@ -1481,9 +1466,7 @@ namespace etl //********************************************************************* size_type find(const_pointer s, size_type pos, size_type n) const { - size_t sz = etl::strlen(s); - - return find_impl(s, s + n, sz, pos); + return find_impl(s, s + n, n, pos); } //********************************************************************* @@ -2742,6 +2725,7 @@ namespace etl iterator>::type copy_characters(TIterator1 from, size_t n, iterator to) { +#include "etl/private/diagnostic_stringop_overflow_push.h" size_t count = 0; while (count != n) @@ -2750,6 +2734,7 @@ namespace etl ++count; } +#include "etl/private/diagnostic_pop.h" return to; } @@ -3218,8 +3203,6 @@ namespace etl #endif } // namespace etl -#undef ETL_USING_WCHAR_T_H - #include "private/minmax_pop.h" #endif diff --git a/include/etl/binary.h b/include/etl/binary.h index e7d9eec0..230fbf4a 100644 --- a/include/etl/binary.h +++ b/include/etl/binary.h @@ -1742,7 +1742,7 @@ namespace etl { count = 1U; - if ((value & 0xFFFFFFFFF0000000ULL) == 0U) + if ((value & 0xFFFFFFFF00000000ULL) == 0U) { value <<= 32U; count += 32U; @@ -1795,7 +1795,7 @@ namespace etl { typedef typename etl::make_unsigned::type unsigned_t; - return static_cast(count_trailing_ones(static_cast(value))); + return static_cast(count_leading_zeros(static_cast(value))); } #if ETL_USING_8BIT_TYPES @@ -1927,8 +1927,8 @@ namespace etl if ((value & 0xFFFF0000UL) == 0xFFFF0000UL) { - value <<= 8U; - count += 8U; + value <<= 16U; + count += 16U; } if ((value & 0xFF000000UL) == 0xFF000000UL) @@ -1988,14 +1988,14 @@ namespace etl if ((value & 0xFFFFFFFF00000000ULL) == 0xFFFFFFFF00000000ULL) { - value <<= 8U; - count += 8U; + value <<= 32U; + count += 32U; } if ((value & 0xFFFF000000000000ULL) == 0xFFFF000000000000ULL) { - value <<= 8U; - count += 8U; + value <<= 16U; + count += 16U; } if ((value & 0xFF00000000000000ULL) == 0xFF00000000000000ULL) diff --git a/include/etl/bip_buffer_spsc_atomic.h b/include/etl/bip_buffer_spsc_atomic.h index 0901af80..fcb955da 100644 --- a/include/etl/bip_buffer_spsc_atomic.h +++ b/include/etl/bip_buffer_spsc_atomic.h @@ -51,7 +51,6 @@ SOFTWARE. #include "utility.h" #include -#include #if ETL_HAS_ATOMIC @@ -404,7 +403,7 @@ namespace etl //************************************************************************* void read_commit(const span& reserve) { - size_type rindex = etl::distance(p_buffer, reserve.data()); + size_type rindex = static_cast(etl::distance(p_buffer, reserve.data())); apply_read_reserve(rindex, reserve.size()); } @@ -438,7 +437,7 @@ namespace etl //************************************************************************* void write_commit(const span& reserve) { - size_type windex = etl::distance(p_buffer, reserve.data()); + size_type windex = static_cast(etl::distance(p_buffer, reserve.data())); apply_write_reserve(windex, reserve.size()); } diff --git a/include/etl/bresenham_line.h b/include/etl/bresenham_line.h index 02ebc49a..a2888d9d 100644 --- a/include/etl/bresenham_line.h +++ b/include/etl/bresenham_line.h @@ -33,10 +33,10 @@ SOFTWARE. #include "platform.h" #include "iterator.h" -#include "static_assert.h" #include "type_traits.h" #include "utility.h" +#include #include namespace etl diff --git a/include/etl/buffer_descriptors.h b/include/etl/buffer_descriptors.h index 100846d7..61ec7890 100644 --- a/include/etl/buffer_descriptors.h +++ b/include/etl/buffer_descriptors.h @@ -36,10 +36,9 @@ SOFTWARE. #include "array.h" #include "cyclic_value.h" #include "delegate.h" -#include "static_assert.h" #include "type_traits.h" -#include +#include #if ETL_USING_CPP11 diff --git a/include/etl/byte_stream.h b/include/etl/byte_stream.h index 4af209ed..51dc9e92 100644 --- a/include/etl/byte_stream.h +++ b/include/etl/byte_stream.h @@ -40,13 +40,11 @@ SOFTWARE. #include "integral_limits.h" #include "iterator.h" #include "memory.h" -#include "nullptr.h" #include "optional.h" #include "span.h" #include "type_traits.h" -#include -#include +#include namespace etl { diff --git a/include/etl/callback_timer.h b/include/etl/callback_timer.h index 0b3da8ba..800a34e2 100644 --- a/include/etl/callback_timer.h +++ b/include/etl/callback_timer.h @@ -43,8 +43,8 @@ SOFTWARE. #include #if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL - #define ETL_DISABLE_TIMER_UPDATES - #define ETL_ENABLE_TIMER_UPDATES + #define ETL_DISABLE_TIMER_UPDATES ((void)0) + #define ETL_ENABLE_TIMER_UPDATES ((void)0) #define ETL_TIMER_UPDATES_ENABLED true #undef ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK @@ -629,7 +629,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -758,22 +757,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -788,16 +778,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/callback_timer_atomic.h b/include/etl/callback_timer_atomic.h index 0aba1f97..98d92651 100644 --- a/include/etl/callback_timer_atomic.h +++ b/include/etl/callback_timer_atomic.h @@ -33,8 +33,6 @@ SOFTWARE. #include "algorithm.h" #include "delegate.h" #include "error_handler.h" -#include "function.h" -#include "nullptr.h" #include "placement_new.h" #include "static_assert.h" #include "timer.h" @@ -487,7 +485,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -616,22 +613,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -646,16 +634,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/callback_timer_deferred_locked.h b/include/etl/callback_timer_deferred_locked.h index 91927d8d..e9679bc7 100644 --- a/include/etl/callback_timer_deferred_locked.h +++ b/include/etl/callback_timer_deferred_locked.h @@ -79,7 +79,7 @@ namespace etl //******************************************* /// Handle the tick call //******************************************* - bool tick(uint32_t count) final + bool tick(uint32_t count) ETL_FINAL { if (enabled) { diff --git a/include/etl/callback_timer_interrupt.h b/include/etl/callback_timer_interrupt.h index 3522bd22..a67754cb 100644 --- a/include/etl/callback_timer_interrupt.h +++ b/include/etl/callback_timer_interrupt.h @@ -33,7 +33,6 @@ SOFTWARE. #include "algorithm.h" #include "delegate.h" #include "error_handler.h" -#include "nullptr.h" #include "placement_new.h" #include "static_assert.h" #include "timer.h" @@ -487,7 +486,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -616,22 +614,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -646,16 +635,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/callback_timer_locked.h b/include/etl/callback_timer_locked.h index 4c53f259..5579ec0b 100644 --- a/include/etl/callback_timer_locked.h +++ b/include/etl/callback_timer_locked.h @@ -33,7 +33,6 @@ SOFTWARE. #include "algorithm.h" #include "delegate.h" #include "error_handler.h" -#include "nullptr.h" #include "placement_new.h" #include "static_assert.h" #include "timer.h" @@ -460,7 +459,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -589,22 +587,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -619,16 +608,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; @@ -709,7 +696,7 @@ namespace etl //******************************************* /// Handle the tick call //******************************************* - bool tick(uint32_t count) final + bool tick(uint32_t count) ETL_FINAL { if (enabled) { diff --git a/include/etl/chrono.h b/include/etl/chrono.h index facde8a4..b452ca4e 100644 --- a/include/etl/chrono.h +++ b/include/etl/chrono.h @@ -35,10 +35,6 @@ SOFTWARE. #include "platform.h" -#if ETL_NOT_USING_CPP11 && !defined(ETL_IN_UNIT_TEST) - #error NOT SUPPORTED FOR C++03 OR BELOW -#endif - #if ETL_USING_CPP11 #include "hash.h" diff --git a/include/etl/circular_buffer.h b/include/etl/circular_buffer.h index da6877e3..4c8290c4 100644 --- a/include/etl/circular_buffer.h +++ b/include/etl/circular_buffer.h @@ -1206,7 +1206,7 @@ namespace etl { this->clear(); - for (typename etl::icircular_buffer::const_iterator itr = other.begin(); itr != other.end(); ++itr) + for (typename etl::icircular_buffer::iterator itr = other.begin(); itr != other.end(); ++itr) { this->push(etl::move(*itr)); } diff --git a/include/etl/closure.h b/include/etl/closure.h index 54b0fb36..7f11bbcf 100644 --- a/include/etl/closure.h +++ b/include/etl/closure.h @@ -33,53 +33,62 @@ SOFTWARE. #include "platform.h" #include "delegate.h" +#include "invoke.h" #include "tuple.h" #include "type_list.h" #include "utility.h" namespace etl { -#if ETL_USING_CPP11 && !defined(ETL_CLOSURE_FORCE_CPP03_IMPLEMENTATION) //************************************************************************* /// Base template for closure. + /// \tparam TSignature The callback signature. + /// \tparam TCallback The callback type, defaults to etl::delegate. //************************************************************************* - template + template > class closure; +#if ETL_USING_CPP11 && !defined(ETL_CLOSURE_FORCE_CPP03_IMPLEMENTATION) //************************************************************************* - /// Closure for binding arguments to a delegate and invoking it later. - /// Stores a delegate and its arguments, allowing deferred execution. + /// Closure for binding arguments to a callback and invoking it later. + /// Stores a callback and its arguments, allowing deferred execution. /// Arguments are stored in a tuple and can be rebound using bind(). /// Example usage: /// \code /// etl::closure c(my_delegate, 1, 2); /// c(); // Invokes my_delegate(1, 2) /// \endcode - /// \tparam TReturn The return type of the delegate. - /// \tparam TArgs The argument types of the delegate. + /// \tparam TReturn The return type of the callback. + /// \tparam TArgs The argument types of the callback. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - using delegate_type = etl::delegate; ///< The delegate type to be invoked. - using argument_types = etl::type_list; ///< The type list of arguments. + using delegate_type ETL_DEPRECATED_REASON("Use callback_type") = + TCallback; ///< The callback type to be invoked. Deprecated, use callback_type instead. + using callback_type = TCallback; ///< The callback type to be invoked. + using argument_types = etl::type_list; ///< The type list of arguments. + + static_assert(etl::is_invocable_r::value, "Callback is not invocable with the specified arguments"); + static_assert(etl::is_copy_constructible::value, "Callback type must be copy constructible"); //********************************************************************* - /// Construct a closure with a delegate and its arguments. - /// \param f The delegate to be invoked. - /// \param args The arguments to bind to the delegate. + /// Construct a closure with a callback and its arguments. + /// \param f The callback to be invoked. + /// \param args The arguments to bind to the callback. //********************************************************************* - ETL_CONSTEXPR14 closure(const delegate_type& f, const TArgs... args) + ETL_CONSTEXPR14 closure(const callback_type& f, const TArgs... args) : m_f(f) , m_args(args...) { } //********************************************************************* - /// Invoke the stored delegate with the bound arguments. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound arguments. + /// \return The result of the callback invocation. //********************************************************************* ETL_CONSTEXPR14 TReturn operator()() const { @@ -131,7 +140,7 @@ namespace etl //********************************************************************* /// Execute the closure with the stored arguments. /// \tparam idx Index sequence for tuple unpacking. - /// \return The result of the delegate invocation. + /// \return The result of the callback invocation. //********************************************************************* template ETL_CONSTEXPR14 TReturn execute(etl::index_sequence) const @@ -139,43 +148,38 @@ namespace etl return m_f(etl::get(m_args)...); } - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. etl::tuple m_args; ///< The bound arguments. }; #else //************************************************************************* - /// Base template for closure. - //************************************************************************* - template - class closure; - - //************************************************************************* - /// Closure for binding one argument to a delegate and invoking it later. - /// \tparam TReturn The return type of the delegate. + /// Closure for binding one argument to a callback and invoking it later. + /// \tparam TReturn The return type of the callback. /// \tparam TArg0 The type of the argument. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - /// The delegate type to be invoked. - typedef etl::delegate delegate_type; + /// The callback type to be invoked. + typedef TCallback callback_type; //********************************************************************* - /// Construct a closure with a delegate and its argument. - /// \param f The delegate to be invoked. - /// \param arg0 The argument to bind to the delegate. + /// Construct a closure with a callback and its argument. + /// \param f The callback to be invoked. + /// \param arg0 The argument to bind to the callback. //********************************************************************* - closure(const delegate_type& f, const TArg0 arg0) + closure(const callback_type& f, const TArg0 arg0) : m_f(f) , m_arg0(arg0) { } //********************************************************************* - /// Invoke the stored delegate with the bound argument. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound argument. + /// \return The result of the callback invocation. //********************************************************************* TReturn operator()() const { @@ -184,30 +188,31 @@ namespace etl private: - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. TArg0 m_arg0; }; //************************************************************************* - /// Closure for binding two arguments to a delegate and invoking it later. - /// \tparam TReturn The return type of the delegate. + /// Closure for binding two arguments to a callback and invoking it later. + /// \tparam TReturn The return type of the callback. /// \tparam TArg0 The type of the first argument. /// \tparam TArg1 The type of the second argument. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - typedef etl::delegate delegate_type; + typedef TCallback callback_type; //********************************************************************* - /// Construct a closure with a delegate and its arguments. - /// \param f The delegate to be invoked. + /// Construct a closure with a callback and its arguments. + /// \param f The callback to be invoked. /// \param arg0 The first argument to bind. /// \param arg1 The second argument to bind. //********************************************************************* - closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1) + closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1) : m_f(f) , m_arg0(arg0) , m_arg1(arg1) @@ -215,8 +220,8 @@ namespace etl } //********************************************************************* - /// Invoke the stored delegate with the bound arguments. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound arguments. + /// \return The result of the callback invocation. //********************************************************************* TReturn operator()() const { @@ -225,33 +230,34 @@ namespace etl private: - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. TArg0 m_arg0; TArg1 m_arg1; }; //************************************************************************* - /// Closure for binding three arguments to a delegate and invoking it later. - /// \tparam TReturn The return type of the delegate. + /// Closure for binding three arguments to a callback and invoking it later. + /// \tparam TReturn The return type of the callback. /// \tparam TArg0 The type of the first argument. /// \tparam TArg1 The type of the second argument. /// \tparam TArg2 The type of the third argument. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - typedef etl::delegate delegate_type; + typedef TCallback callback_type; //********************************************************************* - /// Construct a closure with a delegate and its arguments. - /// \param f The delegate to be invoked. + /// Construct a closure with a callback and its arguments. + /// \param f The callback to be invoked. /// \param arg0 The first argument to bind. /// \param arg1 The second argument to bind. /// \param arg2 The third argument to bind. //********************************************************************* - closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2) + closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2) : m_f(f) , m_arg0(arg0) , m_arg1(arg1) @@ -260,8 +266,8 @@ namespace etl } //********************************************************************* - /// Invoke the stored delegate with the bound arguments. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound arguments. + /// \return The result of the callback invocation. //********************************************************************* TReturn operator()() const { @@ -270,36 +276,37 @@ namespace etl private: - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. TArg0 m_arg0; TArg1 m_arg1; TArg2 m_arg2; }; //************************************************************************* - /// Closure for binding four arguments to a delegate and invoking it later. - /// \tparam TReturn The return type of the delegate. + /// Closure for binding four arguments to a callback and invoking it later. + /// \tparam TReturn The return type of the callback. /// \tparam TArg0 The type of the first argument. /// \tparam TArg1 The type of the second argument. /// \tparam TArg2 The type of the third argument. /// \tparam TArg3 The type of the fourth argument. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - typedef etl::delegate delegate_type; + typedef TCallback callback_type; //********************************************************************* - /// Construct a closure with a delegate and its arguments. - /// \param f The delegate to be invoked. + /// Construct a closure with a callback and its arguments. + /// \param f The callback to be invoked. /// \param arg0 The first argument to bind. /// \param arg1 The second argument to bind. /// \param arg2 The third argument to bind. /// \param arg3 The fourth argument to bind. //********************************************************************* - closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3) + closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3) : m_f(f) , m_arg0(arg0) , m_arg1(arg1) @@ -309,8 +316,8 @@ namespace etl } //********************************************************************* - /// Invoke the stored delegate with the bound arguments. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound arguments. + /// \return The result of the callback invocation. //********************************************************************* TReturn operator()() const { @@ -319,7 +326,7 @@ namespace etl private: - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. TArg0 m_arg0; TArg1 m_arg1; TArg2 m_arg2; @@ -327,31 +334,32 @@ namespace etl }; //************************************************************************* - /// Closure for binding five arguments to a delegate and invoking it later. - /// \tparam TReturn The return type of the delegate. + /// Closure for binding five arguments to a callback and invoking it later. + /// \tparam TReturn The return type of the callback. /// \tparam TArg0 The type of the first argument. /// \tparam TArg1 The type of the second argument. /// \tparam TArg2 The type of the third argument. /// \tparam TArg3 The type of the fourth argument. /// \tparam TArg4 The type of the fifth argument. + /// \tparam TCallback The callback type. //************************************************************************* - template - class closure + template + class closure { public: - typedef etl::delegate delegate_type; + typedef TCallback callback_type; //********************************************************************* - /// Construct a closure with a delegate and its arguments. - /// \param f The delegate to be invoked. + /// Construct a closure with a callback and its arguments. + /// \param f The callback to be invoked. /// \param arg0 The first argument to bind. /// \param arg1 The second argument to bind. /// \param arg2 The third argument to bind. /// \param arg3 The fourth argument to bind. /// \param arg4 The fifth argument to bind. //********************************************************************* - closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3, const TArg4 arg4) + closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3, const TArg4 arg4) : m_f(f) , m_arg0(arg0) , m_arg1(arg1) @@ -362,8 +370,8 @@ namespace etl } //********************************************************************* - /// Invoke the stored delegate with the bound arguments. - /// \return The result of the delegate invocation. + /// Invoke the stored callback with the bound arguments. + /// \return The result of the callback invocation. //********************************************************************* TReturn operator()() const { @@ -372,7 +380,7 @@ namespace etl private: - delegate_type m_f; ///< The delegate to invoke. + callback_type m_f; ///< The callback to invoke. TArg0 m_arg0; TArg1 m_arg1; TArg2 m_arg2; diff --git a/include/etl/concepts.h b/include/etl/concepts.h index ce5cec97..9376264b 100644 --- a/include/etl/concepts.h +++ b/include/etl/concepts.h @@ -33,13 +33,10 @@ SOFTWARE. #include "platform.h" +#include "invoke.h" #include "type_traits.h" #include "utility.h" -#if ETL_NOT_USING_CPP20 && !defined(ETL_IN_UNIT_TEST) - #error NOT SUPPORTED FOR BELOW C++20 -#endif - #if ETL_USING_CPP20 #if ETL_USING_STL @@ -54,12 +51,31 @@ namespace etl using std::assignable_from; using std::common_reference_with; using std::common_with; + using std::constructible_from; using std::convertible_to; + using std::copy_constructible; + using std::copyable; + using std::default_initializable; using std::derived_from; + using std::destructible; + using std::equality_comparable; + using std::equivalence_relation; using std::floating_point; using std::integral; + using std::invocable; + using std::movable; + using std::move_constructible; + using std::predicate; + using std::regular; + using std::regular_invocable; + using std::relation; using std::same_as; + using std::semiregular; using std::signed_integral; + using std::strict_weak_order; + using std::swappable; + using std::swappable_with; + using std::totally_ordered; using std::unsigned_integral; #else // not ETL_USING_STL @@ -118,6 +134,112 @@ namespace etl { lhs = etl::forward(rhs) } -> etl::same_as; }; + //*************************************************************************** + template + concept invocable = etl::is_invocable_v; + + //*************************************************************************** + template + concept regular_invocable = etl::invocable; + + //*************************************************************************** + template + concept destructible = requires(T& t) { + { t.~T() } noexcept; + }; + + //*************************************************************************** + template + concept constructible_from = etl::destructible && etl::is_constructible_v; + + //*************************************************************************** + template + concept default_initializable = etl::constructible_from && requires { + T{}; + ::new T; + }; + + //*************************************************************************** + template + concept move_constructible = etl::constructible_from && etl::convertible_to; + + //*************************************************************************** + template + concept copy_constructible = + etl::move_constructible && etl::constructible_from && etl::convertible_to && etl::constructible_from + && etl::convertible_to && etl::constructible_from && etl::convertible_to; + + //*************************************************************************** + namespace private_concepts + { + template + concept boolean_testable = etl::convertible_to && requires(T&& t) { + { !etl::forward(t) } -> etl::convertible_to; + }; + } // namespace private_concepts + + //*************************************************************************** + template + concept equality_comparable = requires(const etl::remove_reference_t& a, const etl::remove_reference_t& b) { + { a == b } -> private_concepts::boolean_testable; + { a != b } -> private_concepts::boolean_testable; + }; + + //*************************************************************************** + template + concept totally_ordered = etl::equality_comparable && requires(const etl::remove_reference_t& a, const etl::remove_reference_t& b) { + { a < b } -> private_concepts::boolean_testable; + { a > b } -> private_concepts::boolean_testable; + { a <= b } -> private_concepts::boolean_testable; + { a >= b } -> private_concepts::boolean_testable; + }; + + //*************************************************************************** + template + concept swappable = requires(T& a, T& b) { etl::swap(a, b); }; + + //*************************************************************************** + template + concept swappable_with = etl::common_reference_with&, etl::remove_reference_t&> && requires(T&& t, U&& u) { + etl::swap(etl::forward(t), etl::forward(t)); + etl::swap(etl::forward(u), etl::forward(u)); + etl::swap(etl::forward(t), etl::forward(u)); + etl::swap(etl::forward(u), etl::forward(t)); + }; + + //*************************************************************************** + template + concept movable = etl::is_object_v && etl::move_constructible && etl::assignable_from && etl::swappable; + + //*************************************************************************** + template + concept copyable = etl::copy_constructible && etl::movable && etl::assignable_from && etl::assignable_from + && etl::assignable_from; + + //*************************************************************************** + template + concept semiregular = etl::copyable && etl::default_initializable; + + //*************************************************************************** + template + concept regular = etl::semiregular && etl::equality_comparable; + + //*************************************************************************** + template + concept predicate = etl::regular_invocable && private_concepts::boolean_testable >; + + //*************************************************************************** + template + concept relation = etl::predicate && etl::predicate && etl::predicate && etl::predicate; + + //*************************************************************************** + template + concept equivalence_relation = etl::relation; + + //*************************************************************************** + template + concept strict_weak_order = etl::relation; + #endif } // namespace etl #endif diff --git a/include/etl/const_map.h b/include/etl/const_map.h index aa20fb4d..f96f616b 100644 --- a/include/etl/const_map.h +++ b/include/etl/const_map.h @@ -33,10 +33,6 @@ SOFTWARE. #include "platform.h" -#if ETL_NOT_USING_CPP11 - #error NOT SUPPORTED FOR C++03 OR BELOW -#endif - #include "algorithm.h" #include "functional.h" #include "nth_type.h" @@ -45,6 +41,8 @@ SOFTWARE. #include "private/comparator_is_transparent.h" +#if ETL_USING_CPP11 + ///\defgroup const_map const_map ///\ingroup containers @@ -528,14 +526,14 @@ namespace etl value_type element_list[Size]; }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_map(TElements...) -> const_map::first_type, typename etl::nth_type_t<0, TElements...>::second_type, sizeof...(TElements)>; -#endif + #endif //********************************************************************* /// Map type designed for constexpr. @@ -586,16 +584,16 @@ namespace etl } }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_map_ext(const etl::span&) -> const_map_ext; template const_map_ext(const TElements (&)[Size]) -> const_map_ext; -#endif + #endif //************************************************************************* /// Equality test. @@ -659,3 +657,4 @@ namespace etl } // namespace etl #endif +#endif diff --git a/include/etl/const_multimap.h b/include/etl/const_multimap.h index 6f5f8208..dea60ee1 100644 --- a/include/etl/const_multimap.h +++ b/include/etl/const_multimap.h @@ -33,10 +33,6 @@ SOFTWARE. #include "platform.h" -#if ETL_NOT_USING_CPP11 - #error NOT SUPPORTED FOR C++03 OR BELOW -#endif - #include "algorithm.h" #include "functional.h" #include "nth_type.h" @@ -45,6 +41,8 @@ SOFTWARE. #include "private/comparator_is_transparent.h" +#if ETL_USING_CPP11 + ///\defgroup const_multimap const_multimap ///\ingroup containers @@ -478,14 +476,14 @@ namespace etl value_type element_list[Size]; }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_multimap(TPairs...) -> const_multimap::first_type, typename etl::nth_type_t<0, TPairs...>::second_type, sizeof...(TPairs)>; -#endif + #endif //********************************************************************* /// Map type designed for constexpr. @@ -536,16 +534,16 @@ namespace etl } }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_multimap_ext(const etl::span&) -> const_multimap_ext; template const_multimap_ext(const TElements (&)[Size]) -> const_multimap_ext; -#endif + #endif //************************************************************************* /// Equality test. @@ -626,3 +624,4 @@ namespace etl } // namespace etl #endif +#endif diff --git a/include/etl/const_multiset.h b/include/etl/const_multiset.h index 1fb0bd9e..6184f9e7 100644 --- a/include/etl/const_multiset.h +++ b/include/etl/const_multiset.h @@ -33,10 +33,6 @@ SOFTWARE. #include "platform.h" -#if ETL_NOT_USING_CPP11 - #error NOT SUPPORTED FOR C++03 OR BELOW -#endif - #include "algorithm.h" #include "functional.h" #include "nth_type.h" @@ -45,6 +41,8 @@ SOFTWARE. #include "private/comparator_is_transparent.h" +#if ETL_USING_CPP11 + ///\defgroup const_multiset const_multiset ///\ingroup containers @@ -433,13 +431,13 @@ namespace etl value_type element_list[Size]; }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_multiset(TElements...) -> const_multiset, sizeof...(TElements)>; -#endif + #endif //********************************************************************* /// Multiset type designed for constexpr. @@ -488,16 +486,16 @@ namespace etl } }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_multiset_ext(const etl::span&) -> const_multiset_ext; template const_multiset_ext(const TElements (&)[Size]) -> const_multiset_ext; -#endif + #endif //************************************************************************* /// Equality test. @@ -572,3 +570,4 @@ namespace etl } // namespace etl #endif +#endif diff --git a/include/etl/const_set.h b/include/etl/const_set.h index e4629d71..df7f0989 100644 --- a/include/etl/const_set.h +++ b/include/etl/const_set.h @@ -33,10 +33,6 @@ SOFTWARE. #include "platform.h" -#if ETL_NOT_USING_CPP11 - #error NOT SUPPORTED FOR C++03 OR BELOW -#endif - #include "algorithm.h" #include "functional.h" #include "nth_type.h" @@ -45,6 +41,8 @@ SOFTWARE. #include "private/comparator_is_transparent.h" +#if ETL_USING_CPP11 + ///\defgroup const_set const_set ///\ingroup containers @@ -426,13 +424,13 @@ namespace etl value_type element_list[Size]; }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_set(TElements...) -> const_set, sizeof...(TElements)>; -#endif + #endif //********************************************************************* /// Map type designed for constexpr. @@ -481,16 +479,16 @@ namespace etl } }; - //************************************************************************* - /// Template deduction guides. - //************************************************************************* -#if ETL_USING_CPP17 + //************************************************************************* + /// Template deduction guides. + //************************************************************************* + #if ETL_USING_CPP17 template const_set_ext(const etl::span&) -> const_set_ext; template const_set_ext(const TElements (&)[Size]) -> const_set_ext; -#endif + #endif //************************************************************************* /// Equality test. @@ -548,3 +546,4 @@ namespace etl } // namespace etl #endif +#endif diff --git a/include/etl/container.h b/include/etl/container.h index c020dc7d..0452d2b4 100644 --- a/include/etl/container.h +++ b/include/etl/container.h @@ -34,6 +34,4 @@ SOFTWARE. #include "platform.h" #include "iterator.h" -#include - #endif diff --git a/include/etl/cyclic_value.h b/include/etl/cyclic_value.h index d1c2f47b..c33b306b 100644 --- a/include/etl/cyclic_value.h +++ b/include/etl/cyclic_value.h @@ -38,11 +38,8 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" #include "exception.h" -#include "static_assert.h" #include "type_traits.h" -#include - namespace etl { //*************************************************************************** diff --git a/include/etl/debounce.h b/include/etl/debounce.h index f554c8b9..2618d5e3 100644 --- a/include/etl/debounce.h +++ b/include/etl/debounce.h @@ -32,9 +32,6 @@ SOFTWARE. #define ETL_DEBOUNCE_INCLUDED #include "platform.h" -#include "static_assert.h" - -#include namespace etl { diff --git a/include/etl/delegate_service.h b/include/etl/delegate_service.h index 8b7ef718..6ca7b40c 100644 --- a/include/etl/delegate_service.h +++ b/include/etl/delegate_service.h @@ -34,9 +34,10 @@ SOFTWARE. #include "platform.h" #include "array.h" #include "delegate.h" -#include "nullptr.h" #include "static_assert.h" +#include + namespace etl { //*************************************************************************** diff --git a/include/etl/deque.h b/include/etl/deque.h index 888ac06a..bd9afa46 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -44,7 +44,6 @@ SOFTWARE. #include "utility.h" #include -#include #include "private/minmax_push.h" @@ -462,19 +461,6 @@ namespace etl private: - //*************************************************** - difference_type distance(difference_type firstIndex, difference_type index_) const - { - if (index_ < firstIndex) - { - return static_cast(p_deque->Buffer_Size) + index_ - firstIndex; - } - else - { - return index_ - firstIndex; - } - } - //*************************************************** iterator(difference_type index_, ideque& the_deque, pointer p_buffer_) : index(index_) @@ -619,10 +605,10 @@ namespace etl } //*************************************************** - reference operator[](size_t i) + const_reference operator[](size_t i) const { - iterator result(*this); - result += i; + const_iterator result(*this); + result += static_cast(i); return *result; } @@ -721,19 +707,6 @@ namespace etl private: - //*************************************************** - difference_type distance(difference_type firstIndex, difference_type index_) const - { - if (index_ < firstIndex) - { - return static_cast(p_deque->Buffer_Size) + index_ - firstIndex; - } - else - { - return index_ - firstIndex; - } - } - //*************************************************** const_iterator(difference_type index_, ideque& the_deque, pointer p_buffer_) : index(index_) diff --git a/include/etl/doxygen.h b/include/etl/doxygen.h index f9adce19..0251f1b6 100644 --- a/include/etl/doxygen.h +++ b/include/etl/doxygen.h @@ -27,31 +27,29 @@ SOFTWARE. ******************************************************************************/ ///\defgroup etl Embedded Template Library. -https - : // github.com/ETLCPP/etl - http - : // www.etlcpp.com +/// https://github.com/ETLCPP/etl +/// http://www.etlcpp.com - ///\defgroup containers Containers - ///\ingroup etl +///\defgroup containers Containers +///\ingroup etl - ///\defgroup utilities Utilities - /// A set of utility templates. - ///\ingroup etl +///\defgroup utilities Utilities +/// A set of utility templates. +///\ingroup etl - ///\defgroup maths Maths - /// A set of mathematical templates. - ///\ingroup etl +///\defgroup maths Maths +/// A set of mathematical templates. +///\ingroup etl - ///\defgroup patterns Patterns - /// A set of templated design patterns. - ///\ingroup etl +///\defgroup patterns Patterns +/// A set of templated design patterns. +///\ingroup etl - ///\defgroup crc CRC - /// A set of CRC calculations - ///\ingroup maths +///\defgroup crc CRC +/// A set of CRC calculations +///\ingroup maths - ///\ingroup etl - namespace etl +///\ingroup etl +namespace etl { } diff --git a/include/etl/expected.h b/include/etl/expected.h index 2bb2ca9a..655be8b1 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -206,7 +206,7 @@ namespace etl //******************************************* /// Get the error. //******************************************* - ETL_CONSTEXPR14 TError&& error() const&& ETL_NOEXCEPT + ETL_CONSTEXPR14 const TError&& error() const&& ETL_NOEXCEPT { return etl::move(error_value); } @@ -604,6 +604,38 @@ namespace etl return etl::move(etl::get(storage)); } + //******************************************* + /// Get the error or a default value. + //******************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::enable_if_t::value, error_type> error_or(G&& default_error) const& + { + if (has_value()) + { + return static_cast(etl::forward(default_error)); + } + else + { + return error(); + } + } + + //******************************************* + /// Get the error or a default value. + //******************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::enable_if_t::value, error_type> error_or(G&& default_error) && + { + if (has_value()) + { + return static_cast(etl::forward(default_error)); + } + else + { + return etl::move(error()); + } + } + //******************************************* /// Swap with another etl::expected. //******************************************* @@ -661,6 +693,22 @@ namespace etl { return etl::get(storage); } + + //******************************************* + /// Get the error or a default value. + //******************************************* + template + error_type error_or(const G& default_error) const + { + if (has_value()) + { + return static_cast(default_error); + } + else + { + return error(); + } + } #endif //******************************************* @@ -725,6 +773,39 @@ namespace etl } #endif + //******************************************* + /// Returns a pointer to the value if has_value(), otherwise returns nullptr. + /// Allows expected to be used as a range of 0 or 1 elements. + //******************************************* + ETL_NODISCARD ETL_CONSTEXPR14 value_type* begin() ETL_NOEXCEPT + { + return has_value() ? &etl::get(storage) : ETL_NULLPTR; + } + + //******************************************* + /// Returns a pointer past the value if has_value(), otherwise returns nullptr. + //******************************************* + ETL_NODISCARD ETL_CONSTEXPR14 value_type* end() ETL_NOEXCEPT + { + return has_value() ? &etl::get(storage) + 1 : ETL_NULLPTR; + } + + //******************************************* + /// Returns a const pointer to the value if has_value(), otherwise returns nullptr. + //******************************************* + ETL_NODISCARD ETL_CONSTEXPR14 const value_type* begin() const ETL_NOEXCEPT + { + return has_value() ? &etl::get(storage) : ETL_NULLPTR; + } + + //******************************************* + /// Returns a const pointer past the value if has_value(), otherwise returns nullptr. + //******************************************* + ETL_NODISCARD ETL_CONSTEXPR14 const value_type* end() const ETL_NOEXCEPT + { + return has_value() ? &etl::get(storage) + 1 : ETL_NULLPTR; + } + #if ETL_USING_CPP11 template ::type>::type> auto transform(F&& f) & -> expected @@ -827,13 +908,12 @@ namespace etl enum { - Uninitialised, Value_Type, Error_Type }; - typedef etl::variant storage_type; - storage_type storage; + typedef etl::variant storage_type; + storage_type storage; #if ETL_USING_CPP11 template < typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if::value>::type> @@ -1066,6 +1146,38 @@ namespace etl { return etl::move(etl::get(storage)); } + + //******************************************* + /// Get the error or a default value. + //******************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::enable_if_t::value, error_type> error_or(G&& default_error) const& + { + if (has_value()) + { + return static_cast(etl::forward(default_error)); + } + else + { + return error(); + } + } + + //******************************************* + /// Get the error or a default value. + //******************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 etl::enable_if_t::value, error_type> error_or(G&& default_error) && + { + if (has_value()) + { + return static_cast(etl::forward(default_error)); + } + else + { + return etl::move(error()); + } + } #else //******************************************* /// Returns the error @@ -1075,6 +1187,22 @@ namespace etl { return etl::get(storage); } + + //******************************************* + /// Get the error or a default value. + //******************************************* + template + error_type error_or(const G& default_error) const + { + if (has_value()) + { + return static_cast(default_error); + } + else + { + return error(); + } + } #endif //******************************************* @@ -1181,7 +1309,7 @@ namespace etl template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> auto transform_error(F&& f) const&& -> expected { - return transform_error_impl(etl::forward(f), *this); + return transform_error_impl(etl::forward(f), etl::move(*this)); } #endif @@ -1189,7 +1317,7 @@ namespace etl enum { - Uninitialised, + Void_Type, Error_Type }; @@ -1381,24 +1509,25 @@ namespace etl { return !(lhs == rhs); } + + //******************************************* + /// Swap etl::expected. + //******************************************* + template + ETL_CONSTEXPR14 void swap(etl::expected& lhs, etl::expected& rhs) + { + lhs.swap(rhs); + } + + //******************************************* + /// Swap etl::unexpected. + //******************************************* + template + ETL_CONSTEXPR14 void swap(etl::unexpected& lhs, etl::unexpected& rhs) + { + lhs.swap(rhs); + } + } // namespace etl -//******************************************* -/// Swap etl::expected. -//******************************************* -template -ETL_CONSTEXPR14 void swap(etl::expected& lhs, etl::expected& rhs) -{ - lhs.swap(rhs); -} - -//******************************************* -/// Swap etl::unexpected. -//******************************************* -template -ETL_CONSTEXPR14 void swap(etl::unexpected& lhs, etl::unexpected& rhs) -{ - lhs.swap(rhs); -} - #endif diff --git a/include/etl/flags.h b/include/etl/flags.h index 4fd77d89..3896458e 100644 --- a/include/etl/flags.h +++ b/include/etl/flags.h @@ -39,8 +39,6 @@ SOFTWARE. #include "type_traits.h" #include -#include -#include namespace etl { diff --git a/include/etl/flat_map.h b/include/etl/flat_map.h index 8e2cd39f..a9188642 100644 --- a/include/etl/flat_map.h +++ b/include/etl/flat_map.h @@ -100,26 +100,6 @@ namespace etl private: - //********************************************************************* - /// How to compare elements and keys. - //********************************************************************* - class compare - { - public: - - bool operator()(const value_type& element, key_type key) const - { - return comp(element.first, key); - } - - bool operator()(key_type key, const value_type& element) const - { - return comp(key, element.first); - } - - key_compare comp; - }; - public: //********************************************************************* diff --git a/include/etl/fnv_1.h b/include/etl/fnv_1.h index 0ac9c5d7..69b7c5c0 100644 --- a/include/etl/fnv_1.h +++ b/include/etl/fnv_1.h @@ -34,7 +34,6 @@ SOFTWARE. #include "platform.h" #include "frame_check_sequence.h" #include "ihash.h" -#include "static_assert.h" #include "type_traits.h" #include diff --git a/include/etl/format.h b/include/etl/format.h index 72c79be0..e2142cec 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -76,17 +76,409 @@ namespace etl } }; - template - ETL_CONSTEXPR14 bool check_f(const char* fmt) + #if ETL_USING_CPP20 + namespace private_format_check { - // to be implemented later - // return fmt[0] == 0; // actual check + // Type category for compile-time type/specifier compatibility checking + enum class type_category + { + NONE, // monostate + BOOLEAN, // bool + CHAR, // char + INTEGER, // int, unsigned, long long, unsigned long long, short, etc. + FLOAT, // float, double, long double + STRING, // const char*, string_view + POINTER // const void* + }; + + // Map a type to its category. Decays and removes cv-qualifiers. + template + constexpr type_category get_type_category() + { + using U = typename etl::remove_cv::type>::type; + + // Order matters: bool before integral, char before integral + if (etl::is_same::value) + return type_category::BOOLEAN; + if (etl::is_same::value) + return type_category::CHAR; + if (etl::is_same::value) + return type_category::CHAR; + if (etl::is_same::value) + return type_category::CHAR; + if (etl::is_integral::value) + return type_category::INTEGER; + if (etl::is_same::value) + return type_category::FLOAT; + if (etl::is_same::value) + return type_category::FLOAT; + if (etl::is_same::value) + return type_category::FLOAT; + if (etl::is_same::value) + return type_category::STRING; + if (etl::is_same::value) + return type_category::STRING; + if (etl::is_same::value) + return type_category::STRING; + if (etl::is_base_of::value) + return type_category::STRING; + if (etl::is_pointer::value) + return type_category::POINTER; + if (etl::is_same::value) + return type_category::POINTER; + if (etl::is_same::value) + return type_category::POINTER; + return type_category::NONE; // unknown type: custom formatter, be permissive + } + + // Check if a format type character is valid for a given type category. + // '\0' means no explicit type was specified (always valid — uses default presentation). + inline constexpr bool ct_check_type_spec(type_category cat, char type_char) + { + if (type_char == '\0') + { + return true; // no explicit type: always OK, uses default + } + + switch (cat) + { + case type_category::BOOLEAN: + // bool: s (as "true"/"false"), b, B, c, d, o, x, X (as integer 0/1) + return type_char == 's' || type_char == 'b' || type_char == 'B' || type_char == 'c' || type_char == 'd' || type_char == 'o' + || type_char == 'x' || type_char == 'X'; + + case type_category::CHAR: + // char: c (default), b, B, d, o, x, X (as integer), s, ? + return type_char == 'c' || type_char == '?' || type_char == 'b' || type_char == 'B' || type_char == 'd' || type_char == 'o' + || type_char == 'x' || type_char == 'X' || type_char == 's'; + + case type_category::INTEGER: + // integers: b, B, c, d, o, x, X + return type_char == 'b' || type_char == 'B' || type_char == 'c' || type_char == 'd' || type_char == 'o' || type_char == 'x' + || type_char == 'X'; + + case type_category::FLOAT: + // floats: a, A, e, E, f, F, g, G + return type_char == 'a' || type_char == 'A' || type_char == 'e' || type_char == 'E' || type_char == 'f' || type_char == 'F' + || type_char == 'g' || type_char == 'G'; + + case type_category::STRING: + // strings: s, ? + return type_char == 's' || type_char == '?'; + + case type_category::POINTER: + // pointers: p, P + return type_char == 'p' || type_char == 'P'; + + case type_category::NONE: + default: return true; // unknown/custom type: be permissive, let runtime handle it + } + } + + inline constexpr bool ct_is_digit(char c) + { + return c >= '0' && c <= '9'; + } + + // Parse an unsigned integer from fmt starting at pos. Updates pos past the digits. + // Returns the parsed number, or -1 if no digits found, or -2 on overflow. + inline constexpr int ct_parse_num(const char* fmt, int& pos) + { + if (!ct_is_digit(fmt[pos])) + { + return -1; + } + int result = 0; + while (ct_is_digit(fmt[pos])) + { + int new_result = result * 10 + (fmt[pos] - '0'); + if (new_result < result) + { + // Overflow detected + return -2; + } + result = new_result; + ++pos; + } + return result; + } + + inline constexpr bool ct_is_align(char c) + { + return c == '<' || c == '>' || c == '^'; + } + + inline constexpr bool ct_is_sign(char c) + { + return c == '+' || c == '-' || c == ' '; + } + + inline constexpr bool ct_is_type(char c) + { + // All valid type characters from the format spec + return (c == 's') || (c == '?') || (c == 'b') || (c == 'B') || (c == 'c') || (c == 'd') || (c == 'o') || (c == 'x') || (c == 'X') || (c == 'a') + || (c == 'A') || (c == 'e') || (c == 'E') || (c == 'f') || (c == 'F') || (c == 'g') || (c == 'G') || (c == 'p') || (c == 'P'); + } + + // Validate a nested replacement field like {}, {0}, {1} inside width/precision. + // pos should be at the '{'. Updates pos past the closing '}'. + // Updates auto_count / has_manual / has_auto. Returns false on error. + inline constexpr bool ct_parse_nested_replacement(const char* fmt, int& pos, int n_args, int& auto_count, bool& has_auto, bool& has_manual) + { + if (fmt[pos] != '{') + return false; + ++pos; // skip '{' + + int num = ct_parse_num(fmt, pos); + if (num == -2) + return false; // overflow + if (num >= 0) + { + // manual index + if (has_auto) + return false; // mixing + has_manual = true; + if (num >= n_args) + return false; + } + else + { + // automatic index + if (has_manual) + return false; // mixing + has_auto = true; + if (auto_count >= n_args) + return false; + ++auto_count; + } + + if (fmt[pos] != '}') + return false; + ++pos; // skip '}' + return true; + } + + // Skip/validate the format-spec portion after the colon: + // [[fill]align][sign][#][0][width][.precision][L][type] + // pos is right after ':'. Returns false on invalid spec. + // parsed_type is set to the type character found, or '\0' if none. + inline constexpr bool ct_skip_format_spec(const char* fmt, int& pos, int n_args, int& auto_count, bool& has_auto, bool& has_manual, + char& parsed_type, bool& parsed_has_precision) + { + parsed_type = '\0'; + parsed_has_precision = false; + + if (fmt[pos] == '\0' || fmt[pos] == '}') + { + return true; // empty spec is valid + } + + // fill-and-align: either [align] or [fill][align] + // Look ahead: if second char is an align char, first is fill + if (fmt[pos + 1] != '\0' && ct_is_align(fmt[pos + 1])) + { + char fill = fmt[pos]; + if (fill == '{' || fill == '}') + return false; // { and } not allowed as fill + pos += 2; // skip fill + align + } + else if (ct_is_align(fmt[pos])) + { + ++pos; // skip align only + } + + // sign + if (ct_is_sign(fmt[pos])) + { + ++pos; + } + + // '#' + if (fmt[pos] == '#') + { + ++pos; + } + + // '0' + if (fmt[pos] == '0') + { + ++pos; + } + + // width: number or nested replacement + if (ct_is_digit(fmt[pos])) + { + if (ct_parse_num(fmt, pos) == -2) + return false; // overflow + } + else if (fmt[pos] == '{') + { + if (!ct_parse_nested_replacement(fmt, pos, n_args, auto_count, has_auto, has_manual)) + { + return false; + } + } + + // precision: '.' followed by number or nested replacement + bool has_precision = false; + if (fmt[pos] == '.') + { + has_precision = true; + ++pos; + if (ct_is_digit(fmt[pos])) + { + if (ct_parse_num(fmt, pos) == -2) + return false; // overflow + } + else if (fmt[pos] == '{') + { + if (!ct_parse_nested_replacement(fmt, pos, n_args, auto_count, has_auto, has_manual)) + { + return false; + } + } + // else: '.' with no precision number/replacement — still valid (empty precision) + } + + // locale-specific: 'L' + if (fmt[pos] == 'L') + { + ++pos; + } + + // type + if (ct_is_type(fmt[pos])) + { + parsed_type = fmt[pos]; + ++pos; + } + + // After parsing the spec, we must be at '}' (the closing brace is consumed by the caller) + // Any remaining characters before '}' means invalid spec + if (fmt[pos] != '}') + { + return false; + } + + parsed_has_precision = has_precision; + + return true; + } + } // namespace private_format_check + + template + constexpr bool check_format(const char* fmt) + { + const int n_args = static_cast(sizeof...(Args)); + int pos = 0; + int auto_count = 0; + bool has_auto = false; + bool has_manual = false; + + // Build a constexpr array mapping arg index -> type category + const private_format_check::type_category arg_categories[] = { + private_format_check::get_type_category()..., + private_format_check::type_category::NONE // sentinel for zero-arg case + }; + + while (fmt[pos] != '\0') + { + char c = fmt[pos]; + ++pos; + + if (c == '{') + { + if (fmt[pos] == '{') + { + // escaped '{' + ++pos; + continue; + } + + // Start of a replacement field: [arg_id][:format_spec] + int resolved_index = -1; + int arg_index = private_format_check::ct_parse_num(fmt, pos); + if (arg_index == -2) + return false; // overflow in arg index + if (arg_index >= 0) + { + // manual index + if (has_auto) + return false; // mixing auto and manual + has_manual = true; + if (arg_index >= n_args) + return false; // index out of range + resolved_index = arg_index; + } + else + { + // automatic index + if (has_manual) + return false; // mixing auto and manual + has_auto = true; + if (auto_count >= n_args) + return false; // too many arguments + resolved_index = auto_count; + ++auto_count; + } + + char type_char = '\0'; + bool has_precision = false; + if (fmt[pos] == ':') + { + ++pos; // skip ':' + if (!private_format_check::ct_skip_format_spec(fmt, pos, n_args, auto_count, has_auto, has_manual, type_char, has_precision)) + { + return false; + } + } + + // Validate type specifier against argument type + if (resolved_index >= 0 && resolved_index < n_args) + { + if (!private_format_check::ct_check_type_spec(arg_categories[resolved_index], type_char)) + { + return false; + } + + // Precision is not allowed for integer, boolean, or pointer types + if (has_precision) + { + auto cat = arg_categories[resolved_index]; + if (cat == private_format_check::type_category::INTEGER || cat == private_format_check::type_category::BOOLEAN + || cat == private_format_check::type_category::POINTER) + { + return false; + } + // Precision is also invalid for char when presented as char (not as integer) + if (cat == private_format_check::type_category::CHAR && (type_char == '\0' || type_char == 'c' || type_char == '?')) + { + return false; + } + } + } + + if (fmt[pos] != '}') + { + return false; // missing closing brace + } + ++pos; // skip '}' + } + else if (c == '}') + { + if (fmt[pos] != '}') + { + return false; // unmatched '}' + } + ++pos; // skip second '}' + } + } - (void)fmt; return true; } - inline void please_note_this_is_error_message_1() noexcept {} + inline void please_note_this_is_error_message_format_string_syntax_error() noexcept {} + #endif // ETL_USING_CPP20 template struct basic_format_string @@ -94,20 +486,15 @@ namespace etl inline ETL_CONSTEVAL basic_format_string(const char* fmt) : _sv(fmt) { - bool format_string_ok = check_f(fmt); - - if (!format_string_ok) + #if ETL_USING_CPP20 + // Compile-time validation: check_format runs at compile time via consteval. + // In pre-C++20, runtime checks in vformat_to/parse_format_spec/etc. are sufficient. + if (!check_format(fmt)) { - // if (etl::is_constant_evaluated()) // compile time error path - //{ - // // calling a non-constexpr function in a consteval context to - // trigger a compile error please_note_this_is_error_message_1(); - // } - // else // run time error path - //{ - ETL_ASSERT_FAIL_AND_RETURN(ETL_ERROR(bad_format_string_exception)); - //} + // Calling a non-constexpr function in a consteval context triggers a compile error. + please_note_this_is_error_message_format_string_syntax_error(); } + #endif } ETL_CONSTEXPR basic_format_string(const basic_format_string& other) = default; @@ -181,7 +568,6 @@ namespace etl // automatic number generation only allowed if not already in manual mode ETL_ASSERT(manual_mode == false, ETL_ERROR(bad_format_string_exception)); automatic_mode = true; - // TODO: compile time check ETL_ASSERT(current < num_args, ETL_ERROR(bad_format_string_exception) /* not enough arguments for generated index */); return current++; } @@ -598,18 +984,6 @@ namespace etl return false; } - inline bool parse_sequence(format_parse_context& parse_ctx, etl::string_view sequence) - { - auto fmt_it = parse_ctx.begin(); - if (etl::equal(sequence.cbegin(), sequence.cend(), fmt_it)) - { - fmt_it += sequence.size(); - parse_ctx.advance_to(fmt_it); - return true; - } - return false; - } - inline bool is_align_character(char c) { return c == '<' || c == '>' || c == '^'; @@ -730,7 +1104,20 @@ namespace etl format_spec = format_spec_t(); // reset format_spec to defaults - format_spec.index = parse_num(parse_ctx); // optional + format_spec.index = parse_num(parse_ctx); // optional explicit index + + // Consume the value's auto-index before parsing the format spec body, + // so that nested replacement fields for width/precision get correct + // auto-indices. Per C++ standard, in {:{}}, the value arg is consumed + // first (arg 0), then the width arg (arg 1). + if (!format_spec.index.has_value()) + { + format_spec.index = parse_ctx.next_arg_id(); + } + else + { + parse_ctx.check_arg_id(*format_spec.index); + } bool colon = parse_char(parse_ctx, ':'); if (colon) @@ -865,7 +1252,7 @@ namespace etl } template - void format_alternate_form(OutputIt& it, const format_spec_t& spec) + void format_alternate_form(OutputIt& it, T value, const format_spec_t& spec) { if (spec.hash && spec.type.has_value()) { @@ -873,7 +1260,13 @@ namespace etl { case 'b': format_sequence(it, "0b"); break; case 'B': format_sequence(it, "0B"); break; - case 'o': format_sequence(it, "0"); break; + case 'o': + // Per C++ standard, # for octal adds leading 0 only if not already present + if (value != 0) + { + format_sequence(it, "0"); + } + break; case 'x': format_sequence(it, "0x"); break; case 'X': format_sequence(it, "0X"); @@ -1027,34 +1420,109 @@ namespace etl { size_t width = 0; format_sign(it, value, spec); - format_alternate_form(it, spec); + format_alternate_form(it, value, spec); adjust_width_from_spec(spec, width); check_precision(spec); format_plain_num(it, value, spec, width); } #if ETL_USING_FORMAT_FLOATING_POINT + #if ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH + //*********************************** + // Math function wrappers to handle toolchains that don't provide + // long double math functions (log10l, floorl, powl, modfl, roundl). + // When ETL_FORMAT_NO_LONG_DOUBLE_MATH is defined, long double overloads + // cast through double. For float and double, the standard functions are + // called directly via the template versions. + //*********************************** + inline long double format_log10(long double value) + { + return static_cast(::log10(static_cast(value))); + } + inline long double format_floor(long double value) + { + return static_cast(::floor(static_cast(value))); + } + inline long double format_pow(long double base, long double exp) + { + return static_cast(::pow(static_cast(base), static_cast(exp))); + } + inline long double format_round(long double value) + { + return static_cast(::round(static_cast(value))); + } + inline long double format_modf(long double value, long double* iptr) + { + double d_iptr; + double result = ::modf(static_cast(value), &d_iptr); + *iptr = static_cast(d_iptr); + return static_cast(result); + } + #endif + + template + T format_log10(T value) + { + return ::log10(value); + } + template + T format_floor(T value) + { + return ::floor(value); + } + template + T format_pow(T base, T exp) + { + return ::pow(base, exp); + } + template + T format_round(T value) + { + return ::round(value); + } + template + T format_modf(T value, T* iptr) + { + return ::modf(value, iptr); + } + template void format_floating_default(OutputIt& it, T value, const format_spec_t& spec) { const size_t fractional_decimals = 6; // default - T integral; - T fractional = modf(value, &integral); - bool sign; - unsigned long long int fractional_int; - unsigned long long int integral_int; - if (integral < 0.0) + // Detect sign using signbit to correctly handle -0.0 + bool sign = signbit(value); + T abs_value = sign ? -value : value; + + // Use scientific notation for values that would overflow unsigned long long + // or that are too small for meaningful fixed-point digits + if (abs_value >= static_cast(1e18) || (abs_value > static_cast(0) && abs_value < static_cast(1e-6))) { - sign = true; - fractional_int = static_cast(-fractional * pow(10., fractional_decimals)); - integral_int = static_cast(-integral); + format_spec_t spec_e = spec; + spec_e.type = 'e'; + format_floating_e(it, value, spec_e); + return; } - else + + T integral; + T fractional = format_modf(value, &integral); + + // Take absolute values to avoid casting negative values to unsigned + if (sign) { - sign = false; - fractional_int = static_cast(fractional * pow(10., fractional_decimals)); - integral_int = static_cast(integral); + fractional = -fractional; + integral = -integral; + } + + unsigned long long int scale = int_pow(10, fractional_decimals); + unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int integral_int = static_cast(integral); + + if (fractional_int == scale) + { + fractional_int = 0; + ++integral_int; } private_format::format_sign(it, sign ? -1 : 0, spec); @@ -1071,38 +1539,41 @@ namespace etl static const size_t exponent_decimals = 1; long long int exponent_int = 0; - bool sign; - unsigned long long int fractional_int; - unsigned long long int integral_int; + // Detect sign using signbit to correctly handle -0.0 + bool sign = signbit(value); T integral; - T fractional = modf(value, &integral); + T fractional = format_modf(value, &integral); while (value >= 0x10 || value <= -0x10) { ++exponent_int; value /= 0x10; - fractional = modf(value, &integral); + fractional = format_modf(value, &integral); } while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1)) { --exponent_int; value *= 0x10; - fractional = modf(value, &integral); + fractional = format_modf(value, &integral); } - if (integral < 0.0) + // Take absolute values to avoid casting negative values to unsigned + if (sign) { - sign = true; - fractional_int = static_cast(-fractional * pow(static_cast(0x10), fractional_decimals)); - integral_int = static_cast(-integral); + fractional = -fractional; + integral = -integral; } - else + + unsigned long long int scale = int_pow(0x10, fractional_decimals); + unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int integral_int = static_cast(integral); + + if (fractional_int == scale) { - sign = false; - fractional_int = static_cast(fractional * pow(static_cast(0x10), fractional_decimals)); - integral_int = static_cast(integral); + fractional_int = 0; + ++integral_int; } private_format::format_sign(it, sign ? -1 : 0, spec); @@ -1133,38 +1604,49 @@ namespace etl static const size_t exponent_decimals = 2; long long int exponent_int = 0; - bool sign; - unsigned long long int fractional_int; - unsigned long long int integral_int; + // Detect sign using signbit to correctly handle -0.0 + bool sign = signbit(value); - T integral; - T fractional = modf(value, &integral); + T abs_value = sign ? -value : value; - while (value >= 10 || value <= -10) + if (abs_value > static_cast(0)) { - ++exponent_int; - value /= 10; - fractional = modf(value, &integral); - } - - while ((value > 0.0000000000001 && value < 1) || (value < -0.0000000000001 && value > -1)) - { - --exponent_int; - value *= 10; - fractional = modf(value, &integral); - } - - if (integral < 0.0) - { - sign = true; - fractional_int = static_cast(-fractional * pow(10., fractional_decimals)); - integral_int = static_cast(-integral); + exponent_int = static_cast(format_floor(format_log10(abs_value))); + value = abs_value / format_pow(static_cast(10), static_cast(exponent_int)); + // Correct for floating-point rounding in log10/pow + if (value >= static_cast(10)) + { + value /= static_cast(10); + ++exponent_int; + } + else if (value < static_cast(1)) + { + value *= static_cast(10); + --exponent_int; + } } else { - sign = false; - fractional_int = static_cast(fractional * pow(10., fractional_decimals)); - integral_int = static_cast(integral); + value = static_cast(0); + } + + T integral; + T fractional = format_modf(value, &integral); + + unsigned long long int scale = int_pow(10, fractional_decimals); + unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int integral_int = static_cast(integral); + + if (fractional_int == scale) + { + fractional_int = 0; + ++integral_int; + + if (integral_int == 10) + { + integral_int = 1; + ++exponent_int; + } } private_format::format_sign(it, sign ? -1 : 0, spec); @@ -1186,22 +1668,27 @@ namespace etl { const size_t fractional_decimals = 6; // default - T integral; - T fractional = modf(value, &integral); - bool sign; - unsigned long long int fractional_int; - unsigned long long int integral_int; - if (integral < 0.0) + // Detect sign using signbit to correctly handle -0.0 + bool sign = signbit(value); + + T integral; + T fractional = format_modf(value, &integral); + + // Take absolute values to avoid casting negative values to unsigned + if (sign) { - sign = true; - fractional_int = static_cast(-fractional * pow(10., fractional_decimals)); - integral_int = static_cast(-integral); + fractional = -fractional; + integral = -integral; } - else + + unsigned long long int scale = int_pow(10, fractional_decimals); + unsigned long long int fractional_int = static_cast(format_round(fractional * scale)); + unsigned long long int integral_int = static_cast(integral); + + if (fractional_int == scale) { - sign = false; - fractional_int = static_cast(fractional * pow(10., fractional_decimals)); - integral_int = static_cast(integral); + fractional_int = 0; + ++integral_int; } private_format::format_sign(it, sign ? -1 : 0, spec); @@ -1443,6 +1930,98 @@ namespace etl fmt_context.advance_to(tmp); } + // Visitor to extract an integer value as size_t from a format arg (for nested replacement fields) + struct size_t_extractor + { + size_t value; + + size_t_extractor() + : value(0) + { + } + + void operator()(int v) + { + value = static_cast(v); + } + void operator()(unsigned int v) + { + value = static_cast(v); + } + void operator()(long long int v) + { + value = static_cast(v); + } + void operator()(unsigned long long int v) + { + value = static_cast(v); + } + + // All other types are invalid for width/precision - ignore + template + void operator()(T) + { + } + }; + + // Resolve nested replacement fields for width and precision in the format spec. + // When width_nested_replacement or precision_nested_replacement is true, the + // width/precision value holds the arg index, which must be resolved to the actual value. + template + void resolve_nested_replacements(format_spec_t& spec, format_args& args) + { + if (spec.width_nested_replacement && spec.width.has_value()) + { + format_arg width_arg = args.get(spec.width.value()); + size_t_extractor ext; + width_arg.template visit(ext); + spec.width = ext.value; + spec.width_nested_replacement = false; + } + if (spec.precision_nested_replacement && spec.precision.has_value()) + { + format_arg prec_arg = args.get(spec.precision.value()); + size_t_extractor ext; + prec_arg.template visit(ext); + spec.precision = ext.value; + spec.precision_nested_replacement = false; + } + } + + // Compute prefix/suffix padding sizes for alignment. + // default_align_start: if true, NONE defaults to left-align (START); otherwise right-align (END). + inline void compute_padding(size_t pad, spec_align_t align, bool default_align_start, size_t& prefix_size, size_t& suffix_size) + { + switch (align) + { + case spec_align_t::START: + prefix_size = 0; + suffix_size = pad; + break; + case spec_align_t::CENTER: + prefix_size = pad / 2; + suffix_size = pad - prefix_size; + break; + case spec_align_t::END: + prefix_size = pad; + suffix_size = 0; + break; + case spec_align_t::NONE: + default: + if (default_align_start) + { + prefix_size = 0; + suffix_size = pad; + } + else + { + prefix_size = pad; + suffix_size = 0; + } + break; + } + } + template typename format_context::iterator format_aligned_int(Int arg, format_context& fmt_ctx) { @@ -1451,32 +2030,13 @@ namespace etl if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_num(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } + compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size); } } @@ -1495,42 +2055,76 @@ namespace etl size_t prefix_size = 0; size_t suffix_size = 0; + // For zero-padding ({:0Nf}), use '0' as fill and right-align (padding after sign) + char_type fill_char = fmt_ctx.format_spec.fill; + if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE) + { + fill_char = '0'; + } + if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_floating(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) + if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE) { - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); + // Zero-padding: all padding goes between sign and digits + prefix_size = pad; + } + else + { + compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size); } } } // actual output OutputIt it = fmt_ctx.out(); - private_format::fill(it, prefix_size, fmt_ctx.format_spec.fill); - private_format::format_floating(it, arg, fmt_ctx.format_spec); - private_format::fill(it, suffix_size, fmt_ctx.format_spec.fill); + + if (fmt_ctx.format_spec.zero && fmt_ctx.format_spec.align == spec_align_t::NONE) + { + // Output sign first, then zero-fill, then the unsigned part + bool sign = signbit(arg); + if (sign || fmt_ctx.format_spec.sign != spec_sign_t::MINUS) + { + // Output the sign character + char_type sc = '\0'; + if (sign) + { + sc = '-'; + } + else + { + switch (fmt_ctx.format_spec.sign) + { + case spec_sign_t::PLUS: sc = '+'; break; + case spec_sign_t::SPACE: sc = ' '; break; + default: break; + } + } + if (sc != '\0') + { + *it = sc; + ++it; + } + } + private_format::fill(it, prefix_size, '0'); + // Format without sign (sign already emitted) + format_spec_t no_sign_spec = fmt_ctx.format_spec; + no_sign_spec.sign = spec_sign_t::MINUS; + Float abs_arg = sign ? -arg : arg; + private_format::format_floating(it, abs_arg, no_sign_spec); + } + else + { + private_format::fill(it, prefix_size, fill_char); + private_format::format_floating(it, arg, fmt_ctx.format_spec); + private_format::fill(it, suffix_size, fill_char); + } return it; } #endif @@ -1593,32 +2187,13 @@ namespace etl if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_string_view(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } + compute_padding(pad, fmt_ctx.format_spec.align, true, prefix_size, suffix_size); } } @@ -1630,99 +2205,10 @@ namespace etl return it; } - template - void format_chars(OutputIt& it, const char* arg, const format_spec_t& spec) - { - bool escaped = false; - if (spec.type.has_value()) - { - switch (spec.type.value()) - { - case 's': - // default output - break; - case '?': - // escaped string - escaped = true; - break; - default: - // invalid type for string - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } - } - size_t limit = etl::numeric_limits::max(); - if (spec.precision.has_value()) - { - limit = spec.precision.value(); - } - - if (escaped) - { - format_plain_char(it, '"'); - } - const char_type* arg_it = arg; - while (*arg_it != '\0' && limit > 0) - { - if (escaped) - { - format_escaped_char(it, *arg_it); - } - else - { - format_plain_char(it, *arg_it); - } - ++arg_it; - --limit; - } - if (escaped) - { - format_plain_char(it, '"'); - } - } - template typename format_context::iterator format_aligned_chars(const char* arg, format_context& fmt_ctx) { - size_t prefix_size = 0; - size_t suffix_size = 0; - - if (fmt_ctx.format_spec.width) - { - // calculate size - private_format::counter_iterator counter; - private_format::format_chars(counter, arg, fmt_ctx.format_spec); - - if (counter.value() < fmt_ctx.format_spec.width.value()) - { - size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } - } - } - - // actual output - OutputIt it = fmt_ctx.out(); - private_format::fill(it, prefix_size, fmt_ctx.format_spec.fill); - private_format::format_chars(it, arg, fmt_ctx.format_spec); - private_format::fill(it, suffix_size, fmt_ctx.format_spec.fill); - return it; + return format_aligned_string_view(etl::string_view(arg), fmt_ctx); } inline void check_char_spec(const format_spec_t& spec) @@ -1777,43 +2263,16 @@ namespace etl if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_char(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::NONE: // default - if (!fmt_ctx.format_spec.type.has_value() || fmt_ctx.format_spec.type.value() == 'c' || fmt_ctx.format_spec.type.value() == '?') - { - prefix_size = 0; - suffix_size = pad; - } - else - { - prefix_size = pad; - suffix_size = 0; - } - break; - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } + // char type defaults to left-align, integer presentation defaults to right-align + bool default_start = + !fmt_ctx.format_spec.type.has_value() || fmt_ctx.format_spec.type.value() == 'c' || fmt_ctx.format_spec.type.value() == '?'; + compute_padding(pad, fmt_ctx.format_spec.align, default_start, prefix_size, suffix_size); } } @@ -1861,32 +2320,13 @@ namespace etl if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_bool(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } + compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size); } } @@ -1930,32 +2370,13 @@ namespace etl if (fmt_ctx.format_spec.width) { - // calculate size private_format::counter_iterator counter; private_format::format_pointer(counter, arg, fmt_ctx.format_spec); if (counter.value() < fmt_ctx.format_spec.width.value()) { size_t pad = fmt_ctx.format_spec.width.value() - counter.value(); - switch (fmt_ctx.format_spec.align) - { - case private_format::spec_align_t::START: - prefix_size = 0; - suffix_size = pad; - break; - case private_format::spec_align_t::CENTER: - prefix_size = pad / 2; - suffix_size = pad - prefix_size; - break; - case private_format::spec_align_t::NONE: // default - case private_format::spec_align_t::END: - prefix_size = pad; - suffix_size = 0; - break; - default: - // invalid alignment specification - ETL_ASSERT_FAIL(ETL_ERROR(bad_format_string_exception)); - } + compute_padding(pad, fmt_ctx.format_spec.align, false, prefix_size, suffix_size); } } @@ -2201,16 +2622,13 @@ namespace etl else { private_format::parse_format_spec(parse_context, fmt_context); - etl::optional index = fmt_context.format_spec.index; - if (index.has_value()) - { - parse_context.check_arg_id(*index); - } - else - { - index = parse_context.next_arg_id(); - } - format_arg arg = args.get(*index); + + // Resolve nested replacement fields for width/precision + private_format::resolve_nested_replacements(fmt_context.format_spec, args); + + // Value index is always resolved in parse_format_spec + size_t index = fmt_context.format_spec.index.value(); + format_arg arg = args.get(index); arg.template visit(v); ETL_ASSERT(*parse_context.begin() == '}', ETL_ERROR(bad_format_string_exception) /*"Closing brace missing"*/); diff --git a/include/etl/forward_list.h b/include/etl/forward_list.h index 4518be05..cfba7bf9 100644 --- a/include/etl/forward_list.h +++ b/include/etl/forward_list.h @@ -116,15 +116,15 @@ namespace etl }; //*************************************************************************** - /// Unsorted exception for the list. - ///\ingroup list + /// Unsorted exception for the forward_list. + ///\ingroup forward_list //*************************************************************************** class forward_list_no_pool : public forward_list_exception { public: forward_list_no_pool(string_type file_name_, numeric_type line_number_) - : forward_list_exception(ETL_ERROR_TEXT("list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_) + : forward_list_exception(ETL_ERROR_TEXT("forward_list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_) { } }; diff --git a/include/etl/fsm.h b/include/etl/fsm.h index ba25d495..f38efd36 100644 --- a/include/etl/fsm.h +++ b/include/etl/fsm.h @@ -60,7 +60,7 @@ namespace etl #endif // For internal FSM use. - typedef typename etl::larger_type::type fsm_internal_id_t; + typedef etl::larger_type::type fsm_internal_id_t; #if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template @@ -189,6 +189,7 @@ namespace etl template ETL_CONSTANT fsm_state_id_t ifsm_state_helper::Self_Transition; +#if ETL_USING_CPP11 // Compile-time: TState::ID must equal its index in the type list (0..N-1) template struct check_ids : etl::true_type @@ -200,6 +201,7 @@ namespace etl : etl::integral_constant< bool, (TState0::STATE_ID == Id) && private_fsm::check_ids::value> { }; +#endif //*************************************************************************** /// RAII detection mechanism to catch reentrant calls to methods that might diff --git a/include/etl/gamma.h b/include/etl/gamma.h index 4def080e..4ad83ffb 100644 --- a/include/etl/gamma.h +++ b/include/etl/gamma.h @@ -36,7 +36,6 @@ SOFTWARE. #include "type_traits.h" #include -#include namespace etl { diff --git a/include/etl/hfsm.h b/include/etl/hfsm.h index e9fccf93..ebab16b8 100644 --- a/include/etl/hfsm.h +++ b/include/etl/hfsm.h @@ -184,6 +184,12 @@ namespace etl etl::fsm_state_id_t next_state_id; // State which was active when the on_enter triggered a state change etl::fsm_state_id_t active_state_id; + + do_enters_result(etl::fsm_state_id_t next_state_id_, etl::fsm_state_id_t active_state_id_) + : next_state_id(next_state_id_) + , active_state_id(active_state_id_) + { + } }; //******************************************* @@ -221,7 +227,7 @@ namespace etl // changed if (next_state != ifsm_state::No_State_Change) { - return {next_state, p_target->get_state_id()}; + return do_enters_result(next_state, p_target->get_state_id()); } // Activate default child if we need to activate any initial states in an @@ -238,7 +244,7 @@ namespace etl // state changed if (next_state != ifsm_state::No_State_Change) { - return {next_state, p_target->get_state_id()}; + return do_enters_result(next_state, p_target->get_state_id()); } } @@ -247,7 +253,7 @@ namespace etl // Wrapping No_State_Change in a static_cast gets rid of the "undefined // reference" error when compiling on C++11 - return {next_state, static_cast(ifsm_state::No_State_Change)}; + return do_enters_result(next_state, static_cast(ifsm_state::No_State_Change)); } //******************************************* diff --git a/include/etl/histogram.h b/include/etl/histogram.h index 195076dd..95943b76 100644 --- a/include/etl/histogram.h +++ b/include/etl/histogram.h @@ -479,7 +479,7 @@ namespace etl //********************************* const_iterator end() const { - return accumulator.begin(); + return accumulator.end(); } //********************************* @@ -487,7 +487,7 @@ namespace etl //********************************* const_iterator cend() const { - return accumulator.cbegin(); + return accumulator.cend(); } //********************************* diff --git a/include/etl/ihash.h b/include/etl/ihash.h index 10890044..08db0a3b 100644 --- a/include/etl/ihash.h +++ b/include/etl/ihash.h @@ -36,8 +36,6 @@ SOFTWARE. #include "exception.h" #include "utility.h" -#include - ///\defgroup ihash Common data for all hash type classes. ///\ingroup hash diff --git a/include/etl/index_of_type.h b/include/etl/index_of_type.h index 53128447..2d799832 100644 --- a/include/etl/index_of_type.h +++ b/include/etl/index_of_type.h @@ -31,7 +31,8 @@ SOFTWARE. #include "platform.h" #include "integral_limits.h" -#include "static_assert.h" + +#include namespace etl { diff --git a/include/etl/integral_limits.h b/include/etl/integral_limits.h index 45d7a026..cf741e30 100644 --- a/include/etl/integral_limits.h +++ b/include/etl/integral_limits.h @@ -35,7 +35,6 @@ SOFTWARE. #include "type_traits.h" #include -#include #include "private/minmax_push.h" diff --git a/include/etl/intrusive_links.h b/include/etl/intrusive_links.h index 6355ee0a..3d23f326 100644 --- a/include/etl/intrusive_links.h +++ b/include/etl/intrusive_links.h @@ -39,7 +39,7 @@ SOFTWARE. #include "type_traits.h" #include "utility.h" -#include +#include //***************************************************************************** // Note: @@ -91,25 +91,25 @@ namespace etl }; //*********************************** - forward_link() + ETL_CONSTEXPR forward_link() : etl_next(ETL_NULLPTR) { } //*********************************** - forward_link(forward_link* p_next) + ETL_CONSTEXPR forward_link(forward_link* p_next) : etl_next(p_next) { } //*********************************** - forward_link(const forward_link& other) + ETL_CONSTEXPR forward_link(const forward_link& other) : etl_next(other.etl_next) { } //*********************************** - forward_link& operator=(const forward_link& other) + ETL_CONSTEXPR14 forward_link& operator=(const forward_link& other) { etl_next = other.etl_next; @@ -495,28 +495,28 @@ namespace etl }; //*********************************** - bidirectional_link() + ETL_CONSTEXPR bidirectional_link() : etl_previous(ETL_NULLPTR) , etl_next(ETL_NULLPTR) { } //*********************************** - bidirectional_link(bidirectional_link* p_previous, bidirectional_link* p_next) + ETL_CONSTEXPR bidirectional_link(bidirectional_link* p_previous, bidirectional_link* p_next) : etl_previous(p_previous) , etl_next(p_next) { } //*********************************** - bidirectional_link(const bidirectional_link& other) + ETL_CONSTEXPR bidirectional_link(const bidirectional_link& other) : etl_previous(other.etl_previous) , etl_next(other.etl_next) { } //*********************************** - bidirectional_link& operator=(const bidirectional_link& other) + ETL_CONSTEXPR14 bidirectional_link& operator=(const bidirectional_link& other) { etl_previous = other.etl_previous; etl_next = other.etl_next; @@ -890,7 +890,10 @@ namespace etl template typename etl::enable_if< etl::is_same >::value, void>::type link_clear_range(TLink* start) { - etl::link_clear_range(*start); + if (start != ETL_NULLPTR) + { + etl::link_clear_range(*start); + } } #if ETL_USING_CPP17 diff --git a/include/etl/invert.h b/include/etl/invert.h index fc1a4e50..fddcebf9 100644 --- a/include/etl/invert.h +++ b/include/etl/invert.h @@ -35,8 +35,6 @@ SOFTWARE. #include "functional.h" #include "limits.h" -#include - namespace etl { //*************************************************************************** diff --git a/include/etl/io_port.h b/include/etl/io_port.h index 7ddca02e..02b99c05 100644 --- a/include/etl/io_port.h +++ b/include/etl/io_port.h @@ -43,6 +43,8 @@ SOFTWARE. #include +#include "private/diagnostic_cxx_11_extensions_push.h" + namespace etl { template @@ -1375,4 +1377,6 @@ namespace etl }; } // namespace etl +#include "private/diagnostic_pop.h" + #endif diff --git a/include/etl/ipool.h b/include/etl/ipool.h index c71254bf..1fdd996a 100644 --- a/include/etl/ipool.h +++ b/include/etl/ipool.h @@ -37,9 +37,10 @@ SOFTWARE. #include "iterator.h" #include "memory.h" #include "placement_new.h" -#include "static_assert.h" #include "utility.h" +#include + #define ETL_POOL_CPP03_CODE 0 namespace etl @@ -644,7 +645,7 @@ namespace etl // in debug. #if ETL_IS_DEBUG_BUILD // Is the address on a valid object boundary? - bool is_valid_address = ((distance % Item_Size) == 0); + bool is_valid_address = ((static_cast(distance) % Item_Size) == 0); #else bool is_valid_address = true; #endif diff --git a/include/etl/iterator.h b/include/etl/iterator.h index 8a09e7c0..cda84758 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -68,9 +68,22 @@ namespace etl //*************************************************************************** // iterator_traits - // For anything not a fundamental type. - template ::value, void>::type> +#if ETL_USING_CPP11 + + // Primary template: falls through to std::iterator_traits when STL is available (C++20+), + // otherwise empty (SFINAE-safe fallback for iterators without nested typedefs). + template struct iterator_traits + #if ETL_USING_STL && ETL_USING_CPP20 + : std::iterator_traits + #endif + { + }; + + // Specialization for iterators that define the standard nested typedefs. + template + struct iterator_traits> { typedef typename TIterator::iterator_category iterator_category; typedef typename TIterator::value_type value_type; @@ -79,6 +92,14 @@ namespace etl typedef typename TIterator::reference reference; }; + #if ETL_USING_STL && ETL_USING_CPP20 + // Specialization for std::common_iterator (C++20). + template + struct iterator_traits, void> : std::iterator_traits> + { + }; + #endif + // For pointers. template struct iterator_traits @@ -101,6 +122,43 @@ namespace etl typedef const T& reference; }; +#else // C++03 + + // Primary template: unconditionally extracts nested typedefs. + template + struct iterator_traits + { + typedef typename TIterator::iterator_category iterator_category; + typedef typename TIterator::value_type value_type; + typedef typename TIterator::difference_type difference_type; + typedef typename TIterator::pointer pointer; + typedef typename TIterator::reference reference; + }; + + // For pointers. + template + struct iterator_traits + { + typedef ETL_OR_STD::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef typename etl::remove_cv::type* pointer; + typedef T& reference; + }; + + // For const pointers. + template + struct iterator_traits + { + typedef ETL_OR_STD::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef const typename etl::remove_cv::type* pointer; + typedef const T& reference; + }; + +#endif + //*************************************************************************** // advance template @@ -958,13 +1016,121 @@ namespace etl template ETL_CONSTANT bool is_random_access_iterator_concept::value; +#if ETL_USING_CPP11 + //*************************************************************************** + /// Trait to detect if a type is a container (has iterator and begin/end) + /// but is not itself an iterator. + /// Used to constrain begin()/end() free functions in C++26. + //*************************************************************************** + namespace private_iterator + { + // Check if T has iterator_category (i.e., is an iterator) + template + struct has_iterator_category : etl::false_type + { + }; + + template + struct has_iterator_category> : etl::true_type + { + }; + + // is_container: has iterator/const_iterator/begin/end but does NOT have iterator_category + template + struct is_container : etl::false_type + { + }; + + template + struct is_container< + T, etl::void_t< typename T::iterator, typename T::const_iterator, decltype(etl::declval().begin()), decltype(etl::declval().end()) >> + : etl::bool_constant::value> + { + }; + } // namespace private_iterator +#endif + #if ETL_NOT_USING_STL || ETL_CPP11_NOT_SUPPORTED + #if ETL_USING_CPP11 + + //***************************************************************************** + /// Get the 'begin' iterator. + /// Note: Contains SFINAE guard, ensuring they only participate in overload + /// resolution when TContainer actually has the corresponding member function. + /// This prevents ADL from matching these templates when std::ranges::begin + /// performs an unqualified call on etl:: iterator types. + ///\ingroup container + //***************************************************************************** + template ().begin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::iterator>::type + begin(TContainer& container) + { + return container.begin(); + } + + //***************************************************************************** + /// Get the 'begin' const_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().begin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_iterator>::type + begin(const TContainer& container) + { + return container.begin(); + } + + //***************************************************************************** + /// Get the 'begin' const_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().cbegin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_iterator>::type + cbegin(const TContainer& container) + { + return container.cbegin(); + } + + //***************************************************************************** + /// Get the 'end' iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().end())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::iterator>::type end(TContainer& container) + { + return container.end(); + } + + //***************************************************************************** + /// Get the 'end' const_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().end())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_iterator>::type + end(const TContainer& container) + { + return container.end(); + } + + //***************************************************************************** + /// Get the 'end' const_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().cend())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_iterator>::type + cend(const TContainer& container) + { + return container.cend(); + } + #else + // C++03 fallback: + // - no SFINAE guards needed since std::ranges does not exist + // - no constraint needed as C++26 ADL issue doesn't apply //***************************************************************************** /// Get the 'begin' iterator. ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::iterator begin(TContainer& container) + typename TContainer::iterator begin(TContainer& container) { return container.begin(); } @@ -974,7 +1140,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_iterator begin(const TContainer& container) + typename TContainer::const_iterator begin(const TContainer& container) { return container.begin(); } @@ -984,7 +1150,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_iterator cbegin(const TContainer& container) + typename TContainer::const_iterator cbegin(const TContainer& container) { return container.cbegin(); } @@ -994,7 +1160,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::iterator end(TContainer& container) + typename TContainer::iterator end(TContainer& container) { return container.end(); } @@ -1004,7 +1170,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_iterator end(const TContainer& container) + typename TContainer::const_iterator end(const TContainer& container) { return container.end(); } @@ -1014,10 +1180,11 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_iterator cend(const TContainer& container) + typename TContainer::const_iterator cend(const TContainer& container) { return container.cend(); } + #endif //***************************************************************************** /// Get the 'begin' pointer for an array. @@ -1081,12 +1248,83 @@ namespace etl #endif #if ETL_NOT_USING_STL || ETL_CPP14_NOT_SUPPORTED + #if ETL_USING_CPP11 + //***************************************************************************** + /// Get the 'begin' reverse_iterator for a container. + /// Note: Contains SFINAE guard (see begin/end above for rationale). + ///\ingroup container + //***************************************************************************** + template ().rbegin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::reverse_iterator>::type + rbegin(TContainer& container) + { + return container.rbegin(); + } + + //***************************************************************************** + /// Get the 'begin' reverse_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().rbegin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_reverse_iterator>::type + rbegin(const TContainer& container) + { + return container.rbegin(); + } + + //***************************************************************************** + /// Get the 'begin' reverse_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().crbegin())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_reverse_iterator>::type + crbegin(const TContainer& container) + { + return container.crbegin(); + } + + //***************************************************************************** + /// Get the 'end' reverse_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().rend())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::reverse_iterator>::type + rend(TContainer& container) + { + return container.rend(); + } + + //***************************************************************************** + /// Get the 'end' reverse_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().rend())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_reverse_iterator>::type + rend(const TContainer& container) + { + return container.rend(); + } + + //***************************************************************************** + /// Get the 'end' reverse_iterator for a container. + ///\ingroup container + //***************************************************************************** + template ().crend())> > + ETL_CONSTEXPR typename etl::enable_if::value, typename TContainer::const_reverse_iterator>::type + crend(const TContainer& container) + { + return container.crend(); + } + #else + // C++03 fallback: + // - no SFINAE guards needed since std::ranges does not exist. + // - no constraint needed as C++26 ADL issue doesn't apply //***************************************************************************** /// Get the 'begin' reverse_iterator for a container. ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::reverse_iterator rbegin(TContainer& container) + typename TContainer::reverse_iterator rbegin(TContainer& container) { return container.rbegin(); } @@ -1096,7 +1334,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_reverse_iterator rbegin(const TContainer& container) + typename TContainer::const_reverse_iterator rbegin(const TContainer& container) { return container.rbegin(); } @@ -1106,7 +1344,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_reverse_iterator crbegin(const TContainer& container) + typename TContainer::const_reverse_iterator crbegin(const TContainer& container) { return container.crbegin(); } @@ -1116,7 +1354,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::reverse_iterator rend(TContainer& container) + typename TContainer::reverse_iterator rend(TContainer& container) { return container.rend(); } @@ -1126,7 +1364,7 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_reverse_iterator rend(const TContainer& container) + typename TContainer::const_reverse_iterator rend(const TContainer& container) { return container.rend(); } @@ -1136,10 +1374,11 @@ namespace etl ///\ingroup container //***************************************************************************** template - ETL_CONSTEXPR typename TContainer::const_reverse_iterator crend(const TContainer& container) + typename TContainer::const_reverse_iterator crend(const TContainer& container) { return container.crend(); } + #endif //***************************************************************************** /// Get the 'begin' reverse_iterator for an array. diff --git a/include/etl/jenkins.h b/include/etl/jenkins.h index 682a2187..5ea72099 100644 --- a/include/etl/jenkins.h +++ b/include/etl/jenkins.h @@ -36,7 +36,6 @@ SOFTWARE. #include "frame_check_sequence.h" #include "ihash.h" #include "iterator.h" -#include "static_assert.h" #include "type_traits.h" #include diff --git a/include/etl/limiter.h b/include/etl/limiter.h index 7876582a..5b285df2 100644 --- a/include/etl/limiter.h +++ b/include/etl/limiter.h @@ -36,8 +36,6 @@ SOFTWARE. #include "functional.h" #include "type_traits.h" -#include - namespace etl { namespace private_limiter diff --git a/include/etl/limits.h b/include/etl/limits.h index 5f98fd73..c274a337 100644 --- a/include/etl/limits.h +++ b/include/etl/limits.h @@ -43,7 +43,6 @@ SOFTWARE. #include #include #include -#include #include "private/minmax_push.h" diff --git a/include/etl/manchester.h b/include/etl/manchester.h index 032d9e29..b385565d 100644 --- a/include/etl/manchester.h +++ b/include/etl/manchester.h @@ -35,6 +35,8 @@ SOFTWARE. #include "span.h" #include "static_assert.h" +#if ETL_USING_CPP11 + ///\defgroup manchester manchester /// Manchester encoding and decoding ///\ingroup utilities @@ -51,13 +53,13 @@ namespace etl struct is_encodable { static const bool value = -#if ETL_USING_8BIT_TYPES + #if ETL_USING_8BIT_TYPES etl::is_same::value || -#endif + #endif etl::is_same::value -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES || etl::is_same::value -#endif + #endif ; }; @@ -69,13 +71,13 @@ namespace etl struct is_decodable { static const bool value = -#if ETL_USING_8BIT_TYPES + #if ETL_USING_8BIT_TYPES etl::is_same::value || -#endif + #endif etl::is_same::value -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES || etl::is_same::value -#endif + #endif ; }; @@ -91,13 +93,13 @@ namespace etl ETL_STATIC_ASSERT(sizeof(T) == 0, "Manchester encoding type should be one of [uint8_t, uint16_t, uint32_t]"); }; -#if ETL_USING_8BIT_TYPES + #if ETL_USING_8BIT_TYPES template <> struct encoded { typedef uint16_t type; }; -#endif + #endif template <> struct encoded @@ -105,13 +107,13 @@ namespace etl typedef uint32_t type; }; -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES template <> struct encoded { typedef uint64_t type; }; -#endif + #endif //************************************************************************* /// Type trait to determine the decoded type for a given encoded type. @@ -125,13 +127,13 @@ namespace etl ETL_STATIC_ASSERT(sizeof(T) == 0, "Manchester decoding type should be one of [uint16_t, uint32_t, uint64_t]"); }; -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES template <> struct decoded { typedef uint8_t type; }; -#endif + #endif template <> struct decoded @@ -139,24 +141,24 @@ namespace etl typedef uint16_t type; }; -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES template <> struct decoded { typedef uint32_t type; }; -#endif + #endif //************************************************************************* /// Normal Manchester encoding type (no inversion). //************************************************************************* struct manchester_type_normal { -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES static const uint64_t inversion_mask = 0x0000000000000000ULL; -#else + #else static const uint32_t inversion_mask = 0x00000000UL; -#endif + #endif }; //************************************************************************* @@ -164,11 +166,11 @@ namespace etl //************************************************************************* struct manchester_type_inverted { -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES static const uint64_t inversion_mask = 0xFFFFFFFFFFFFFFFFULL; -#else + #else static const uint32_t inversion_mask = 0xFFFFFFFFUL; -#endif + #endif }; //************************************************************************* @@ -247,11 +249,11 @@ namespace etl ETL_STATIC_ASSERT(CHAR_BIT == etl::numeric_limits::digits, "Manchester requires uint_least8_t to have the same number of bits as CHAR (CHAR_BITS)"); - //************************************************************************* - // Encoding functions - //************************************************************************* + //************************************************************************* + // Encoding functions + //************************************************************************* -#if ETL_USING_8BIT_TYPES + #if ETL_USING_8BIT_TYPES //************************************************************************* /// Encode a 8-bit unsigned value and return 16-bit result. ///\param decoded The value to encode. @@ -273,7 +275,7 @@ namespace etl ^ (0xAAAAU ^ static_cast(TManchesterType::inversion_mask))); return encoded; } -#endif + #endif //************************************************************************* /// Encode a 16-bit unsigned value and return the 32-bit result. @@ -297,7 +299,7 @@ namespace etl return encoded; } -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES //************************************************************************* /// Encode a 32-bit unsigned value and return the 64-bit result. ///\param decoded The value to encode. @@ -320,7 +322,7 @@ namespace etl encoded = (encoded | (encoded << 1U)) ^ (0xAAAAAAAAAAAAAAAAULL ^ TManchesterType::inversion_mask); return encoded; } -#endif + #endif //************************************************************************* /// Encode a span of data with the specified chunk size. @@ -350,11 +352,11 @@ namespace etl } } - //************************************************************************* - // Decoding functions - //************************************************************************* + //************************************************************************* + // Decoding functions + //************************************************************************* -#if ETL_USING_8BIT_TYPES + #if ETL_USING_8BIT_TYPES //************************************************************************* /// Decode a 16-bit value and return the 8-bit result. ///\param encoded The value to decode. @@ -373,7 +375,7 @@ namespace etl encoded = static_cast((static_cast(encoded) | (static_cast(encoded) >> 2)) & 0x0F0FU); return static_cast(static_cast(encoded) | (static_cast(encoded) >> 4U)); } -#endif + #endif //************************************************************************* /// Decode a 32-bit value and return the 16-bit result. @@ -394,7 +396,7 @@ namespace etl return static_cast(encoded | (encoded >> 8U)); } -#if ETL_USING_64BIT_TYPES + #if ETL_USING_64BIT_TYPES //************************************************************************* /// Decode a 64-bit value and return the 32-bit result. ///\param encoded The value to decode. @@ -414,7 +416,7 @@ namespace etl encoded = (encoded | (encoded >> 8)) & 0x0000FFFF0000FFFFULL; return static_cast(encoded | (encoded >> 16U)); } -#endif + #endif //************************************************************************* /// Decode a span of data using the specified chunk type. @@ -500,3 +502,4 @@ namespace etl } // namespace etl #endif +#endif diff --git a/include/etl/math_constants.h b/include/etl/math_constants.h index d27279ed..60c50700 100644 --- a/include/etl/math_constants.h +++ b/include/etl/math_constants.h @@ -31,6 +31,8 @@ SOFTWARE. #include "platform.h" +#include "private/diagnostic_gnu_static_float_init_push.h" + namespace etl { namespace private_math_constants @@ -86,4 +88,6 @@ namespace etl }; } // namespace etl +#include "private/diagnostic_pop.h" + #endif diff --git a/include/etl/mem_cast.h b/include/etl/mem_cast.h index 987fc7ef..853aa18c 100644 --- a/include/etl/mem_cast.h +++ b/include/etl/mem_cast.h @@ -565,7 +565,7 @@ namespace etl ETL_NODISCARD size_t alignment() const { - typedef typename etl::smallest_uint_for_bits::type type; + typedef etl::smallest_uint_for_bits::type type; const type p = reinterpret_cast(pbuffer); diff --git a/include/etl/memory.h b/include/etl/memory.h index b1456298..6bd07d61 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -42,7 +42,6 @@ SOFTWARE. #include "private/addressof.h" -#include #include #if defined(ETL_IN_UNIT_TEST) || ETL_USING_STL @@ -795,12 +794,8 @@ namespace etl template TOutputIterator uninitialized_move_n(TInputIterator i_begin, TSize n, TOutputIterator o_begin) { - // Move not supported. Defer to copy. - #if ETL_USING_CPP11 - return std::uninitialized_copy_n(i_begin, n, o_begin); - #else + // Move not supported. Defer to copy. return etl::uninitialized_copy_n(i_begin, n, o_begin); - #endif } //***************************************************************************** @@ -814,12 +809,8 @@ namespace etl { count += TCounter(n); - // Move not supported. Defer to copy. - #if ETL_USING_CPP11 - return std::uninitialized_copy_n(i_begin, n, o_begin); - #else + // Move not supported. Defer to copy. return etl::uninitialized_copy_n(i_begin, n, o_begin); - #endif } #endif @@ -1798,6 +1789,117 @@ namespace etl } // namespace ranges #endif +#if ETL_USING_CPP11 + //***************************************************************************** + /// Trivially relocate a range of objects. + /// This function relocates objects by copying their bytes using memmove. + /// The source objects' lifetimes are ended without calling destructors. + /// Based on C++26 P2786R13. + /// https://en.cppreference.com/w/cpp/memory/trivially_relocate + ///\ingroup memory + //***************************************************************************** + template + typename etl::enable_if::value && !etl::is_const::value, T*>::type trivially_relocate(T* first, T* last, + T* result) + { + if (first == result) + { + return last; + } + + const size_t count = static_cast(last - first); + + if (count > 0) + { + // Use memmove to handle overlapping ranges + ::memmove(static_cast(result), static_cast(first), count * sizeof(T)); + } + + return result + count; + } + + //***************************************************************************** + /// Relocate implementation for trivially relocatable types. + /// Delegates to etl::trivially_relocate. + /// Uses SFINAE (enable_if) so that etl::trivially_relocate is never + /// instantiated for non-trivially relocatable types on pre-C++17 compilers, + /// avoiding the ill-formed instantiation that would occur with a plain + /// ETL_IF_CONSTEXPR branch. + ///\ingroup memory + //***************************************************************************** + template + typename etl::enable_if::value, T*>::type relocate_impl(T* first, T* last, T* result) + { + return etl::trivially_relocate(first, last, result); + } + + //***************************************************************************** + /// Relocate implementation for non-trivially relocatable types. + /// Uses move construction + destroy. + ///\ingroup memory + //***************************************************************************** + template + typename etl::enable_if::value, T*>::type relocate_impl(T* first, T* last, T* result) + { + const ptrdiff_t count = last - first; + + // Check if ranges overlap and handle accordingly + if (result < first || result >= last) + { + // No overlap or destination is after source - iterate forward + T* src = first; + T* dst = result; + while (src != last) + { + ::new (static_cast(dst)) T(etl::move(*src)); + src->~T(); + ++src; + ++dst; + } + } + else + { + // Destination overlaps with source from below - iterate backward + T* src = last; + T* dst = result + count; + while (src != first) + { + --src; + --dst; + ::new (static_cast(dst)) T(etl::move(*src)); + src->~T(); + } + } + + return result + count; + } + + //***************************************************************************** + /// Relocate a range of objects. + /// For trivially relocatable types, uses trivially_relocate via relocate_impl. + /// For other nothrow relocatable types, uses move + destroy via relocate_impl. + /// Delegates to SFINAE-guarded relocate_impl overloads instead of using + /// ETL_IF_CONSTEXPR, so that etl::trivially_relocate is never instantiated + /// for non-trivially relocatable types on pre-C++17 compilers. + /// Based on C++26 P2786R13. + /// https://en.cppreference.com/w/cpp/memory/relocate + ///\ingroup memory + //***************************************************************************** + template + typename etl::enable_if::value && !etl::is_const::value, T*>::type relocate(T* first, T* last, T* result) + { + // Handle trivial relocation case + if (first == result || first == last) + { + return (first == result) ? last : result; + } + + // SFINAE on etl::is_trivially_relocatable selects the correct overload + // so that etl::trivially_relocate is only instantiated when valid. + return relocate_impl(first, last, result); + } +#endif + //***************************************************************************** /// Default deleter. ///\tparam T The pointed to type type. @@ -2259,47 +2361,43 @@ namespace etl pointer p; TDeleter deleter; }; -} // namespace etl -//***************************************************************************** -// Global functions for unique_ptr -//***************************************************************************** -template -bool operator==(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) -{ - return lhs.get() == rhs.get(); -} + //***************************************************************************** + // Comparison operators for unique_ptr + //***************************************************************************** + template + bool operator==(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) + { + return lhs.get() == rhs.get(); + } -//********************************* -template -bool operator<(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) -{ - return reinterpret_cast(lhs.get()) < reinterpret_cast(rhs.get()); -} + //********************************* + template + bool operator<(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) + { + return reinterpret_cast(lhs.get()) < reinterpret_cast(rhs.get()); + } -//********************************* -template -bool operator<=(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) -{ - return !(rhs < lhs); -} + //********************************* + template + bool operator<=(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) + { + return !(rhs < lhs); + } -//********************************* -template -bool operator>(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) -{ - return (rhs < lhs); -} + //********************************* + template + bool operator>(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) + { + return (rhs < lhs); + } -//********************************* -template -bool operator>=(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) -{ - return !(lhs < rhs); -} - -namespace etl -{ + //********************************* + template + bool operator>=(const etl::unique_ptr& lhs, const etl::unique_ptr& rhs) + { + return !(lhs < rhs); + } //***************************************************************************** /// Default construct an item at address p. ///\ingroup memory @@ -2536,6 +2634,14 @@ namespace etl { *p++ = 0; } + + // Prevent the compiler from optimising away the volatile stores + // as dead stores (observed with GCC -O3 in C++23 mode). +#if defined(ETL_COMPILER_GCC) || defined(ETL_COMPILER_CLANG) + __asm__ __volatile__("" : : : "memory"); +#elif defined(ETL_COMPILER_MICROSOFT) + _ReadWriteBarrier(); +#endif } //***************************************************************************** diff --git a/include/etl/message.h b/include/etl/message.h index 4002023c..a21769c6 100644 --- a/include/etl/message.h +++ b/include/etl/message.h @@ -36,8 +36,6 @@ SOFTWARE. #include "static_assert.h" #include "type_traits.h" -#include - namespace etl { //*************************************************************************** diff --git a/include/etl/message_broker.h b/include/etl/message_broker.h index b8dc8ad8..25e54f62 100644 --- a/include/etl/message_broker.h +++ b/include/etl/message_broker.h @@ -36,8 +36,6 @@ SOFTWARE. #include "nullptr.h" #include "span.h" -#include - namespace etl { //*************************************************************************** diff --git a/include/etl/message_bus.h b/include/etl/message_bus.h index c2e7bfbc..a5310ae8 100644 --- a/include/etl/message_bus.h +++ b/include/etl/message_bus.h @@ -36,9 +36,10 @@ SOFTWARE. #include "message.h" #include "message_router.h" #include "message_types.h" -#include "nullptr.h" #include "vector.h" +#include "private/diagnostic_unnamed_type_template_args_push.h" + #include namespace etl @@ -431,4 +432,6 @@ namespace etl }; } // namespace etl +#include "private/diagnostic_pop.h" + #endif diff --git a/include/etl/message_packet.h b/include/etl/message_packet.h index d42eca61..fdaed2b3 100644 --- a/include/etl/message_packet.h +++ b/include/etl/message_packet.h @@ -39,8 +39,6 @@ SOFTWARE. #include "type_list.h" #include "utility.h" -#include - namespace etl { #if ETL_USING_CPP17 && !defined(ETL_MESSAGE_PACKET_FORCE_CPP03_IMPLEMENTATION) diff --git a/include/etl/message_router.h b/include/etl/message_router.h index 216b4710..6461da00 100644 --- a/include/etl/message_router.h +++ b/include/etl/message_router.h @@ -38,14 +38,12 @@ SOFTWARE. #include "message.h" #include "message_packet.h" #include "message_types.h" -#include "nullptr.h" -#include "placement_new.h" #include "shared_message.h" #include "successor.h" #include "type_list.h" #include "type_traits.h" -#include +#include namespace etl { @@ -77,6 +75,7 @@ namespace etl namespace private_message_router { +#if ETL_USING_CPP11 //*************************************************************************** // Traits for a message router. // message packet type @@ -86,8 +85,6 @@ namespace etl template class traits { -#if ETL_USING_CPP11 - private: using message_id_sequence = etl::index_sequence; @@ -102,7 +99,6 @@ namespace etl static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); -#endif }; //*************************************************************************** @@ -116,12 +112,19 @@ namespace etl { public: -#if ETL_USING_CPP11 using message_packet = etl::message_packet<>; using message_types = etl::type_list<>; using sorted_message_types = etl::type_list<>; -#endif }; +#else + //*************************************************************************** + // C++03 empty traits placeholder. + //*************************************************************************** + template + class traits + { + }; +#endif } // namespace private_message_router //*************************************************************************** diff --git a/include/etl/message_router_registry.h b/include/etl/message_router_registry.h index b57ac6b9..87942090 100644 --- a/include/etl/message_router_registry.h +++ b/include/etl/message_router_registry.h @@ -38,7 +38,7 @@ SOFTWARE. #include "memory.h" #include "message_router.h" -#include +#include namespace etl { diff --git a/include/etl/message_timer.h b/include/etl/message_timer.h index 378ff36f..eaf506c8 100644 --- a/include/etl/message_timer.h +++ b/include/etl/message_timer.h @@ -44,8 +44,8 @@ SOFTWARE. #include #if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL - #define ETL_DISABLE_TIMER_UPDATES - #define ETL_ENABLE_TIMER_UPDATES + #define ETL_DISABLE_TIMER_UPDATES ((void)0) + #define ETL_ENABLE_TIMER_UPDATES ((void)0) #define ETL_TIMER_UPDATES_ENABLED true #undef ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK @@ -157,7 +157,6 @@ namespace etl list(etl::message_timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -286,22 +285,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -316,16 +306,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; etl::message_timer_data* const ptimers; }; diff --git a/include/etl/message_timer_atomic.h b/include/etl/message_timer_atomic.h index 584cc491..93e328c3 100644 --- a/include/etl/message_timer_atomic.h +++ b/include/etl/message_timer_atomic.h @@ -466,7 +466,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -595,22 +594,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -625,16 +615,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/message_timer_interrupt.h b/include/etl/message_timer_interrupt.h index acb3c344..5aaeb424 100644 --- a/include/etl/message_timer_interrupt.h +++ b/include/etl/message_timer_interrupt.h @@ -472,7 +472,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -601,22 +600,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -631,16 +621,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/message_timer_locked.h b/include/etl/message_timer_locked.h index 8826ad5e..4eb689ee 100644 --- a/include/etl/message_timer_locked.h +++ b/include/etl/message_timer_locked.h @@ -480,7 +480,6 @@ namespace etl timer_list(timer_data* ptimers_) : head(etl::timer::id::NO_TIMER) , tail(etl::timer::id::NO_TIMER) - , current(etl::timer::id::NO_TIMER) , ptimers(ptimers_) { } @@ -609,22 +608,13 @@ namespace etl //******************************* etl::timer::id::type begin() { - current = head; - return current; - } - - //******************************* - etl::timer::id::type previous(etl::timer::id::type last) - { - current = ptimers[last].previous; - return current; + return head; } //******************************* etl::timer::id::type next(etl::timer::id::type last) { - current = ptimers[last].next; - return current; + return ptimers[last].next; } //******************************* @@ -639,16 +629,14 @@ namespace etl timer.next = etl::timer::id::NO_TIMER; } - head = etl::timer::id::NO_TIMER; - tail = etl::timer::id::NO_TIMER; - current = etl::timer::id::NO_TIMER; + head = etl::timer::id::NO_TIMER; + tail = etl::timer::id::NO_TIMER; } private: etl::timer::id::type head; etl::timer::id::type tail; - etl::timer::id::type current; timer_data* const ptimers; }; diff --git a/include/etl/mutex.h b/include/etl/mutex.h index ec40c7c6..fc7d2f4d 100644 --- a/include/etl/mutex.h +++ b/include/etl/mutex.h @@ -40,7 +40,7 @@ SOFTWARE. #elif defined(ETL_TARGET_OS_THREADX) #include "mutex/mutex_threadx.h" #define ETL_HAS_MUTEX 1 -#elif ETL_USING_STL && ETL_USING_CPP11 +#elif ETL_USING_STL && ETL_USING_CPP11 && ETL_USING_STD_MUTEX #include "mutex/mutex_std.h" #define ETL_HAS_MUTEX 1 #elif defined(ETL_COMPILER_ARM5) || defined(ETL_COMPILER_ARM6) || defined(ETL_COMPILER_ARM7) || defined(ETL_COMPILER_ARM8) diff --git a/include/etl/not_null.h b/include/etl/not_null.h index fc8366f3..25823d68 100644 --- a/include/etl/not_null.h +++ b/include/etl/not_null.h @@ -35,7 +35,6 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "memory.h" -#include "static_assert.h" #include "type_traits.h" namespace etl diff --git a/include/etl/numeric.h b/include/etl/numeric.h index 633755e5..7e740839 100644 --- a/include/etl/numeric.h +++ b/include/etl/numeric.h @@ -130,11 +130,11 @@ namespace etl { if (a > b) { - return b + (etl::distance(b, a) / 2U); + return b + (etl::distance(b, a) / 2); } else { - return a + (etl::distance(a, b) / 2U); + return a + (etl::distance(a, b) / 2); } } @@ -151,11 +151,11 @@ namespace etl { if (a > b) { - return b + (etl::distance(b, a) / 2U); + return b + (etl::distance(b, a) / 2); } else { - return a + (etl::distance(a, b) / 2U); + return a + (etl::distance(a, b) / 2); } } @@ -172,7 +172,7 @@ namespace etl || etl::is_same::iterator_category, ETL_OR_STD::bidirectional_iterator_tag>::value)), int>::type = 0) { - etl::advance(a, etl::distance(a, b) / 2U); + etl::advance(a, etl::distance(a, b) / 2); return a; } @@ -204,6 +204,284 @@ namespace etl return typecast_a(a) + (typecast_t(t) * (typecast_b(b) - typecast_a(a))); } + + //*************************************************************************** + /// Saturating addition for unsigned integers. + /// Returns x + y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_unsigned::value, T>::type add_sat(T x, T y) ETL_NOEXCEPT + { + T result = static_cast(x + y); + // Overflow occurred if result < x + if (result < x) + { + return etl::numeric_limits::max(); + } + return result; + } + + //*************************************************************************** + /// Saturating addition for signed integers. + /// Returns x + y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_signed::value, T>::type add_sat(T x, T y) ETL_NOEXCEPT + { + // Check for overflow: both operands have same sign and result has different sign + if (y > T(0)) + { + // Positive overflow check + if (x > (etl::numeric_limits::max() - y)) + { + return etl::numeric_limits::max(); + } + } + else + { + // Negative overflow check + if (x < (etl::numeric_limits::min() - y)) + { + return etl::numeric_limits::min(); + } + } + + return static_cast(x + y); + } + + //*************************************************************************** + /// Saturating subtraction for unsigned integers. + /// Returns x - y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_unsigned::value, T>::type sub_sat(T x, T y) ETL_NOEXCEPT + { + // Underflow occurred if y > x + if (y > x) + { + return T(0); + } + return static_cast(x - y); + } + + //*************************************************************************** + /// Saturating subtraction for signed integers. + /// Returns x - y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_signed::value, T>::type sub_sat(T x, T y) ETL_NOEXCEPT + { + // Check for overflow/underflow + if (y > T(0)) + { + // Subtracting positive: check for underflow + if (x < (etl::numeric_limits::min() + y)) + { + return etl::numeric_limits::min(); + } + } + else + { + // Subtracting negative (adding): check for overflow + if (x > (etl::numeric_limits::max() + y)) + { + return etl::numeric_limits::max(); + } + } + + return static_cast(x - y); + } + + //*************************************************************************** + /// Saturating multiplication for unsigned integers. + /// Returns x * y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_unsigned::value, T>::type mul_sat(T x, T y) ETL_NOEXCEPT + { + if ((x == T(0)) || (y == T(0))) + { + return T(0); + } + + // Check for overflow: x * y > max => x > max / y + if (x > (etl::numeric_limits::max() / y)) + { + return etl::numeric_limits::max(); + } + + return static_cast(x * y); + } + + //*************************************************************************** + /// Saturating multiplication for signed integers. + /// Returns x * y, clamped to the range of T. + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_signed::value, T>::type mul_sat(T x, T y) ETL_NOEXCEPT + { + if ((x == T(0)) || (y == T(0))) + { + return T(0); + } + + // Both positive + if ((x > T(0)) && (y > T(0))) + { + if (x > (etl::numeric_limits::max() / y)) + { + return etl::numeric_limits::max(); + } + } + // Both negative + else if ((x < T(0)) && (y < T(0))) + { + if (x < (etl::numeric_limits::max() / y)) + { + return etl::numeric_limits::max(); + } + } + // Different signs (x positive, y negative) + else if ((x > T(0)) && (y < T(0))) + { + if (y < (etl::numeric_limits::min() / x)) + { + return etl::numeric_limits::min(); + } + } + // Different signs (x negative, y positive) + else + { + if (x < (etl::numeric_limits::min() / y)) + { + return etl::numeric_limits::min(); + } + } + + return static_cast(x * y); + } + + //*************************************************************************** + /// Saturating division for unsigned integers. + /// Returns x / y. For unsigned types, no saturation is needed. + /// \pre y != 0 (undefined behaviour if y is zero, matching C++26). + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_unsigned::value, T>::type div_sat(T x, T y) ETL_NOEXCEPT + { + return static_cast(x / y); + } + + //*************************************************************************** + /// Saturating division for signed integers. + /// Returns x / y, clamped to the range of T. + /// The only case of overflow is min / -1. + /// \pre y != 0 (undefined behaviour if y is zero, matching C++26). + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value && etl::is_signed::value, T>::type div_sat(T x, T y) ETL_NOEXCEPT + { + // The only overflow case: min / -1 would be max + 1 + if ((x == etl::numeric_limits::min()) && (y == T(-1))) + { + return etl::numeric_limits::max(); + } + + return static_cast(x / y); + } + + //*************************************************************************** + /// saturate_cast + /// Converts an integer value to another integer type, clamping the value + /// to the representable range of the destination type. + /// C++26 equivalent of std::saturate_cast. + /// + /// When the source value is within the range of R, returns the value as R. + /// When the source value is below R's minimum, returns numeric_limits::min(). + /// When the source value is above R's maximum, returns numeric_limits::max(). + //*************************************************************************** + + // Case 1: Both unsigned. + template + ETL_CONSTEXPR14 + typename etl::enable_if::value && etl::is_integral::value && etl::is_unsigned::value && etl::is_unsigned::value, + R>::type + saturate_cast(T value) ETL_NOEXCEPT + { + // If sizeof(R) >= sizeof(T), all values of T fit in R. + // If sizeof(R) < sizeof(T), clamp to R's max. The comparison is safe + // because R's max fits in T when T is wider. + if ((sizeof(R) < sizeof(T)) && (value > static_cast(etl::numeric_limits::max()))) + { + return etl::numeric_limits::max(); + } + return static_cast(value); + } + + // Case 2: Both signed. + template + ETL_CONSTEXPR14 + typename etl::enable_if::value && etl::is_integral::value && etl::is_signed::value && etl::is_signed::value, R>::type + saturate_cast(T value) ETL_NOEXCEPT + { + // Only need to clamp when narrowing (R is smaller than T). + // When sizeof(R) >= sizeof(T), all values of T fit in R. + if (sizeof(R) < sizeof(T)) + { + if (value > static_cast(etl::numeric_limits::max())) + { + return etl::numeric_limits::max(); + } + if (value < static_cast(etl::numeric_limits::min())) + { + return etl::numeric_limits::min(); + } + } + return static_cast(value); + } + + // Case 3: Signed source -> Unsigned destination. + template + ETL_CONSTEXPR14 + typename etl::enable_if::value && etl::is_integral::value && etl::is_unsigned::value && etl::is_signed::value, + R>::type + saturate_cast(T value) ETL_NOEXCEPT + { + if (value < T(0)) + { + return R(0); + } + + typedef typename etl::make_unsigned::type unsigned_t; + unsigned_t uvalue = static_cast(value); + + // Compare in unsigned domain. R's max is always representable as unsigned_t + // when sizeof(T) > sizeof(R), and when sizeof(R) >= sizeof(T) all positive + // values of T fit in R. + if ((sizeof(R) < sizeof(T)) && (uvalue > static_cast(etl::numeric_limits::max()))) + { + return etl::numeric_limits::max(); + } + return static_cast(value); + } + + // Case 4: Unsigned source -> Signed destination. + template + ETL_CONSTEXPR14 + typename etl::enable_if::value && etl::is_integral::value && etl::is_signed::value && etl::is_unsigned::value, + R>::type + saturate_cast(T value) ETL_NOEXCEPT + { + // R's max is positive, so we can safely represent it as T (unsigned) when + // sizeof(T) >= sizeof(R). When sizeof(T) < sizeof(R), all values of T fit. + typedef typename etl::make_unsigned::type unsigned_r; + + if (value > static_cast(static_cast(etl::numeric_limits::max()))) + { + return etl::numeric_limits::max(); + } + return static_cast(value); + } } // namespace etl #endif diff --git a/include/etl/optional.h b/include/etl/optional.h index fc0db4f7..244aa3cb 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -127,7 +127,7 @@ namespace etl /// Constructor. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl() + optional_impl() ETL_NOEXCEPT : storage() { } @@ -136,17 +136,18 @@ namespace etl /// Constructor with nullopt. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl(etl::nullopt_t) + optional_impl(etl::nullopt_t) ETL_NOEXCEPT : storage() { } #include "private/diagnostic_uninitialized_push.h" + //*************************************************************************** /// Copy constructor. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl(const optional_impl& other) + optional_impl(const optional_impl& other) ETL_NOEXCEPT_IF(etl::is_nothrow_copy_constructible::value) { if (other.has_value()) { @@ -160,7 +161,7 @@ namespace etl /// Move constructor. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl(optional_impl&& other) + optional_impl(optional_impl&& other) ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value) { if (other.has_value()) { @@ -177,7 +178,7 @@ namespace etl typename etl::enable_if< etl::is_constructible::value && !etl::is_same::type, etl::in_place_t>::value && !etl::is_same::type, optional_impl>::value, int>::type = 0> - ETL_CONSTEXPR20_STL optional_impl(U&& value_) + ETL_CONSTEXPR20_STL optional_impl(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); } @@ -186,7 +187,7 @@ namespace etl /// Constructor from variadic args. //*************************************************************************** template - ETL_CONSTEXPR20_STL optional_impl(etl::in_place_t, TArgs&&... args) + ETL_CONSTEXPR20_STL optional_impl(etl::in_place_t, TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(args)...); } @@ -197,6 +198,7 @@ namespace etl //******************************************* template ETL_CONSTEXPR20_STL optional_impl(etl::in_place_t, std::initializer_list ilist, TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) { storage.construct(ilist, etl::forward(args)...); } @@ -207,7 +209,7 @@ namespace etl /// Destructor. //*************************************************************************** ETL_CONSTEXPR20_STL - ~optional_impl() + ~optional_impl() ETL_NOEXCEPT { storage.destroy(); } @@ -216,7 +218,7 @@ namespace etl /// Assignment operator from nullopt. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl& operator=(etl::nullopt_t) + optional_impl& operator=(etl::nullopt_t) ETL_NOEXCEPT { if (has_value()) { @@ -230,7 +232,7 @@ namespace etl /// Assignment operator from optional_impl. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl& operator=(const optional_impl& other) + optional_impl& operator=(const optional_impl& other) ETL_NOEXCEPT_IF(etl::is_nothrow_copy_constructible::value) { if (this != &other) { @@ -252,7 +254,7 @@ namespace etl /// Assignment operator from optional_impl. //*************************************************************************** ETL_CONSTEXPR20_STL - optional_impl& operator=(optional_impl&& other) + optional_impl& operator=(optional_impl&& other) ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value) { if (this != &other) { @@ -277,7 +279,7 @@ namespace etl template ::value && !etl::is_same::type, optional_impl>::value, int>::type = 0> - ETL_CONSTEXPR20_STL optional_impl& operator=(U&& value_) + ETL_CONSTEXPR20_STL optional_impl& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(value_)); @@ -477,18 +479,18 @@ namespace etl /// Swaps this value with another. //*************************************************************************** ETL_CONSTEXPR20_STL - void swap(optional_impl& other) + void swap(optional_impl& other) ETL_NOEXCEPT_IF(etl::is_nothrow_move_constructible::value) { - optional_impl temp(*this); - *this = other; - other = temp; + optional_impl temp(ETL_MOVE(*this)); + *this = ETL_MOVE(other); + other = ETL_MOVE(temp); } //*************************************************************************** /// Reset back to invalid. //*************************************************************************** ETL_CONSTEXPR20_STL - void reset() + void reset() ETL_NOEXCEPT { storage.destroy(); } @@ -497,7 +499,7 @@ namespace etl /// //************************************************************************* ETL_CONSTEXPR20_STL - T& emplace(const optional_impl& other) + T& emplace(const optional_impl& other) { #if ETL_IS_DEBUG_BUILD ETL_ASSERT(other.has_value(), ETL_ERROR(optional_invalid)); @@ -518,7 +520,7 @@ namespace etl template < typename U, typename... URest, typename etl::enable_if< !etl::is_base_of< optional_impl, typename etl::remove_cv< typename etl::remove_reference::type>::type>::value, int>::type = 0> - ETL_CONSTEXPR20_STL T& emplace(U&& first, URest&&... rest) + ETL_CONSTEXPR20_STL T& emplace(U&& first, URest&&... rest) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { storage.construct(etl::forward(first), etl::forward(rest)...); @@ -529,7 +531,7 @@ namespace etl /// Emplaces with zero arguments, i.e. default construct emplace. //************************************************************************* ETL_CONSTEXPR20_STL - T& emplace() + T& emplace() ETL_NOEXCEPT_IF(etl::is_nothrow_default_constructible::value) { storage.construct(); @@ -735,7 +737,7 @@ namespace etl //*************************************************************************** /// Constructor. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl() + ETL_CONSTEXPR14 optional_impl() ETL_NOEXCEPT : storage() { } @@ -743,16 +745,17 @@ namespace etl //*************************************************************************** /// Constructor with nullopt. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl(etl::nullopt_t) + ETL_CONSTEXPR14 optional_impl(etl::nullopt_t) ETL_NOEXCEPT : storage() { } #include "private/diagnostic_uninitialized_push.h" + //*************************************************************************** /// Copy constructor. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl(const optional_impl& other) + ETL_CONSTEXPR14 optional_impl(const optional_impl& other) ETL_NOEXCEPT { if (other.has_value()) { @@ -765,7 +768,7 @@ namespace etl //*************************************************************************** /// Move constructor. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl(optional_impl&& other) + ETL_CONSTEXPR14 optional_impl(optional_impl&& other) ETL_NOEXCEPT { if (other.has_value()) { @@ -776,7 +779,7 @@ namespace etl //*************************************************************************** /// Constructor from value type. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl(const T& value_) + ETL_CONSTEXPR14 optional_impl(const T& value_) ETL_NOEXCEPT { storage.construct(value_); } @@ -784,7 +787,7 @@ namespace etl //*************************************************************************** /// Constructor from value type. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl(T&& value_) + ETL_CONSTEXPR14 optional_impl(T&& value_) ETL_NOEXCEPT { storage.construct(etl::move(value_)); } @@ -793,7 +796,7 @@ namespace etl /// Constructor from variadic args. //*************************************************************************** template - ETL_CONSTEXPR14 optional_impl(etl::in_place_t, TArgs&&... args) + ETL_CONSTEXPR14 optional_impl(etl::in_place_t, TArgs&&... args) ETL_NOEXCEPT { storage.construct(etl::forward(args)...); } @@ -803,7 +806,7 @@ namespace etl /// Construct from initializer_list and arguments. //******************************************* template - ETL_CONSTEXPR14 optional_impl(etl::in_place_t, std::initializer_list ilist, TArgs&&... args) + ETL_CONSTEXPR14 optional_impl(etl::in_place_t, std::initializer_list ilist, TArgs&&... args) ETL_NOEXCEPT { storage.construct(ilist, etl::forward(args)...); } @@ -813,7 +816,7 @@ namespace etl //*************************************************************************** /// Assignment operator from nullopt. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl& operator=(etl::nullopt_t) + ETL_CONSTEXPR14 optional_impl& operator=(etl::nullopt_t) ETL_NOEXCEPT { if (has_value()) { @@ -826,7 +829,7 @@ namespace etl //*************************************************************************** /// Assignment operator from optional_impl. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl& operator=(const optional_impl& other) + ETL_CONSTEXPR14 optional_impl& operator=(const optional_impl& other) ETL_NOEXCEPT { if (this != &other) { @@ -847,7 +850,7 @@ namespace etl //*************************************************************************** /// Assignment operator from optional_impl. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl& operator=(optional_impl&& other) + ETL_CONSTEXPR14 optional_impl& operator=(optional_impl&& other) ETL_NOEXCEPT { if (this != &other) { @@ -868,7 +871,7 @@ namespace etl //*************************************************************************** /// Assignment operator from value type. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl& operator=(const T& value_) + ETL_CONSTEXPR14 optional_impl& operator=(const T& value_) ETL_NOEXCEPT { storage.construct(value_); @@ -879,7 +882,7 @@ namespace etl //*************************************************************************** /// Assignment operator from value type. //*************************************************************************** - ETL_CONSTEXPR14 optional_impl& operator=(T&& value_) + ETL_CONSTEXPR14 optional_impl& operator=(T&& value_) ETL_NOEXCEPT { storage.construct(etl::move(value_)); @@ -1058,7 +1061,7 @@ namespace etl //*************************************************************************** /// Swaps this value with another. //*************************************************************************** - ETL_CONSTEXPR14 void swap(optional_impl& other) + ETL_CONSTEXPR14 void swap(optional_impl& other) ETL_NOEXCEPT { optional_impl temp(*this); *this = other; @@ -1068,7 +1071,7 @@ namespace etl //*************************************************************************** /// Reset back to invalid. //*************************************************************************** - ETL_CONSTEXPR14 void reset() + ETL_CONSTEXPR14 void reset() ETL_NOEXCEPT { storage.destroy(); } @@ -1077,7 +1080,7 @@ namespace etl /// //************************************************************************* ETL_CONSTEXPR20_STL - T& emplace(const optional_impl& other) + T& emplace(const optional_impl& other) { #if ETL_IS_DEBUG_BUILD ETL_ASSERT(other.has_value(), ETL_ERROR(optional_invalid)); @@ -1094,7 +1097,7 @@ namespace etl ///\param args The arguments to construct with. //************************************************************************* template - ETL_CONSTEXPR14 T& emplace(TArgs&&... args) + ETL_CONSTEXPR14 T& emplace(TArgs&&... args) ETL_NOEXCEPT { storage.construct(etl::forward(args)...); @@ -1286,7 +1289,7 @@ namespace etl /// Constructor. //*************************************************************************** template - ETL_CONSTEXPR14 optional() + ETL_CONSTEXPR14 optional() ETL_NOEXCEPT : impl_t() { } @@ -1295,12 +1298,12 @@ namespace etl /// Constructor. //*************************************************************************** template - ETL_CONSTEXPR20_STL optional() + ETL_CONSTEXPR20_STL optional() ETL_NOEXCEPT : impl_t() { } #else - optional() + optional() ETL_NOEXCEPT : impl_t() { } @@ -1311,7 +1314,7 @@ namespace etl /// Constructor with nullopt. //*************************************************************************** template - ETL_CONSTEXPR14 optional(etl::nullopt_t) + ETL_CONSTEXPR14 optional(etl::nullopt_t) ETL_NOEXCEPT : impl_t(etl::nullopt) { } @@ -1320,7 +1323,7 @@ namespace etl /// Constructor with nullopt. //*************************************************************************** template - ETL_CONSTEXPR20_STL optional(etl::nullopt_t) + ETL_CONSTEXPR20_STL optional(etl::nullopt_t) ETL_NOEXCEPT : impl_t(etl::nullopt) { } @@ -1328,31 +1331,15 @@ namespace etl //*************************************************************************** /// Constructor with nullopt. //*************************************************************************** - optional(etl::nullopt_t) + optional(etl::nullopt_t) ETL_NOEXCEPT : impl_t(etl::nullopt) { } #endif #include "private/diagnostic_uninitialized_push.h" -#if ETL_USING_CPP11 - //*************************************************************************** - /// Copy constructor. - //*************************************************************************** - template - ETL_CONSTEXPR14 optional(const optional& other) - : impl_t(other) - { - } - //*************************************************************************** - /// Copy constructor. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL optional(const optional& other) - : impl_t(other) - { - } +#if ETL_USING_CPP11 #else //*************************************************************************** /// Copy constructor. @@ -1364,26 +1351,6 @@ namespace etl #endif #include "private/diagnostic_pop.h" -#if ETL_USING_CPP11 - //*************************************************************************** - /// Move constructor. - //*************************************************************************** - template - ETL_CONSTEXPR14 optional(optional&& other) - : impl_t(other) - { - } - - //*************************************************************************** - /// Move constructor. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL optional(optional&& other) - : impl_t(other) - { - } -#endif - #if ETL_USING_CPP11 //*************************************************************************** /// Converting constructor from value type. @@ -1396,7 +1363,7 @@ namespace etl && !etl::is_same::type, etl::nullopt_t>::value && etl::is_pod::type>::value, int>::type = 0> - ETL_CONSTEXPR14 optional(U&& value_) + ETL_CONSTEXPR14 optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { } @@ -1410,7 +1377,7 @@ namespace etl && !etl::is_same::type, etl::nullopt_t>::value && !etl::is_pod::type>::value, int>::type = 0> - ETL_CONSTEXPR20_STL optional(U&& value_) + ETL_CONSTEXPR20_STL optional(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::forward(value_)) { } @@ -1429,7 +1396,7 @@ namespace etl /// Emplace construct from arguments. //*************************************************************************** template - ETL_CONSTEXPR14 explicit optional(etl::in_place_t, Args&&... args) + ETL_CONSTEXPR14 explicit optional(etl::in_place_t, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::in_place_t{}, etl::forward(args)...) { } @@ -1438,7 +1405,7 @@ namespace etl /// Emplace construct from arguments. //*************************************************************************** template - ETL_CONSTEXPR20_STL explicit optional(etl::in_place_t, Args&&... args) + ETL_CONSTEXPR20_STL explicit optional(etl::in_place_t, Args&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) : impl_t(etl::in_place_t{}, etl::forward(args)...) { } @@ -1449,6 +1416,7 @@ namespace etl //******************************************* template ETL_CONSTEXPR14 explicit optional(etl::in_place_t, std::initializer_list ilist, TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) : impl_t(etl::in_place_t{}, ilist, etl::forward(args)...) { } @@ -1458,6 +1426,7 @@ namespace etl //******************************************* template ETL_CONSTEXPR20_STL explicit optional(etl::in_place_t, std::initializer_list ilist, TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) : impl_t(etl::in_place_t{}, ilist, etl::forward(args)...) { } @@ -1469,7 +1438,7 @@ namespace etl /// Assignment operator from nullopt. //*************************************************************************** template - ETL_CONSTEXPR14 optional& operator=(etl::nullopt_t) + ETL_CONSTEXPR14 optional& operator=(etl::nullopt_t) ETL_NOEXCEPT { impl_t::operator=(etl::nullopt); @@ -1480,7 +1449,7 @@ namespace etl /// Assignment operator from nullopt. //*************************************************************************** template - ETL_CONSTEXPR20_STL optional& operator=(etl::nullopt_t) + ETL_CONSTEXPR20_STL optional& operator=(etl::nullopt_t) ETL_NOEXCEPT { impl_t::operator=(etl::nullopt); @@ -1490,7 +1459,7 @@ namespace etl //*************************************************************************** /// Assignment operator from nullopt. //*************************************************************************** - optional& operator=(etl::nullopt_t) + optional& operator=(etl::nullopt_t) ETL_NOEXCEPT { impl_t::operator=(etl::nullopt); @@ -1499,27 +1468,6 @@ namespace etl #endif #if ETL_USING_CPP11 - //*************************************************************************** - /// Assignment operator from optional. - //*************************************************************************** - template - ETL_CONSTEXPR14 optional& operator=(const optional& other) - { - impl_t::operator=(other); - - return *this; - } - - //*************************************************************************** - /// Assignment operator from optional. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL optional& operator=(const optional& other) - { - impl_t::operator=(other); - - return *this; - } #else //*************************************************************************** /// Assignment operator from optional. @@ -1532,30 +1480,6 @@ namespace etl } #endif -#if ETL_USING_CPP11 - //*************************************************************************** - /// Move assignment operator from optional. - //*************************************************************************** - template - ETL_CONSTEXPR14 optional& operator=(optional&& other) - { - impl_t::operator=(etl::move(other)); - - return *this; - } - - //*************************************************************************** - /// Move assignment operator from optional. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL optional& operator=(optional&& other) - { - impl_t::operator=(etl::move(other)); - - return *this; - } -#endif - #if ETL_USING_CPP11 //*************************************************************************** /// Converting assignment operator from value type. @@ -1565,7 +1489,7 @@ namespace etl && !etl::is_same::type, etl::nullopt_t>::value && etl::is_pod::type>::value, int>::type = 0> - ETL_CONSTEXPR14 optional& operator=(U&& value_) + ETL_CONSTEXPR14 optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -1580,7 +1504,7 @@ namespace etl && !etl::is_same::type, etl::nullopt_t>::value && !etl::is_pod::type>::value, int>::type = 0> - ETL_CONSTEXPR20_STL optional& operator=(U&& value_) + ETL_CONSTEXPR20_STL optional& operator=(U&& value_) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) { impl_t::operator=(etl::forward(value_)); @@ -2223,6 +2147,71 @@ namespace etl #include "private/diagnostic_pop.h" +#if ETL_CPP11_SUPPORTED + //*************************************************************************** + /// Creates an optional object from `value`. + //*************************************************************************** + template + ETL_CONSTEXPR14 etl::optional::type> make_optional(T&& value) // + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::type, T&&>::value)) + { + return etl::optional::type>(etl::forward(value)); + } + template + ETL_CONSTEXPR20_STL etl::optional::type> make_optional(T&& value) // + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::type, T&&>::value)) + { + return etl::optional::type>(etl::forward(value)); + } + + //*************************************************************************** + /// Creates an optional object constructed in-place from `args...` . + /// Equivalent to `return etl::optional(etl::in_place, etl::forward(args)...);`. + /// This overload participates in overload resolution only if + /// `etl::is_constructible_v` is true. + //*************************************************************************** + template ::value, int>::type = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP14> + ETL_CONSTEXPR14 etl::optional make_optional(Args&&... args) // + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) + { + return etl::optional(etl::in_place_t{}, etl::forward(args)...); + } + template ::value, int>::type = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + ETL_CONSTEXPR20_STL etl::optional make_optional(Args&&... args) // + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible::value)) + { + return etl::optional(etl::in_place_t{}, etl::forward(args)...); + } + + #if ETL_HAS_INITIALIZER_LIST + //*************************************************************************** + /// Creates an optional object constructed in-place from `ilist` and `args...`. + /// Equivalent to `return etl::optional(std::in_place, ilist, std::forward(args)...);`. + /// This overload participates in overload resolution only if + /// `etl::is_constructible_v&, Args...>` is true. + //*************************************************************************** + template &, Args...>::value, int>::type = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP14> + ETL_CONSTEXPR14 etl::optional make_optional(std::initializer_list ilist, Args&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) + { + return etl::optional(etl::in_place_t{}, ilist, etl::forward(args)...); + } + template &, Args...>::value, int>::type = 0, // + typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL> + ETL_CONSTEXPR20_STL etl::optional make_optional(std::initializer_list ilist, Args&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible&, Args...>::value)) + { + return etl::optional(etl::in_place_t{}, ilist, etl::forward(args)...); + } + #endif +#else //*************************************************************************** /// Make an optional. //*************************************************************************** @@ -2231,6 +2220,7 @@ namespace etl { return etl::optional::type>(value); } +#endif //*************************************************************************** /// Template deduction guides. @@ -2239,16 +2229,17 @@ namespace etl template optional(T) -> optional; #endif -} // namespace etl -//************************************************************************* -/// Swaps the values. -//************************************************************************* -template -ETL_CONSTEXPR20_STL void swap(etl::optional& lhs, etl::optional& rhs) -{ - lhs.swap(rhs); -} + //************************************************************************* + /// Swaps the values. + //************************************************************************* + template + ETL_CONSTEXPR20_STL void swap(etl::optional& lhs, etl::optional& rhs) ETL_NOEXCEPT_FROM(lhs.swap(rhs)) + { + lhs.swap(rhs); + } + +} // namespace etl #undef ETL_OPTIONAL_ENABLE_CPP14 #undef ETL_OPTIONAL_ENABLE_CPP20_STL diff --git a/include/etl/parameter_pack.h b/include/etl/parameter_pack.h index 5dd21717..bed2949c 100644 --- a/include/etl/parameter_pack.h +++ b/include/etl/parameter_pack.h @@ -32,13 +32,9 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" -#include +#include -#if ETL_CPP11_NOT_SUPPORTED - #if !defined(ETL_IN_UNIT_TEST) - #error NOT SUPPORTED FOR C++03 OR BELOW - #endif -#else +#if ETL_CPP11_SUPPORTED namespace etl { //*************************************************************************** diff --git a/include/etl/platform.h b/include/etl/platform.h index a9918b24..564d0b5e 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -102,6 +102,16 @@ SOFTWARE. #define ETL_NOT_USING_STL 0 #endif +//************************************* +// Helper macros for ETL_NO_STD_MUTEX. +#if defined(ETL_NO_STD_MUTEX) + #define ETL_USING_STD_MUTEX 0 + #define ETL_NOT_USING_STD_MUTEX 1 +#else + #define ETL_USING_STD_MUTEX 1 + #define ETL_NOT_USING_STD_MUTEX 0 +#endif + //************************************* // Helper macros for ETL_STLPORT. #if defined(ETL_STLPORT) @@ -164,6 +174,19 @@ SOFTWARE. #define ETL_NOT_USING_FORMAT_FLOATING_POINT 0 #endif +//************************************* +// Helper macro for ETL_FORMAT_NO_LONG_DOUBLE_MATH. +// Define ETL_FORMAT_NO_LONG_DOUBLE_MATH if the toolchain does not provide +// long double math functions (log10l, floorl, powl, modfl, roundl). +// When defined, long double arguments are cast to double for math operations. +#if defined(ETL_FORMAT_NO_LONG_DOUBLE_MATH) + #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 0 + #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 1 +#else + #define ETL_USING_FORMAT_LONG_DOUBLE_MATH 1 + #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 0 +#endif + //************************************* // Figure out things about the compiler, if haven't already done so in // etl_profile.h @@ -489,6 +512,18 @@ SOFTWARE. #define ETL_ASSUME ETL_DO_NOTHING #endif +//************************************* +// C++26 +#if defined(__has_cpp_attribute) + #if __has_cpp_attribute(indeterminate) + #define ETL_INDETERMINATE [[indeterminate]] + #else + #define ETL_INDETERMINATE + #endif +#else + #define ETL_INDETERMINATE +#endif + //************************************* // Determine if the ETL can use char8_t type. #if ETL_NO_SMALL_CHAR_SUPPORT @@ -650,6 +685,7 @@ namespace etl static ETL_CONSTANT bool using_cpp17 = (ETL_USING_CPP17 == 1); static ETL_CONSTANT bool using_cpp20 = (ETL_USING_CPP20 == 1); static ETL_CONSTANT bool using_cpp23 = (ETL_USING_CPP23 == 1); + static ETL_CONSTANT bool using_cpp26 = (ETL_USING_CPP26 == 1); static ETL_CONSTANT bool using_gcc_compiler = (ETL_USING_GCC_COMPILER == 1); static ETL_CONSTANT bool using_microsoft_compiler = (ETL_USING_MICROSOFT_COMPILER == 1); static ETL_CONSTANT bool using_arm5_compiler = (ETL_USING_ARM5_COMPILER == 1); @@ -697,6 +733,9 @@ namespace etl static ETL_CONSTANT bool has_chrono_literals_microseconds = (ETL_HAS_CHRONO_LITERALS_DURATION == 1); static ETL_CONSTANT bool has_chrono_literals_nanoseconds = (ETL_HAS_CHRONO_LITERALS_DURATION == 1); static ETL_CONSTANT bool has_std_byteswap = (ETL_HAS_STD_BYTESWAP == 1); + static ETL_CONSTANT bool has_std_is_virtual_base_of = (ETL_HAS_STD_IS_VIRTUAL_BASE_OF == 1); + static ETL_CONSTANT bool has_std_trivially_relocatable = (ETL_HAS_STD_TRIVIALLY_RELOCATABLE == 1); + static ETL_CONSTANT bool has_std_atomic_min_max = (ETL_HAS_STD_ATOMIC_MIN_MAX == 1); static ETL_CONSTANT bool has_noexcept_function_type = (ETL_HAS_NOEXCEPT_FUNCTION_TYPE == 1); // Is... diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index e72d4908..2623fc07 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -439,7 +439,9 @@ namespace etl //************************************************************************* void clone(const ipriority_queue& other) { +#include "etl/private/diagnostic_uninitialized_push.h" assign(other.container.cbegin(), other.container.cend()); +#include "etl/private/diagnostic_pop.h" } #if ETL_USING_CPP11 diff --git a/include/etl/private/bitset_legacy.h b/include/etl/private/bitset_legacy.h index 7fa70ce3..8846e034 100644 --- a/include/etl/private/bitset_legacy.h +++ b/include/etl/private/bitset_legacy.h @@ -1523,16 +1523,16 @@ namespace etl { return !(lhs == rhs); } -} // namespace etl -//************************************************************************* -/// swap -//************************************************************************* -template -void swap(etl::bitset& lhs, etl::bitset& rhs) -{ - lhs.swap(rhs); -} + //************************************************************************* + /// swap + //************************************************************************* + template + void swap(etl::bitset& lhs, etl::bitset& rhs) + { + lhs.swap(rhs); + } +} // namespace etl #include "minmax_pop.h" diff --git a/include/etl/private/bitset_new.h b/include/etl/private/bitset_new.h index 89d4a0a8..e33f3b10 100644 --- a/include/etl/private/bitset_new.h +++ b/include/etl/private/bitset_new.h @@ -1078,7 +1078,7 @@ namespace etl /// Extract an value from multiple elements. //************************************************************************* template - static ETL_CONSTEXPR14 typename etl::make_unsigned::type extract_from_multiple_elements(const element_type* pbuffer, int element_index, + static ETL_CONSTEXPR14 typename etl::make_unsigned::type extract_from_multiple_elements(const element_type* pbuffer, size_t element_index, size_t active_bits_in_msb, size_t length) ETL_NOEXCEPT { typedef typename etl::make_unsigned::type unsigned_t; @@ -1133,8 +1133,8 @@ namespace etl unsigned_t value(0); - const int Msb_Element_Index = (position + length - 1) >> etl::log2::value; - const int Lsb_Element_Index = position >> etl::log2::value; + const size_t Msb_Element_Index = (position + length - 1) >> etl::log2::value; + const size_t Lsb_Element_Index = position >> etl::log2::value; // Is the value contained within one element? if (Msb_Element_Index == Lsb_Element_Index) @@ -1150,7 +1150,7 @@ namespace etl size_t active_bits_in_msb = (position + length) - (static_cast(Msb_Element_Index) * Bits_Per_Element); // Start with index of the element containing the msb. - int element_index = Msb_Element_Index; + size_t element_index = Msb_Element_Index; value = extract_from_multiple_elements(pbuffer, element_index, active_bits_in_msb, length); } @@ -1169,7 +1169,7 @@ namespace etl { typedef typename etl::make_unsigned::type unsigned_t; - const int Element_Index = (Position + Length - 1) >> etl::log2::value; + const size_t Element_Index = (Position + Length - 1) >> etl::log2::value; const unsigned_t Mask = etl::lsb_mask::value; const unsigned_t Shift = Position % Bits_Per_Element; @@ -1186,7 +1186,7 @@ namespace etl extract_from_buffer(const_pointer pbuffer) { // Start with index of the element containing the msb. - const int Msb_Element_Index = (Position + Length - 1) >> etl::log2::value; + const size_t Msb_Element_Index = (Position + Length - 1) >> etl::log2::value; // Get the number of active bits in the first element const size_t Active_Bits_In_Msb = ((Position + Length - 1) % Bits_Per_Element) + 1; @@ -2519,32 +2519,30 @@ namespace etl temp ^= rhs; return temp; } -} // namespace etl -//*************************************************************************** -/// operator != -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator!=(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} + //*************************************************************************** + /// operator != + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator!=(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } -//************************************************************************* -/// swap -//************************************************************************* -template -ETL_CONSTEXPR14 void swap(etl::bitset& lhs, etl::bitset& rhs) ETL_NOEXCEPT -{ - lhs.swap(rhs); -} + //************************************************************************* + /// swap + //************************************************************************* + template + ETL_CONSTEXPR14 void swap(etl::bitset& lhs, etl::bitset& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } + + //*************************************************************************** + /// bitset_ext + //*************************************************************************** -//*************************************************************************** -/// bitset_ext -//*************************************************************************** -namespace etl -{ //*************************************************************************** template class bitset_ext; @@ -3396,29 +3394,26 @@ namespace etl // Pointer to the storage for the bitset. element_type* pbuffer; }; -} // namespace etl -//*************************************************************************** -/// operator != -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator!=(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} + //*************************************************************************** + /// operator != + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator!=(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } -//************************************************************************* -/// swap -//************************************************************************* -template -ETL_CONSTEXPR14 void swap(etl::bitset_ext& lhs, etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - lhs.swap(rhs); -} + //************************************************************************* + /// swap + //************************************************************************* + template + ETL_CONSTEXPR14 void swap(etl::bitset_ext& lhs, etl::bitset_ext& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } -namespace etl -{ namespace private_bitset { //************************************************************************* @@ -3464,216 +3459,217 @@ namespace etl return true; } } // namespace private_bitset + + //*************************************************************************** + /// operator == + /// bitset + /// Different element types + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator==(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + // Get a span of each type. + typename etl::bitset::const_span_type lhs_span = lhs.span(); + typename etl::bitset::const_span_type rhs_span = rhs.span(); + + // Put the bitset with the largest element type as the first argument. + if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) + { + return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); + } + else + { + return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); + } + } + + //*************************************************************************** + /// operator != + /// bitset + /// Different element types + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator!=(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*************************************************************************** + /// operator == + /// bitset_ext + /// Different element types + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator==(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + // Get a span of each type. + typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); + typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); + + // Put the bitset with the largest element type as the first argument. + if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) + { + return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); + } + else + { + return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); + } + } + + //*************************************************************************** + /// operator != + /// bitset_ext + /// Different element types + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator!=(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*************************************************************************** + /// operator == + /// bitset compared with bitset_ext, same element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator==(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + const char Storage_Model = etl::bitset::Storage_Model; + const size_t Number_Of_Elements = etl::bitset::Number_Of_Elements; + + typename etl::bitset::const_span_type lhs_span = lhs.span(); + typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); + + typedef etl::bitset_impl implementation; + + return implementation::operator_equality(lhs_span.begin(), rhs_span.begin(), Number_Of_Elements); + } + + //*************************************************************************** + /// operator != + /// bitset compared with bitset_ext, same element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator!=(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*************************************************************************** + /// operator == + /// bitset_ext compared with bitset, same element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator==(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + const char Storage_Model = etl::bitset::Storage_Model; + const size_t Number_Of_Elements = etl::bitset::Number_Of_Elements; + + typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); + typename etl::bitset::const_span_type rhs_span = rhs.span(); + + typedef etl::bitset_impl implementation; + + return implementation::operator_equality(lhs_span.begin(), rhs_span.begin(), Number_Of_Elements); + } + + //*************************************************************************** + /// operator != + /// bitset_ext compared with bitset, same element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 bool operator!=(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*************************************************************************** + /// operator == + /// bitset compared with bitset_ext, different element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator==(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + // Get a span of each type. + typename etl::bitset::const_span_type lhs_span = lhs.span(); + typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); + + // Put the bitset with the largest element type as the first argument. + if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) + { + return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); + } + else + { + return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); + } + } + + //*************************************************************************** + /// operator != + /// bitset compared with bitset_ext, different element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator!=(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + + //*************************************************************************** + /// operator == + /// bitset_ext compared with bitset, different element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator==(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + // Get a span of each type. + typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); + typename etl::bitset::const_span_type rhs_span = rhs.span(); + + // Put the bitset with the largest element type as the first argument. + if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) + { + return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); + } + else + { + return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); + } + } + + //*************************************************************************** + /// operator != + /// bitset_ext compared with bitset, different element types. + ///\ingroup bitset + //*************************************************************************** + template + ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type + operator!=(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT + { + return !(lhs == rhs); + } + } // namespace etl -//*************************************************************************** -/// operator == -/// bitset -/// Different element types -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator==(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - // Get a span of each type. - typename etl::bitset::const_span_type lhs_span = lhs.span(); - typename etl::bitset::const_span_type rhs_span = rhs.span(); - - // Put the bitset with the largest element type as the first argument. - if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) - { - return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); - } - else - { - return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); - } -} - -//*************************************************************************** -/// operator != -/// bitset -/// Different element types -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator!=(const etl::bitset& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - -//*************************************************************************** -/// operator == -/// bitset_ext -/// Different element types -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator==(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - // Get a span of each type. - typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); - typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); - - // Put the bitset with the largest element type as the first argument. - if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) - { - return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); - } - else - { - return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); - } -} - -//*************************************************************************** -/// operator != -/// bitset_ext -/// Different element types -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator!=(const etl::bitset_ext& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - -//*************************************************************************** -/// operator == -/// bitset compared with bitset_ext, same element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator==(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - const char Storage_Model = etl::bitset::Storage_Model; - const size_t Number_Of_Elements = etl::bitset::Number_Of_Elements; - - typename etl::bitset::const_span_type lhs_span = lhs.span(); - typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); - - typedef etl::bitset_impl implementation; - - return implementation::operator_equality(lhs_span.begin(), rhs_span.begin(), Number_Of_Elements); -} - -//*************************************************************************** -/// operator != -/// bitset compared with bitset_ext, same element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator!=(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - -//*************************************************************************** -/// operator == -/// bitset_ext compared with bitset, same element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator==(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - const char Storage_Model = etl::bitset::Storage_Model; - const size_t Number_Of_Elements = etl::bitset::Number_Of_Elements; - - typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); - typename etl::bitset::const_span_type rhs_span = rhs.span(); - - typedef etl::bitset_impl implementation; - - return implementation::operator_equality(lhs_span.begin(), rhs_span.begin(), Number_Of_Elements); -} - -//*************************************************************************** -/// operator != -/// bitset_ext compared with bitset, same element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 bool operator!=(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - -//*************************************************************************** -/// operator == -/// bitset compared with bitset_ext, different element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator==(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - // Get a span of each type. - typename etl::bitset::const_span_type lhs_span = lhs.span(); - typename etl::bitset_ext::const_span_type rhs_span = rhs.span(); - - // Put the bitset with the largest element type as the first argument. - if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) - { - return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); - } - else - { - return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); - } -} - -//*************************************************************************** -/// operator != -/// bitset compared with bitset_ext, different element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator!=(const etl::bitset& lhs, const etl::bitset_ext& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - -//*************************************************************************** -/// operator == -/// bitset_ext compared with bitset, different element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator==(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - // Get a span of each type. - typename etl::bitset_ext::const_span_type lhs_span = lhs.span(); - typename etl::bitset::const_span_type rhs_span = rhs.span(); - - // Put the bitset with the largest element type as the first argument. - if ETL_IF_CONSTEXPR (sizeof(TLhsElement) > sizeof(TRhsElement)) - { - return etl::private_bitset::compare_bitset_spans(lhs_span, rhs_span); - } - else - { - return etl::private_bitset::compare_bitset_spans(rhs_span, lhs_span); - } -} - -//*************************************************************************** -/// operator != -/// bitset_ext compared with bitset, different element types. -///\ingroup bitset -//*************************************************************************** -template -ETL_CONSTEXPR14 typename etl::enable_if::value, bool>::type - operator!=(const etl::bitset_ext& lhs, const etl::bitset& rhs) ETL_NOEXCEPT -{ - return !(lhs == rhs); -} - #include "minmax_pop.h" #endif diff --git a/include/etl/private/chrono/year_month_weekday.h b/include/etl/private/chrono/year_month_weekday.h index c557138a..8d7f4d52 100644 --- a/include/etl/private/chrono/year_month_weekday.h +++ b/include/etl/private/chrono/year_month_weekday.h @@ -174,11 +174,11 @@ namespace etl unsigned int target_wd = ymwd.weekday().c_encoding(); unsigned int target_index = ymwd.index(); - etl::chrono::weekday first_weekday(static_cast(sd.time_since_epoch().count())); + etl::chrono::weekday first_weekday(sd); unsigned int first_wd = first_weekday.c_encoding(); unsigned int offset = (target_wd - first_wd + 7U) % 7U; - unsigned int day_of_month = offset + (target_index - 1U) * 7U; + unsigned int day_of_month = 1U + offset + (target_index - 1U) * 7U; etl::chrono::year_month_day result(year(), month(), etl::chrono::day(day_of_month)); @@ -382,7 +382,7 @@ namespace etl { etl::chrono::year_month_day ymd(year(), month(), etl::chrono::day(d)); etl::chrono::sys_days ymd_sys_days = static_cast(ymd); - etl::chrono::weekday wd(static_cast(ymd_sys_days.time_since_epoch().count())); + etl::chrono::weekday wd(ymd_sys_days); if (wd == weekday()) { diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index 33d993e2..27b443c0 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -172,7 +172,7 @@ namespace etl //************************************************************************* // Construct from a function pointer. //************************************************************************* - delegate(function_ptr fp) ETL_NOEXCEPT + explicit delegate(function_ptr fp) ETL_NOEXCEPT { assign(fp, function_ptr_stub); } @@ -534,7 +534,14 @@ namespace etl //************************************************************************* delegate& operator=(function_ptr fp) ETL_NOEXCEPT { - assign(fp, function_ptr_stub); + if (fp == ETL_NULLPTR) + { + invocation.clear(); + } + else + { + assign(fp, function_ptr_stub); + } return *this; } @@ -559,6 +566,14 @@ namespace etl //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR14 bool is_valid() const ETL_NOEXCEPT { + // GCC's UBSan instruments function pointer comparisons, which prevents + // constexpr evaluation. Use implicit bool conversion at compile time + // to avoid the instrumented != comparison while still checking validity. + if (etl::is_constant_evaluated()) + { + return static_cast(invocation.stub); + } + return invocation.stub != ETL_NULLPTR; } diff --git a/include/etl/private/diagnostic_cxx_11_extensions_push.h b/include/etl/private/diagnostic_cxx_11_extensions_push.h new file mode 100644 index 00000000..6d991898 --- /dev/null +++ b/include/etl/private/diagnostic_cxx_11_extensions_push.h @@ -0,0 +1,39 @@ +///\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. +******************************************************************************/ + +/* + * The header include guard has been intentionally omitted. + * This file is intended to evaluated multiple times by design. + */ + +#if defined(__clang__) || defined(__llvm__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wc++11-extensions" +#endif diff --git a/include/etl/private/diagnostic_gnu_static_float_init_push.h b/include/etl/private/diagnostic_gnu_static_float_init_push.h new file mode 100644 index 00000000..2f243d55 --- /dev/null +++ b/include/etl/private/diagnostic_gnu_static_float_init_push.h @@ -0,0 +1,44 @@ +///\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. +******************************************************************************/ + +/* + * The header include guard has been intentionally omitted. + * This file is intended to evaluated multiple times by design. + */ + +// Matching GCC push for later pop +#if defined(__GNUC__) && !defined(__clang__) && !defined(__llvm__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic push +#endif + +#if defined(__clang__) || defined(__llvm__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wgnu-static-float-init" +#endif diff --git a/include/etl/private/diagnostic_sign_conversion_push.h b/include/etl/private/diagnostic_sign_conversion_push.h new file mode 100644 index 00000000..50c0532e --- /dev/null +++ b/include/etl/private/diagnostic_sign_conversion_push.h @@ -0,0 +1,44 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +/* + * The header include guard has been intentionally omitted. + * This file is intended to evaluated multiple times by design. + */ + +#if defined(__GNUC__) && !defined(__clang__) && !defined(__llvm__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#if defined(__clang__) || defined(__llvm__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wsign-conversion" +#endif diff --git a/include/etl/private/diagnostic_unnamed_type_template_args_push.h b/include/etl/private/diagnostic_unnamed_type_template_args_push.h new file mode 100644 index 00000000..17123437 --- /dev/null +++ b/include/etl/private/diagnostic_unnamed_type_template_args_push.h @@ -0,0 +1,39 @@ +///\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. +******************************************************************************/ + +/* + * The header include guard has been intentionally omitted. + * This file is intended to evaluated multiple times by design. + */ + +#if defined(__clang__) || defined(__llvm__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunnamed-type-template-args" +#endif diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 5598f896..3b621582 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -47,6 +47,7 @@ SOFTWARE. #include "../visitor.h" #include +#include #if defined(ETL_COMPILER_KEIL) #pragma diag_suppress 940 @@ -60,8 +61,7 @@ SOFTWARE. #else //***************************************************************************** ///\defgroup variant variant -/// A class that can contain one a several specified types in a type safe -/// manner. +/// A class that can contain one a several specified types in a type safe manner. ///\ingroup containers //***************************************************************************** @@ -70,171 +70,188 @@ namespace etl namespace private_variant { //******************************************* - // The traits an object may have. + /// Switch-based dispatch for destroy/copy/move. + /// Replaces the per-instance function pointer with + /// an inline if-else chain the compiler can optimise + /// into a switch / jump-table, enabling inlining and + /// dead-code elimination. //******************************************* - static constexpr bool Copyable = true; - static constexpr bool Non_Copyable = false; - static constexpr bool Moveable = true; - static constexpr bool Non_Moveable = false; + template + struct variant_operations; + + // Base case: no types left. + template + struct variant_operations + { + static void destroy(char*, size_t) {} + static void copy(char*, const char*, size_t) {} + static void move(char*, const char*, size_t) {} + }; + + // Recursive case. + template + struct variant_operations + { + static void destroy(char* data, size_t type_id) + { + if (type_id == Index) + { + reinterpret_cast(data)->~THead(); + } + else + { + variant_operations::destroy(data, type_id); + } + } + + static void copy(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + copy_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::copy(dst, src, type_id); + } + } + + static void move(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + move_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::move(dst, src, type_id); + } + } + + private: + + static void copy_impl(char* dst, const char* src, etl::true_type) + { + ::new (dst) THead(*reinterpret_cast(src)); + } + + static void copy_impl(char*, const char*, etl::false_type) {} + + static void move_impl(char* dst, const char* src, etl::true_type) + { + ::new (dst) THead(etl::move(*reinterpret_cast(const_cast(src)))); + } + + static void move_impl(char*, const char*, etl::false_type) {} + }; //******************************************* - // The types of operations we can perform. + /// Trait: are all types trivially destructible? //******************************************* - static constexpr int Copy = 0; - static constexpr int Move = 1; - static constexpr int Destroy = 2; + template + struct are_all_trivially_destructible : etl::conjunction...> + { + }; //******************************************* - // operation_type + /// Recursive variadic union for constexpr-friendly storage. + /// Used when all variant types are trivially destructible. //******************************************* - template - struct operation_type; + template + union variadic_union; - //******************************************* - // Specialisation for null operation. + /// Base case: empty union. template <> - struct operation_type + union variadic_union<> { - static void do_operation(int, char*, const char*) + constexpr variadic_union() ETL_NOEXCEPT {} + }; + + /// Recursive case: union of head type and tail union. + template + union variadic_union + { + THead head; + variadic_union tail; + + constexpr variadic_union() ETL_NOEXCEPT + : tail() + { + } + + // Constructor for head element (index 0). + template + constexpr variadic_union(etl::in_place_index_t<0>, T&& value) + : head(etl::forward(value)) + { + } + + // Constructor for tail elements (index > 0). + template + constexpr variadic_union(etl::in_place_index_t, T&& value) + : tail(etl::in_place_index_t{}, etl::forward(value)) { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif } }; //******************************************* - // Specialisation for no-copyable & non-moveable types. - template - struct operation_type - { - static void do_operation(int operation, char* pstorage, const char* /*pvalue*/) - { - switch (operation) - { - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - + /// Constexpr get by index from variadic_union. //******************************************* - // Specialisation for no-copyable & moveable types. - template - struct operation_type + // Non-const lvalue reference + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index == 0), THead&> variadic_union_get(variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Move: - { - ::new (pstorage) T(etl::move(*reinterpret_cast(const_cast(pvalue)))); - break; - } + return u.head; + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - - //******************************************* - // Specialisation for copyable & non-moveable types. - template - struct operation_type + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index != 0), etl::nth_type_t&> + variadic_union_get(variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Copy: - { - ::new (pstorage) T(*reinterpret_cast(pvalue)); - break; - } + return variadic_union_get(u.tail); + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - - //******************************************* - // Specialisation for copyable & moveable types. - template - struct operation_type + // Const lvalue reference + template + constexpr typename etl::enable_if_t<(Index == 0), const THead&> variadic_union_get(const variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Copy: - { - ::new (pstorage) T(*reinterpret_cast(pvalue)); - break; - } + return u.head; + } - case Move: - { - ::new (pstorage) T(etl::move(*reinterpret_cast(const_cast(pvalue)))); - break; - } + template + constexpr typename etl::enable_if_t<(Index != 0), const etl::nth_type_t&> + variadic_union_get(const variadic_union& u) ETL_NOEXCEPT + { + return variadic_union_get(u.tail); + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } + // Rvalue reference + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index == 0), THead&&> variadic_union_get(variadic_union&& u) ETL_NOEXCEPT + { + return etl::move(u.head); + } - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index != 0), etl::nth_type_t&&> + variadic_union_get(variadic_union&& u) ETL_NOEXCEPT + { + return variadic_union_get(etl::move(u.tail)); + } + + // Const rvalue reference + template + constexpr typename etl::enable_if_t<(Index == 0), const THead&&> variadic_union_get(const variadic_union&& u) ETL_NOEXCEPT + { + return etl::move(u.head); + } + + template + constexpr typename etl::enable_if_t<(Index != 0), const etl::nth_type_t&&> + variadic_union_get(const variadic_union&& u) ETL_NOEXCEPT + { + return variadic_union_get(etl::move(u.tail)); + } } // namespace private_variant /// Definition of variant_npos. @@ -387,14 +404,91 @@ namespace etl } }; + namespace private_variant + { + //*************************************************************************** + /// variant_base for non-trivially destructible types. + /// Uses uninitialized_buffer (char array) storage and switch-based + /// dispatch for copy/move/destroy (no per-instance function pointer). + //*************************************************************************** + template + struct variant_base + { + using largest_t = typename largest_type::type; + static const size_t Size = sizeof(largest_t); + static const size_t Alignment = etl::largest_alignment::value; + + etl::uninitialized_buffer data; + size_t type_id; + + ETL_CONSTEXPR14 variant_base() noexcept + : type_id(variant_npos) + { + } + + ETL_CONSTEXPR14 variant_base(size_t id) noexcept + : type_id(id) + { + } + + ~variant_base() + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + } + type_id = variant_npos; + } + }; + + //*************************************************************************** + /// variant_base specialisation for trivially destructible types. + /// Uses variadic_union storage. Destructor is trivial (defaulted), making + /// the variant a literal type eligible for constexpr / ROM placement. + /// No operation function pointer is needed since destroy/copy/move are + /// all handled without indirection for trivially destructible types. + //*************************************************************************** + template + struct variant_base + { + variadic_union data; + size_t type_id; + + constexpr variant_base() noexcept + : data() + , type_id(variant_npos) + { + } + + constexpr variant_base(size_t id) noexcept + : data() + , type_id(id) + { + } + + template + constexpr variant_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : data(etl::in_place_index_t{}, etl::forward(value)) + , type_id(id) + { + } + + ~variant_base() = default; + }; + } // namespace private_variant + //*************************************************************************** - /// A template class that can store any of the types defined in the template - /// parameter list. + /// A template class that can store any of the types defined in the template parameter list. ///\ingroup variant //*************************************************************************** template - class variant + class variant : private private_variant::variant_base::value, TTypes...> { + using base_type = private_variant::variant_base::value, TTypes...>; + + static constexpr bool Is_Trivially_Destructible_Suite = private_variant::are_all_trivially_destructible::value; + public: using type_list = etl::type_list; @@ -409,10 +503,10 @@ namespace etl friend ETL_CONSTEXPR14 etl::variant_alternative_t >&& get(etl::variant&& v); template - friend ETL_CONSTEXPR14 const etl::variant_alternative_t< Index, const etl::variant >& get(const etl::variant& v); + friend ETL_CONSTEXPR14 const etl::variant_alternative_t >& get(const etl::variant& v); template - friend ETL_CONSTEXPR14 const etl::variant_alternative_t< Index, const etl::variant >&& get(const etl::variant&& v); + friend ETL_CONSTEXPR14 const etl::variant_alternative_t >&& get(const etl::variant&& v); template friend ETL_CONSTEXPR14 T& get(etl::variant& v); @@ -434,38 +528,6 @@ namespace etl private: - // All types of variant are friends. - template - friend class variant; - - //*************************************************************************** - /// The largest type. - //*************************************************************************** - using largest_t = typename largest_type::type; - - //*************************************************************************** - /// The largest size. - //*************************************************************************** - static const size_t Size = sizeof(largest_t); - - //*************************************************************************** - /// The largest alignment. - //*************************************************************************** - static const size_t Alignment = etl::largest_alignment::value; - - //*************************************************************************** - /// The operation templates. - //*************************************************************************** - template - using operation_type = private_variant::operation_type; - - //******************************************* - // The types of operations we can perform. - //******************************************* - static constexpr int Copy = private_variant::Copy; - static constexpr int Move = private_variant::Move; - static constexpr int Destroy = private_variant::Destroy; - //******************************************* // Get the index of a type. //******************************************* @@ -478,21 +540,32 @@ namespace etl template using type_from_index = typename etl::type_list_type_at_index, Index>::type; + //******************************************* + // Bring base members into scope. + //******************************************* + using base_type::data; + using base_type::type_id; + public: //*************************************************************************** /// Default constructor. - /// Constructs a variant holding the value-initialized value of the first - /// alternative (index() is zero). + /// Constructs a variant holding the value-initialized value of the first alternative (index() is zero). //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant() + template = 0> + ETL_CONSTEXPR14 variant() noexcept(etl::is_nothrow_default_constructible >::value) { using type = type_from_index<0U>; default_construct_in_place(data); - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; - type_id = 0U; + type_id = 0U; + } + + template = 0> + constexpr variant() noexcept(etl::is_nothrow_default_constructible >::value) + : base_type(etl::in_place_index_t<0>{}, type_from_index<0U>{}, 0U) + { } #include "diagnostic_pop.h" @@ -500,31 +573,22 @@ namespace etl /// Construct from a value. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template , variant>::value, int> = 0> + template , variant>::value && !Trivial_, int> = 0> ETL_CONSTEXPR14 variant(T&& value) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + : base_type(index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); construct_in_place >(data, etl::forward(value)); } - #include "diagnostic_pop.h" - //*************************************************************************** - /// Construct from arguments. - //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - template - ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + template , variant>::value && Trivial_, int> = 0> + constexpr variant(T&& value) + : base_type(etl::in_place_index_t::value>{}, etl::forward(value), index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - - construct_in_place_args >(data, etl::forward(args)...); } #include "diagnostic_pop.h" @@ -532,16 +596,43 @@ namespace etl /// Construct from arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template + template = 0> + ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) + : base_type(index_of_type::value) + { + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + + construct_in_place_args >(data, etl::forward(args)...); + } + + template = 0> + constexpr explicit variant(etl::in_place_type_t, TArgs&&... args) + : base_type(etl::in_place_index_t::value>{}, etl::remove_cvref_t(etl::forward(args)...), index_of_type::value) + { + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + } + #include "diagnostic_pop.h" + + //*************************************************************************** + /// Construct from arguments. + //*************************************************************************** + #include "diagnostic_uninitialized_push.h" + template = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, TArgs&&... args) - : type_id(Index) + : base_type(Index) { using type = type_from_index; static_assert(etl::is_one_of::value, "Unsupported type"); construct_in_place_args(data, etl::forward(args)...); + } - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + template = 0> + constexpr explicit variant(etl::in_place_index_t, TArgs&&... args) + : base_type(etl::in_place_index_t{}, type_from_index(etl::forward(args)...), Index) + { + using type = type_from_index; + static_assert(etl::is_one_of::value, "Unsupported type"); } #include "diagnostic_pop.h" @@ -552,9 +643,7 @@ namespace etl #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, std::initializer_list init, TArgs&&... args) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + : base_type(index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); @@ -568,14 +657,12 @@ namespace etl #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, std::initializer_list init, TArgs&&... args) - : type_id(Index) + : base_type(Index) { using type = type_from_index; static_assert(etl::is_one_of::value, "Unsupported type"); construct_in_place_args(data, init, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; } #include "diagnostic_pop.h" #endif @@ -585,17 +672,12 @@ namespace etl ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(const variant& other) - : operation(other.operation) - , type_id(other.type_id) + ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction...>::value) + : base_type(other.type_id) { - if (other.valueless_by_exception()) + if (other.index() != variant_npos) { - type_id = variant_npos; - } - else - { - operation(private_variant::Copy, data, other.data); + do_copy_from(other, etl::integral_constant{}); } } #include "diagnostic_pop.h" @@ -605,57 +687,39 @@ namespace etl ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(variant&& other) - : operation(other.operation) - , type_id(other.type_id) + ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction...>::value) + : base_type(other.type_id) { - if (other.valueless_by_exception()) + if (other.index() != variant_npos) { - type_id = variant_npos; - } - else - { - operation(private_variant::Move, data, other.data); + do_move_from(other, etl::integral_constant{}); } } #include "diagnostic_pop.h" //*************************************************************************** /// Destructor. + /// Handled by variant_base (trivial for trivially destructible types, + /// non-trivial otherwise). //*************************************************************************** - ~variant() - { - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - operation = operation_type::do_operation; // Null operation. - type_id = variant_npos; - } + // ~variant() is provided by base_type //*************************************************************************** /// Emplace by type with variadic constructor parameters. //*************************************************************************** template - T& emplace(TArgs&&... args) + T& emplace(TArgs&&... args) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) { static_assert(etl::is_one_of::value, "Unsupported type"); using type = etl::remove_cvref_t; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(etl::forward(args)...)); type_id = index_of_type::value; - return *static_cast(data); + return get_value::value>(); } #if ETL_HAS_INITIALIZER_LIST @@ -664,23 +728,18 @@ namespace etl //*************************************************************************** template T& emplace(std::initializer_list il, TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) { static_assert(etl::is_one_of::value, "Unsupported type"); using type = etl::remove_cvref_t; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, il, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(il, etl::forward(args)...)); type_id = index_of_type::value; - return *static_cast(data); + return get_value::value>(); } #endif @@ -689,23 +748,18 @@ namespace etl //*************************************************************************** template typename etl::variant_alternative_t >& emplace(TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) { static_assert(Index < sizeof...(TTypes), "Index out of range"); using type = type_from_index; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(etl::forward(args)...)); type_id = Index; - return *static_cast(data); + return get_value(); } #if ETL_HAS_INITIALIZER_LIST @@ -714,23 +768,18 @@ namespace etl //*************************************************************************** template typename etl::variant_alternative_t >& emplace(std::initializer_list il, TArgs&&... args) + ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) { static_assert(Index < sizeof...(TTypes), "Index out of range"); using type = type_from_index; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, il, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(il, etl::forward(args)...)); type_id = Index; - return *static_cast(data); + return get_value(); } #endif @@ -738,22 +787,17 @@ namespace etl /// Move assignment operator for type. ///\param value The value to assign. //*************************************************************************** - template , variant>::value, int> = 0> + template , variant>::value, int> = 0> variant& operator=(T&& value) { using type = etl::remove_cvref_t; static_assert(etl::is_one_of::value, "Unsupported type"); - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } + do_destroy(); + do_construct(etl::forward(value)); - construct_in_place(data, etl::forward(value)); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; - type_id = index_of_type::value; + type_id = index_of_type::value; return *this; } @@ -762,23 +806,19 @@ namespace etl /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(const variant& other) + variant& operator=(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) { if (this != &other) { - if (!valueless_by_exception()) - { - operation(Destroy, data, nullptr); - } - - if (other.valueless_by_exception()) + if (other.index() == variant_npos) { type_id = variant_npos; } else { - operation = other.operation; - operation(Copy, data, other.data); + do_destroy(); + + do_copy_from(other, etl::integral_constant{}); type_id = other.type_id; } @@ -791,23 +831,19 @@ namespace etl /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** - variant& operator=(variant&& other) + variant& operator=(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) { if (this != &other) { - if (!valueless_by_exception()) - { - operation(Destroy, data, nullptr); - } - - if (other.valueless_by_exception()) + if (other.index() == variant_npos) { type_id = variant_npos; } else { - operation = other.operation; - operation(Move, data, other.data); + do_destroy(); + + do_move_from(other, etl::integral_constant{}); type_id = other.type_id; } @@ -845,8 +881,8 @@ namespace etl } //*************************************************************************** - /// Checks to see if the type currently stored is the same as that specified - /// in the template parameter. For compatibility with legacy variant API. + /// Checks to see if the type currently stored is the same as that specified in the template parameter. + /// For compatibility with legacy variant API. ///\return true if it is the specified type, otherwise false. //*************************************************************************** template (), int> = 0> @@ -863,9 +899,9 @@ namespace etl } //*************************************************************************** - /// Checks if the other variant holds the same type as the current stored - /// type. For variants with the same type declarations. For compatibility - /// with legacy variant API. + /// Checks if the other variant holds the same type as the current stored type. + /// For variants with the same type declarations. + /// For compatibility with legacy variant API. ///\return true if the types are the same, otherwise false. //*************************************************************************** constexpr bool is_same_type(const variant& other) const @@ -1009,29 +1045,15 @@ namespace etl private: - /// The operation function type. - using operation_function = void (*)(int, char*, const char*); - //*************************************************************************** - /// Construct the type in-place. lvalue reference. + /// Construct the type in-place via perfect forwarding. //*************************************************************************** - template - static void construct_in_place(char* pstorage, const T& value) + template + static void construct_in_place(char* pstorage, U&& value) { using type = etl::remove_cvref_t; - ::new (pstorage) type(value); - } - - //*************************************************************************** - /// Construct the type in-place. rvalue reference. - //*************************************************************************** - template - static void construct_in_place(char* pstorage, T&& value) - { - using type = etl::remove_cvref_t; - - ::new (pstorage) type(etl::move(value)); + ::new (pstorage) type(etl::forward(value)); } //*************************************************************************** @@ -1049,13 +1071,84 @@ namespace etl /// Default construct the type in-place. //*************************************************************************** template - static void default_construct_in_place(char* pstorage) + static void default_construct_in_place(char* pstorage) ETL_NOEXCEPT_IF((etl::is_nothrow_default_constructible >::value)) { using type = etl::remove_cvref_t; ::new (pstorage) type(); } + //*************************************************************************** + /// Destroy the currently held value (only for non-trivially destructible). + //*************************************************************************** + void do_destroy() + { + do_destroy_impl(etl::integral_constant{}); + } + + void do_destroy_impl(etl::integral_constant) + { + // Trivially destructible: no-op. + } + + void do_destroy_impl(etl::integral_constant) + { + private_variant::variant_operations<0, TTypes...>::destroy(data, type_id); + } + + //*************************************************************************** + /// Construct a value in the union or buffer storage. + //*************************************************************************** + template + void do_construct(U&& value) + { + do_construct_impl(etl::forward(value), etl::integral_constant{}); + } + + template + void do_construct_impl(U&& value, etl::integral_constant) + { + // Trivially destructible: assign directly into the variadic_union. + private_variant::variadic_union_get::value>(data) = etl::forward(value); + } + + template + void do_construct_impl(U&& value, etl::integral_constant) + { + // Non-trivially destructible: use placement new. + ::new (static_cast(data)) T(etl::forward(value)); + } + + //*************************************************************************** + /// Copy from another variant. + //*************************************************************************** + void do_copy_from(const variant& other, etl::integral_constant) + { + // Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union. + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void do_copy_from(const variant& other, etl::integral_constant) + { + // Non-trivially destructible: use switch-based dispatch. + private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + } + + //*************************************************************************** + /// Move from another variant. + //*************************************************************************** + void do_move_from(variant& other, etl::integral_constant) + { + // Trivially destructible: copy via memcpy (trivial move == copy). + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void do_move_from(variant& other, etl::integral_constant) + { + // Non-trivially destructible: use switch-based dispatch. + private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + } + #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. @@ -1103,8 +1196,8 @@ namespace etl if (Index == index()) { // Workaround for MSVC (2023/05/13) - // It doesn't compile 'visitor.visit(etl::get(*this))' correctly - // for C++17 & C++20. Changed all of the instances for consistency. + // It doesn't compile 'visitor.visit(etl::get(*this))' correctly for C++17 & C++20. + // Changed all of the instances for consistency. auto& v = etl::get(*this); visitor.visit(v); return true; @@ -1124,8 +1217,8 @@ namespace etl if (Index == index()) { // Workaround for MSVC (2023/05/13) - // It doesn't compile 'visitor.visit(etl::get(*this))' correctly - // for C++17 & C++20. Changed all of the instances for consistency. + // It doesn't compile 'visitor.visit(etl::get(*this))' correctly for C++17 & C++20. + // Changed all of the instances for consistency. auto& v = etl::get(*this); visitor.visit(v); return true; @@ -1239,20 +1332,91 @@ namespace etl } //*************************************************************************** - /// The internal storage. - /// Aligned on a suitable boundary, which should be good for all types. + /// Get a reference to the stored value by index. + /// For trivially destructible types, accesses the variadic_union directly. + /// For non-trivially destructible types, uses pointer cast on uninitialized_buffer. //*************************************************************************** - etl::uninitialized_buffer data; + template + ETL_CONSTEXPR14 type_from_index& get_value() ETL_NOEXCEPT + { + return get_value_impl(etl::integral_constant{}); + } + + template + constexpr const type_from_index& get_value() const ETL_NOEXCEPT + { + return get_value_impl(etl::integral_constant{}); + } + + // Trivially destructible: use variadic_union accessor + template + ETL_CONSTEXPR14 type_from_index& get_value_impl(etl::integral_constant) ETL_NOEXCEPT + { + return private_variant::variadic_union_get(data); + } + + template + constexpr const type_from_index& get_value_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return private_variant::variadic_union_get(data); + } + + // Non-trivially destructible: use pointer cast on uninitialized_buffer + template + ETL_CONSTEXPR14 type_from_index& get_value_impl(etl::integral_constant) ETL_NOEXCEPT + { + return *static_cast*>(data); + } + + template + ETL_CONSTEXPR14 const type_from_index& get_value_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return *static_cast*>(data); + } //*************************************************************************** - /// The operation function. + /// Get a pointer to the stored value by type. //*************************************************************************** - operation_function operation; + template + ETL_CONSTEXPR14 T* get_value_ptr() ETL_NOEXCEPT + { + return get_value_ptr_impl(etl::integral_constant{}); + } - //*************************************************************************** - /// The id of the current stored type. - //*************************************************************************** - size_t type_id; + template + constexpr const T* get_value_ptr() const ETL_NOEXCEPT + { + return get_value_ptr_impl(etl::integral_constant{}); + } + + // Trivially destructible: use variadic_union accessor + template + ETL_CONSTEXPR14 T* get_value_ptr_impl(etl::integral_constant) ETL_NOEXCEPT + { + return &private_variant::variadic_union_get::value>(data); + } + + template + constexpr const T* get_value_ptr_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return &private_variant::variadic_union_get::value>(data); + } + + // Non-trivially destructible: use pointer cast on uninitialized_buffer + template + ETL_CONSTEXPR14 T* get_value_ptr_impl(etl::integral_constant) ETL_NOEXCEPT + { + return static_cast(data); + } + + template + ETL_CONSTEXPR14 const T* get_value_ptr_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return static_cast(data); + } + + // data and type_id are inherited from base_type. + // operation is inherited only for non-trivially destructible variants. }; namespace private_variant @@ -1351,9 +1515,7 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return *static_cast(v.data); + return v.template get_value(); } //*********************************** @@ -1366,9 +1528,7 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return etl::move(*static_cast(v.data)); + return etl::move(v.template get_value()); } //*********************************** @@ -1381,24 +1541,20 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return *static_cast(v.data); + return v.template get_value(); } //*********************************** template ETL_CONSTEXPR14 const etl::variant_alternative_t >&& get(const etl::variant&& v) { - #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) + #if ETL_USING_CPP17 & !defined(ETL_VARIANT_FORCE_CPP11) static_assert(Index < sizeof...(TTypes), "Index out of range"); #endif ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return etl::move(*static_cast(v.data)); + return etl::move(v.template get_value()); } //*********************************** @@ -1407,7 +1563,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return *static_cast(v.data); + return *v.template get_value_ptr(); } //*********************************** @@ -1416,7 +1572,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return etl::move(*static_cast(v.data)); + return etl::move(*v.template get_value_ptr()); } //*********************************** @@ -1425,7 +1581,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return *static_cast(v.data); + return *v.template get_value_ptr(); } //*********************************** @@ -1434,14 +1590,14 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return etl::move(*static_cast(v.data)); + return etl::move(*v.template get_value_ptr()); } //*************************************************************************** /// get_if //*************************************************************************** template < size_t Index, typename... TTypes > - ETL_CONSTEXPR14 etl::add_pointer_t< etl::variant_alternative_t > > get_if(etl::variant* pv) ETL_NOEXCEPT + ETL_CONSTEXPR14 etl::add_pointer_t > > get_if(etl::variant* pv) ETL_NOEXCEPT { if ((pv != nullptr) && (pv->index() == Index)) { @@ -1455,7 +1611,7 @@ namespace etl //*********************************** template < size_t Index, typename... TTypes > - ETL_CONSTEXPR14 etl::add_pointer_t< const etl::variant_alternative_t > > get_if(const etl::variant* pv) + ETL_CONSTEXPR14 etl::add_pointer_t > > get_if(const etl::variant* pv) ETL_NOEXCEPT { if ((pv != nullptr) && (pv->index() == Index)) @@ -1474,7 +1630,7 @@ namespace etl { if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return static_cast(pv->data); + return pv->template get_value_ptr(); } else { @@ -1488,7 +1644,7 @@ namespace etl { if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return static_cast(pv->data); + return pv->template get_value_ptr(); } else { @@ -1535,9 +1691,9 @@ namespace etl static ETL_CONSTEXPR14 TRet do_visit_single(TCallable&& f, TVariant&& v, TNext&&, TVariants&&... vs); //*************************************************************************** - /// Dummy-struct used to indicate that the return type should be - /// auto-deduced from the callable object and the alternatives in the - /// variants passed to a visit. Should never explicitly be used by an user. + /// Dummy-struct used to indicate that the return type should be auto-deduced + /// from the callable object and the alternatives in the variants passed to + /// a visit. Should never explicitly be used by an user. //*************************************************************************** struct visit_auto_return { @@ -1564,13 +1720,12 @@ namespace etl using rlref_copy = conditional_t::value, T&, T&&>; //*************************************************************************** - /// Evaluates all permutations of calls to a callable object that can be - /// done based upon the variants input. Need a `index_sequence<...>` as - /// second argument that contains all possible indices of the first - /// following variant. The first argument is essentially a - /// `single_visit_result_type`-prototype in which every recursive - /// instantiation of `visit_result_helper` appends more elements and give it - /// a pass through `common_type_t`. + /// Evaluates all permutations of calls to a callable object that can be done + /// based upon the variants input. Need a `index_sequence<...>` as second + /// argument that contains all possible indices of the first following variant. + /// The first argument is essentially a `single_visit_result_type`-prototype + /// in which every recursive instantiation of `visit_result_helper` appends + /// more elements and give it a pass through `common_type_t`. //*************************************************************************** template