This is for working on libfn to use the library see the README Development environment Building and testing libfn needs a C 20 toolchain with one exception polyfills C 23 26 standard library utilities builds on rather than the newer standard library The minimum supported compilers are gcc 12 and clang 16 if your OS does not ship one recent enough use the devcontainer or Nix see nix README md You may also use Apple Clang 16 0 or Microsoft Visual Studio 2022 or newer The exception the C 23 validation lane CMake option below needs a compiler with solid C 23 support gcc 15 or clang 21 or newer and is rejected with MSVC Likewise the C 26 lane below needs a compiler implementing and is rejected with MSVC Standard mode feature reliance The headers rely on C 20 only in every default mode build The C 23 validation lane compiles the same sources under and relies on no C 23 feature The mode relies on exactly one C 26 feature feature test macro implemented by gcc 16 A compiler without it rejects the mode with an naming this requirement if your build fails there either drop or switch to a compiler that implements the feature never relies on post C 20 features in any mode it is a polyfill precisely for compilers without the newer standard library Building locally Both and target C 20 The unit tests and examples build in C 20 by default If you have a recent enough compiler use the CMake option to additionally build them in C 23 requires This also enables and which run the test suites against the standard library s own and validating the polyfills and the tests themselves against the real thing The C 26 mode is separate from the standard level defining switches the library s internal type ordering to see the feature reliance note above and moves into its own ABI namespace since the two orderings may disagree on alternative order and the layouts must never link as one The CMake option enables the mode for this build tree and requires and additionally builds the tests and examples in C 26 with the mode enabled For a quick check of a single example without the full CMake Catch2 setup Unit tests Although we aim for 100% unit test coverage executing every line and branch tells only part of the story in this project Many important guarantees are compile time properties such as overload resolution conversions constraints and specifications Tests must therefore exercise an interface with combinations of dimensions not merely execute every line Structure A consistent test shape helps make those combinations visible The guidelines below are a living standard and may evolve when a clearer structure reveals coverage more effectively Use one for each file level entity a class a specialisation an overload set etc and one top level for each member function or overload set Use nested sections for the combinations that the member supports normally value category first then type properties Put constraint checks last A test s place in the hierarchy should say what it tests do not put it in the nearest convenient section or a catch all case Choose the dimensions relevant to the interface under test Common dimensions include the four cv ref value categories and and properties of the participating types such as whether they support default copy or move construction and assignment and whether those operations are trivial or nothrow These examples are not exhaustive they illustrate the range of cases a thorough unit test may need to cover Keep section names short Use not the BDD macros and Declare the subject alias and reusable probe lambdas once at scope rather than redeclaring them in nested sections Use only when the same test battery genuinely applies to several subjects Because it is a macro give template arguments containing commas a named alias before passing them to it When restructuring tests run them and record Catch2 s test case and assertion counts before and after the change The counts should not change accidentally contributes one assertion per leaf section while an assertion in a parent section runs once for every leaf below it Assertions Match the assertion to the kind of fact being tested Use without a runtime twin for a fact decided by the compiler a specification overload viability concept satisfaction a result type or a property of default copy or move construction and assignment including whether an operation is trivial or nothrow Do not apply a runtime assertion such as to a compiler generated constant for example it adds neither coverage nor useful information Test behaviour performed by the program a computed value side effect or state change both at runtime with and in constant evaluation with a twin that repeats the same operations The runtime test provides coverage and lets sanitizers observe the execution The verifies that users can perform the same operation in constant evaluation allowing them to use the compiler to diagnose undefined behaviour For code that may throw test the expected exception at runtime with and where an operand remains observable after the throw assert its state Also exercise the same operation in a non throwing case both at runtime and in constant evaluation a throwing path test alone never lets that code run to completion To reach a throw inside relocation machinery arm a local fixture whose n th copy or move throws pre constructing the operands so that only the operation under test consumes the count End a containing only compile time assertions with This satisfies Catch2 s no assertion warning and makes such sections easy to find Compile time probes Pair every negative assertion with its converse For example also passes if the specification is unconditionally false Add a witness for which it is true so the pair proves that the specification is conditional Give a negative constraint or viability probe a positive control for the same reason choosing witnesses that exercise the relevant corner cases Make the expression in a negative viability probe dependent on a template parameter A requires expression over concrete types is not a substitution context and an invalid requirement is a hard error Put the dependent probe in a generic lambda or a type keyed concept so that invalid substitution produces Choose fixtures with care when probing exception specifications has a separate constructor for each value category and its non const lvalue copy constructor is always A configured to throw is therefore not throwing for every value category and a specification that remains for that constructor may be correct Use a plain local type when the test needs every relevant construction to be potentially throwing Client code Code written against the library examples documentation snippets reproducers anything a user of libfn might imitate Let the library derive graded error types Never spell a multi alternative by hand the canonical alternative order is internal and platform dependent MSVC orders class types after fundamentals use and pin a deduced type with where the type is the point Give behaviour a constant evaluation twin a replaying the same operations UB is ill formed in a constant expression so the compiler diagnoses what a runtime run may silently get wrong users rely on the library in constexpr for exactly this Prefer monadic composition to unchecked access is the library s only throw reaching for it where would carry the value teaches the wrong idiom Don t trust a bare probe of a libfn call several refusals on mismatched branches on grade mismatches are s inside the callee so the probe answers true and instantiation hard errors Compile the call to know negative viability probes must be dependent see above Outside the internal type ordering is not expected to work with unnamed types or types without linkage no lambdas or local types as alternatives in portable code One libfn version per binary header only makes mixing versions an ODR violation including two releases of the same line default and builds never link as one by design see README A standalone reproducer compiler or library bug proves nothing until it is UB free gate it on UBSan and ASan with empty stderr as the criterion not the exit code Header layering Four header trees under each depending only on those below it may use and may use never the C 23 26 polyfill standalone except for the version header the sole root header no dependencies To make an level facility available to hoist it the implementation moves into as detail headers are not user facing and carry no Doxygen remains a thin public wrapper re exporting it as pattern Versioning in the repository root is the single source of truth for the project version A pre commit hook mirrors it into and the header defining the macro that names the ABI versioning inline namespace wrapping and its mode less sibling wrapping the layer rule below Do not hand edit those version literals edit and let the hook sync them The namespace spelling is derived not copied 0 y lines with y 1 share z bumps are ABI compatible the 0 0 z line versions per patch a SemVer prerelease is appended and the twin selected by defining keeps last is mode less its layouts never depend on the C 26 type ordering or other language features so it wraps in the plain spelling regardless of mode and its types stay link compatible across modes A second hook verifies the layer rule every opening in carries every opening Pre commit This repository uses pre commit to enforce formatting of the C source code and perform other checks The details can be seen in To install git commit hooks which will run checks on the repository as you commit changes configure a Python virtual environment install the requirements from run in your local repository You can run all checks manually as follows If a hook modifies files e g clang format or the version sync above the commit is aborted re stage the changes and commit again Running CI on a fork The and workflows are fork aware they target the upstream projects only when run from and otherwise report against your own accounts Set these in your fork Settings Secrets and variables Actions Codecov works with no configuration uploads are tokenless They are heavily throttled which a low traffic fork will not notice set the secret to lift the throttle SonarCloud needs all three of the secret and the and repository variables Without the token the SonarCloud scan steps no op the build still runs but no analysis is uploaded With the token set but a variable missing no SonarCloud analysis is produced and it never falls back to the upstream project a misconfigured push fails fast with a pointer here GitHub Actions workflow pitfalls A few conventions for files under Don t pin on without a reason The default already pins to for and so is redundant in the common case If you do need it e g so the working tree matches a downstream nix input s leave a comment saying so Pin actions to a commit SHA not a tag is a supply chain trust decision whoever can move the tag can execute code in your workflow Pin the SHA with a trailing version comment e g Dependabot keeps it current and enforces it via pre commit One concern per job matrix ify variations Don t pack several install build test sequences into one job especially if they need an inter step cleanup Each variation should be its own matrix entry gate per variation steps with when needed The build everything run gets its own matrix slot If one combination should run a superset of the per mode targets add it via e g and gate the mode specific step with Avoid the build clean rebuild dance inside a single job Multi line uses and The plain scalar form followed by indented continuation lines folds into one shell line which is awkward to copy from a diff into a terminal The literal block form lines ending in reads and pastes as a shell snippet Insecure shell inside blocks runs via pre commit on every block if is on without it the check is silently skipped Install to avoid surprises when CI runs against your PR pfn pfn expected pfn optional pfn invoke_r pfn unreachable fn pfn VALIDATE_CXX23 VALIDATE_CXX26 std type_order std c 23 LIBFN_CXX26 std type_order __cpp_lib_type_order #error LIBFN_CXX26 pfn fn include fn pfn include pfn VALIDATE_CXX23 ON LIBFN_TESTS ON tests pfn expected_validation cpp tests pfn optional_validation cpp pfn std expected std optional LIBFN_CXX26 std type_order fn copack choice LIBFN_CXX26 ON VALIDATE_CXX26 ON LIBFN_TESTS ON LIBFN_CXX26 ON g std c 20 Iinclude examples polygon main cpp o tmp polygon noexcept TEST_CASE SECTION TEST_CASE SECTION & const & && const && SECTION GIVEN WHEN THEN TEST_CASE TEMPLATE_TEST_CASE SUCCEED static_assert noexcept CHECK noexcept f CHECK static_assert static_assert CHECK_THROWS_AS SECTION SUCCEED static_assert not noexcept expr false helper_t noexcept helper_t noexcept copack< > copack_for< > static_assert static_assert value and_then transform requires apply and_then static_assert ### Compile time probes LIBFN_CXX26 copack z y LIBFN_CXX26 ## Versioning and ABI include fn fn detail pfn fn detail pfn fn pfn libfn_version hpp fn fn detail fn detail X hpp fn detail _name fn X hpp fn name fn functional hpp VERSION scripts sync_versions py ports libfn vcpkg json version semver MODULE bazel include libfn_version hpp LIBFN_VERSION fn LIBFN_VERSION_BASE pfn VERSION v0_<y> dev _dev _cxx26 LIBFN_CXX26 _cxx26 pfn LIBFN_VERSION_BASE scripts check_namespace_wrap py namespace fn include inline namespace LIBFN_VERSION namespace pfn inline namespace LIBFN_VERSION_BASE pre commit config yaml ci pre commit requirements txt pre commit install # Set up a virtual environment to install pre commit python3 m venv venv source venv bin activate pip install r ci pre commit requirements txt # Install the pre commit hooks locally pre commit install # Source the virtual environment to access pre commit source venv bin activate # Run pre commit on local files pre commit run all files codecov sonarcloud libfn libfn functional CODECOV_TOKEN SONAR_TOKEN SONAR_ORGANIZATION SONAR_PROJECT_KEY github workflows ref actions checkout github sha push pull_request workflow_dispatch ref $ github sha rev $ github sha uses owner repo@v1 actions checkout@34e1148 # v4 3 1 zizmor vcpkg remove cmake build target clean if matrix name X matrix include mode all if matrix mode all run | run cmd run | run actionlint shellcheck run shellcheck $PATH shellcheck