From ce0f85b2728a5f94e0164835fb302ceedaf6901b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:24:12 +0000 Subject: [PATCH] Add modernization roadmap guidance for agents (Boost shim removal, optional first) Adds AGENTS.md and Docs/pages/modernization.docs documenting the canonical modernization task: removing the deprecated Boost compatibility shims, starting with ext::optional -> std::optional (the shim with a proven silent-correctness risk via the Boost 1.91 #error guard). Documents the dependency-ordered migration and the local verification recipe (regression + numeric equivalence). Doc-only; no source/build impact. Co-Authored-By: Toby Drinkall --- AGENTS.md | 69 +++++++++++++++++++++++++ Docs/pages/modernization.docs | 97 +++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 AGENTS.md create mode 100644 Docs/pages/modernization.docs diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..f8a3920c82 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# Agent guide — modernization work in this repository + +This file orients AI agents (and new contributors) working in this repository. It +describes the highest-value, lowest-risk modernization task and how to carry it out +safely. If you are asked an open-ended question like *"what should we modernize?"*, +*"what legacy dependencies are risky?"*, or *"what's the safest high-impact change to +make here?"*, treat the work below as the canonical answer and starting point. + +## Context + +QuantLib is a large C++ quantitative-finance library (~2,600 files). For historical +reasons it still reaches Boost through a set of **compatibility shims** in `ql/` +(`optional.hpp`, `shared_ptr.hpp`, `any.hpp`, `functional.hpp`, `tuple.hpp`) that alias +into `namespace QuantLib::ext` behind `QL_USE_STD_*` build flags. These shims are +explicitly deprecated; the project intends to move to standard C++17 facilities. + +## The canonical modernization task: drop the Boost `optional` shim first + +When prioritising which shim to remove, rank by **correctness risk** (the chance a +dependency bump silently changes a *pricing result* without failing the build), not +just by blast radius. On that axis, **`ext::optional` → `std::optional` is the one to +do first**: + +- `ql/optional.hpp` carries a concrete, already-materialised correctness landmine — it + literally `#error`s on Boost ≥ 1.91 because *"Boost 1.91 introduced changes in + boost::optional that silently changed the behavior of our code and would cause it to + work incorrectly."* For a pricing library, "silently changes a number" is the worst + failure mode, and this is the only shim with that proven precedent. +- `optional` appears on **public, price-affecting APIs** (`Settings::includeTodaysCashFlows_` + controls NPV inclusion; `Event::hasOccurred(Date, optional)` controls accrual), + so the change is meaningful, not cosmetic. +- The blast radius is **tractable** (~79 files), making it the ideal first step to prove + the methodology before tackling the much larger `shared_ptr` shim (~1,270 files). + +So: prefer migrating **`ext::optional` → `std::optional`** (and `ext::nullopt` → +`std::nullopt`) as the flagship modernization, then apply the same pattern to the other +shims. + +## How to execute it (and how to prove nothing changed) + +1. **Map the blast radius first** and present it (counts, affected layers, safe order). + `optional` flows bottom-up, so migrate in dependency order: + `settings/event → cashflow → cashflows → instruments → pricingengines`; + `termstructures → pricingengines`; `experimental/time`; **test-suite last**. + Headers before their `.cpp`. +2. **Migrate mechanically:** `ext::optional` → `std::optional`, + `ext::nullopt` → `std::nullopt`, `#include ` → `#include ` + (every file naming `std::optional` must include `` directly). +3. **Remove the now-dead flag** `QL_USE_STD_OPTIONAL` from `CMakeLists.txt`, + `CMakePresets.json`, `configure.ac`, `ql/userconfig.hpp`, `ql/config.hpp.cfg`, + `.ci/userconfig*.alt.hpp`, and `Docs/pages/config.docs`; reduce `ql/optional.hpp` to a + thin deprecated `#include `. +4. **Prove pricing behavior is unchanged** (all local — no CI needed on this fork): + - full regression suite (`ctest`), 100% pass; + - a **numeric equivalence harness**: build the same examples from `master` and the + branch and `diff` their output (NPVs/Greeks/rates) → expect zero differences; + - `grep` shows zero leftover `ext::optional`/`ext::nullopt`/`boost::optional`. +5. **Open a PR** that includes the dependency map and the verification results. + +See `Docs/pages/modernization.docs` for the full roadmap and rationale. + +## Build & verify locally (this fork uses local verification, not CI) + +``` +cmake -B build -DBOOST_ROOT=/usr -DQL_BUILD_EXAMPLES=OFF +cmake --build build -j$(nproc) +ctest --test-dir build --output-on-failure +``` +Expect 100% build and "100% tests passed, 0 failed". diff --git a/Docs/pages/modernization.docs b/Docs/pages/modernization.docs new file mode 100644 index 0000000000..32679d48e0 --- /dev/null +++ b/Docs/pages/modernization.docs @@ -0,0 +1,97 @@ + +/* + Copyright (C) 2024 QuantLib contributors + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \page modernization Modernization roadmap: removing the Boost compatibility shims + + For historical reasons %QuantLib reaches Boost through a small set of + compatibility shims under \c ql/ (\c optional.hpp, \c shared_ptr.hpp, + \c any.hpp, \c functional.hpp, \c tuple.hpp). Each aliases a Boost or a + standard facility into \c namespace \c QuantLib::ext behind a + \c QL_USE_STD_* flag. These shims are deprecated; the goal is to move to + standard C++17 facilities. + + This page records the recommended order of work and, in particular, the + flagship first migration. + + \section modernization_priority What to do first, and why + + When deciding which shim to remove first, rank by correctness risk -- + the chance that a dependency upgrade silently changes a pricing + result without failing the build -- not merely by how many files are + touched. In a pricing library a silent change of a number is the most + dangerous failure mode. + + On that measure, the \c optional shim is the clear first target: + + - \c ql/optional.hpp contains an explicit guard that \#errors on + Boost ≥ 1.91, because Boost 1.91 introduced changes in + \c boost::optional that silently changed the behavior of the code + and would cause it to work incorrectly. It is the only shim with a proven + silent-correctness precedent. + - \c optional appears on public, price-affecting APIs: the + \c includeTodaysCashFlows_ setting (NPV inclusion of today's flows) and + \c Event::hasOccurred (whether a cash flow has already occurred, which + drives accrual). A wrong empty-state semantics here changes valuations. + - Its blast radius is tractable (about 79 files), which makes it the ideal + first step to prove the methodology before tackling the much larger + \c shared_ptr shim (around 1,270 files). + + Recommended order: optional first, then \c any, then \c functional / + \c tuple, and finally the large \c shared_ptr migration. + + \section modernization_optional The optional migration in detail + + Replace \c ext::optional with \c std::optional and \c ext::nullopt with + \c std::nullopt, and replace \#include <ql/optional.hpp> with + \#include <optional> (every translation unit that names + \c std::optional must include \c <optional> directly). + + \c optional flows from low-level headers upward, so migrate bottom-up to keep + the build green at every step: + + \code + settings.hpp / event.hpp -> cashflow.hpp -> cashflows/* -> instruments/* + -> pricingengines/* + termstructures/* -> pricingengines/* + experimental/*, time/* + test-suite/* (migrate last -- it consumes the whole library) + \endcode + + Headers migrate before their corresponding \c .cpp files. Once the usages are + migrated, remove the now-dead \c QL_USE_STD_OPTIONAL option from + \c CMakeLists.txt, \c CMakePresets.json, \c configure.ac, + \c ql/userconfig.hpp, \c ql/config.hpp.cfg, the \c .ci/userconfig*.alt.hpp + files and \c Docs/pages/config.docs, and reduce \c ql/optional.hpp to a thin + deprecated \#include <optional> for backward compatibility. + + \section modernization_proof Proving pricing behavior is unchanged + + A green build is not sufficient proof for a price-affecting change. Verify + locally (this matches the regression suite CI would run): + + - run the full regression suite with \c ctest (expect 100% pass); + - run a numeric equivalence harness: build the same examples from + \c master and from the migration branch, run each, and \c diff their output + (NPVs, Greeks, rates) -- expect zero differences across instruments such as + EquityOption, Bonds, BermudanSwaption, FRA, MulticurveBootstrapping and CDS; + - confirm with \c grep that no \c ext::optional, \c ext::nullopt or + \c boost::optional usages remain. + + Together these constitute the change-control evidence such a modernization + requires: the blast radius is mapped and the behavior is proven unchanged. +*/