From 8622292b8beabcf89a77b88e0e25a4f7443120c9 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Sun, 18 Oct 2026 23:26:27 +0200 Subject: [PATCH 1/2] docs: preserve the historical C++23 audit and set-aside probes --- ChangeLog | 10 + doc/Makefile.am | 6 + doc/refactor/5-cxx23-justification.md | 119 +++++ doc/refactor/cxx23-audit/README.md | 33 ++ .../cxx23-audit/cxx17-library-probe.cpp | 2 + .../cxx23-audit/cxx23-library-probe.cpp | 20 + .../cxx23-audit/set-aside-cxx17-notes.md | 36 ++ .../cxx23-audit/set-aside-cxx17.patch | 413 ++++++++++++++++++ 8 files changed, 639 insertions(+) create mode 100644 doc/refactor/5-cxx23-justification.md create mode 100644 doc/refactor/cxx23-audit/README.md create mode 100644 doc/refactor/cxx23-audit/cxx17-library-probe.cpp create mode 100644 doc/refactor/cxx23-audit/cxx23-library-probe.cpp create mode 100644 doc/refactor/cxx23-audit/set-aside-cxx17-notes.md create mode 100644 doc/refactor/cxx23-audit/set-aside-cxx17.patch diff --git a/ChangeLog b/ChangeLog index 156d3ba2..df14f1ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- preserve the historical C++23 requirement audit, compiler probes and the + set-aside compatibility experiment from issue #244 + - require ViennaRNA >= 2.7.0 and its logger/prepared-window APIs at configure time; install the logger lock for every supported version (PR #267) @@ -138,6 +141,13 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261018 Alexander Mitrofanov + * doc/refactor/5-cxx23-justification.md, doc/refactor/cxx23-audit/, + doc/Makefile.am + + recover the untracked historical assessment and standalone compiler probes + + archive the abandoned C++17 patch without changing the supported baseline + * distinguish the pinned September assessment from later source changes + 261015 Alexander Mitrofanov * configure.ac, conda-build-env.yml : * require ViennaRNA >= 2.7.0, the first release with vrna_log_lock_set() diff --git a/doc/Makefile.am b/doc/Makefile.am index cb78b081..6939ab75 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -4,6 +4,12 @@ ################################################################ EXTRA_DIST = \ + refactor/5-cxx23-justification.md \ + refactor/cxx23-audit/README.md \ + refactor/cxx23-audit/cxx23-library-probe.cpp \ + refactor/cxx23-audit/cxx17-library-probe.cpp \ + refactor/cxx23-audit/set-aside-cxx17.patch \ + refactor/cxx23-audit/set-aside-cxx17-notes.md \ conda.txt \ api-documentation.md \ build-api.sh \ diff --git a/doc/refactor/5-cxx23-justification.md b/doc/refactor/5-cxx23-justification.md new file mode 100644 index 00000000..8becdea7 --- /dev/null +++ b/doc/refactor/5-cxx23-justification.md @@ -0,0 +1,119 @@ +# Is the C++23 requirement justified? — issue #244 + +Assessment of upstream `0a4568ad6e3da52f935221ff0da98a4a171e9833`, +21 September 2026. This is an assessment of the requirement's benefits, +not a proposal to downgrade the implementation. + +**Conclusion:** C++23 has a concrete implementation rationale, principally +efficient construction of ViennaRNA constraint strings. The current source +really uses C++23, so changing only `configure.ac` would be incorrect. +However, the available evidence does not establish that mandatory C++23 +delivers a material performance or correctness advantage over an earlier +baseline. Requiring it is defensible as a deliberate maintenance/toolchain +policy; it should not be presented as necessary for the scientific algorithms +or as a demonstrated speed improvement. + +## Historical scope and recovery + +This document preserves the September 2026 assessment of the revision named +above. Later changes, including the mdspan migration in PR #248, are outside +its scope. Source line numbers refer to that historical revision. + +The shorter decision was published on [issue #244](https://github.com/BackofenLab/IntaRNA/issues/244), +but this full audit remained an untracked file in the local audit worktree. +The accompanying [archive](cxx23-audit/README.md) preserves the standalone +compiler probes and an abandoned C++17 compatibility experiment. The patch is +historical material for review and is not applied to the current build. + +## Which benefits actually require C++23? + +| Facility and locations | Standard | Benefit | Weight in the justification | +| --- | --- | --- | --- | +| `string::resize_and_overwrite` in `AccessibilityVrna.cpp:59` and `InteractionEnergyVrna.cpp:48` | C++23 | Fills owned string storage directly without first initializing bytes that will immediately be overwritten; the resulting string is null-terminated for ViennaRNA | The strongest C++23-specific reason: avoids redundant work with a standard API | +| `string::contains` in `RnaSequence.h:319`, `:503`, `:573` | C++23 | Expresses alphabet membership and ambiguity checks directly | Readability benefit; no inherent algorithmic advantage over `find(...) != npos` | +| String-stream `view()` in `OutputHandlerCsv.cpp`, `OutputHandlerEnsemble.cpp`, `OutputHandlerText.cpp` | C++20 | Inserts completed output without making an intermediate owning string | A real copy reduction, but cannot justify **23** specifically | +| Structured bindings and `unordered_map::try_emplace` in `PredictorMfeEns.cpp:121` | C++17 | Updates a partition entry with one lookup while preserving addition order | Useful optimization, independent of C++23 | +| `unique_ptr`, custom deleters, `make_unique`, direct sequence `c_str()` access | C++11/14 or earlier | Automatic cleanup, exception safety and removal of redundant sequence copies | Valuable modernization, but independent of C++23 | + +The language/library classification is documented in the +[libstdc++ implementation tables](https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html). +The purpose and tradeoffs of direct string construction are described in +[WG21 P1072R10](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html). + +The constraint helpers build buffers around ViennaRNA calls, rather than +changing predictor recurrences. Automatic ownership of those buffers is +useful, but ownership and exception safety do not themselves require +`resize_and_overwrite`. Its specific additional benefit is avoiding redundant +initialization while keeping a normal `std::string`. + +No C++23-specific language construct was identified in the production source. +The requirement is driven by library APIs. The migration explicitly deferred +facilities such as `mdspan`, `format` and `print`; possible future uses do not +establish a present requirement. See the original +[migration record](2-c++23.md#implemented-c23-modernization). + +## What the existing measurements establish + +The [recorded modernization benchmark](2-c++23.md#substantive-c23-follow-up-benchmark) +compares `321364c56e914e9eff56ddba4846852297fe5c55` with +`7a9517b0587dd308659b61f81b26373ac7a5d0b5` on the same pinned +GCC 14.4.0 / Boost 1.85.0 / ViennaRNA 2.7.2 environment. +The evidence is also identified in +[PR #234](https://github.com/BackofenLab/IntaRNA/pull/234) and +[benchmark evidence commit 69174eb](https://github.com/BackofenLab/IntaRNA-refactor-benchmark/commit/69174eb00354e24427a6ee37c98d4363930114ca). + +- Reported core timing ratios span 0.987x–1.022x (previous/modernized). +- The output-heavy case is 0.994x: 0.163162 s versus 0.164213 s. +- Canonical scientific outputs match, and peak RSS is near-stable. +- The recorded analysis explicitly finds no demonstrated general speedup. + +These measurements support preserving behavior across the modernization. +They do **not** isolate a C++23 benefit: both revisions already request +C++23, and the candidate bundles string changes with ownership, sequence-copy +and partition-lookup changes. Nor do small mixed timings prove that the +C++23 facilities have zero benefit. There is no isolated end-to-end result +showing that `resize_and_overwrite` materially improves an IntaRNA workload. + +The most useful missing evidence would compare the current implementation +with an otherwise identical constraint-buffer implementation, using the same +compiler and C++23 mode. That isolates the reason for the requirement without +confounding it with compiler versions or unrelated optimizations. It should +cover constraint-heavy workloads and representative normal use, and report +dispersion and output equality. A microbenchmark alone would not establish +an application-level benefit. + +## Maintenance and compiler policy + +Keeping one C++23 baseline allows the implementation to use the standard +buffer API directly without compatibility branches. The GCC 14 and Apple +Clang CI results establish a tested support matrix. Those are legitimate +maintenance considerations, but the same migration tests do not establish +that older standards are intrinsically unsafe or unsuitable. + +There is also a distinction between using C++23 APIs and requiring a compiler +to advertise the final C++23 language value. The vendored Autoconf macro +rejects `__cplusplus < 202302L`. Locally, GCC 13.3 advertises `202100L` in +`-std=c++23` mode, but compiles and runs the **unchanged, exact library probe +from current `configure.ac`** with `-pedantic-errors`. The existing configure +check rejects that compiler before reaching its library probe. This is a +narrow diagnostic of the gate, not certification of GCC 13 for the complete +application. It shows why a compiler-version policy needs its own rationale. + +## Lead-developer recommendation + +Keep the current source and configure requirement unchanged for this audit. +Answer issue #244 with the actual dependency and its limited justification: + +> C++23 is currently used intentionally: `resize_and_overwrite` avoids +> redundant initialization when constructing ViennaRNA constraint strings, +> and `contains` simplifies sequence checks. The stream-copy optimization +> requires only C++20, while ownership and partition-map improvements use +> older standards. Existing benchmarks preserve scientific output but do not +> demonstrate a material speedup attributable to C++23. Consequently, C++23 +> is a chosen implementation/toolchain baseline, not a scientific necessity. +> If that baseline excludes users we intend to support, the present +> performance evidence is insufficient to insist on it. + +I would defend the code's use of C++23 as purposeful. I would not defend the +hard requirement on correctness or demonstrated performance grounds with +the evidence currently available. No downgrade is included in this audit. diff --git a/doc/refactor/cxx23-audit/README.md b/doc/refactor/cxx23-audit/README.md new file mode 100644 index 00000000..8060888a --- /dev/null +++ b/doc/refactor/cxx23-audit/README.md @@ -0,0 +1,33 @@ +# Historical compiler probes and compatibility experiment + +These files were recovered from the local issue #244 validation directory. +They accompany the [historical C++23 assessment](../5-cxx23-justification.md) +of commit `0a4568ad6e3da52f935221ff0da98a4a171e9833`. + +- `cxx23-library-probe.cpp` exercises the library facilities in that revision's + configure probe. It checks availability, rather than application performance. +- `cxx17-library-probe.cpp` checks the retained `try_emplace` requirement. +- `set-aside-cxx17.patch` and its notes preserve an abandoned compatibility + proposal against the historical source. They are not applied by the build + or CI and have not been rebased or validated against current master. + Statements in the saved notes describe the proposal, not the supported + compiler policy. The recorded issue decision retained C++23. + +These were local investigation artifacts rather than proposed production +changes. The full audit was left untracked after its shorter conclusion was +posted on issue #244. The compatibility patch was explicitly set aside; +its filename and the saved consensus record establish that it was not part +of the final decision. They are committed now to avoid losing that context +when the local workspace is removed. + +For a compiler supporting the corresponding language mode: + +```sh +c++ -std=c++23 -pedantic-errors cxx23-library-probe.cpp -o /tmp/intarna-cxx23-probe +/tmp/intarna-cxx23-probe +c++ -std=c++17 -pedantic-errors cxx17-library-probe.cpp -o /tmp/intarna-cxx17-probe +/tmp/intarna-cxx17-probe +``` + +The local Git attribute preserves normal unified-diff context whitespace in +the archived patch; it does not alter whitespace checks for production code. diff --git a/doc/refactor/cxx23-audit/cxx17-library-probe.cpp b/doc/refactor/cxx23-audit/cxx17-library-probe.cpp new file mode 100644 index 00000000..d81495cc --- /dev/null +++ b/doc/refactor/cxx23-audit/cxx17-library-probe.cpp @@ -0,0 +1,2 @@ +#include +int main() { std::unordered_map p; auto [it,ok]=p.try_emplace(1,2.0); return it->second==2.0 && ok ? 0 : 1; } diff --git a/doc/refactor/cxx23-audit/cxx23-library-probe.cpp b/doc/refactor/cxx23-audit/cxx23-library-probe.cpp new file mode 100644 index 00000000..439db221 --- /dev/null +++ b/doc/refactor/cxx23-audit/cxx23-library-probe.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +int main() { +std::string text{"IntaRNA"}; +const bool has_rna = text.contains("RNA") && text.contains('R'); +text.resize_and_overwrite(8, [](char * data, std::size_t size) noexcept { + for (std::size_t i = 0; i < size; ++i) + data[i] = 'x'; + return size; +}); +std::stringstream stream; +stream << text; +const std::string_view stream_view = stream.view(); +std::stringstream sink; +sink << stream.view(); +(void)has_rna; +(void)stream_view; +} diff --git a/doc/refactor/cxx23-audit/set-aside-cxx17-notes.md b/doc/refactor/cxx23-audit/set-aside-cxx17-notes.md new file mode 100644 index 00000000..628b2d17 --- /dev/null +++ b/doc/refactor/cxx23-audit/set-aside-cxx17-notes.md @@ -0,0 +1,36 @@ +# C++17 compiler baseline (issue #244) + +The audit of upstream `0a4568ad6e3da52f935221ff0da98a4a171e9833` found that +lowering only `configure.ac` would fail: the source used two C++23 library +facilities and one C++20 facility. None is required by the prediction +algorithms. This change replaces them and makes strict C++17 the supported +baseline for IntaRNA and its library consumers. + +| Source | Previous requirement | C++17-compatible replacement | +| --- | --- | --- | +| `RnaSequence.h` | C++23 `string::contains` | `find` compared with `string::npos` | +| `AccessibilityVrna.cpp`, `InteractionEnergyVrna.cpp` | C++23 `string::resize_and_overwrite` | Sized, owned strings filled by index | +| `OutputHandlerCsv.cpp`, `OutputHandlerEnsemble.cpp`, `OutputHandlerText.cpp` | C++20 string-stream `view` | String-stream `str` | +| `PredictorMfeEns.cpp` | C++17 structured bindings and `unordered_map::try_emplace` | Retained unchanged | + +C++14 is insufficient for the retained partition accumulation. Keeping +`try_emplace` preserves its single lookup and floating-point addition order. +The scoped ownership, direct `c_str()` calls into ViennaRNA, and other +prediction optimizations also remain in place. + +The compatibility tradeoff is an initialization pass over constraint buffers +and temporary string copies when inserting completed output. Constraints +remain owned and null-terminated; every position is filled before ViennaRNA +uses them. Output uses the same string insertion semantics and the same +OpenMP critical sections. This change makes no performance-equivalence claim. + +`configure.ac` requires strict C++17 and separately probes the standard +library's `unordered_map::try_emplace`, so a mismatched compiler/library pair +fails at configure time. The vendored Autoconf macro already supports C++17 +and needs no changes. Installed pkg-config metadata continues to leave the +language mode to the consumer. + +CI builds with GCC 11, GCC 14 (release and debug), and Apple Clang. Each job +uses C++17 with `-pedantic-errors`, runs the API and CLI suites, installs the +library, compiles every installed public header independently, and links and +runs an external pkg-config consumer. diff --git a/doc/refactor/cxx23-audit/set-aside-cxx17.patch b/doc/refactor/cxx23-audit/set-aside-cxx17.patch new file mode 100644 index 00000000..d1892ca2 --- /dev/null +++ b/doc/refactor/cxx23-audit/set-aside-cxx17.patch @@ -0,0 +1,413 @@ +diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml +index 5c786aa..ee4a005 100644 +--- a/.github/workflows/build.yml ++++ b/.github/workflows/build.yml +@@ -18,13 +18,20 @@ jobs: + fail-fast: false + matrix: + include: ++ - name: GCC 11 release ++ os: ubuntu-22.04 ++ compiler: gcc ++ gcc_version: 11 ++ configure_debug: --disable-debug + - name: GCC 14 release + os: ubuntu-24.04 + compiler: gcc ++ gcc_version: 14 + configure_debug: --disable-debug + - name: GCC 14 debug + os: ubuntu-24.04 + compiler: gcc ++ gcc_version: 14 + configure_debug: --enable-debug + - name: Apple Clang release + os: macos-15 +@@ -42,22 +49,22 @@ jobs: + environment-file: conda-build-env.yml + activate-environment: intarna-build-env + +- - name: Install and select GCC 14 ++ - name: Install and select GCC ${{ matrix.gcc_version }} + if: matrix.compiler == 'gcc' + run: | + sudo apt-get update +- sudo apt-get install --yes gcc-14 g++-14 +- echo "CC=gcc-14" >> "$GITHUB_ENV" +- echo "CXX=g++-14" >> "$GITHUB_ENV" ++ sudo apt-get install --yes gcc-${{ matrix.gcc_version }} g++-${{ matrix.gcc_version }} ++ echo "CC=gcc-${{ matrix.gcc_version }}" >> "$GITHUB_ENV" ++ echo "CXX=g++-${{ matrix.gcc_version }}" >> "$GITHUB_ENV" + echo "LDFLAGS=-L${CONDA_PREFIX}/lib -Wl,-rpath,${CONDA_PREFIX}/lib" >> "$GITHUB_ENV" + +- - name: Verify GCC 14 ++ - name: Verify GCC ${{ matrix.gcc_version }} + if: matrix.compiler == 'gcc' + run: | + "$CC" --version + "$CXX" --version +- test "$("$CC" -dumpfullversion | cut -d. -f1)" = 14 +- test "$("$CXX" -dumpfullversion | cut -d. -f1)" = 14 ++ test "$("$CC" -dumpfullversion | cut -d. -f1)" = ${{ matrix.gcc_version }} ++ test "$("$CXX" -dumpfullversion | cut -d. -f1)" = ${{ matrix.gcc_version }} + + - name: Set up Apple Clang with OpenMP + if: matrix.compiler == 'apple-clang' +@@ -83,6 +90,8 @@ jobs: + + - name: Bootstrap and configure + run: | ++ export CXXFLAGS="${CXXFLAGS:-} -pedantic-errors" ++ echo "CXXFLAGS=$CXXFLAGS" >> "$GITHUB_ENV" + bash autotools-init.sh + ./configure \ + --prefix="$RUNNER_TEMP/intarna-install" \ +@@ -91,7 +100,7 @@ jobs: + --with-boost-libdir="$CONDA_PREFIX/lib" \ + --with-zlib="$CONDA_PREFIX" \ + ${{ matrix.configure_debug }} +- grep -Eq '^CXX = .* -std=c\+\+23([[:space:]]|$)' Makefile ++ grep -Eq '^CXX = .* -std=c\+\+17([[:space:]]|$)' Makefile + grep -q '^#define INTARNA_MULITHREADING 1' src/config.h + + - name: Build, install, and run full test suite +@@ -106,7 +115,7 @@ jobs: + header_probe="$RUNNER_TEMP/intarna-header-probe.cpp" + for header in "$RUNNER_TEMP/intarna-install/include/IntaRNA/"*.h; do + printf '#include \nint main() { return 0; }\n' "$(basename "$header")" > "$header_probe" +- "$CXX" -std=c++23 ${CPPFLAGS:-} ${CXXFLAGS:-} \ ++ "$CXX" -std=c++17 ${CPPFLAGS:-} ${CXXFLAGS:-} \ + -I"$RUNNER_TEMP/intarna-install/include" \ + -I"$CONDA_PREFIX/include" \ + -fsyntax-only "$header_probe" +@@ -117,7 +126,7 @@ jobs: + consumer="$RUNNER_TEMP/intarna-consumer.cpp" + printf '#include \nint main() { IntaRNA::RnaSequence rna("test", "ACGU"); return rna.size() == 4 ? 0 : 1; }\n' > "$consumer" + export PKG_CONFIG_PATH="$RUNNER_TEMP/intarna-install/lib/pkgconfig" +- "$CXX" -std=c++23 ${CPPFLAGS:-} ${CXXFLAGS:-} "$consumer" \ ++ "$CXX" -std=c++17 ${CPPFLAGS:-} ${CXXFLAGS:-} "$consumer" \ + ${LDFLAGS:-} $(pkg-config --cflags --libs IntaRNA) \ + -o "$RUNNER_TEMP/intarna-consumer" + "$RUNNER_TEMP/intarna-consumer" +diff --git a/ChangeLog b/ChangeLog +index 5a79cce..efcec0c 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -14,7 +14,7 @@ + + - remove redundant accessibility checks from predictor, helix and seed handling + +-- C++23 required; build and installation checks with GCC 14 and Apple Clang ++- C++17 required; build and installation checks with GCC 11, GCC 14 and Apple Clang + - reduce temporary allocations and fix cleanup on error paths + - avoid repeated partition-map lookups in exact ensemble prediction + - add exhaustive interaction and seed oracles for short-sequence tests +@@ -38,6 +38,19 @@ + ################################################################################ + ################################################################################ + ++260921 ++ * configure.ac / .github/workflows/build.yml / README.md : ++ * lower the required C++ standard to C++17 (issue #244) ++ * check unordered_map::try_emplace and compile consumers in C++17 mode ++ + GCC 11 CI build alongside GCC 14 and Apple Clang ++ * IntaRNA/RnaSequence : use find() for alphabet and ambiguity checks ++ * IntaRNA/AccessibilityVrna / InteractionEnergyVrna : ++ * fill sized C++ strings instead of requiring resize_and_overwrite() ++ * IntaRNA/OutputHandlerCsv / OutputHandlerEnsemble / OutputHandlerText : ++ * use str() for output insertion instead of requiring stream views ++ * tests/RnaSequence_test.cpp : normalization and boundary ambiguity regression ++ + doc/refactor/5-c++17.md : standard audit and compatibility tradeoffs ++ + 260919 Alexander Mitrofanov + * IntaRNA/PredictorMfe2d* : + * IntaRNA/PredictorMfeEns2d* : +diff --git a/README.md b/README.md +index 9830271..050edfc 100644 +--- a/README.md ++++ b/README.md +@@ -180,7 +180,7 @@ you with an encapsulated IntaRNA installation. + If you are going to compile IntaRNA from source, ensure you meet the following + dependencies: + +-- compiler supporting C++23 and OpenMP (GCC 14 or Apple Clang) ++- compiler and standard library supporting C++17, with OpenMP for multithreading + - [boost C++ library](http://www.boost.org/) version >= 1.50.0 + (ensure the following libraries are installed for development (not just runtime libraries!); or install all e.g. in Ubuntu via package `libboost-all-dev`) + - libboost_regex +@@ -2186,8 +2186,8 @@ processed using doxygen to generate html/pdf versions. + When IntaRNA is build while `pkg-config` is present, according pkg-config + information is generated and installed too. + +-IntaRNA's public headers require C++23. External consumers must therefore +-select `-std=c++23` (or a newer standard) in their own build; the installed ++IntaRNA's supported compilation baseline is C++17. External consumers must therefore ++select `-std=c++17` (or a newer standard) in their own build; the installed + pkg-config metadata supplies IntaRNA and dependency flags but deliberately + does not override the consuming project's language mode. + +diff --git a/configure.ac b/configure.ac +index c675415..4f371a5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -54,37 +54,25 @@ AM_INIT_AUTOMAKE([1.11]) + # use the C++ compiler for the following checks + AC_LANG([C++]) + +-# ensure we are using the C++23 standard +-AX_CXX_COMPILE_STDCXX([23], [noext], [mandatory]) ++# Structured bindings and unordered_map::try_emplace require C++17. ++AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory]) + +-# Check the C++23 library surface used by IntaRNA. A compiler accepting +-# -std=c++23 is not sufficient when paired with an older standard library. +-AC_MSG_CHECKING([for required C++23 standard library features]) ++# Check the library as well: accepting -std=c++17 does not guarantee that ++# the compiler is paired with a library providing unordered_map::try_emplace. ++AC_MSG_CHECKING([for required C++17 standard library features]) + intarna_saved_CXXFLAGS=$CXXFLAGS + CXXFLAGS="$CXXFLAGS -pedantic-errors" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( +- [[#include +-#include +-#include +-#include ]], +- [[std::string text{"IntaRNA"}; +-const bool has_rna = text.contains("RNA") && text.contains('R'); +-text.resize_and_overwrite(8, [](char * data, std::size_t size) noexcept { +- for (std::size_t i = 0; i < size; ++i) +- data[i] = 'x'; +- return size; +-}); +-std::stringstream stream; +-stream << text; +-const std::string_view stream_view = stream.view(); +-std::stringstream sink; +-sink << stream.view(); +-(void)has_rna; +-(void)stream_view;]])], ++ [[#include ]], ++ [[std::unordered_map partitions; ++auto [entry, inserted] = partitions.try_emplace(1, 2.0); ++if (!inserted) { ++ entry->second += 2.0; ++}]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) +- AC_MSG_ERROR([A C++23 standard library providing std::string::contains, std::string::resize_and_overwrite, and std::stringstream::view is required. Upgrade libstdc++/libc++ or select a newer C++23 toolchain.])]) ++ AC_MSG_ERROR([A C++17 standard library providing std::unordered_map::try_emplace is required. Upgrade libstdc++/libc++ or select a newer C++17 toolchain.])]) + CXXFLAGS=$intarna_saved_CXXFLAGS + + # check if python is available +diff --git a/doc/refactor/2-c++23.md b/doc/refactor/2-c++23.md +index a865385..5ce46b4 100644 +--- a/doc/refactor/2-c++23.md ++++ b/doc/refactor/2-c++23.md +@@ -1,5 +1,9 @@ + # Phase 2: C++23 migration and performance roadmap + ++The C++23 baseline below records the phase-2 migration. The later ++[issue #244 audit](5-c++17.md) lowers the current requirement to C++17; ++use the README and `configure.ac` for the current build contract. ++ + This document implements the scope of phase 2 in + [discussion #232](https://github.com/BackofenLab/IntaRNA/discussions/232). + C++23 is a compilation baseline here, not a reason to rewrite working +diff --git a/src/IntaRNA/AccessibilityVrna.cpp b/src/IntaRNA/AccessibilityVrna.cpp +index 5ec9d6c..d2493a8 100644 +--- a/src/IntaRNA/AccessibilityVrna.cpp ++++ b/src/IntaRNA/AccessibilityVrna.cpp +@@ -55,15 +55,10 @@ getVrnaConstraint( const IntaRNA::Accessibility & acc ) + { + const std::size_t sequenceLength = acc.getSequence().size(); + const IntaRNA::AccessibilityConstraint & constraintSpec = acc.getAccConstraint(); +- std::string constraint; +- constraint.resize_and_overwrite( +- sequenceLength, +- [&constraintSpec, sequenceLength]( char * const data, const std::size_t ) noexcept { +- for (std::size_t i = 0; i < sequenceLength; ++i) { +- data[i] = constraintSpec.getVrnaDotBracket(i); +- } +- return sequenceLength; +- }); ++ std::string constraint(sequenceLength, '.'); ++ for (std::size_t i = 0; i < sequenceLength; ++i) { ++ constraint[i] = constraintSpec.getVrnaDotBracket(i); ++ } + return constraint; + } + +@@ -169,8 +164,6 @@ addConstraints( vrna_fold_compound_t & fold_compound, const Accessibility & acc + if ( ! acc.getAccConstraint().isEmpty() ) { + + // Build the exact pseudo dot-bracket byte sequence in owned C++ storage. +- // resize_and_overwrite avoids value-initializing a buffer that is replaced +- // byte-for-byte before ViennaRNA consumes it. + const std::string structure = getVrnaConstraint(acc); + + // Adding hard constraints from pseudo dot-bracket +@@ -512,4 +505,3 @@ fillByRNAplfold( const VrnaHandler &vrnaHandler + ///////////////////////////////////////////////////////////////////////////// + + } // namespace +- +diff --git a/src/IntaRNA/InteractionEnergyVrna.cpp b/src/IntaRNA/InteractionEnergyVrna.cpp +index 553226e..23c7386 100644 +--- a/src/IntaRNA/InteractionEnergyVrna.cpp ++++ b/src/IntaRNA/InteractionEnergyVrna.cpp +@@ -44,15 +44,10 @@ getVrnaConstraint( const IntaRNA::Accessibility & acc ) + { + const std::size_t sequenceLength = acc.getSequence().size(); + const IntaRNA::AccessibilityConstraint & constraintSpec = acc.getAccConstraint(); +- std::string constraint; +- constraint.resize_and_overwrite( +- sequenceLength, +- [&constraintSpec, sequenceLength]( char * const data, const std::size_t ) noexcept { +- for (std::size_t i = 0; i < sequenceLength; ++i) { +- data[i] = constraintSpec.getVrnaDotBracket(i); +- } +- return sequenceLength; +- }); ++ std::string constraint(sequenceLength, '.'); ++ for (std::size_t i = 0; i < sequenceLength; ++i) { ++ constraint[i] = constraintSpec.getVrnaDotBracket(i); ++ } + return constraint; + } + +diff --git a/src/IntaRNA/OutputHandlerCsv.cpp b/src/IntaRNA/OutputHandlerCsv.cpp +index 79a7d24..e5f8547 100644 +--- a/src/IntaRNA/OutputHandlerCsv.cpp ++++ b/src/IntaRNA/OutputHandlerCsv.cpp +@@ -470,7 +470,7 @@ add( const Interaction & i ) + #pragma omp critical(intarna_omp_outputStreamUpdate) + #endif + { +- out << outTmp.view(); ++ out << outTmp.str(); + } // omp critical(intarna_omp_outputStreamUpdate) + } + +diff --git a/src/IntaRNA/OutputHandlerEnsemble.cpp b/src/IntaRNA/OutputHandlerEnsemble.cpp +index ef5a158..d1977dc 100644 +--- a/src/IntaRNA/OutputHandlerEnsemble.cpp ++++ b/src/IntaRNA/OutputHandlerEnsemble.cpp +@@ -52,7 +52,7 @@ OutputHandlerEnsemble:: + #pragma omp critical(intarna_omp_outputStreamUpdate) + #endif + { +- out <getId() <<'\n' + // get position in s1 +- <getId() <<'\n' + ; +@@ -429,7 +429,7 @@ add( const Interaction & i ) + #pragma omp critical(intarna_omp_outputStreamUpdate) + #endif + { +- out <seqString)) +- , ambiguous(this->seqString.contains('N')) ++ , ambiguous(this->seqString.find('N') != std::string::npos) + , idxPos0(idxPos0) + , seqNumber(seqNumber) + { +@@ -500,7 +500,7 @@ getUpperCase( const std::string & seqString ) + seqRet[i] = 'U'; + } + // overwrite non-ACGU characters with N = ambiguous (no distinction needed) +- if (!SequenceAlphabet.contains(seqRet[i])) { ++ if (SequenceAlphabet.find(seqRet[i]) == std::string::npos) { + seqRet[i] = 'N'; + } + } +@@ -570,7 +570,7 @@ getCodeForChar( const char nucleotide ) + { + #if INTARNA_IN_DEBUG_MODE + // check if nucleotide character is NOT supported +- if (!SequenceAlphabet.contains(nucleotide)) ++ if (SequenceAlphabet.find(nucleotide) == std::string::npos) + throw std::runtime_error("RnaSequence::getCodeForChar() : unsupported nucleotide character '"+toString(nucleotide)+"' in sequence"); + #endif + +diff --git a/tests/RnaSequence_test.cpp b/tests/RnaSequence_test.cpp +index eaa67c2..d11842b 100644 +--- a/tests/RnaSequence_test.cpp ++++ b/tests/RnaSequence_test.cpp +@@ -18,6 +18,24 @@ TEST_CASE( "RnaSequence", "[RNAsequence]" ) { + REQUIRE( exceptionRaised ); + } + ++ SECTION( "alphabet normalization and ambiguity at sequence boundaries" ) { ++ RnaSequence canonical("canonical", "acgt"); ++ REQUIRE( canonical.asString() == "ACGU" ); ++ REQUIRE_FALSE( canonical.isAmbiguous() ); ++ ++ RnaSequence ambiguous("ambiguous", "nacgtRyn"); ++ REQUIRE( ambiguous.asString() == "NACGUNNN" ); ++ REQUIRE( ambiguous.isAmbiguous() ); ++ REQUIRE( ambiguous.isAmbiguous(0) ); ++ REQUIRE_FALSE( ambiguous.isAmbiguous(1) ); ++ REQUIRE( ambiguous.isAmbiguous(ambiguous.size()-1) ); ++ REQUIRE( ambiguous.asCodes().front() == RnaSequence::getCodeForChar('N') ); ++ REQUIRE( ambiguous.asCodes().at(1) == RnaSequence::getCodeForChar('A') ); ++#if INTARNA_IN_DEBUG_MODE ++ REQUIRE_THROWS_AS( RnaSequence::getCodeForChar('?'), std::runtime_error ); ++#endif ++ } ++ + SECTION( "getter", "[RNAsequence]" ) { + + std::string id= "test", seq="AAAUUUGGGCCC"; From f00f0602f4552c9ffc69358af79c62ac6b4b48e3 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Sun, 18 Oct 2026 23:27:08 +0200 Subject: [PATCH 2/2] docs: preserve unified-diff whitespace in the audit archive --- doc/refactor/cxx23-audit/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/refactor/cxx23-audit/.gitattributes diff --git a/doc/refactor/cxx23-audit/.gitattributes b/doc/refactor/cxx23-audit/.gitattributes new file mode 100644 index 00000000..24e115f5 --- /dev/null +++ b/doc/refactor/cxx23-audit/.gitattributes @@ -0,0 +1 @@ +*.patch -whitespace