Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ jobs:
echo ${{ matrix.config.cmake_args }}
echo ${{ matrix.config.toolchain }}
rm -rf .build
cmake ${{ matrix.config.cmake_args }} -DCMAKE_INSTALL_PREFIX=.install -DCMAKE_TOOLCHAIN_FILE="etc/${{ matrix.config.toolchain }}-toolchain.cmake" -B .build -S .
cmake ${{ matrix.config.cmake_args }} \
-DCMAKE_INSTALL_PREFIX=.install \
-DCMAKE_TOOLCHAIN_FILE="etc/${{ matrix.config.toolchain }}-toolchain.cmake" \
-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="./cmake/use-fetch-content.cmake" \
-B .build \
-S .
- name: CMake ASAN Build
run: |
set -x
Expand Down
6 changes: 0 additions & 6 deletions .markdownlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
# Disable inline html linter is needed for <details> <summary>
MD033: false

# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md013.md
# Conforms to .clang-format ColumnLimit
# Update the comment in .clang-format if we no-longer tie these two column limits.
MD013:
line_length: 119

# MD024/no-duplicate-heading : https://github.com/DavidAnson/markdownlint/blob/main/doc/md024.md
# Supress warning about the same heading twice unless they are in the same block
MD024:
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ repos:
rev: v0.43.0
hooks:
- id: markdownlint
args: ['--disable', 'MD013', ' --']
exclude: ^papers/

# Config file: .codespell_ignore
Expand Down
48 changes: 23 additions & 25 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@ option(
${PROJECT_IS_TOP_LEVEL}
)

# Build the tests if enabled via the option OPTIONAL_ENABLE_TESTING
if(OPTIONAL_ENABLE_TESTING)
# Fetch GoogleTest
FetchContent_Declare(
googletest
EXCLUDE_FROM_ALL
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG
e39786088138f2749d64e9e90e0f9902daa77c40 # release-1.15.0
)
FetchContent_MakeAvailable(googletest)
endif()

set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ON)

# Create the library target and named header set for beman_optional
Expand All @@ -41,18 +28,29 @@ target_sources(
)

if(OPTIONAL_ENABLE_TESTING)
# Create the library target and named header set for testing beman_optional
# and mark the set private
add_executable(beman_optional_test)
target_sources(
beman_optional_test
PRIVATE
FILE_SET beman_optional_test_headers
TYPE HEADERS
BASE_DIRS tests
)

add_subdirectory(tests/beman/optional)
find_package(GTest QUIET)
if(GTest_FOUND)
# Create the library target and named header set for testing beman_optional
# and mark the set private
add_executable(beman_optional_test)
target_sources(
beman_optional_test
PRIVATE
FILE_SET beman_optional_test_headers
TYPE HEADERS
BASE_DIRS tests
)
# Tests
add_subdirectory(tests/beman/optional)
else()
message(
WARNING
"
No provider for GTest. Unable to build tests.
Consider using CMAKE_PROJECT_TOP_LEVEL_INCLUDES=./cmake/use-fetch-content.cmake as documented in the README.md
"
)
endif()
endif()

add_subdirectory(include/beman/optional)
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ define run_cmake =
-DCMAKE_CONFIGURATION_TYPES=$(_configuration_types) \
-DCMAKE_INSTALL_PREFIX=$(abspath $(INSTALL_PREFIX)) \
-DCMAKE_EXPORT_COMPILE_COMMANDS=1 \
-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="./cmake/use-fetch-content.cmake" \
$(_cmake_args) \
$(CURDIR)
endef
Expand Down
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ SPDX-License-Identifier: 2.0 license with LLVM exceptions
<img src="https://github.com/bemanproject/beman/blob/main/images/logos/beman_logo-beman_library_production_ready_api_may_undergo_changes.png" style="width:5%; height:auto;"> ![CI Tests](https://github.com/bemanproject/optional/actions/workflows/ci.yml/badge.svg) [![Coverage](https://coveralls.io/repos/github/bemanproject/optional/badge.svg?branch=main)](https://coveralls.io/github/bemanproject/optional?branch=main)
<!-- markdownlint-enable -->

This repository implements `std::optional` extensions targeting C++26. The `beman.optional` library aims to evaluate
the stability, the usability, and the performance of these proposed changes before they are officially adopted by WG21
into the C++ Working Draft. Additionally, it allows developers to use these new features before they are implemented in
major standard library compilers.
This repository implements `std::optional` extensions targeting C++26. The `beman.optional` library aims to evaluate the stability, the usability, and the performance of these proposed changes before they are officially adopted by WG21 into the C++ Working Draft. Additionally, it allows developers to use these new features before they are implemented in major standard library compilers.

**Implements**: [Give *std::optional* Range Support (P3168R2)](https://wg21.link/P3168R2) and [`std::optional<T&>` (P2988R5)](https://wg21.link/P2988R5)

Expand All @@ -27,11 +24,9 @@ Documentation and associated papers are licensed with the Creative Commons Attri

// SPDX-License-Identifier: CC-BY-4.0

The intent is that the source and documentation are available for use by people implementing their own optional types
as well as people using the optional presented here as-is.
The intent is that the source and documentation are available for use by people implementing their own optional types as well as people using the optional presented here as-is.

The README itself is licensed with CC0 1.0 Universal. Copy the contents and incorporate in your own work as you see
fit.
The README itself is licensed with CC0 1.0 Universal. Copy the contents and incorporate in your own work as you see fit.

// SPDX-License-Identifier: CC0-1.0

Expand Down Expand Up @@ -68,7 +63,7 @@ Full code can be found in [./examples/range_loop.cpp](./examples/range_loop.cpp)
### optional_ref

The next code snippet shows optional reference support added in [`std::optional<T&>`
(P2988R5)](https://wg21.link/P2988R5):
(P2988)](https://wg21.link/P2988):

```cpp
#include <beman/optional/optional.hpp>
Expand Down Expand Up @@ -107,8 +102,7 @@ Default build: `C++23`. Please check `etc/${compiler}-flags.cmake`.

### Dependencies

This project is mainly tested on `Ubuntu 22.04` and `Ubuntu 24.04`, but it should be as portable as CMake is. This
project has no C or C++ dependencies.
This project is mainly tested on `Ubuntu 22.04` and `Ubuntu 24.04`, but it should be as portable as CMake is. This project has no C or C++ dependencies.

Build-time dependencies:

Expand All @@ -128,14 +122,29 @@ apt-get install \
clang-18 clang++-18 clang-17 clang++-17
```

<details>
<summary> Build GoogleTest dependency from github.com </summary>

If you do not have GoogleTest installed on your development system, you may
optionally configure this project to download a known-compatible release of
GoogleTest from source and build it as well.

```shell
cmake -B build -S . -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./cmake/use-fetch-content.cmake
```

The precise version of GoogleTest that will be used is maintained in
`./lockfile.json`.

</details>

### Instructions

Full set of supported toolchains can be found in [.github/workflows/ci.yml](.github/workflows/ci.yml).

#### Preset CMake Flows

This project strives to be as normal and simple a CMake project as possible. This build workflow in particular will
work, producing a static `beman_optional` library, ready to package:
This project strives to be as normal and simple a CMake project as possible. This build workflow in particular will work, producing a static `beman_optional` library, ready to package:

```shell
# List available preset configurations:
Expand Down Expand Up @@ -238,14 +247,9 @@ No tests were found!!!

#### Pre-Commit for Linting

Various linting tools are configured and installed via the [pre-commit](https://pre-commit.com/) framework. This
requires a working python environment, but also allows the tools, such as clang-format and cmake-lint, to be versioned
on a per project basis rather than being installed globally. Version changes in lint checks often means differences in
success or failure between the versions in CI and the versions used by a developer. By using the same configurations,
this problem is avoided.
Various linting tools are configured and installed via the [pre-commit](https://pre-commit.com/) framework. This requires a working python environment, but also allows the tools, such as clang-format and cmake-lint, to be versioned on a per project basis rather than being installed globally. Version changes in lint checks often means differences in success or failure between the versions in CI and the versions used by a developer. By using the same configurations, this problem is avoided.

In order to set up a python environment, using a python virtual environment can simplify maintaining different
configurations between projects. There is no particular dependency on a particular python3 version.
In order to set up a python environment, using a python virtual environment can simplify maintaining different configurations between projects. There is no particular dependency on a particular python3 version.

##### Creating and configuring a venv

Expand All @@ -258,8 +262,7 @@ python3 -m venv .venv
. .venv/bin/activate && exec bash
```

This will create the venv, install the python and python development tools, and run bash with the PATH and other
environment variables set to use the venv preferentially.
This will create the venv, install the python and python development tools, and run bash with the PATH and other environment variables set to use the venv preferentially.

##### Running the linting tools

Expand Down Expand Up @@ -291,3 +294,4 @@ Latest revision(s) of the papers can be built / found at:
* issue: [#1661](https://github.com/cplusplus/papers/issues/1661)
* LEWG:
* Reviewed in Tokyo 2024.
* Forwarded by LEWG in 2025 in Hagenberg.
176 changes: 176 additions & 0 deletions cmake/use-fetch-content.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
cmake_minimum_required(VERSION 3.24)

if(NOT BEMAN_OPTIONAL_LOCKFILE)
set(BEMAN_OPTIONAL_LOCKFILE
"lockfile.json"
CACHE FILEPATH
"Path to the dependency lockfile for the Beman Optional."
)
endif()

set(BemanOptional_projectDir "${CMAKE_CURRENT_LIST_DIR}/..")
message(TRACE "BemanOptional_projectDir=\"${BemanOptional_projectDir}\"")

message(TRACE "BEMAN_OPTIONAL_LOCKFILE=\"${BEMAN_OPTIONAL_LOCKFILE}\"")
file(
REAL_PATH
"${BEMAN_OPTIONAL_LOCKFILE}"
BemanOptional_lockfile
BASE_DIRECTORY "${BemanOptional_projectDir}"
EXPAND_TILDE
)
message(DEBUG "Using lockfile: \"${BemanOptional_lockfile}\"")

# Force CMake to reconfigure the project if the lockfile changes
set_property(
DIRECTORY "${BemanOptional_projectDir}"
APPEND
PROPERTY CMAKE_CONFIGURE_DEPENDS "${BemanOptional_lockfile}"
)

# For more on the protocol for this function, see:
# https://cmake.org/cmake/help/latest/command/cmake_language.html#provider-commands
function(BemanOptional_provideDependency method package_name)
# Read the lockfile
file(READ "${BemanOptional_lockfile}" BemanOptional_rootObj)

# Get the "dependencies" field and store it in BemanOptional_dependenciesObj
string(
JSON
BemanOptional_dependenciesObj
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_rootObj}"
"dependencies"
)
if(BemanOptional_error)
message(FATAL_ERROR "${BemanOptional_lockfile}: ${BemanOptional_error}")
endif()

# Get the length of the libraries array and store it in BemanOptional_dependenciesObj
string(
JSON
BemanOptional_numDependencies
ERROR_VARIABLE BemanOptional_error
LENGTH "${BemanOptional_dependenciesObj}"
)
if(BemanOptional_error)
message(FATAL_ERROR "${BemanOptional_lockfile}: ${BemanOptional_error}")
endif()

# Loop over each dependency object
math(EXPR BemanOptional_maxIndex "${BemanOptional_numDependencies} - 1")
foreach(BemanOptional_index RANGE "${BemanOptional_maxIndex}")
set(BemanOptional_errorPrefix
"${BemanOptional_lockfile}, dependency ${BemanOptional_index}"
)

# Get the dependency object at BemanOptional_index
# and store it in BemanOptional_depObj
string(
JSON
BemanOptional_depObj
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_dependenciesObj}"
"${BemanOptional_index}"
)
if(BemanOptional_error)
message(
FATAL_ERROR
"${BemanOptional_errorPrefix}: ${BemanOptional_error}"
)
endif()

# Get the "name" field and store it in BemanOptional_name
string(
JSON
BemanOptional_name
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_depObj}"
"name"
)
if(BemanOptional_error)
message(
FATAL_ERROR
"${BemanOptional_errorPrefix}: ${BemanOptional_error}"
)
endif()

# Get the "package_name" field and store it in BemanOptional_pkgName
string(
JSON
BemanOptional_pkgName
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_depObj}"
"package_name"
)
if(BemanOptional_error)
message(
FATAL_ERROR
"${BemanOptional_errorPrefix}: ${BemanOptional_error}"
)
endif()

# Get the "git_repository" field and store it in BemanOptional_repo
string(
JSON
BemanOptional_repo
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_depObj}"
"git_repository"
)
if(BemanOptional_error)
message(
FATAL_ERROR
"${BemanOptional_errorPrefix}: ${BemanOptional_error}"
)
endif()

# Get the "git_tag" field and store it in BemanOptional_tag
string(
JSON
BemanOptional_tag
ERROR_VARIABLE BemanOptional_error
GET "${BemanOptional_depObj}"
"git_tag"
)
if(BemanOptional_error)
message(
FATAL_ERROR
"${BemanOptional_errorPrefix}: ${BemanOptional_error}"
)
endif()

if(method STREQUAL "FIND_PACKAGE")
if(package_name STREQUAL BemanOptional_pkgName)
string(
APPEND
BemanOptional_debug
"Redirecting find_package calls for ${BemanOptional_pkgName} "
"to FetchContent logic fetching ${BemanOptional_repo} at "
"${BemanOptional_tag} according to ${BemanOptional_lockfile}."
)
message(STATUS "${BemanOptional_debug}")
FetchContent_Declare(
"${BemanOptional_name}"
GIT_REPOSITORY "${BemanOptional_repo}"
GIT_TAG "${BemanOptional_tag}"
EXCLUDE_FROM_ALL
)
set(INSTALL_GTEST OFF) # Disable GoogleTest installation
FetchContent_MakeAvailable("${BemanOptional_name}")

# Important! <PackageName>_FOUND tells CMake that `find_package` is
# not needed for this package anymore
message(STATUS "setting ${BemanOptional_pkgName}_FOUND to true")
set("${BemanOptional_pkgName}_FOUND" TRUE PARENT_SCOPE)
endif()
endif()
endforeach()

# set(GTest_FOUND TRUE PARENT_SCOPE)
endfunction()

cmake_language(
SET_DEPENDENCY_PROVIDER BemanOptional_provideDependency
SUPPORTED_METHODS FIND_PACKAGE
)
Loading