diff --git a/.clang-format b/.clang-format
index aab677f7..ddf30aa2 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,7 +4,6 @@ Language: Json
IndentWidth: 2
UseTab: Never
---
-BasedOnStyle: LLVM
Language: JavaScript
IndentWidth: 4
UseTab: Never
diff --git a/.clang-tidy b/.clang-tidy
index a2bebd6b..70566ae8 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -4,8 +4,10 @@ Checks:
bugprone-*,
cert-*,
-cert-dcl58-cpp,
+ -cert-err58-cpp,
clang-analyzer-*,
concurrency-*,
+ -concurrency-mt-unsafe,
-cppcoreguidelines-*,
-google-*,
hicpp-*,
diff --git a/.codespellignore b/.codespellignore
index 2fddd5ed..39db8394 100644
--- a/.codespellignore
+++ b/.codespellignore
@@ -2,4 +2,6 @@ cancelled
copyable
pullrequest
snd
+dur
statics
+Claus
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 49fa500f..d78741f6 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# Codeowners for reviews on PRs
-* @dietmarkuehl @camio @neatudarius
+* @dietmarkuehl @camio
diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
index c86fedb9..7285f129 100644
--- a/.github/workflows/ci_tests.yml
+++ b/.github/workflows/ci_tests.yml
@@ -13,10 +13,10 @@ on:
jobs:
beman-submodule-check:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.3.0
preset-test:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.3.0
with:
matrix_config: >
[
@@ -29,7 +29,7 @@ jobs:
]
build-and-test:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.3.0
with:
matrix_config: >
{
@@ -41,7 +41,8 @@ jobs:
{ "stdlibs": ["libstdc++"],
"tests": [
"Debug.Default", "Release.Default", "Release.MaxSan",
- "Debug.Dynamic", "Debug.Coverage"
+ "Debug.Coverage", "Debug.Werror",
+ "Debug.Dynamic", "Release.Dynamic"
]
}
]
@@ -51,7 +52,7 @@ jobs:
}
]
},
- { "versions": ["14", "13"],
+ { "versions": ["14"],
"tests": [
{ "cxxversions": ["c++26", "c++23"],
"tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}]
@@ -60,30 +61,30 @@ jobs:
}
],
"clang": [
- { "versions": ["20"],
+ { "versions": ["22"],
"tests": [
{"cxxversions": ["c++26"],
"tests": [
- { "stdlibs": ["libstdc++", "libc++"],
+ { "stdlibs": ["libc++"],
"tests": [
"Debug.Default", "Release.Default", "Release.MaxSan",
- "Debug.Dynamic"
+ "Debug.Dynamic", "Release.Dynamic"
]
}
]
},
{ "cxxversions": ["c++23"],
"tests": [
- {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]}
+ {"stdlibs": ["libc++"], "tests": ["Release.Default"]}
]
}
]
},
- { "versions": ["19"],
+ { "versions": ["21", "20", "19"],
"tests": [
{ "cxxversions": ["c++26", "c++23"],
"tests": [
- {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]}
+ {"stdlibs": ["libc++"], "tests": ["Release.Default"]}
]
}
]
@@ -95,7 +96,9 @@ jobs:
{ "cxxversions": ["c++23"],
"tests": [
{ "stdlibs": ["stl"],
- "tests": ["Debug.Default", "Release.Default"]
+ "tests": ["Debug.Default", "Release.Default", "Release.MaxSan",
+ "Debug.Dynamic", "Release.Dynamic"
+ ]
}
]
}
@@ -107,4 +110,4 @@ jobs:
create-issue-when-fault:
needs: [preset-test, build-and-test]
if: failure() && github.event_name == 'schedule'
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.3.0
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit-check.yml
similarity index 90%
rename from .github/workflows/pre-commit.yml
rename to .github/workflows/pre-commit-check.yml
index 70895b4e..2f911038 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit-check.yml
@@ -10,4 +10,4 @@ on:
jobs:
pre-commit:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.3.0
diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml
new file mode 100644
index 00000000..930b750c
--- /dev/null
+++ b/.github/workflows/pre-commit-update.yml
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+name: Weekly pre-commit autoupdate
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 16 * * 0"
+
+jobs:
+ auto-update-pre-commit:
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-update-pre-commit.yml@1.3.0
+ secrets:
+ APP_ID: ${{ secrets.AUTO_PR_BOT_APP_ID }}
+ PRIVATE_KEY: ${{ secrets.AUTO_PR_BOT_PRIVATE_KEY }}
diff --git a/.gitignore b/.gitignore
index 166fdc85..bc32eb4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,19 +35,23 @@ CMakeUserPresets.json
*.app
build
+cmake-build-*
.DS_store
.vs
.cache
.vscode
+.idea
compile_commands.json
stagedir
# In-source builds are not allowed
CMakeCache.txt
CMakeFiles/
+GNUmakefile
*.log
docs/html
docs/latex
docs/code/*.cpp
docs/tutorial.md
+gcm.cache
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5baf30b5..0742c317 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,10 +13,10 @@ repos:
# Config file: .codespellrc
- repo: https://github.com/codespell-project/codespell
- rev: v2.4.1
+ rev: v2.4.2
hooks:
- id: codespell
- files: ^.*\.(cmake|cpp|hpp|txt|md|json|in|yaml|yml|py|toml)$
+ files: ^.*\.(cmake|cpp|cppm|hpp|txt|md|mds|json|js|in|yaml|yml|py|toml)$
args: ["--write", "--ignore-words", ".codespellignore" ]
# Clang-format for C++
@@ -24,15 +24,15 @@ repos:
# See also: https://github.com/ssciwr/clang-format-wheel
# Config file: .clang-format
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v21.1.7
+ rev: v22.1.5
hooks:
- id: clang-format
- types_or: [c++, c, json]
+ types_or: [c++, c, json, javascript]
exclude: docs/TODO.json
# CMake linting and formatting
- - repo: https://github.com/BlankSpruce/gersemi
- rev: 0.23.2
+ - repo: https://github.com/BlankSpruce/gersemi-pre-commit
+ rev: 0.27.7
hooks:
- id: gersemi
name: CMake linting
@@ -48,15 +48,21 @@ repos:
# Config file: pyproject.toml
# first Python sort imports
- repo: https://github.com/pycqa/isort
- rev: 7.0.0
+ rev: 9.0.0a3
hooks:
- id: isort
# Config file: pyproject.toml
# second Python code formatting
- repo: https://github.com/psf/black
- rev: 25.11.0
+ rev: 26.5.1
hooks:
- id: black
+ # Beman Standard checking via beman-tidy
+ # - repo : https://github.com/bemanproject/beman-tidy
+ # rev: v0.5.1
+ # hooks:
+ # - id: beman-tidy
+
exclude: 'infra/'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 409e197c..58de2a65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,74 +3,92 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# gersemi: on
-cmake_minimum_required(VERSION 3.25...4.2)
+cmake_minimum_required(VERSION 3.30...4.3)
-#========================== pre project settings ===============================
-# gersemi: off
-if(CMAKE_VERSION VERSION_EQUAL 4.2)
- set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
-
- if(CMAKE_CXX_STDLIB_MODULES_JSON)
- message(
- STATUS
- "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}"
- )
- endif()
-endif()
-# gersemi: on
-#===============================================================================
+include(./cmake/prelude.cmake)
#===================================================
-project(beman_execution VERSION 0.0.1 LANGUAGES CXX)
+project(beman.execution VERSION 0.3.0 LANGUAGES CXX)
#===================================================
-if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
- message(FATAL_ERROR "In-source builds are not allowed!")
-endif()
+# Modules opt in only on compilers that support it: msvc, g++-15 and clang-20+
+include(./cmake/cxx-modules-rules.cmake)
-set(TARGET_NAME execution)
-set(TARGET_NAMESPACE beman)
-set(TARGET_PREFIX ${TARGET_NAMESPACE}.${TARGET_NAME})
-set(TARGET_LIBRARY ${PROJECT_NAME})
-set(TARGET_ALIAS ${TARGET_NAMESPACE}::${TARGET_NAME})
-set(TARGET_PACKAGE_NAME ${PROJECT_NAME}-config)
-set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-config-targets)
-
-#========================== post project settings ==============================
-# Tell CMake that we explicitly want `import std`.
-# This will initialize the property on all targets declared after this to 1
-message(STATUS "CMAKE_CXX_COMPILER_IMPORT_STD=${CMAKE_CXX_COMPILER_IMPORT_STD}")
-if(${CMAKE_CXX_STANDARD} IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD)
- set(CMAKE_CXX_MODULE_STD ON)
- message(STATUS "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}")
-endif()
+set(BEMAN_EXECUTION_TARGET_NAME beman.execution_headers) # used in src, and docs
+set(BEMAN_EXECUTION_TARGET_PREFIX ${PROJECT_NAME}) # NOTE: used in src, and docs?
-if(CMAKE_CXX_SCAN_FOR_MODULES AND ${CMAKE_GENERATOR} STREQUAL Ninja)
- set(BEMAN_USE_MODULES ON)
- message(STATUS "BEMAN_USE_MODULES=${BEMAN_USE_MODULES}")
-else()
- message(WARNING "Missing support for CMAKE_CXX_SCAN_FOR_MODULES!")
-endif()
+#===============================================================================
+if(BEMAN_USE_MODULES)
+ option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
+
+ set(CMAKE_CXX_SCAN_FOR_MODULES ON)
+ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
+
+ # CMake requires the language standard to be specified as compile feature
+ # when a target provides C++23 modules and the target will be installed
+ add_library(${BEMAN_EXECUTION_TARGET_PREFIX} STATIC) # FIXME: only static yet! CK
+ add_library(beman::execution ALIAS ${BEMAN_EXECUTION_TARGET_PREFIX})
+ target_compile_features(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC cxx_std_${CMAKE_CXX_STANDARD}
+ )
-# gersemi: off
-if(CMAKE_EXPORT_COMPILE_COMMANDS)
- set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
- message(
- STATUS
- "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}"
+ # TODO(CK): the export header should be used!
+ include(GenerateExportHeader)
+ generate_export_header(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ BASE_NAME ${BEMAN_EXECUTION_TARGET_PREFIX}
+ EXPORT_FILE_NAME beman/execution/modules_export.hpp
+ )
+ target_sources(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS include ${CMAKE_CURRENT_BINARY_DIR}
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/beman/execution/modules_export.hpp
+ )
+ target_compile_definitions(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC BEMAN_HAS_MODULES
+ )
+ set_target_properties(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ DLL_NAME_WITH_SOVERSION TRUE
)
-endif()
-# gersemi: on
-# CMake requires the language standard to be specified as compile feature
-# when a target provides C++23 modules and the target will be installed
-add_library(${TARGET_NAME} STATIC)
-target_compile_features(${TARGET_NAME} PUBLIC cxx_std_23)
+ # FIXME: Quickfix only to prevent linker problems on windows dll? CK
+ if(WIN32 AND BUILD_SHARED_LIBS)
+ set_target_properties(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON
+ )
+ endif()
-if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD)
- target_compile_definitions(${TARGET_NAME} PUBLIC BEMAN_HAS_IMPORT_STD)
+ find_package(Threads REQUIRED)
+ target_link_libraries(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC Threads::Threads
+ )
else()
- message(WARNING "Missing support for CMAKE_CXX_MODULE_STD!")
+ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+endif()
+
+if(BEMAN_USE_MODULES AND BEMAN_HAS_IMPORT_STD)
+ target_compile_definitions(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC BEMAN_HAS_IMPORT_STD
+ )
+ set_target_properties(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PROPERTIES CXX_MODULE_STD ON
+ )
+elseif(BEMAN_USE_MODULES)
+ message(WARNING "Missing or disabled support for CMAKE_CXX_MODULE_STD!")
endif()
#===============================================================================
@@ -86,20 +104,19 @@ option(
${PROJECT_IS_TOP_LEVEL}
)
-option(
- BEMAN_EXECUTION_ENABLE_INSTALL
- "Install the project components. Values: { ON, OFF }."
- ${PROJECT_IS_TOP_LEVEL}
-)
+add_subdirectory(src/beman/execution)
-include(GNUInstallDirs)
-set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+#===============================================================================
+# NOTE: this must be done before tests! CK
+include(infra/cmake/beman-install-library.cmake)
+beman_install_library(${BEMAN_EXECUTION_TARGET_PREFIX} TARGETS ${BEMAN_EXECUTION_TARGET_NAME} ${BEMAN_EXECUTION_TARGET_PREFIX}
+ DEPENDENCIES Threads
+)
+#===============================================================================
-add_subdirectory(src/beman/execution)
+enable_testing()
if(BEMAN_EXECUTION_ENABLE_TESTING)
- enable_testing()
-
add_subdirectory(tests/beman/execution)
endif()
@@ -107,31 +124,3 @@ if(BEMAN_EXECUTION_BUILD_EXAMPLES)
add_subdirectory(examples)
add_subdirectory(docs/code)
endif()
-
-if(NOT BEMAN_EXECUTION_ENABLE_INSTALL OR CMAKE_SKIP_INSTALL_RULES)
- return()
-endif()
-
-include(CMakePackageConfigHelpers)
-
-write_basic_package_version_file(
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PACKAGE_NAME}-version.cmake
- VERSION ${CMAKE_PROJECT_VERSION}
- COMPATIBILITY AnyNewerVersion
-)
-
-configure_package_config_file(
- "cmake/Config.cmake.in"
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PACKAGE_NAME}.cmake
- INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
-)
-
-install(
- FILES
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PACKAGE_NAME}.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PACKAGE_NAME}-version.cmake
- DESTINATION ${INSTALL_CONFIGDIR}
-)
-
-set(CPACK_GENERATOR TGZ)
-include(CPack)
diff --git a/CMakePresets.json b/CMakePresets.json
index b56d96e6..61065330 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -7,11 +7,13 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
+ "BEMAN_USE_MODULES": true,
+ "BEMAN_USE_STD_MODULE": true,
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_EXTENSIONS": true,
- "CMAKE_CXX_SCAN_FOR_MODULES": false,
"CMAKE_CXX_STANDARD_REQUIRED": true,
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
+ "CMAKE_INSTALL_MESSAGE": "LAZY",
"CMAKE_SKIP_TEST_ALL_DEPENDENCY": false,
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "infra/cmake/use-fetch-content.cmake"
}
@@ -19,6 +21,17 @@
{
"name": "_debug-base",
"hidden": true,
+ "warnings": {
+ "dev": true,
+ "deprecated": true,
+ "uninitialized": true,
+ "unusedCli": true,
+ "systemVars": false
+ },
+ "errors": {
+ "dev": false,
+ "deprecated": false
+ },
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BEMAN_BUILDSYS_SANITIZER": "MaxSan"
@@ -61,7 +74,12 @@
"_debug-base"
],
"cacheVariables": {
- "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake"
+ "BEMAN_USE_STD_MODULE": false,
+ "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake"
+ },
+ "environment": {
+ "CXX": "clang++",
+ "CMAKE_CXX_FLAGS": "-stdlib=libc++"
}
},
{
@@ -72,28 +90,39 @@
"_release-base"
],
"cacheVariables": {
- "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake"
+ "BEMAN_USE_STD_MODULE": false,
+ "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-libc++-toolchain.cmake"
+ },
+ "environment": {
+ "CXX": "clang++",
+ "CMAKE_CXX_FLAGS": "-stdlib=libc++"
}
},
{
"name": "appleclang-debug",
+ "hidden": true,
"displayName": "Appleclang Debug Build",
"inherits": [
"_root-config",
"_debug-base"
],
"cacheVariables": {
+ "BEMAN_USE_STD_MODULE": false,
+ "BEMAN_USE_MODULES": false,
"CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake"
}
},
{
"name": "appleclang-release",
+ "hidden": true,
"displayName": "Appleclang Release Build",
"inherits": [
"_root-config",
"_release-base"
],
"cacheVariables": {
+ "BEMAN_USE_STD_MODULE": false,
+ "BEMAN_USE_MODULES": false,
"CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake"
}
},
@@ -134,7 +163,11 @@
{
"name": "_root-build",
"hidden": true,
- "jobs": 0
+ "jobs": 0,
+ "targets": [
+ "all_verify_interface_header_sets",
+ "all"
+ ]
},
{
"name": "gcc-debug",
diff --git a/LICENSE.txt b/LICENSE
similarity index 93%
rename from LICENSE.txt
rename to LICENSE
index 0873f35a..f6db814c 100644
--- a/LICENSE.txt
+++ b/LICENSE
@@ -1,6 +1,3 @@
-==============================================================================
-The Beman Project is under the Apache License v2.0 with LLVM Exceptions:
-==============================================================================
Apache License
Version 2.0, January 2004
@@ -220,15 +217,3 @@ conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
-
-==============================================================================
-Software from third parties included in the Beman Project:
-==============================================================================
-The Beman Project contains third party software which is under different license
-terms. All such code will be identified clearly using at least one of two
-mechanisms:
-1) It will be in a separate directory tree with its own `LICENSE.txt` or
- `LICENSE` file at the top containing the specific license and restrictions
- which apply to that software, or
-2) It will contain specific license and restriction terms at the top of every
- file.
diff --git a/Makefile b/Makefile
index 825c9daf..6bd96c88 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ SANITIZERS := run
# SANITIZERS += asan # TODO: tsan msan
# endif
-.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format unstage $(SANITIZERS)
+.PHONY: default release debug doc run update check ce todo distclean clean codespell clang-tidy build test install all format unstage $(SANITIZERS) module build-module test-module build-interface run-ci
SYSROOT ?=
TOOLCHAIN ?=
@@ -28,6 +28,9 @@ ifeq ($(CXX_BASE),clang++)
COMPILER=clang++
endif
+# NOTE default gmake use this flags!
+# COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
+# LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
LDFLAGS ?=
SAN_FLAGS ?=
CXX_FLAGS ?= -g
@@ -42,22 +45,29 @@ EXAMPLE = beman.execution.examples.stop_token
################################################
ifeq (${hostSystemName},Darwin)
- export LLVM_PREFIX:=$(shell brew --prefix llvm)
- export LLVM_DIR:=$(shell realpath ${LLVM_PREFIX})
- export PATH:=${LLVM_DIR}/bin:${PATH}
+ # export LLVM_PREFIX:=$(shell brew --prefix llvm)
+ # export LLVM_DIR:=$(shell realpath ${LLVM_PREFIX})
+ # export PATH:=${LLVM_DIR}/bin:${PATH}
- # export CMAKE_CXX_STDLIB_MODULES_JSON=${LLVM_DIR}/lib/c++/libc++.modules.json
# export CXX=clang++
# export LDFLAGS=-L$(LLVM_DIR)/lib/c++ -lc++abi -lc++ # -lc++experimental
# export GCOV="llvm-cov gcov"
### TODO: to test g++-15:
- export GCC_PREFIX:=$(shell brew --prefix gcc)
- export GCC_DIR:=$(shell realpath ${GCC_PREFIX})
-
- export CMAKE_CXX_STDLIB_MODULES_JSON=${GCC_DIR}/lib/gcc/current/libstdc++.modules.json
- # export CXX:=g++-15
- # export CXXFLAGS:=-stdlib=libstdc++
+ # export GCC_PREFIX:=$(shell brew --prefix gcc)
+ # export GCC_DIR:=$(shell realpath ${GCC_PREFIX})
+
+ ifeq ($(origin CXX),default)
+ $(info CXX is using the built-in default: $(CXX))
+ ifeq ($(shell uname -m),arm64)
+ export CXX=clang++
+ CXXLIB=libc++
+ else
+ export CXX=g++-15
+ CXXLIB=libstdc++
+ endif
+ export CXX_FLAGS=-stdlib=$(CXXLIB)
+ endif
export GCOV="gcov"
else ifeq (${hostSystemName},Linux)
export LLVM_DIR=/usr/lib/llvm-20
@@ -97,7 +107,23 @@ endif
# TODO: beman.execution.examples.modules
# FIXME: beman.execution.execution-module.test beman.execution.stop-token-module.test
-default: test
+default: simple
+
+SIMPLE_BUILD = build/simple-$(shell uname -s)
+
+.PHONY: simple simple-configure simple-build simple-test
+
+simple: simple-test
+
+simple-config:
+ cmake -G Ninja -S . -B $(SIMPLE_BUILD) -DBEMAN_USE_MODULES=OFF -DCXXFLAGS=
+
+simple-build: simple-config
+ CXXFLAGS= cmake --build $(SIMPLE_BUILD)
+
+simple-test: simple-build
+ ctest --test-dir $(SIMPLE_BUILD)
+
all: $(SANITIZERS)
@@ -108,35 +134,83 @@ doc:
./bin/mk-doc.py docs/*.mds
doxygen docs/Doxyfile
+LLVM_VERSION=22.1.0
+DEV_DIR=build/dev-$(shell uname -s)
+TESTCASE = *
+DEV_TEST_OPTION=-DBEMAN_EXECUTION_TEST_CASE=$(TESTCASE)
+
+dev-config:
+ PATH=/opt/llvm-$(LLVM_VERSION)/bin:$$PATH CXX=clang++ cmake -B $(DEV_DIR) -G Ninja -DBEMAN_EXECUTION_BUILD_EXAMPLES=OFF -DBEMAN_EXECUTION_INSTALL_CONFIG_FILE_PACKAGE=OFF $(DEV_TEST_OPTION)
+
+dev-build: dev-config
+ PATH=/opt/llvm-$(LLVM_VERSION)/bin:$$PATH CXX=clang++ cmake --build $(DEV_DIR)
+
+dev-test: dev-build
+ PATH=/opt/llvm-$(LLVM_VERSION)/bin:$$PATH CXX=clang++ ctest --test-dir $(DEV_DIR) -R beman.execution.$(TESTCASE).test
+
+dev: dev-test
+
+
# $(SANITIZERS):
# $(MAKE) SANITIZER=$@
-build:
- cmake --fresh -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \
+# ==========================================================
+# NOTE: cmake configure to only test without modules! CK
+# ==========================================================
+build build-interface:
+ @echo CXX=$(CXX) CXX_FLAGS=$(CXX_FLAGS)
+ cmake -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \
-D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
-D CMAKE_SKIP_INSTALL_RULES=ON \
-D CMAKE_CXX_STANDARD=23 \
-D CMAKE_CXX_EXTENSIONS=ON \
-D CMAKE_CXX_STANDARD_REQUIRED=ON \
- -D CMAKE_CXX_COMPILER=$(CXX) # XXX -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)"
- cmake --build $(BUILD)
+ -D BEMAN_USE_MODULES=OFF \
+ -D BEMAN_USE_STD_MODULE=OFF \
+ -D CMAKE_BUILD_TYPE=Release \
+ -D CMAKE_SKIP_TEST_ALL_DEPENDENCY=OFF \
+ -D CMAKE_CXX_COMPILER=$(CXX) --log-level=VERBOSE --fresh \
+ -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)"
+ # XXX -D CMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)"
+ cmake --build $(BUILD) --target all_verify_interface_header_sets
+ cmake --build $(BUILD) --target all
# NOTE: without install, see CMAKE_SKIP_INSTALL_RULES! CK
test: build
ctest --test-dir $(BUILD) --rerun-failed --output-on-failure
+module build-module:
+ cmake -G Ninja -S $(SOURCEDIR) -B $(BUILD) $(TOOLCHAIN) $(SYSROOT) \
+ -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
+ -D CMAKE_SKIP_INSTALL_RULES=OFF \
+ -D CMAKE_CXX_STANDARD=23 \
+ -D CMAKE_CXX_EXTENSIONS=ON \
+ -D CMAKE_CXX_STANDARD_REQUIRED=ON \
+ -D BEMAN_USE_MODULES=ON \
+ -D BEMAN_USE_STD_MODULE=ON \
+ -D CMAKE_BUILD_TYPE=Release \
+ -D CMAKE_INSTALL_MESSAGE=LAZY \
+ -D CMAKE_BUILD_TYPE=Release \
+ -D CMAKE_CXX_COMPILER=$(CXX) --log-level=VERBOSE
+ cmake --build $(BUILD)
+
+test-module: build-module
+ ctest --test-dir $(BUILD) --rerun-failed --output-on-failure
+
install: test
cmake --install $(BUILD) --prefix /opt/local
CMakeUserPresets.json:: cmake/CMakeUserPresets.json
ln -s $< $@
-release: CMakeUserPresets.json
+# ==========================================================
+appleclang-release llvm-release release:
cmake --preset $@ --log-level=TRACE # XXX --fresh
ln -fs $(BUILDROOT)/$@/compile_commands.json .
cmake --workflow --preset $@
-debug: CMakeUserPresets.json
+# ==========================================================
+appleclang-debug llvm-debug debug:
cmake --preset $@ --log-level=TRACE # XXX --fresh
ln -fs $(BUILDROOT)build/$@/compile_commands.json .
cmake --workflow --preset $@
@@ -165,8 +239,14 @@ clang-tidy: $(BUILD)/compile_commands.json
codespell:
pre-commit run $@
+run-ci:
+ /Library/Frameworks/Python.framework/Versions/3.14/bin/beman-local-ci -p 2
+
+# ==========================================================
format:
+ pre-commit autoupdate
pre-commit run --all
+# ==========================================================
cmake-format:
pre-commit run gersemi
diff --git a/README.md b/README.md
index 5f22e037..d9bb3a8e 100644
--- a/README.md
+++ b/README.md
@@ -5,9 +5,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-
-[](https://coveralls.io/github/bemanproject/execution?branch=main)
+  [](https://coveralls.io/github/bemanproject/execution?branch=main)
`beman.execution` provides the basic vocabulary for asynchronous
programming as well as important algorithms implemented in terms
@@ -41,7 +39,7 @@ e.g.:
**Implements:** [`std::execution` (P2300R10)](http://wg21.link/P2300R10).
-**Status**: [Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/BEMAN_LIBRARY_MATURITY_MODEL.md#under-development-and-not-yet-ready-for-production-use)
+**Status**: [Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/beman_library_maturity_model.md#under-development-and-not-yet-ready-for-production-use)
## Help Welcome!
@@ -55,7 +53,7 @@ contains some links for general information about the sender/receivers and `std:
## Preconditions
- cmake v3.30 or newer
-- ninja v1.11.1 or newer
+- ninja v1.13.0 or newer
- A compiler that supports at least C++23
@@ -75,8 +73,6 @@ The following instructions build the library and the examples:
"gcc-release"
"llvm-debug"
"llvm-release"
- "appleclang-debug"
- "appleclang-release"
"msvc-debug"
"msvc-release"
@@ -86,6 +82,18 @@ The following instructions build the library and the examples:
The implementation compiles and passes tests using [clang](https://clang.llvm.org/),
[gcc](http://gcc.gnu.org), and [MSVC++](https://visualstudio.microsoft.com/vs/features/cplusplus/).
+### Supported Platforms
+
+| Compiler | Version | C++ Standards | Standard Library |
+|----------|---------|---------------|------------------|
+| GCC | 15-14 | C++26, C++23 | libstdc++ |
+| Clang | 22-19 | C++26, C++23 | libc++ |
+| MSVC | latest | C++23 | MSVC STL |
+
+## License
+
+beman.execution is licensed under the Apache License v2.0 with LLVM Exceptions.
+
## Examples
- `` example: [Compiler Explorer](https://godbolt.org/z/4r4x9q1r7)
diff --git a/bin/mk-module.py b/bin/mk-module.py
new file mode 100755
index 00000000..c0c44feb
--- /dev/null
+++ b/bin/mk-module.py
@@ -0,0 +1,270 @@
+#!/usr/bin/python3
+# bin/mk-module.py
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+import glob
+import inspect
+import os
+import re
+import sys
+
+head_re = re.compile("include/(?P.*)\.hpp")
+
+
+def make_loc(file, number):
+ loc = {}
+ loc["number"] = number
+ loc["file"] = file
+ return loc
+
+
+class common_writer:
+ def write(self, loc, line):
+ self.do_write(loc, line)
+
+ def write_same(self, line):
+ self.do_write(self.loc, line)
+
+ def write_next(self, line):
+ self.do_next()
+ self.do_write(self.do_get_loc(), line)
+
+
+def clean_name(file):
+ match = head_re.match(file)
+ return match.group("name")
+
+
+top = []
+for toplevel in glob.glob("include/?eman/*/*.hpp"):
+ top.append(clean_name(toplevel))
+
+all = top.copy()
+for detail in glob.glob("include/?eman/*/?etail/*.hpp"):
+ all.append(clean_name(detail))
+
+headers = {}
+beman_re = re.compile('#include ["<](?P[bB]eman/.*)\.hpp[">]')
+other_re = re.compile('#include ["<](?P.*)[">]')
+
+
+class comment_writer(common_writer):
+ comment_re = re.compile("(.*)/\*.*\*/(.*)")
+ export_re = re.compile(".*export.*")
+ start_re = re.compile("(.*)/\*.*")
+ end_re = re.compile(".*\*/\s*(.*)")
+
+ def __init__(self, to):
+ self.to = to
+ self.in_comment = False
+
+ def do_get_loc(self):
+ return self.to.do_get_loc()
+
+ def do_next(self):
+ self.to.do_next()
+
+ def do_write(self, loc, line):
+ if self.in_comment:
+ match = self.end_re.match(line)
+ if match:
+ self.in_comment = False
+ self.to.write(loc, match.group(1))
+ return
+ match = self.comment_re.match(line)
+ if match:
+ if not self.export_re.match(match.group(1)):
+ self.to.write(loc, (match.group(1) + " " + match.group(2)).rstrip())
+ else:
+ self.to.write(loc, line)
+ return
+ match = self.start_re.match(line)
+ if match:
+ self.in_comment = True
+ line = match.group(1).rstrip()
+ self.to.write(loc, line)
+
+
+class filter_writer(common_writer):
+ included_re = re.compile(".*INCLUDED_BEMAN.*")
+ file_re = re.compile("// include/beman\S*\s*-.-C..-.-")
+ spdx_re = re.compile(".*SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.*")
+ namespace_re = re.compile(".*// namespace.*")
+ nolint_re = re.compile(".*// NOLINT.*")
+ comment_re = re.compile("(.*)//.*")
+ close_re = re.compile("^\s*}\s*$")
+ public_re = re.compile("^\s*public:\s*$")
+ else_re = re.compile("^\s*#\s*else\s*$")
+
+ def __init__(self, to):
+ self.to = to
+ self.previous_line_empty = False
+
+ def do_get_loc(self):
+ return self.to.do_get_loc()
+
+ def do_next(self):
+ self.to.do_next()
+
+ def include_this_line(self, line):
+ return (
+ not beman_re.match(line)
+ and not other_re.match(line)
+ and not self.included_re.match(line)
+ and not self.file_re.match(line)
+ and not self.spdx_re.match(line)
+ )
+
+ def do_write(self, loc, line):
+ if not self.include_this_line(line):
+ return
+ match = self.comment_re.match(line)
+ if (
+ match
+ and not self.namespace_re.match(line)
+ and not self.nolint_re.match(line)
+ ):
+ line = match.group(1).rstrip()
+ if line != "" or not self.previous_line_empty:
+ self.previous_line_empty = (
+ (line == "")
+ or (self.close_re.match(line) is not None)
+ or (self.public_re.match(line) is not None)
+ or (self.else_re.match(line) is not None)
+ )
+ self.to.write(loc, line)
+
+
+def get_dependencies(component):
+ deps = []
+ with open("include/" + component + ".hpp") as file:
+ for line in file.readlines():
+ if beman_re.match(line):
+ deps.append(beman_re.match(line).group("name"))
+ elif other_re.match(line):
+ header = other_re.match(line).group("name")
+ if header not in headers:
+ headers[header] = 1
+
+ return deps
+
+
+dependencies = {}
+
+for component in all:
+ dependencies[component] = get_dependencies(component)
+
+if len(sys.argv) != 2:
+ print(f"usage: {sys.argv[0]} ")
+ sys.exit(1)
+
+module_file = sys.argv[1]
+
+project_re = re.compile("(?P(?P[bB]eman)/.*)/")
+define_re = re.compile("#define")
+export_re = re.compile("BEMAN_EXECUTION_EXPORT (.*)")
+
+
+def write_header(to, header):
+ filename = f"include/{header}.hpp"
+ print(f"writing {filename}...")
+ with open(filename) as file:
+ number = 0
+ for line in file.readlines():
+ number += 1
+ match = export_re.match(line)
+ loc = make_loc(filename, number)
+ if match:
+ to.write(loc, f"export /* --------- */ {match.group(1)}")
+ else:
+ to.write(loc, line.rstrip())
+
+
+deps = {}
+
+
+def build_header(file, header):
+ todo = dependencies[header].copy()
+ while 0 < len(todo):
+ if not todo[0] in deps:
+ deps[todo[0]] = dependencies[todo[0]].copy()
+ for new in dependencies[todo[0]]:
+ todo.append(new)
+ todo = todo[1:]
+
+
+class file_writer(common_writer):
+ def __init__(self, to):
+ self.first_line = True
+ self.to = to
+ self.loc = make_loc("", 0)
+
+ def do_get_loc(self):
+ return self.loc
+
+ def do_next(self):
+ self.loc["number"] += 1
+
+ def do_write(self, loc, line):
+ if not self.first_line and (
+ loc["file"] != self.loc["file"] or loc["number"] != self.loc["number"]
+ ):
+ self.to.write(f"#line {loc['number']} \"{loc['file']}\"\n")
+ self.to.write(f"{line}\n")
+ self.loc = loc
+ self.loc["number"] += 1
+ self.first_line = False
+
+
+with open(module_file, "w") as file:
+ file_to = file_writer(file)
+ to = filter_writer(file_to)
+ to = comment_writer(to)
+
+ file_to.write(make_loc(sys.argv[0], inspect.currentframe().f_lineno), "module;")
+ file_to.write_next("// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception")
+ file_to.write_next("// *****************************************************;")
+ file_to.write_next("// *** WARNING: this file is generated: do not edit! ***;")
+ file_to.write_next("// *****************************************************;")
+ file_to.write_next(f"// generated by {sys.argv[0]} {sys.argv[1]}")
+ file_to.write_next("")
+ file_to.write_next("#include ")
+ file_to.write_next("#include ")
+ file_to.write_next("")
+ file_to.write_next("#ifdef BEMAN_HAS_IMPORT_STD")
+ file_to.write_next("import std;")
+ file_to.write_next("#else")
+ includes = list(headers.keys())
+ for include in sorted(includes):
+ file_to.write(
+ make_loc(sys.argv[0], inspect.currentframe().f_lineno),
+ f"#include <{include}>",
+ )
+ file_to.write_next("#endif")
+ file_to.write_next("")
+ file_to.write_next("export module beman.execution;")
+ file_to.write_next("")
+
+ for header in top:
+ if re.match(".*execution26.*", header):
+ continue
+
+ prolog_done = False
+ filename = f"include/{header}.hpp"
+ with open(filename) as file:
+ number = 0
+ for line in file.readlines():
+ number += 1
+ if not prolog_done and define_re.match(line):
+ prolog_done = True
+ to.write(make_loc(filename, number), "")
+ build_header(file, header)
+ to.write_next("")
+
+ while 0 < len(deps):
+ empty = [item for item in deps.keys() if 0 == len(deps[item])]
+ for e in empty:
+ write_header(to, e)
+ deps.pop(e, None)
+ for d in deps.keys():
+ deps[d] = [item for item in deps[d] if e != item]
diff --git a/bin/run-docker b/bin/run-docker
new file mode 100644
index 00000000..88ddd5c8
--- /dev/null
+++ b/bin/run-docker
@@ -0,0 +1 @@
+docker run -it --rm -v $PWD:$PWD -u $(id -u):$(id -g) ghcr.io/bemanproject/infra-containers-gcc:15 bash
diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in
deleted file mode 100644
index 40463a38..00000000
--- a/cmake/Config.cmake.in
+++ /dev/null
@@ -1,7 +0,0 @@
-# cmake/Config.cmake.in -*-makefile-*-
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-@PACKAGE_INIT@
-
-include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
-check_required_components("@TARGET_LIBRARY@")
diff --git a/cmake/README.md b/cmake/README.md
new file mode 100644
index 00000000..182a47a1
--- /dev/null
+++ b/cmake/README.md
@@ -0,0 +1,393 @@
+
+
+# CMake Support for Header-Only Libraries and C++ Modules
+
+This directory provides a small set of CMake helper modules that make it easier
+to **build and install a library that works both with and without C++20
+modules**.
+
+The helpers wrap CMake’s evolving support for `FILE_SET CXX_MODULE` and package
+installation so that:
+
+- consumers can use the library in **classic header-only mode**, or
+
+- consumers with a compatible toolchain can use **C++ modules**, including
+ optional `import std;` support.
+
+The goal is to keep project `CMakeLists.txt` files readable while hiding most of
+the platform- and compiler-specific complexity.
+
+---
+
+## Motivation
+
+Today, supporting C++ modules often requires duplicating targets and custom
+install logic:
+
+- one target for headers only,
+- another target that owns module interface units,
+- conditional logic depending on compiler and CMake version,
+- non-trivial install/export rules.
+
+The helpers in this directory centralize that logic and provide a single,
+consistent API for projects that want to offer both usage models.
+
+---
+
+## Provided CMake Modules
+
+The following modules are expected to live in `infra/cmake`:
+
+- `prelude.cmake`
+ Common project setup and option handling.
+
+- `cxx-modules-rules.cmake`
+ Compiler and CMake feature checks related to C++ modules.
+
+- `beman-install-library.cmake`
+ High-level helper to install libraries, headers, module interface units, and CMake package files.
+
+- `Config.cmake.in`
+ Template used to generate the `-config.cmake` file for consumers.
+
+---
+
+## New Configuration Options
+
+ * BEMAN_USE_MODULES:BOOL=ON
+ * BEMAN_USE_STD_MODULE:BOOL=OFF
+ * BEMAN_HAS_IMPORT_STD::BOOL=${CMAKE_CXX_SCAN_FOR_MODULES} # only if toolchain supports it.
+
+### The recommended usage in CMake code
+
+```cmake
+if(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
+ option(BEMAN_USE_MODULES "Build CXX_MODULES" ${CMAKE_CXX_SCAN_FOR_MODULES})
+endif()
+if(BEMAN_USE_MODULES)
+ target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_MODULES)
+endif()
+
+BEMAN_USE_STD_MODULE:BOOL=ON
+# -> "Check if 'import std;' is possible with the toolchain?"
+
+if(BEMAN_USE_MODULES AND BEMAN_HAS_IMPORT_STD)
+ target_compile_definitions(beman.execution PUBLIC BEMAN_HAS_IMPORT_STD)
+ set_target_properties(beman.execution PROPERTIES CXX_MODULE_STD ON)
+endif()
+```
+
+Typical projects will only toggle `BEMAN_USE_MODULES`; the remaining options are
+detected automatically.
+
+---
+
+## Installing a Library
+
+Installation is handled by the `beman_install_library()` helper.
+
+### Function Signature
+
+```cmake
+beman_install_library(name
+ TARGETS [ ...]
+ [DEPENDENCIES [ ...]]
+ [NAMESPACE ]
+ [EXPORT_NAME ]
+ [DESTINATION ]
+)
+```
+
+### Arguments
+
+- **name**
+ Logical package name (e.g. "beman.utility").
+ Used to derive config file names and cache variable prefixes.
+
+- **TARGETS**
+ Targets to install and export.
+
+- **DEPENDENCIES (optional)**
+ Semicolon-separated list, one dependency per entry.
+ Each entry is a valid `find_dependency()` argument list.
+
+ **NOTE:** you must use the bracket form for quoting if not only a package name is used!
+
+ `"[===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt"`
+
+- **NAMESPACE (optional)**
+ Namespace for imported targets.
+ Defaults to "beman::".
+
+- **EXPORT_NAME (optional)**
+ Name of the generated CMake export set.
+ Defaults to "-targets".
+
+- **DESTINATION (optional)**
+ Installation directory for C++ module interface units.
+ Defaults to `${CMAKE_INSTALL_INCLUDEDIR}/beman/modules`.
+
+This function installs the specified project TARGETS and its `FILE_SET
+TYPE HEADERS` to the default CMAKE install destination.
+
+It also handles the installation of the CMake config package files if
+needed. If the given targets has `FILE_SET TYPE CXX_MODULE`, it will also
+installed to the given DESTINATION
+
+- **Used Cache variables**
+
+`BEMAN_INSTALL_CONFIG_FILE_PACKAGES`
+ List of package names for which config files should be installed.
+
+`_INSTALL_CONFIG_FILE_PACKAGE`
+ Per-package override to enable/disable config file installation.
+ is the uppercased package name with dots replaced by underscores.
+
+### Caveats
+
+- **Only one `FILE_SET CXX_MODULES` is yet supported to install with this
+ function!**
+
+- **Only header files contained in a `PUBLIC FILE_SET TYPE HEADERS` will be
+ installed with this function!**
+
+---
+
+## The possible usage in CMakeLists.txt
+
+```cmake
+cmake_minimum_required(VERSION 3.30...4.2)
+
+include(./cmake/prelude.cmake)
+project(beman.execution VERSION 0.2.0 LANGUAGES CXX)
+include(./cmake/cxx-modules-rules.cmake)
+
+set(BEMAN_EXECUTION_TARGET_PREFIX ${PROJECT_NAME})
+
+#===============================================================================
+if(BEMAN_USE_MODULES)
+ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+ set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
+
+ # CMake requires the language standard to be specified as compile feature
+ # when a target provides C++23 modules and the target will be installed
+ add_library(${BEMAN_EXECUTION_TARGET_PREFIX} STATIC)
+ add_library(beman::execution ALIAS ${BEMAN_EXECUTION_TARGET_PREFIX})
+ target_compile_features(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC cxx_std_${CMAKE_CXX_STANDARD}
+ )
+
+ include(GenerateExportHeader)
+ generate_export_header(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ BASE_NAME ${BEMAN_EXECUTION_TARGET_PREFIX}
+ EXPORT_FILE_NAME beman/execution/modules_export.hpp
+ )
+ target_sources(
+ ${BEMAN_EXECUTION_TARGET_PREFIX}
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS include ${CMAKE_CURRENT_BINARY_DIR}
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/beman/execution/modules_export.hpp
+ )
+ target_compile_definitions(${BEMAN_EXECUTION_TARGET_PREFIX} PUBLIC BEMAN_HAS_MODULES)
+endif()
+
+if(BEMAN_USE_MODULES AND CMAKE_CXX_MODULE_STD)
+ target_compile_definitions(${BEMAN_EXECUTION_TARGET_PREFIX} PUBLIC BEMAN_HAS_IMPORT_STD)
+else()
+ message(WARNING "Missing support for CMAKE_CXX_MODULE_STD!")
+endif()
+#===============================================================================
+
+# ...
+
+# NOTE: this must be done before tests! CK
+include(beman-install-library)
+beman_install_library(${PROJECT_NAME} TARGETS ${BEMAN_EXECUTION_TARGET_PREFIX} beman.exemplar_headers #
+ # TODO(add): DEPENDENCIES [===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt
+)
+```
+
+---
+
+## Possible cmake config output
+
+**NOTE:** Exact output depend on the build host, used toolchain, and
+whether module support is enabled and supported.
+
+```bash
+bash-5.3$ make test-module
+cmake -G Ninja -S /Users/clausklein/Workspace/cpp/beman-project/execution26 -B build/Darwin/default \
+ -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
+ -D CMAKE_SKIP_INSTALL_RULES=OFF \
+ -D CMAKE_CXX_STANDARD=23 \
+ -D CMAKE_CXX_EXTENSIONS=ON \
+ -D CMAKE_CXX_STANDARD_REQUIRED=ON \
+ -D CMAKE_CXX_SCAN_FOR_MODULES=ON \
+ -D BEMAN_USE_MODULES=ON \
+ -D CMAKE_BUILD_TYPE=Release \
+ -D CMAKE_INSTALL_MESSAGE=LAZY \
+ -D CMAKE_BUILD_TYPE=Release \
+ -D CMAKE_CXX_COMPILER=clang++ --log-level=VERBOSE
+-- use ccache
+-- CXXFLAGS=-stdlib=libc++
+'brew' '--prefix' 'llvm'
+-- LLVM_DIR=/usr/local/Cellar/llvm/21.1.8
+-- CMAKE_CXX_STDLIB_MODULES_JSON=/usr/local/Cellar/llvm/21.1.8/lib/c++/libc++.modules.json
+-- CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=/usr/local/Cellar/llvm/21.1.8/include/c++/v1;/usr/local/Cellar/llvm/21.1.8/lib/clang/21/include;/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/usr/include
+-- CMAKE_CXX_STDLIB_MODULES_JSON=/usr/local/Cellar/llvm/21.1.8/lib/c++/libc++.modules.json
+-- BEMAN_USE_STD_MODULE=ON
+-- CMAKE_CXX_COMPILER_IMPORT_STD=23;26
+-- CMAKE_CXX_MODULE_STD=ON
+-- BEMAN_HAS_IMPORT_STD=ON
+-- BEMAN_USE_MODULES=ON
+-- CMAKE_CXX_SCAN_FOR_MODULES=ON
+-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY
+-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY - Success
+-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY
+-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY - Success
+-- Performing Test COMPILER_HAS_DEPRECATED_ATTR
+-- Performing Test COMPILER_HAS_DEPRECATED_ATTR - Success
+-- beman_install_library(beman.execution): COMPONENT execution_headers for TARGET 'beman.execution_headers'
+-- beman-install-library(beman.execution): 'beman.execution_headers' has INTERFACE_HEADER_SETS=public_headers
+-- beman_install_library(beman.execution): COMPONENT execution for TARGET 'beman.execution'
+-- beman-install-library(beman.execution): 'beman.execution' has INTERFACE_HEADER_SETS=HEADERS
+-- beman-install-library(beman.execution): 'beman.execution' has CXX_MODULE_SETS=CXX_MODULES
+-- Configuring done (3.1s)
+CMake Warning (dev) in CMakeLists.txt:
+ CMake's support for `import std;` in C++23 and newer is experimental. It
+ is meant only for experimentation and feedback to CMake developers.
+This warning is for project developers. Use -Wno-dev to suppress it.
+
+-- Generating done (0.4s)
+-- Build files have been written to: /Users/clausklein/Workspace/cpp/beman-project/execution26/build/Darwin/default
+bash-5.3$
+```
+
+---
+
+## Possible cmake export config package
+
+**NOTE:** Exact contents depend on the build host, used toolchain, and
+whether module support is enabled.
+
+```bash
+cmake --install build/Darwin/default --prefix $PWD/stagedir
+
+bash-5.3$ cd stagedir/
+bash-5.3$ tree lib/
+lib/
+├── cmake
+│ └── beman.execution
+│ ├── beman.execution-config-version.cmake
+│ ├── beman.execution-config.cmake
+│ ├── beman.execution-targets-debug.cmake
+│ ├── beman.execution-targets-relwithdebinfo.cmake
+│ ├── beman.execution-targets.cmake
+│ ├── bmi-GNU_Debug
+│ │ └── beman.execution.gcm
+│ ├── bmi-GNU_RelWithDebInfo
+│ │ └── beman.execution.gcm
+│ ├── cxx-modules
+│ │ ├── cxx-modules-beman.execution-Debug.cmake
+│ │ ├── cxx-modules-beman.execution-RelWithDebInfo.cmake
+│ │ ├── cxx-modules-beman.execution.cmake
+│ │ ├── target-execution-Debug.cmake
+│ │ └── target-execution-RelWithDebInfo.cmake
+│ └── modules
+│ └── execution.cppm
+├── libbeman.execution.a
+└── libbeman.execution_d.a
+
+7 directories, 15 files
+bash-5.3$
+```
+
+---
+
+## The recommended usage in implementation
+
+```cpp
+// identity.cppm
+module;
+
+#ifdef BEMAN_HAS_IMPORT_STD
+import std;
+#else
+#include
+#endif
+
+export module beman.exemplar;
+
+export namespace beman::exemplar {
+struct __is_transparent;
+
+struct identity {
+ template
+ constexpr T&& operator()(T&& t) const noexcept {
+ return std::forward(t);
+ }
+
+ using is_transparent = __is_transparent;
+};
+} // namespace beman::exemplar
+```
+
+---
+
+## The possible usage in user code
+
+```cpp
+// example-usage.cpp
+
+#ifdef BEMAN_HAS_IMPORT_STD
+import std;
+#else
+#include
+#endif
+
+#ifdef BEMAN_HAS_MODULES
+import beman.exemplar;
+#else
+#include
+#endif
+
+int main() {
+ beman::exemplar::identity id;
+
+ int x = 42;
+ int y = id(x); // y == 42
+
+ std::cout << y << '\n';
+ return 0;
+}
+```
+
+---
+
+## Notes on CMake and Modules
+
+- `FILE_SET CXX_MODULE` support is still evolving and depends on the compiler
+ and CMake version.
+
+- Not all toolchains support installing or consuming prebuilt module interface
+ units yet.
+
+- These helpers aim to provide a pragmatic, forward-compatible structure rather
+ than a final standard solution.
+
+---
+
+## Summary
+
+The helpers in this directory provide a structured way to:
+
+- build header-only and module-based libraries side by side,
+- hide compiler- and platform-specific complexity,
+- install and export targets in a consistent, consumer-friendly way.
+
+They are intended for projects that want to experiment with C++ modules today
+while remaining usable on traditional toolchains.
diff --git a/cmake/cxx-modules-rules.cmake b/cmake/cxx-modules-rules.cmake
new file mode 100644
index 00000000..91939b36
--- /dev/null
+++ b/cmake/cxx-modules-rules.cmake
@@ -0,0 +1,155 @@
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# A CMake language file to be included as the last step of all project() command calls.
+# This file must be included/used as CMAKE_PROJECT_INCLUDE -> after project()
+#
+
+# ---- The include guard applies within the current directory and below ----
+include_guard(DIRECTORY)
+
+if(NOT PROJECT_NAME)
+ message(
+ FATAL_ERROR
+ "This CMake file has to be included as the last step of all project() command calls!"
+ )
+endif()
+
+# Use modules? default NO!
+if(NOT DEFINED CMAKE_CXX_SCAN_FOR_MODULES)
+ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+endif()
+
+# Control whether the test target depends on the all target.
+set(CMAKE_SKIP_TEST_ALL_DEPENDENCY OFF)
+
+# gersemi: off
+option(CMAKE_EXPORT_COMPILE_COMMANDS "Prepare run-clang-tidy" ${PROJECT_IS_TOP_LEVEL})
+if(CMAKE_EXPORT_COMPILE_COMMANDS)
+ message(
+ STATUS
+ "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}"
+ )
+ set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
+endif()
+# gersemi: on
+
+# Ensure non-empty default build type for single-config
+get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(NOT isMultiConfig)
+ set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type")
+endif()
+set(CMAKE_DEBUG_POSTFIX _d)
+
+# ------------------------------------------------------------------------------
+# This property setting also needs to be consistent between the installed shared
+# library and its consumer, otherwise most toolchains will once again reject the
+# consumer's generated BMI.
+# ------------------------------------------------------------------------------
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+ set(CMAKE_CXX_STANDARD 23)
+endif()
+
+# Neither of these two are technically needed, but they make the expectation clear
+set(CMAKE_CXX_EXTENSIONS ON)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# NOTE: only with Ninja generator install of bmi files works yet!
+if(CMAKE_GENERATOR MATCHES "Ninja")
+ if(
+ CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
+ AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0
+ )
+ set(CMAKE_CXX_SCAN_FOR_MODULES ON)
+
+ if(NOT LINUX)
+ string(APPEND CMAKE_CXX_MODULE_MAP_FLAG " -fmodules-reduced-bmi")
+ endif()
+
+ add_compile_options($ENV{CXXFLAGS})
+ add_link_options($ENV{CXXFLAGS})
+ elseif(
+ CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+ AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0
+ )
+ set(CMAKE_CXX_SCAN_FOR_MODULES ON)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_CXX_SCAN_FOR_MODULES ON)
+ else()
+ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+ endif()
+endif()
+
+if(CMAKE_CXX_STDLIB_MODULES_JSON)
+ message(
+ STATUS
+ "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}"
+ )
+endif()
+
+if(NOT DEFINED CMAKE_CXX_MODULE_STD)
+ set(CMAKE_CXX_MODULE_STD OFF)
+endif()
+
+if(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
+ option(BEMAN_USE_MODULES "Build CXX_MODULES" ${CMAKE_CXX_SCAN_FOR_MODULES})
+endif()
+message(STATUS "BEMAN_USE_MODULES=${BEMAN_USE_MODULES}")
+
+option(
+ BEMAN_USE_STD_MODULE
+ "Check if 'import std;' is possible with the toolchain?"
+ OFF
+)
+message(STATUS "BEMAN_USE_STD_MODULE=${BEMAN_USE_STD_MODULE}")
+
+if(BEMAN_USE_MODULES AND BEMAN_USE_STD_MODULE)
+ # -------------------------------------------------------------------------
+ # Tell CMake that we explicitly want `import std`.
+ # This will initialize the property on all targets declared after this to 1
+ # -------------------------------------------------------------------------
+ message(
+ STATUS
+ "CMAKE_CXX_COMPILER_IMPORT_STD=${CMAKE_CXX_COMPILER_IMPORT_STD}"
+ )
+ if(${CMAKE_CXX_STANDARD} IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD)
+ set(CMAKE_CXX_MODULE_STD ON)
+ set(CMAKE_CXX_SCAN_FOR_MODULES ON)
+ option(
+ BEMAN_HAS_IMPORT_STD
+ "Build with import std; is possible and used!"
+ ${CMAKE_CXX_MODULE_STD}
+ )
+ message(STATUS "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}")
+ else()
+ set(CMAKE_CXX_MODULE_STD OFF)
+ message(WARNING "CMAKE_CXX_MODULE_STD=${CMAKE_CXX_MODULE_STD}")
+ endif()
+ message(STATUS "BEMAN_HAS_IMPORT_STD=${BEMAN_HAS_IMPORT_STD}")
+endif()
+
+if(NOT BEMAN_USE_MODULES)
+ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+endif()
+message(STATUS "CMAKE_CXX_SCAN_FOR_MODULES=${CMAKE_CXX_SCAN_FOR_MODULES}")
+
+# ------------------------------------------------------------------------------
+# Avoid creating CMAKE_..._OUTPUT_DIRECTORY as cache variables, they should not
+# be under the control of the developer. They should be controlled by the
+# project because parts of the project may make assumptions about the relative
+# layout of the binaries. More importantly, leaving them as ordinary variables
+# also means they can be unset within subdirectories where test executables are
+# defined, allowing them to avoid being collected with the other main binaries
+# and cluttering up that area.
+# ------------------------------------------------------------------------------
+set(stageDir ${CMAKE_CURRENT_BINARY_DIR}/stagedir)
+include(GNUInstallDirs)
+
+if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_BINDIR})
+endif()
+if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR})
+endif()
+if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR})
+endif()
diff --git a/cmake/prelude.cmake b/cmake/prelude.cmake
new file mode 100644
index 00000000..cdc58981
--- /dev/null
+++ b/cmake/prelude.cmake
@@ -0,0 +1,128 @@
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# This file must be included/used as CMAKE_PROJECT_TOP_LEVEL_INCLUDES -> before project() is called!
+#
+
+# ---- The include guard applies globally to the whole build ----
+include_guard(GLOBAL)
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+ message(
+ FATAL_ERROR
+ "In-source builds are not supported. "
+ "Please read the BUILDING document before trying to build this project. "
+ "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first."
+ )
+endif()
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
+
+# gersemi: off
+# ---------------------------------------------------------------------------
+# The CMAKE_EXPERIMENTAL_CXX_IMPORT_STD is not longer needed except for OSX
+# ---------------------------------------------------------------------------
+if(NOT BEMAN_USE_STD_MODULE OR CMAKE_VERSION VERSION_GREATER_EQUAL 4.4)
+ if(NOT APPLE)
+ return()
+ endif()
+endif()
+# ---------------------------------------------------------------------------
+
+# ---------------------------------------------------------------------------
+# check if import std; is supported by CMAKE_CXX_COMPILER
+# ---------------------------------------------------------------------------
+if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_VERSION VERSION_LESS 4.4)
+ if(PROJECT_NAME)
+ message(
+ WARNING
+ "This CMake file has to be included before first project() command call!"
+ )
+ endif()
+endif()
+
+# ---------------------------------------------------------------------------
+# check if import std; is supported by CMAKE_CXX_COMPILER
+# ---------------------------------------------------------------------------
+if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.3 AND CMAKE_VERSION VERSION_LESS 4.4)
+ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "451f2fe2-a8a2-47c3-bc32-94786d8fc91b")
+elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_VERSION VERSION_LESS 4.3)
+ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
+endif()
+# gersemi: on
+
+# ---------------------------------------------------------------------------
+# TODO(CK): Do we need this HACK still for linux too?
+# ---------------------------------------------------------------------------
+if(NOT APPLE)
+ return()
+endif()
+
+# FIXME: clang++ we still needs to export CXX=clang++
+if("$ENV{CXX}" STREQUAL "" AND CMAKE_CXX_COMPILER)
+ message(WARNING "\$CXX is not set")
+ set(ENV{CXX} ${CMAKE_CXX_COMPILER})
+endif()
+
+# ---------------------------------------------------------------------------
+# Workaround needed for CMAKE and clang++ to find the libc++.modules.json file
+# ---------------------------------------------------------------------------
+if(
+ CMAKE_VERSION VERSION_GREATER_EQUAL 4.2
+ AND ("$ENV{CXX}" MATCHES "clang" OR CMAKE_CXX_COMPILER MATCHES "clang")
+)
+ # NOTE: Always use libc++
+ # see https://releases.llvm.org/19.1.0/projects/libcxx/docs/index.html
+ set(ENV{CXXFLAGS} -stdlib=libc++)
+ message(STATUS "CXXFLAGS=-stdlib=libc++")
+
+ if(APPLE)
+ execute_process(
+ OUTPUT_VARIABLE LLVM_PREFIX
+ COMMAND brew --prefix llvm
+ COMMAND_ECHO STDOUT
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ file(REAL_PATH ${LLVM_PREFIX} LLVM_DIR)
+ set(LLVM_DIR ${LLVM_DIR} CACHE FILEPATH "")
+
+ message(STATUS "LLVM_DIR=${LLVM_DIR}")
+ add_link_options(-L${LLVM_DIR}/lib/c++)
+ include_directories(SYSTEM ${LLVM_DIR}/include)
+
+ # /usr/local/Cellar/llvm/21.1.8_1/lib/c++/libc++.modules.json
+ # "/usr/local/Cellar/llvm/21.1.8_1/share/libc++/v1/std.cppm",
+ set(CMAKE_CXX_STDLIB_MODULES_JSON
+ ${LLVM_DIR}/lib/c++/libc++.modules.json
+ )
+ elseif(LINUX)
+ execute_process(
+ OUTPUT_VARIABLE LLVM_MODULES
+ COMMAND clang++ -print-file-name=libc++.modules.json
+ COMMAND_ECHO STDOUT
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if(NOT CMAKE_CXX_STDLIB_MODULES_JSON)
+ set(CMAKE_CXX_STDLIB_MODULES_JSON ${LLVM_MODULES})
+ endif()
+ message(
+ STATUS
+ "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}"
+ )
+ endif()
+
+ if(EXISTS ${CMAKE_CXX_STDLIB_MODULES_JSON})
+ message(
+ STATUS
+ "CMAKE_CXX_STDLIB_MODULES_JSON=${CMAKE_CXX_STDLIB_MODULES_JSON}"
+ )
+ # gersemi: off
+ set(CACHE{CMAKE_CXX_STDLIB_MODULES_JSON}
+ TYPE FILEPATH
+ HELP "Result of: clang++ -print-file-name=c++/libc++.modules.json"
+ VALUE ${CMAKE_CXX_STDLIB_MODULES_JSON}
+ )
+ # gersemi: on
+ else()
+ message(STATUS "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}")
+ endif()
+endif()
diff --git a/cmake/presets/CMakeDarwinPresets.json b/cmake/presets/CMakeDarwinPresets.json
index 04528268..8ce5fb03 100644
--- a/cmake/presets/CMakeDarwinPresets.json
+++ b/cmake/presets/CMakeDarwinPresets.json
@@ -8,7 +8,6 @@
"name": "debug-base-Darwin",
"hidden": true,
"cacheVariables": {
- "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}",
"CMAKE_BUILD_TYPE": "Debug"
},
"condition": {
@@ -21,7 +20,6 @@
"name": "release-base-Darwin",
"hidden": true,
"cacheVariables": {
- "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}",
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
},
"condition": {
diff --git a/cmake/presets/CMakeGenericPresets.json b/cmake/presets/CMakeGenericPresets.json
index 826484cc..645c009e 100644
--- a/cmake/presets/CMakeGenericPresets.json
+++ b/cmake/presets/CMakeGenericPresets.json
@@ -1,5 +1,5 @@
{
- "version": 6,
+ "version": 9,
"configurePresets": [
{
"name": "root-config",
@@ -12,11 +12,13 @@
"type": "path",
"value": "${sourceDir}/stagedir"
},
+ "BEMAN_USE_MODULES": true,
+ "BEMAN_USE_STD_MODULE": true,
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_EXTENSIONS": true,
- "CMAKE_CXX_SCAN_FOR_MODULES": true,
"CMAKE_CXX_STANDARD_REQUIRED": true,
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
+ "CMAKE_INSTALL_MESSAGE": "LAZY",
"CMAKE_SKIP_TEST_ALL_DEPENDENCY": false
},
"warnings": {
diff --git a/cmake/presets/CMakeLinuxPresets.json b/cmake/presets/CMakeLinuxPresets.json
index 61f9b24a..73716857 100644
--- a/cmake/presets/CMakeLinuxPresets.json
+++ b/cmake/presets/CMakeLinuxPresets.json
@@ -1,5 +1,5 @@
{
- "version": 6,
+ "version": 9,
"include": [
"CMakeGenericPresets.json"
],
@@ -8,7 +8,6 @@
"name": "debug-base-Linux",
"hidden": true,
"cacheVariables": {
- "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}",
"CMAKE_BUILD_TYPE": "Debug"
},
"condition": {
@@ -21,7 +20,6 @@
"name": "release-base-Linux",
"hidden": true,
"cacheVariables": {
- "CMAKE_CXX_STDLIB_MODULES_JSON": "$env{CMAKE_CXX_STDLIB_MODULES_JSON}",
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
},
"condition": {
diff --git a/cmake/presets/CMakeWindowsPresets.json b/cmake/presets/CMakeWindowsPresets.json
index d8834f2c..6ab8f1cf 100644
--- a/cmake/presets/CMakeWindowsPresets.json
+++ b/cmake/presets/CMakeWindowsPresets.json
@@ -1,5 +1,5 @@
{
- "version": 6,
+ "version": 9,
"include": [
"CMakeGenericPresets.json"
],
diff --git a/docs/code/CMakeLists.txt b/docs/code/CMakeLists.txt
index 2d2752d3..6b98e9f3 100644
--- a/docs/code/CMakeLists.txt
+++ b/docs/code/CMakeLists.txt
@@ -3,18 +3,18 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# gersemi: on
-list(APPEND EXAMPLES)
+set(EXAMPLES)
-if(BEMAN_USE_MODULES)
- list(APPEND EXAMPLES modules) # modules.cpp
-endif()
+# if(BEMAN_USE_MODULES)
+# list(APPEND EXAMPLES modules) # modules.cpp
+# endif()
foreach(EXAMPLE ${EXAMPLES})
- set(EXAMPLE_TARGET ${TARGET_PREFIX}.tutorial.${EXAMPLE})
+ set(EXAMPLE_TARGET ${BEMAN_EXECUTION_TARGET_PREFIX}.tutorial.${EXAMPLE})
add_executable(${EXAMPLE_TARGET})
target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp)
target_link_libraries(
${EXAMPLE_TARGET}
- PRIVATE ${TARGET_NAMESPACE}::${TARGET_NAME}
+ PRIVATE beman::${BEMAN_EXECUTION_TARGET_NAME}
)
endforeach()
diff --git a/docs/code/tst-basic-timer.cpp b/docs/code/tst-basic-timer.cpp
index 3779e482..7244945b 100644
--- a/docs/code/tst-basic-timer.cpp
+++ b/docs/code/tst-basic-timer.cpp
@@ -10,7 +10,7 @@ namespace ex = beman::execution;
// ----------------------------------------------------------------------------
struct receiver {
- using receiver_concept = ex::receiver_t;
+ using receiver_concept = ex::receiver_tag;
auto set_value() && noexcept { std::cout << "timer done\n"; }
auto set_error(const std::exception_ptr&) && noexcept { std::cout << "timer error\n"; }
diff --git a/docs/code/tst-repeat_effect_until.hpp b/docs/code/tst-repeat_effect_until.hpp
index bfb6e89f..950584d4 100644
--- a/docs/code/tst-repeat_effect_until.hpp
+++ b/docs/code/tst-repeat_effect_until.hpp
@@ -24,15 +24,15 @@ struct connector {
inline constexpr struct repeat_effect_unilt_t {
template
struct sender {
- using sender_concept = ex::sender_t;
+ using sender_concept = ex::sender_tag;
using completion_signatures =
ex::completion_signatures;
template
struct state {
- using operation_state_concept = ex::operation_state_t;
+ using operation_state_concept = ex::operation_state_tag;
struct own_receiver {
- using receiver_concept = ex::receiver_t;
+ using receiver_concept = ex::receiver_tag;
state* s;
auto set_value() && noexcept -> void {
static_assert(ex::receiver);
diff --git a/docs/code/tst-sync_wait.hpp b/docs/code/tst-sync_wait.hpp
index c7ea2225..341a4fef 100644
--- a/docs/code/tst-sync_wait.hpp
+++ b/docs/code/tst-sync_wait.hpp
@@ -32,7 +32,7 @@ struct add_set_value {
struct add_signature, false> {
using type = tst::ex::completion_signatures;
};
- using sender_concept = tst::ex::sender_t;
+ using sender_concept = tst::ex::sender_tag;
template
constexpr auto get_completion_signatures(const Env& e) noexcept {
using orig = decltype(tst::ex::get_completion_signatures(std::declval(), e));
diff --git a/docs/code/tst-timer.hpp b/docs/code/tst-timer.hpp
index c1ccb5e7..032388be 100644
--- a/docs/code/tst-timer.hpp
+++ b/docs/code/tst-timer.hpp
@@ -29,14 +29,14 @@ class tst::timer {
public:
class when_done_sender {
public:
- using sender_concept = tst::ex::sender_t;
+ using sender_concept = tst::ex::sender_tag;
using completion_signatures = tst::ex::completion_signatures;
template
struct state {
- using operation_state_concept = tst::ex::operation_state_t;
- using scheduler_t = decltype(ex::get_scheduler(ex::get_env(std::declval())));
+ using operation_state_concept = tst::ex::operation_state_tag;
+ using scheduler_tag = decltype(ex::get_scheduler(ex::get_env(std::declval())));
struct execute {
state* s{};
auto operator()() noexcept -> void { this->s->await_one(); }
@@ -46,7 +46,7 @@ class tst::timer {
auto operator()() noexcept -> bool { return this->s->object->queue.empty(); }
};
using inner_sender = decltype(tst::repeat_effect_until(
- ex::schedule(std::declval()) | ex::then(execute()), pred()));
+ ex::schedule(std::declval()) | ex::then(execute()), pred()));
tst::timer* object;
tst::connector inner_op;
@@ -78,11 +78,11 @@ class tst::timer {
virtual auto complete() noexcept -> void = 0;
};
struct resume_after_sender {
- using sender_concept = tst::ex::sender_t;
+ using sender_concept = tst::ex::sender_tag;
using completion_signatures = tst::ex::completion_signatures;
template
struct state : base {
- using operation_state_concept = ex::operation_state_t;
+ using operation_state_concept = ex::operation_state_tag;
tst::timer* object;
std::chrono::milliseconds duration;
std::remove_cvref_t receiver;
diff --git a/docs/dependency.txt b/docs/dependency.txt
index 3af8d059..93393744 100644
--- a/docs/dependency.txt
+++ b/docs/dependency.txt
@@ -3,7 +3,7 @@ as_awaitable is_awaitable
await_result_type get_awaiter
awaitable_receiver forwarding_query
awaitable_receiver get_env
-awaitable_receiver receiver_t
+awaitable_receiver receiver_tag
awaitable_receiver set_error
awaitable_receiver set_stopped
awaitable_receiver set_value
@@ -15,7 +15,7 @@ basic_operation connect_all_result
basic_operation impls_for
basic_operation indices_for
basic_operation inner_ops
-basic_operation operation_state_t
+basic_operation operation_state_tag
basic_operation state_type
basic_operation tag_of_t
basic_operation valid_specialization
@@ -23,7 +23,7 @@ basic_receiver basic_state
basic_receiver callable
basic_receiver env_type
basic_receiver impls_for
-basic_receiver receiver_t
+basic_receiver receiver_tag
basic_receiver set_error
basic_receiver set_stopped
basic_receiver set_value
@@ -35,7 +35,7 @@ basic_sender completion_signatures_for
basic_sender decays_to
basic_sender impls_for
basic_sender product_type
-basic_sender sender_t
+basic_sender sender_tag
basic_state impls_for
basic_state state_type
basic_state tag_of_t
@@ -182,7 +182,7 @@ into_variant value_types_of_t
is_awaitable get_awaiter
is_awaitable is_awaiter
is_awaiter await_suspend_result
-is_sender sender_t
+is_sender sender_tag
join_env queryable
just decayed_typeof
just default_impls
@@ -212,7 +212,7 @@ let_error join_env
let_error make_env
let_error make_sender
let_error movable_value
-let_error receiver_t
+let_error receiver_tag
let_error sched_env
let_error sender_adaptor_closure
let_error sender_for
@@ -237,7 +237,7 @@ let_stopped join_env
let_stopped make_env
let_stopped make_sender
let_stopped movable_value
-let_stopped receiver_t
+let_stopped receiver_tag
let_stopped sched_env
let_stopped sender_adaptor_closure
let_stopped sender_for
@@ -262,7 +262,7 @@ let_value join_env
let_value make_env
let_value make_sender
let_value movable_value
-let_value receiver_t
+let_value receiver_tag
let_value sched_env
let_value sender_adaptor_closure
let_value sender_for
@@ -287,15 +287,15 @@ on sender
on sender_adaptor_closure
on sender_for
on sender_in
-on sender_t
+on sender_tag
on set_value
on transform_sender
on write_env
on_stop_request inplace_stop_source
-operation_state operation_state_t
+operation_state operation_state_tag
operation_state start
operation_state_task connect_awaitable_promise
-operation_state_task operation_state_t
+operation_state_task operation_state_tag
read_env decayed_typeof
read_env default_impls
read_env get_env
@@ -305,7 +305,7 @@ read_env query
read_env try_set_value
receiver get_env
receiver queryable
-receiver receiver_t
+receiver receiver_tag
receiver_of has_completions
run_loop connect
run_loop get_completion_scheduler
@@ -338,7 +338,7 @@ schedule_from impls_for
schedule_from join_env
schedule_from make_sender
schedule_from query_with_default
-schedule_from receiver_t
+schedule_from receiver_tag
schedule_from sched_attrs
schedule_from schedule
schedule_from schedule_result_t
@@ -352,7 +352,7 @@ scheduler get_env
scheduler queryable
scheduler schedule
scheduler schedule_result_t
-scheduler scheduler_t
+scheduler scheduler_tag
scheduler sender_in
scheduler set_value_t
sender enable_sender
@@ -394,7 +394,7 @@ split get_domain_early
split get_stop_token
split inplace_stop_token
split make_sender
-split receiver_t
+split receiver_tag
split sender_in
split set_error
split set_stopped
@@ -441,7 +441,7 @@ sync_wait get_delegation_scheduler
sync_wait get_domain_early
sync_wait get_scheduler
sync_wait into_variant
-sync_wait receiver_t
+sync_wait receiver_tag
sync_wait run_loop
sync_wait sender_in
sync_wait sender_to
diff --git a/docs/modules.md b/docs/modules.md
new file mode 100644
index 00000000..91736936
--- /dev/null
+++ b/docs/modules.md
@@ -0,0 +1,389 @@
+# Supporting Modules
+
+Author: Dietmar Kühl dietmar.kuehl@me.com
+Date: 2026-02-05
+
+Originally [`beman.execution`](https://github.com/bemanproject/execution)
+was implemented with out `module` support. Eventually, `module`
+support in all compilers and in [`cmake`](https://cmake.org/) made
+it reasonable to add `module` support. This document describes some
+of the experiences of the journey adding `module` support for
+[`beman.execution`](https://github.com/bemanproject/execution). It is
+likely that my attempts on how to support `module`s were misguided and
+I'm happy to learn how things can be done properly or better.
+
+## History of Adding Module Support
+
+First, let's start describing what I did. The starting point for
+that was a working
+[`beman.execution`](https://github.com/bemanproject/execution)
+implementation which had no `module` support. It had tests and some
+examples and the `CMakeLists.txt` built these. The default build
+procedure run from the `Makefile` was already set up to use `ninja`.
+
+Claus Klein had started to land `cmake` support of `module`s. In
+particular there were `cmake` rules to detect whether there is a
+dependency scanner and whether `import std;` is supported. The
+statement from Claus was that using `import std;` would be the
+second step - the first step is to actually create a `module` for
+[`beman.execution`](https://github.com/bemanproject/execution)
+including the standard library headers.
+
+### Use `export using`
+
+I had tried to create a `module` right after the Hagenberg meeting
+at the end of 2025. Since I was clueless how to go about that I had
+asked people who worked on the `module` specification and implemented
+the corresponding support in compilers. The recommendation was to
+create a `module` file, include all the headers, start the `module`,
+and have `export using ;` declarations. That is, something
+like this:
+
+```c++
+module;
+
+#include
+
+export module beman.execution;
+
+namespace beman::execution {
+ export using ::beman::execution::forwarding_query_t;
+ // more of these - one for each name to be exported
+}
+```
+
+Creating a file like that was fairly mechanical work and I just did
+it. I tried it. There were a bunch of errors which I could work
+out easily but eventually I got stuck with inscrutable errors.
+Since I never planned to write about the experience I don't recall
+what the errors were. When I asked about it the message was that
+neither `cmake` nor the compilers are quite there. So I abandoned
+this first attempt.
+
+Time passed and in the beginning of 2026 some `cmake` support for
+`module`s was added via a PR to the repository. The code wasn't
+ready to use `module` but it seems reasonable to retry. A first
+feeble try to get things built using the `export using` approach
+again resulted in errors and there were voice stating that this
+won't work but `module` support should actually work. The error
+messages seemed to imply that I should `export` the declarations
+immediately. So I abandoned that particular approach, again.
+
+### Generate Module Friendly Code: `mk-module.py`
+
+The attempts up to this point had indicated a few things which
+were somewhat misaligned with the way the code in
+[`beman.execution`](https://github.com/bemanproject/execution)
+is laid out. The code is structured into lots of small _components_
+(loosely based on John Lakos's idea of components in his 1996 version
+of "Large-Scale C++"). Each component consists of
+
+
+ -
+ a header declaring an entity like a class/class template, a
+ function/function template, or a concept as well as everything
+ needed to do so;
+
+ -
+ a test file verifying that everything promised is, indeed, defined
+ and confirm using tests that it works;
+
+ -
+ an optional source file with the definitions of what is declared
+ in the header; in case of templates the header will actually
+ contain these definitions
+
+
+
+Each component's file includes all necessary headers and a component
+`A` is said to _directly depend on a component_ `D` if any of `A`'s
+files includes `D`'s header. Creating a graph with the components
+as nodes and directed edges from each component to all components
+it directly depends on results in dependency graph which does not
+contain any cycles. Building the code without `module`s just works
+fine.
+
+When trying directly `export` the declarations of names when they
+are first declared, it quickly transpired that this doesn't work
+due to the structure of `module` files required by contemporary
+compilers:
+
+
+ -
+ The first 7 characters of a `module` file shall be `module;`
+ without anything preceding them. [`g++`](https://gcc.gnu.org/)
+ and [`clang++`](https://llvm.org/) are somewhat relaxed about
+ this requirement but some compiler is rather strict. This, however,
+ is at least easily achieved.
+
+ -
+ Any `export` of a name has to follow the `module`'s name declaration,
+ in this case after `export module beman.execution;`. This makes
+ sense: the compiler needs to know what `module` the names belong to.
+
+ -
+ The problem comes with any standard library header included after
+ this name declaration: that entirely confuses the compilers. That
+ is, all headers really need to be included before the name
+ declaration. That is pretty much _not_ how the components in
+ [`beman.execution`](https://github.com/bemanproject/execution) are
+ organized.
+
+
+
+To still achieve the objective of `export`ing a name when it first
+gets declared, the structure needs to be changed. However, the components
+are organized in a consistent structure. So the idea is to use this structure
+to reorganize the files for `module` builds:
+
+
+ -
+ Add an `export` keyword in front of any declaration which should be
+ `export`ed (well, really a name which can be defined so the headers can
+ function both when building a `module` and when just using headers; the
+ implementation uses `BEMAN_EXECUTION_EXPORT`).
+
+ -
+ Use a script (named
+ [`bin/mk-module.py`](https://github.com/bemanproject/execution/blob/main/bin/mk-module.py))
+ to create a `module` definition:
+
+ -
+ Start the file with `module;` (and some header stating the
+ file is generated).
+
+ -
+ Create a list of all used standard library headers and put
+ these right below the files head.
+
+ -
+ Add the `module` name declaration.
+
+ -
+ Copy the declaration from all the components in correct
+ dependency order, i.e., each component's declarations is
+ preceded by the declarations it directly depends on.
+
+
+
+ - Profit!
+
+
+Creating the script to write the file was reasonably straight forward
+although I spend way too much time making it fancy and include
+suitable `#line` directives to find the actual source. Compiling
+the resulting still didn't quite work, of course. There was a bunch
+of silly errors in the component headers which could be quickly
+resolved, though. That wasn't quite as true for the test files,
+though (more [rumination on tests](#modules-vs-testing) below):
+
+- Many tests didn't include all standard library headers they
+ dependent on. Since the corresponding header were actually included
+ by a component header things still worked. So, the corresponding
+ headers needed to be added.
+- Instead of `#include ` the tests
+ now use `import beman.execution;` (well, the test really use
+ conditional compilation to either use a header or an `import`
+ statement). Including any standard library header _after_ the
+ `import` statement again confuses the compiler, i.e., some
+ reordering in the files was needed: the test files deliberately
+ included the component's header first (to make sure all needed
+ headers are included by the component header) but this include
+ statement is now replaced by the `import` statement.
+- The tests actually use some of the implementation-defined entities
+ which were not meant to be `export`ed. To still have these tests
+ I ended up `export`ing the necessary implementation-defined names.
+ That needs to be corrected eventually (assuming that is actually
+ possible which isn't quite as clear).
+- Of course, the tests actually used the various names and it turned
+ out that quite a few names, e.g., the `operator|`, were missing.
+
+That worked OK with one compiler. Then I tried a different compiler
+and lots of issues emerged:
+
+- More names needed to be `export`ed for the tests.
+- Some things just didn't compile at all and needed to be changed
+ (I managed to avoid the problems but I haven't quite understood why).
+- Symbols were undefined.
+
+I ended up spending quite a bit of time reshuffling where headers
+go, fixing some actual bugs, and working around what looks like
+compiler problems. Most of that was, however, fairly mechanical
+and eventually I got a `module` declaration working with all major
+C++ compilers (using recent versions of each).
+
+### Retry `export using`
+
+Using a script to generate a `module` file restructured to better
+match the `module` needs did get me a working `module` definition.
+However, that shouldn't really be necessary. While fixing various
+minor bugs I did fix a few things which looked as if they may have
+had an impact when trying to use `export using `. So tried
+that approach again and this time it worked, at least, for some of
+the compilers. That looked promising!
+
+One compiler put up a fight, though! I'm using the an exposition-only
+`product_type` class template as is described in
+[[exec]](https://eel.is/c++draft/exec#snd.expos-17) and I got a
+compiler error about using std::get<N>(sender).
+After some experimentation I found that `export`ing the `product_type`
+template and the relevant `tuple_size` and `tuple_element`
+specialization I could resolve this problem, too.
+
+Once I got past that I encountered a problem which is probably quite
+common: following the specification of exposition-only `impls_for`
+class template I used lambda functions for the various "overrides".
+One compiler complained about undefined symbols about these! Of
+course, using lambda functions in a header is problematic because
+each instance of a lambda function has a different type, even if
+they are spelled identical! So I replaced all of these lambda functions
+by `struct`s which only have one member which is an `operator()`.
+
+With that I also got a working `module` definition. While I'm quite
+fond of my generator I prefer this approach! There shouldn't be a
+need to rewrite an implementation just to make it a `module`. I
+should also get away not needing any macros to insert/remove the
+`export` keywords from declarations. Instead, the `export`ed names
+are just listed in the module definition file. What is currently
+missing is a bit of a clean-up to remove some of the artifacts.
+Also, there may be more implementation details exported than is
+actually necessary.
+
+### `import std;`
+
+Currently, `import std;` is _not_, yet, used. It should be straight
+forward to conditionally choose between `import std;` and including
+the headers.
+
+## Changes Needed to Support Modules
+
+When enabling modules I needed to apply quite a few, mostly
+rather simple changes. Some of the necessary changes did take
+me a bit to actually discover. Here is broadly what I needed
+to change:
+
+- I had slotted and `import beman.execution;` in where the
+ the project header(s) were included. Some headers came
+ before, others came after. It seems that doesn't work:
+ any header should preceded the `import` statements or
+ the `module` name declaration.
+
+- Especially in the tests I hadn't always included all headers
+ for standard library components which may be potentially
+ used. However, these are necessary, even if the standard
+ library component isn't even named and just used. For
+ example:
+
+ ```c++
+ #include
+ import beman.execution;
+ namespace ex = beman::execution;
+
+ int main() {
+ auto[rc] = *ex::sync_wait(ex::just(0));
+ return rc;
+ }
+ ```
+
+ Removing `#include ` causes a compilation failure:
+ `sync_wait` return an `std::optional>` and the
+ structured binding needs to know about the `std::tuple`. Oddly,
+ the `std::optional` can be dereferenced.
+
+- My biggest blocker was the definition of `join_env`: the original
+ definition used a `requires` clause which checked whether at
+ least one of two expressions were values. Implementation used
+ an `if constexpr` to decide whether the first of the two
+ expression is value and used that and otherwise the other
+ expression would be used. That is, something like this:
+
+ ```c++
+ template
+ struct join_env {
+ E1 e1;
+ E2 e2;
+ template
+ requires(
+ requires(Q q, const E1& e){ q(e); } ||
+ requires(Q q, const E2& e){ q(e); }
+ )
+ auto query(Q q) const noexcept {
+ if constexpr (requires(Q q, const E1& e){ q(e); })
+ return q(this->e1);
+ else
+ return q(this->e2);
+ }
+ };
+ ```
+
+ However, the compiler insisted in the definition of the function
+ that neither of the two expressions from the `requires` clause
+ was valid. Eventually I just turned the `query` into two
+ overloads, the first requiring that `q(e1)` is valid and the
+ second requiring that `q(e1)` is not valid but `q(e2)` is valid.
+ I think it was only one compiler causing this issue.
+
+## Scanning and Building
+
+One of the things which seems odd is that each time `beman.execution`
+is built, the files are scanned for dependencies. That scanning
+often takes longer than the actual build of the respective object
+file. Also, since the `module` gets rebuild, all the tests `import`ing
+the `module` get built again. When developing with including headers
+only the tests which included modified headers (possibly indirectly)
+needed to be rebuilt. Since the components and tests were created
+in dependency order, that normally meant that only one test needed
+to be rebuilt. Only if an already tested component needed to be
+changed multiple tests needed to be built.
+
+The promise of `module`s was that builds get faster. I don't have
+objective measurements but it seems the development actually got
+slower. While concentrating on fixing a particular component I
+often removed all other tests and the examples from the build.
+
+## Modules vs. Testing
+
+Testing the `module` components is, yet, another issue! There are
+a few issues and I haven't worked out all of them, yet:
+
+1. There are quite a few classes and functions which are implementation
+ details. I like to test these. In fact, I normally don't use `private`
+ member functions because I can't test them. Instead, this functionality
+ would become `public` members of an implementation class which then
+ becomes a `private` member of the actual component. The implementation
+ class can be tested separately. However, anything which isn't `export`ed
+ isn't accessible from outside the `module`.
+
+ I still want to test that the `module` works, including all the
+ implementation details. Currently, the implementation details
+ are tested by just using the headers to get the declarations.
+ That doesn't seem quite right, though. Maybe the way to is to
+ have a second `module` for the implementation details, say,
+ `beman.execution.detail`, and use that to test the implementation
+ details.
+
+2. Some tests would benefit from common tools. For example, there could be
+ a `test::scheduler` which is used to verify that the various scheduling
+ operations do the right thing. A `test::scheduler` would be defined in
+ a header and included into each test. Of course, the `test::scheduler`
+ would need the declarations of some `module` components, e.g., of
+ `set_value_t` and, thus, use `import beman.execution`.
+
+ That didn't quite occur to me but it _seems_ that may actually work!
+ An initial test seems to show that the compilers do not get upset about
+ multiple `import beman.execution` statements.
+
+## Conclusion
+
+So far it isn't clear to me whether `module`s do provide a benefit. The
+main sticking points are:
+
+1. I don't known, yet, how to test implementation details without `export`ing
+ them.
+2. The "scan deps" step seems to take quite long.
+3. So far I haven't managed to avoid `export`ing some of the implementation
+ details. However, that _may_ be due to some uses actually requiring them.
+4. There is different behavior between different compilers.
+
+Some of the issues I encountered are likely due to ignorance: probably
+all issues can be resolved with a bit of adjusted practices.
diff --git a/docs/overview.md b/docs/overview.md
index 2fd3606a..b6a98020 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -32,7 +32,7 @@ Operation states represent asynchronous operations ready to be _State_:
-- The type `operation_state_concept` is an alias for `operation_state_t` or a type derived thereof.
+- The type `operation_state_concept` is an alias for `operation_state_tag` or a type derived thereof.
- state.start() & noexcept
@@ -44,7 +44,7 @@ This example shows a simple operation state object which immediately completes s
template
struct example_state
{
- using operation_state_concept = std::execution::operation_state_t;
+ using operation_state_concept = std::execution::operation_state_tag;
std::remove_cvref_t receiver;
auto start() & noexcept {
@@ -67,7 +67,7 @@ Users don’t interact with receivers explicitly except when implementing new se
Required members for _Receiver_:
-- The type `receiver_concept` is an alias for `receiver_t` or a type derived thereof`.
+- The type `receiver_concept` is an alias for `receiver_tag` or a type derived thereof`.
- Rvalues of type _Receiver_ are movable.
- Lvalues of type _Receiver_ are copyable.
- std::execution::get_env(_receiver_) returns an object. By default this operation returns std::execution::env<>.
@@ -88,7 +88,7 @@ The example receiver prints the name of each the received
struct example_receiver
{
- using receiver_concept = std::execution::receiver_t;
+ using receiver_concept = std::execution::receiver_tag;
std::remove_cvref_t nested;
auto get_env() const noexcept {
@@ -128,7 +128,7 @@ The example defines a simple receiver a
```c++
struct example_receiver
{
- using receiver_concept = std::execution::receiver_t;
+ using receiver_concept = std::execution::receiver_tag;
auto set_value(int) && noexcept ->void {}
auto set_stopped() && noexcept ->void {}
@@ -161,7 +161,7 @@ static_assert(not std::execution::receiver_ofschedule operation yielding a sender with a value completion signal without parameters. The completion is on the respective execution context.
Requirements for _Scheduler_:
-- The type _Scheduler_::scheduler_concept is an alias for `scheduler_t` or a type derived thereof.
+- The type _Scheduler_::scheduler_concept is an alias for `scheduler_tag` or a type derived thereof.
- schedule(_scheduler_) -> sender
- The value completion scheduler of the sender’s environment is the _scheduler_:
_scheduler_ == std::execution::get_completion_scheduler<std::execution::set_value_t>(
@@ -176,7 +176,7 @@ Requirements for _Scheduler_:
Senders represent asynchronous work. They may get composed from multiple senders to model a workflow. Senders can’t be run directly. Instead, they are passed to a which connects the sender to a receiver to produce an operation_state which may get started. When using senders to represent work the inner workings shouldn’t matter. They do become relevant when creating sender algorithms.
Requirements for _Sender_:
-- The type _Sender_::sender_concept is an alias for `sender_t` or a type derived thereof or _Sender_ is a suitable _awaitable_.
+- The type _Sender_::sender_concept is an alias for `sender_tag` or a type derived thereof or _Sender_ is a suitable _awaitable_.
- std::execution::get_env(_sender_) is valid. By default this operation returns std::execution::env<>.
- Rvalues of type _Sender_ can be moved.
- Lvalues of type _Sender_ can be copied.
@@ -197,7 +197,7 @@ struct example_sender
template
struct state
{
- using operation_state_concept = std::execution::operation_state_t;
+ using operation_state_concept = std::execution::operation_state_tag;
std::remove_cvref_t receiver;
int value;
auto start() & noexcept {
@@ -207,7 +207,7 @@ struct example_sender
);
}
};
- using sender_concept = std::execution::sender_t;
+ using sender_concept = std::execution::sender_tag;
using completion_signatures = std::execution::completion_signatures<
std::execution::set_value_t(int)
>;
@@ -296,7 +296,7 @@ struct example_state
this->state.stop();
}
};
- using operation_state_concept = std::execution::operation_state_t;
+ using operation_state_concept = std::execution::operation_state_tag;
using env = std::execution::env_of_t;
using token = std::execution::stop_callback_of_t;
using callback = std::execution::stop_callback_of_t;
@@ -493,7 +493,7 @@ To determine the result the sender is first transformed usin
When a sender doesn’t need to compute the completion signatures based on an environment it is easiest to use a the type alias, e.g.:
```c++
struct sender {
- using sender_concept = std::execution::sender_t;
+ using sender_concept = std::execution::sender_tag;
using completion_signatures = std::completion_signatures<
std::execution::set_value_t(int),
std::execution::set_error_t(std::error_code),
@@ -640,11 +640,11 @@ The expression schedule(scheduler) creates a sender which up
The sender adaptors take one or more senders and adapt their respective behavior to complete with a corresponding result. The description uses the informal function completions-of(sender) to represent the completion signatures which sender produces. Also, completion signatures are combined using +: the result is the deduplicated set of the combined completion signatures.
-affine_on(sender) -> sender-of<completions-of(sender)>
-The expression affine_on(sender) creates
+affine(sender) -> sender-of<completions-of(sender)>
+The expression affine(sender) creates
a sender which completes on the same scheduler it was started on, even if sender changes the scheduler. The scheduler to resume on is determined using get_scheduler(get_env(rcvr)) where rcvr is the receiver the sender is connected to.
-The primary use of affine_on is implementing scheduler affinity for task.
+The primary use of affine is implementing scheduler affinity for task.
`bulk`
@@ -727,13 +727,13 @@ The expression into_variant(sender) creates a sender which t
- `env_of_t`
- `error_types_of_t`
- `fwd_env`
-- `operation_state_t`
-- `receiver_t`
+- `operation_state_tag`
+- `receiver_tag`
- `run_loop`
-- `scheduler_t`
+- `scheduler_tag`
- `schedule_result_t`
- `sender_adaptor_closure`
-- `sender_t`
+- `sender_tag`
- `stop_token_of_t`
- `tag_of_t`
- `transform_sender`
diff --git a/docs/resources.md b/docs/resources.md
index bd5b2507..76d264de 100644
--- a/docs/resources.md
+++ b/docs/resources.md
@@ -6,6 +6,6 @@ This page has links to some resources related to this repository:
* Lucian Radu Teodorescu's [Overload](https://accu.org/journals/nonmembers/overload_issue_members/) article [Senders/Receivers: An Introduction](https://accu.org/journals/overload/32/184/teodorescu/).
* Eric Niebler's Working with Asynchrony Generically: A Tour of C++ [Executors part 1](https://youtu.be/xLboNIf7BTg?si=JhXh55lCGW9-EuQl)/[part 2](https://youtu.be/6a0zzUBUNW4?si=7We2cRiJD0eJ7jm9).
-* Goran Arandelovic's presentation [Introduction to Sender/Receiver Framework and `std::execution`](https://eel.is/c++draft/#exec) at [Using std::cpp](https://eventos.uc3m.es/105614/programme/using-std-cpp-2024.html) 2024
+* Goran Arandelovic's presentation [Introduction to Sender/Receiver Framework and `std::execution`](https://eel.is/c++draft/#exec) at [Using std::cpp](https://eventos.uc3m.es/105614/programme/using-std-cpp-2024.html) 2024
* The [`std::execution](https://wg21.link/p2300) proposal.
* The [[exec]](https://eel.is/c++draft/#exec) section of the draft standard.
diff --git a/docs/tutorial.mds b/docs/tutorial.mds
index 90e6ac31..12849dad 100644
--- a/docs/tutorial.mds
+++ b/docs/tutorial.mds
@@ -204,7 +204,7 @@ pipe notation.
result of the algorithm is the value returned from this
invocation (if any). If the invocation of _fun_
throws, the algorithm completes with an error
- provding an `std::exception_ptr` to caught exception. If
+ providing an `std::exception_ptr` to caught exception. If
_sender_ doesn't complete accordingly to the
algorithm's name, the completion is forwarded.
diff --git a/etc/ci-clang-toolchain.cmake b/etc/ci-clang-toolchain.cmake
deleted file mode 100755
index 8158594c..00000000
--- a/etc/ci-clang-toolchain.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang)
-set(CMAKE_CXX_COMPILER clang++)
-
-set(CMAKE_CXX_FLAGS
- "-std=c++20 \
- -Wall -Wextra \
- -stdlib=libc++ -fexperimental-library"
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/clang-16-toolchain.cmake b/etc/clang-16-toolchain.cmake
deleted file mode 100755
index 7f268808..00000000
--- a/etc/clang-16-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-16)
-set(CMAKE_CXX_COMPILER clang++-16)
-
-include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake")
diff --git a/etc/clang-17-toolchain.cmake b/etc/clang-17-toolchain.cmake
deleted file mode 100755
index 5e266b60..00000000
--- a/etc/clang-17-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-17)
-set(CMAKE_CXX_COMPILER clang++-17)
-
-include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake")
diff --git a/etc/clang-18-toolchain.cmake b/etc/clang-18-toolchain.cmake
deleted file mode 100755
index 7aabeded..00000000
--- a/etc/clang-18-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-18)
-set(CMAKE_CXX_COMPILER clang++-18)
-
-include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake")
diff --git a/etc/clang-19-toolchain.cmake b/etc/clang-19-toolchain.cmake
deleted file mode 100755
index 48f6e07a..00000000
--- a/etc/clang-19-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-19)
-set(CMAKE_CXX_COMPILER clang++-19)
-
-include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake")
diff --git a/etc/clang-flags.cmake b/etc/clang-flags.cmake
deleted file mode 100644
index abef953b..00000000
--- a/etc/clang-flags.cmake
+++ /dev/null
@@ -1,41 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_CXX_STANDARD 23)
-
-set(CMAKE_CXX_FLAGS
- "-stdlib=libc++ -Wall -Wextra"
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address,undefined,leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/clang-toolchain.cmake b/etc/clang-toolchain.cmake
deleted file mode 100755
index 7d02a79a..00000000
--- a/etc/clang-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang)
-set(CMAKE_CXX_COMPILER clang++)
-
-include("${CMAKE_CURRENT_LIST_DIR}/clang-flags.cmake")
diff --git a/etc/gcc-11-toolchain.cmake b/etc/gcc-11-toolchain.cmake
deleted file mode 100755
index d45db1e4..00000000
--- a/etc/gcc-11-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-include("${CMAKE_CURRENT_LIST_DIR}/gcc-flags.cmake")
-
-set(CMAKE_C_COMPILER gcc-11)
-set(CMAKE_CXX_COMPILER g++-11)
diff --git a/etc/gcc-12-toolchain.cmake b/etc/gcc-12-toolchain.cmake
deleted file mode 100755
index 5e332219..00000000
--- a/etc/gcc-12-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-include("${CMAKE_CURRENT_LIST_DIR}/gcc-flags.cmake")
-
-set(CMAKE_C_COMPILER gcc-12)
-set(CMAKE_CXX_COMPILER g++-12)
diff --git a/etc/gcc-13-toolchain.cmake b/etc/gcc-13-toolchain.cmake
deleted file mode 100755
index 4d174705..00000000
--- a/etc/gcc-13-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-include("${CMAKE_CURRENT_LIST_DIR}/gcc-flags.cmake")
-
-set(CMAKE_C_COMPILER gcc-13)
-set(CMAKE_CXX_COMPILER g++-13)
diff --git a/etc/gcc-14-toolchain.cmake b/etc/gcc-14-toolchain.cmake
deleted file mode 100755
index f9692866..00000000
--- a/etc/gcc-14-toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-include("${CMAKE_CURRENT_LIST_DIR}/gcc-flags.cmake")
-
-set(CMAKE_C_COMPILER gcc-14)
-set(CMAKE_CXX_COMPILER g++-14)
diff --git a/etc/gcc-flags.cmake b/etc/gcc-flags.cmake
deleted file mode 100644
index dbf06019..00000000
--- a/etc/gcc-flags.cmake
+++ /dev/null
@@ -1,36 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_CXX_STANDARD 23)
-
-set(CMAKE_CXX_FLAGS "-std=c++23 -Wall -Wextra" CACHE STRING "CXX_FLAGS" FORCE)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=undefined"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/gcc-toolchain.cmake b/etc/gcc-toolchain.cmake
deleted file mode 100755
index 960bbf28..00000000
--- a/etc/gcc-toolchain.cmake
+++ /dev/null
@@ -1,43 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER gcc)
-set(CMAKE_CXX_COMPILER g++)
-
-set(CMAKE_CXX_FLAGS
- "-std=c++20 \
- -Wall -Wextra"
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address,undefined,leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/llvm-16-toolchain.cmake b/etc/llvm-16-toolchain.cmake
deleted file mode 100755
index 317eb15f..00000000
--- a/etc/llvm-16-toolchain.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-16)
-set(CMAKE_CXX_COMPILER clang++-16)
-
-set(CMAKE_CXX_FLAGS
- "-std=c++20 \
- -Wall -Wextra \
- -stdlib=libc++ -fexperimental-library"
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/llvm-master-toolchain.cmake b/etc/llvm-master-toolchain.cmake
deleted file mode 100644
index 2dc4c86c..00000000
--- a/etc/llvm-master-toolchain.cmake
+++ /dev/null
@@ -1,51 +0,0 @@
-set(LLVM_ROOT "$ENV{LLVM_ROOT}" CACHE PATH "Path to LLVM installation")
-
-set(CMAKE_C_COMPILER ${LLVM_ROOT}/bin/clang)
-set(CMAKE_CXX_COMPILER ${LLVM_ROOT}/bin/clang++)
-
-set(CMAKE_CXX_FLAGS
- "-std=c++2a \
- -Wall -Wextra \
- -stdlib=libc++"
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_EXE_LINKER_FLAGS
- "-Wl,-rpath,${LLVM_ROOT}/lib"
- CACHE STRING
- "CMAKE_EXE_LINKER_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/llvm-toolchain.cmake b/etc/llvm-toolchain.cmake
deleted file mode 100755
index b0cbfbd9..00000000
--- a/etc/llvm-toolchain.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
-include_guard(GLOBAL)
-
-set(CMAKE_C_COMPILER clang-14)
-set(CMAKE_CXX_COMPILER clang++-14)
-
-set(CMAKE_CXX_FLAGS
- "-std=c++20 \
- -Wall -Wextra \
- -stdlib=libstdc++ "
- CACHE STRING
- "CXX_FLAGS"
- FORCE
-)
-
-set(CMAKE_CXX_FLAGS_DEBUG
- "-O0 -fno-inline -g3"
- CACHE STRING
- "C++ DEBUG Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELEASE
- "-Ofast -g0 -DNDEBUG"
- CACHE STRING
- "C++ Release Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
- "-O3 -g -DNDEBUG"
- CACHE STRING
- "C++ RelWithDebInfo Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_TSAN
- "-O3 -g -DNDEBUG -fsanitize=thread"
- CACHE STRING
- "C++ TSAN Flags"
- FORCE
-)
-set(CMAKE_CXX_FLAGS_ASAN
- "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak"
- CACHE STRING
- "C++ ASAN Flags"
- FORCE
-)
diff --git a/etc/toolchain.cmake b/etc/toolchain.cmake
deleted file mode 100644
index 046d3323..00000000
--- a/etc/toolchain.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-include_guard(GLOBAL)
-
-include("${CMAKE_CURRENT_LIST_DIR}/gcc-flags.cmake")
-
-set(CMAKE_C_COMPILER cc)
-set(CMAKE_CXX_COMPILER c++)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index baa1019e..c27226bc 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -3,34 +3,56 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# gersemi: on
-list(
- APPEND EXAMPLES
- stackoverflow
- inspect
- playground
- sender-demo
- when_all-cancel
- stop_token
- stopping
+cmake_minimum_required(VERSION 3.30...4.3)
+
+include(../cmake/prelude.cmake)
+
+project(beman_execution.example LANGUAGES CXX)
+
+if(PROJECT_IS_TOP_LEVEL)
+ include(../cmake/cxx-modules-rules.cmake)
+
+ find_package(beman.execution 0.3.0 EXACT REQUIRED)
+
+ enable_testing()
+endif()
+
+set(TODO stop_token) #-dk:TODO including that causes a linker error
+set(TODO suspend_never) #-dk:TODO including that causes ASAN errors
+
+set(EXAMPLES
allocator
- intro-1-hello-world
- intro-2-hello-async
- intro-5-consumer
doc-just
doc-just_error
doc-just_stopped
+ inspect
+ intro-1-hello-world
+ intro-2-hello-async
+ intro-5-consumer
+ playground
+ sender-demo
+ stackoverflow
+ stopping
+ when_all-cancel
)
if(BEMAN_USE_MODULES)
- list(APPEND EXAMPLES modules) # modules.cpp
+ #-dk:TODO gcc doesn't like the modules: list(APPEND EXAMPLES modules modules-and-header)
+ list(APPEND EXAMPLES modules-and-header)
endif()
foreach(EXAMPLE ${EXAMPLES})
- set(EXAMPLE_TARGET ${TARGET_PREFIX}.examples.${EXAMPLE})
+ set(EXAMPLE_TARGET ${PROJECT_NAME}.${EXAMPLE})
add_executable(${EXAMPLE_TARGET})
target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp)
- target_link_libraries(
- ${EXAMPLE_TARGET}
- PRIVATE ${TARGET_NAMESPACE}::${TARGET_NAME}
- )
+ if(BEMAN_USE_MODULES)
+ target_compile_definitions(${EXAMPLE_TARGET} PUBLIC BEMAN_HAS_MODULES)
+ target_link_libraries(${EXAMPLE_TARGET} PRIVATE beman::execution)
+ else()
+ target_link_libraries(
+ ${EXAMPLE_TARGET}
+ PRIVATE beman::execution_headers
+ )
+ endif()
+ add_test(NAME ${EXAMPLE_TARGET} COMMAND ${EXAMPLE_TARGET})
endforeach()
diff --git a/examples/allocator.cpp b/examples/allocator.cpp
index 6fbbe49b..3c00ed07 100644
--- a/examples/allocator.cpp
+++ b/examples/allocator.cpp
@@ -1,10 +1,17 @@
-#include
+// examples/allocator.cpp -*-C++-*-
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
#include
#include
#include
#include
#include
#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = beman::execution;
@@ -75,5 +82,5 @@ auto main() -> int {
})};
inline_resource<1024> state_resource("state");
- ex::sync_wait(ex::detail::write_env(std::move(s), allocator_env{&state_resource}));
+ ex::sync_wait(ex::write_env(std::move(s), allocator_env{&state_resource}));
}
diff --git a/examples/doc-just.cpp b/examples/doc-just.cpp
index a63c2d59..d25ded01 100644
--- a/examples/doc-just.cpp
+++ b/examples/doc-just.cpp
@@ -1,9 +1,13 @@
// examples/doc-just.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = beman::execution;
using namespace std::string_literals;
diff --git a/examples/doc-just_error.cpp b/examples/doc-just_error.cpp
index c347485b..7acba327 100644
--- a/examples/doc-just_error.cpp
+++ b/examples/doc-just_error.cpp
@@ -1,9 +1,13 @@
// examples/doc-just_error.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = beman::execution;
namespace {
diff --git a/examples/doc-just_stopped.cpp b/examples/doc-just_stopped.cpp
index 60c854e9..b4fe76f0 100644
--- a/examples/doc-just_stopped.cpp
+++ b/examples/doc-just_stopped.cpp
@@ -1,10 +1,13 @@
// examples/doc-just_stopped.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
#include
-#include //-dk:TODO remove
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = beman::execution;
namespace {
diff --git a/examples/inspect.cpp b/examples/inspect.cpp
index e6fcd470..924a61a4 100644
--- a/examples/inspect.cpp
+++ b/examples/inspect.cpp
@@ -1,21 +1,143 @@
// examples/inspectc.pp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
-#include "meta.hpp"
#include
+#include
#include
+#include
#include
+#include
+#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = beman::execution;
// ----------------------------------------------------------------------------
+namespace meta {
+// The code in this namespace is a fairly basic way to print types. It can
+// almost certainly be done better, in particular using reflection. However,
+// that's beside the point of this example. The important part for the
+// example is that meta::type::name() yields a string representing the
+// type T in some reasonable way
+template class, typename...>
+struct list {
+ static auto name() { return "unknown-list"; }
+};
+template <>
+struct list<::beman::execution::completion_signatures> {
+ static auto name() { return "ex::completion_signatures"; }
+};
+
+template
+struct type {
+ static auto name() { return typeid(T).name(); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("&"); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("&&"); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("*"); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("const"); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("volatile"); }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("const volatile"); }
+};
+// NOLINTBEGIN(hicpp-avoid-c-arrays)
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("[") + std::to_string(N) + "]"; }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("(&)[") + std::to_string(N) + "]"; }
+};
+template
+struct type {
+ static auto name() { return typeid(T).name() + std::string("(*)[") + std::to_string(N) + "]"; }
+};
+// NOLINTEND(hicpp-avoid-c-arrays)
+
+template <>
+struct type<::beman::execution::set_value_t> {
+ static auto name() { return "ex::set_value_t"; }
+};
+template <>
+struct type<::beman::execution::set_error_t> {
+ static auto name() { return "ex::set_error_t"; }
+};
+template <>
+struct type<::beman::execution::set_stopped_t> {
+ static auto name() { return "ex::set_stopped_t"; }
+};
+
+template
+struct type {
+ static auto name() { return type::name() + std::string("()"); }
+};
+template
+struct type {
+ static auto name() {
+ return type::name() + std::string("(") + (type::name() + ... + (std::string(", ") + type::name())) +
+ ")";
+ }
+};
+template
+struct type {
+ static auto name() { return type::name() + std::string("(*)()"); }
+};
+template
+struct type {
+ static auto name() {
+ return type::name() + std::string("(*)(") +
+ (type::name() + ... + (std::string(", ") + type::name())) + ")";
+ }
+};
+
+template class L>
+struct type> {
+ static auto name() { return list::name() + std::string("<>"); }
+};
+
+template class L, typename T, typename... S>
+struct type> {
+ static auto name() {
+ return list::name() + std::string("<") + (type::name() + ... + (std::string(", ") + type::name())) +
+ ">";
+ }
+};
+
+template
+inline std::ostream& operator<<(std::ostream& out, const type& t) {
+ return out << t.name();
+}
+} // namespace meta
+
+// ----------------------------------------------------------------------------
+
namespace {
struct logger_t {
template
struct state {
- using operation_state_concept = ex::operation_state_t;
+ using operation_state_concept = ex::operation_state_tag;
using inner_t = decltype(ex::connect(std::declval(), std::declval()));
inner_t inner;
@@ -23,22 +145,23 @@ struct logger_t {
state(Sndr&& s, Rcvr&& r, Log&& l)
: inner(ex::connect(std::forward(s), std::forward(r))), log(std::forward(l)) {}
auto start() & noexcept -> void {
- this->log(meta::type(),
- ex::get_env(std::declval())))>::name());
+ this->log(meta::type<
+ decltype(ex::get_completion_signatures()))>())>::
+ name());
ex::start(this->inner);
}
};
template
struct sender {
- using sender_concept = ex::sender_t;
+ using sender_concept = ex::sender_tag;
Sndr sndr;
Log log;
- template
- auto get_completion_signatures(const Env& env) const noexcept {
- return ex::get_completion_signatures(this->sndr, env);
+ template
+ static consteval auto get_completion_signatures() noexcept {
+ return ex::get_completion_signatures();
}
template
diff --git a/examples/intro-1-hello-world.cpp b/examples/intro-1-hello-world.cpp
index 87cb8d70..92fb6bc7 100644
--- a/examples/intro-1-hello-world.cpp
+++ b/examples/intro-1-hello-world.cpp
@@ -1,10 +1,16 @@
// examples/intro-1-hello-world.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
+#include
#include
#include
+#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = ::beman::execution;
using namespace std::string_literals;
@@ -14,13 +20,14 @@ using namespace std::string_literals;
int main() {
// clang-format off
- auto [result] = ex::sync_wait(
- ex::when_all(
- ex::just("hello, "s),
- ex::just("world"s)
- ) | ex::then([](auto const& s1, auto const& s2) { return s1 + s2; })
- ).value_or(std::tuple(""s)
- );
+ auto [result] =
+ ex::sync_wait(
+ ex::when_all(
+ ex::just("hello, "s),
+ ex::just("world"s)
+ ) | ex::then([](auto const& s1, auto const& s2) { return s1 + s2; })
+ ) .value_or(std::tuple(""s))
+ ;
// clang-format on
std::cout << result << '\n';
diff --git a/examples/intro-2-hello-async.cpp b/examples/intro-2-hello-async.cpp
index 7809160c..58483a0d 100644
--- a/examples/intro-2-hello-async.cpp
+++ b/examples/intro-2-hello-async.cpp
@@ -1,12 +1,18 @@
// examples/intro-2-hello-async.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
-#include "intro-timer.hpp"
#include
#include
+#include
#include
+#include
#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
+#include "intro-timer.hpp"
namespace ex = ::beman::execution;
using namespace std::string_literals;
diff --git a/examples/intro-5-consumer.cpp b/examples/intro-5-consumer.cpp
index 1e769423..c85802e4 100644
--- a/examples/intro-5-consumer.cpp
+++ b/examples/intro-5-consumer.cpp
@@ -1,27 +1,72 @@
-// examples/intro-1-hello-world.cpp -*-C++-*-
+// examples/intro-5-consumer.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
-#include
+#include
+//-dk:TODO restore if that actually works #include
#include
#include
#include
+#include
#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+import beman.execution.detail;
+#else
+#include
+#endif
namespace ex = ::beman::execution;
using namespace std::string_literals;
+namespace tst {
+template
+struct expected {
+ std::variant value_or_error{};
+ expected(T&& value) noexcept : value_or_error(std::in_place_index<1>, std::forward(value)) {}
+ expected(E&& error) noexcept : value_or_error(std::in_place_index<2>, std::forward(error)) {}
+ explicit operator bool() const noexcept { return this->value_or_error.index() == 1; }
+ auto value_or(T&& default_value) && noexcept -> T {
+ if (this->value_or_error.index() == 1) {
+ return std::move(std::get<1>(this->value_or_error));
+ } else {
+ return std::forward(default_value);
+ }
+ }
+ auto value_or(T&& default_value) & noexcept -> T {
+ if (this->value_or_error.index() == 1) {
+ return std::get<1>(this->value_or_error);
+ } else {
+ return std::forward(default_value);
+ }
+ }
+ auto error_or(E&& default_error) && noexcept -> E {
+ if (this->value_or_error.index() == 2) {
+ return std::move(std::get<2>(this->value_or_error));
+ } else {
+ return std::forward(default_error);
+ }
+ }
+ auto error_or(E&& default_error) & noexcept -> E {
+ if (this->value_or_error.index() == 2) {
+ return std::get<2>(this->value_or_error);
+ } else {
+ return std::forward(default_error);
+ }
+ }
+};
+} // namespace tst
+
enum class success : std::uint8_t { one };
enum class failure : std::uint8_t { fail_one };
struct expected_to_channel_t {
template
struct own_receiver {
- using receiver_concept = ex::receiver_t;
+ using receiver_concept = ex::receiver_tag;
Receiver* receiver;
template
- auto set_value(std::expected&& exp) noexcept -> void {
+ auto set_value(tst::expected&& exp) noexcept -> void {
if (exp) {
std::cout << "received an expected with value from child/upstream\n" << std::flush;
ex::set_value(std::move(*receiver), exp.value_or(Value{}));
@@ -43,7 +88,7 @@ struct expected_to_channel_t {
};
template
struct state {
- using operation_state_concept = ex::operation_state_t;
+ using operation_state_concept = ex::operation_state_tag;
using child_state_t = decltype(ex::connect(std::declval(), std::declval>()));
Receiver parent_receiver;
child_state_t child_state;
@@ -60,13 +105,17 @@ struct expected_to_channel_t {
template
struct sender {
- using sender_concept = ex::sender_t;
+ using sender_concept = ex::sender_tag;
// value_types_of -> set_value_t(std::expected)
// -> completion_signatures
// -> + error_type_of
// -> + sends_stopped -> set_stopped_t()
// -> unique
using completion_signatures = ex::completion_signatures;
+ template
+ static consteval auto get_completion_signatures() noexcept -> completion_signatures {
+ return {};
+ }
CSender child_sender;
template
@@ -83,12 +132,12 @@ struct expected_to_channel_t {
sender> operator()(CSender&& child_sender) const {
return {std::forward(child_sender)};
}
- auto operator()() const { return ex::detail::sender_adaptor{*this}; }
+ auto operator()() const { return ex::detail::make_sender_adaptor(*this); }
};
inline constexpr expected_to_channel_t expected_to_channel{};
int main() {
- ex::sync_wait(ex::just(std::expected(success::one)) | expected_to_channel() |
+ ex::sync_wait(ex::just(tst::expected(success::one)) | expected_to_channel() |
ex::then([](success) noexcept { std::cout << "success\n"; }) |
ex::upon_error([](failure) noexcept { std::cout << "fail\n"; }));
}
diff --git a/examples/intro-timer.hpp b/examples/intro-timer.hpp
index a81ffe6e..acc79871 100644
--- a/examples/intro-timer.hpp
+++ b/examples/intro-timer.hpp
@@ -1,17 +1,9 @@
// examples/intro-timer.hpp -*-C++-*-
-// ----------------------------------------------------------------------------
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-// ----------------------------------------------------------------------------
#ifndef INCLUDED_EXAMPLES_INTRO_TIMER
#define INCLUDED_EXAMPLES_INTRO_TIMER
-#include
-#include
-#include
-#include
-#include
-
// ----------------------------------------------------------------------------
namespace intro {
@@ -28,7 +20,7 @@ struct intro::timer {
};
template
struct state : state_base {
- using operation_state_concept = ex::operation_state_t;
+ using operation_state_concept = ex::operation_state_tag;
timer* self;
std::remove_cvref_t receiver;
std::chrono::milliseconds ms;
@@ -40,8 +32,12 @@ struct intro::timer {
void complete() override { ex::set_value(std::move(receiver)); }
};
struct sender {
- using sender_concept = ex::sender_t;
+ using sender_concept = ex::sender_tag;
using completion_signatures = ex::completion_signatures;
+ template
+ static consteval auto get_completion_signatures() noexcept -> completion_signatures {
+ return {};
+ }
timer* self;
std::chrono::milliseconds ms;
template
@@ -72,19 +68,19 @@ struct intro::timer {
template
struct run_state {
struct recv {
- using receiver_concept = ex::receiver_t;
+ using receiver_concept = ex::receiver_tag;
run_state* self;
auto set_value(auto&&...) noexcept -> void { this->self->run_one(); }
auto set_error(auto&&) noexcept -> void { this->self->run_one(); }
auto set_stopped() noexcept -> void { this->self->run_one(); }
};
- using operation_state_concept = ex::operation_state_t;
- using scheduler_t = decltype(ex::get_delegation_scheduler(ex::get_env(std::declval())));
+ using operation_state_concept = ex::operation_state_tag;
+ using scheduler_tag = decltype(ex::get_delegation_scheduler(ex::get_env(std::declval())));
static_assert(ex::receiver);
- static_assert(ex::scheduler);
- static_assert(ex::sender()))>);
- using state_t = decltype(ex::connect(ex::schedule(std::declval()), std::declval()));
+ static_assert(ex::scheduler);
+ static_assert(ex::sender()))>);
+ using state_t = decltype(ex::connect(ex::schedule(std::declval()), std::declval()));
struct state_ctor {
state_t state;
template
@@ -110,8 +106,12 @@ struct intro::timer {
auto start() & noexcept -> void { this->schedule_one(); }
};
struct run_sender {
- using sender_concept = ex::sender_t;
+ using sender_concept = ex::sender_tag;
using completion_signatures = ex::completion_signatures;
+ template
+ static consteval auto get_completion_signatures() noexcept -> completion_signatures {
+ return {};
+ }
timer* self;
diff --git a/examples/just_stopped.cpp b/examples/just_stopped.cpp
index ad0c668a..ec15cf8c 100644
--- a/examples/just_stopped.cpp
+++ b/examples/just_stopped.cpp
@@ -1,8 +1,15 @@
+// examples/just_stopped.cpp -*-C++-*-
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
#include
+#endif
namespace ex = beman::execution;
struct receiver {
- using receiver_concept = ex::receiver_t;
+ using receiver_concept = ex::receiver_tag;
auto set_value(auto&&...) noexcept -> void {}
auto set_error(auto&&) noexcept -> void {}
auto set_stopped() noexcept -> void {}
diff --git a/examples/meta.hpp b/examples/meta.hpp
index 96d8d4d6..3f6a04ca 100644
--- a/examples/meta.hpp
+++ b/examples/meta.hpp
@@ -4,11 +4,6 @@
#ifndef INCLUDED_EXAMPLES_META
#define INCLUDED_EXAMPLES_META
-#include
-#include
-#include
-#include
-
// ----------------------------------------------------------------------------
namespace meta {
diff --git a/examples/modules-and-header.cpp b/examples/modules-and-header.cpp
new file mode 100644
index 00000000..b531140c
--- /dev/null
+++ b/examples/modules-and-header.cpp
@@ -0,0 +1,11 @@
+// examples/modules-and-header.cpp -*-C++-*-
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include
+import beman.execution;
+namespace ex = beman::execution;
+
+int main() {
+ auto [rc] = *ex::sync_wait(ex::just(0));
+ return rc;
+}
diff --git a/examples/modules.cpp b/examples/modules.cpp
index b3c245c9..d1ffb843 100644
--- a/examples/modules.cpp
+++ b/examples/modules.cpp
@@ -8,14 +8,13 @@ import std;
#include
#include
#include
+#include
+#include
+#include
#endif
-#if __cpp_modules < 201907L
-#include
-#else
-import beman_execution;
-#endif
+import beman.execution;
namespace ex = beman::execution;
diff --git a/examples/playground.cpp b/examples/playground.cpp
index c4ea5220..f62cdea9 100644
--- a/examples/playground.cpp
+++ b/examples/playground.cpp
@@ -1,10 +1,15 @@
// examples/playground.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#include
#include
#include
#include
+#include
+#ifdef BEMAN_HAS_MODULES
+import beman.execution;
+#else
+#include
+#endif
namespace ex = ::beman::execution;
diff --git a/examples/sender-demo.cpp b/examples/sender-demo.cpp
index d9ac194e..c7522737 100644
--- a/examples/sender-demo.cpp
+++ b/examples/sender-demo.cpp
@@ -1,15 +1,24 @@
-#include
-#include