Scientific Computing

C++20 adoption by popular projects

C++20 standard (issued December 2020) is long supported by all modern C++ compilers. In particular Microsoft Visual C++ has been steadily adding support for C++20 (and newer standards).

Major and/or popular projects are already adopting C++20 features in their codebases. C++20 codified several features that had been compiler-specific extensions. Some examples:

One of the biggest discussions about C++20 is modules, which improve compilation times and provide better encapsulation of code compared to traditional header files. The C++ ecosystem from compiler to build system works for C++20 modules with CMake support, but is still maturing.

GitHub Gist branch must be "main"

GitHub Gist only recognizes the “main” branch. Git commits on any other branch will not be visible in the Gist interface.

Check the current branch with:

git branch --show-current

If commits were accidentally made in a non-main branch, change the name of the branch to “main” to make the commits visible in the Gist interface.

git branch -m main

After renaming the branch, it may be necessary to force push to update the remote Gist. First, ensure the remote content is in sync with the local branch to avoid overwriting any changes.

# --force overwrites the remote branch with local changes
git push origin main [--force]

Check if the old branch was pushed

git ls-remote origin

If the old non-main branch (perhaps “master”) still exists on the remote, consider deleting it to avoid confusion.

git push origin --delete master

Perhaps globally configure the computer to always use “main” as the default branch for new repositories:

git config --global init.defaultBranch main

Recovering a dropped Git Commit

After pushing a commit or merging a pull request, one may accidentally drop a Git commit from branch history. If the commit was part of a pull request, GitHub will not reopen a closed pull request whose source branch no longer contains the commit. Instead, check whether the commit still exists in git reflog by searching for its message or hash.

git reflog

If the commit exists in the reflog, recover it using the commit hash:

git checkout -b recovered-branch <commit-hash>

If the commit is not in the reflog, it may still exist as an unreachable object or in another branch or remote. Note the reflog is local: it only records commits that existed in the local repository.

To search for unreachable (dangling) commits:

git fsck --unreachable

For example, to check all branches for the specific commit:

git branch --contains <commit-hash>

To check all remotes for the commit:

git fetch --all
git branch -r --contains <commit-hash>

If the commit is found in another branch or remote, recover it by checking out that branch or creating a new branch from the commit hash.

If the commit is only found in someone else’s repo (perhaps the person that made the pull request), obtain the commit URL and apply it like:

curl -L https://github.invalid/user/repo/commit/hash.patch | git am

git am is used instead of git apply because it creates a commit that preserves the original author and commit message, ready to push.

Scotch and ParMETIS Packaging

Major Linux distros including Ubuntu 26.04 and Debian stable have adopted the Parmetis compatibility layer provided by Scotch for parallel graph partitioning and sparse matrix reordering. Therefore “libparmetis-dev” package has been dropped in favor of the free Scotch-provided compatibility layer in “libscotchparmetis-dev”, which provides libparmetis.so and the headers required for ParMETIS compatibility.

Git Bisect project with Git Submodules

The standard git bisect command helps quickly identify a specific commit that introduced a bug in a Git repository. In projects where branches with lots of commits were previously merged, there may be known-bad commits with build failure unrelated to the current bisect search. It’s possible to skip such commits during the bisect process, or avoid specific commits altogether.

For projects using Git submodules it’s useful to set option submodules.recurse true

git config --global submodule.recurse true

that works for most Git commands and avoids the need during “git bisect” to also have to “git submodule update –init –recursive” at each step of the bisect process.

git bisect start

# if you already know this current commit is good or bad, tell Git about it
git bisect <good|bad> <commit>

# test the current commit by running your test code / program / script
git bisect run <test command>

# ... repeat the above two steps until Git finds the first bad commit

# finish the bisect process and return to the original branch
git bisect reset

Troubleshooting

If running into errors about “dirty” submodules and/or lots of deleted files when Git bisecting or just switching Git branches, try disabling Git submodule.recurse for the command:

git -c submodule.recurse=false submodule update --init --force --recursive

The global submodule.recurse=true setting is convenient but makes checkouts fragile when any nested submodule gets into a bad state.

Lossless convert PNG image stack to PDF

The Python package img2pdf losslessly converts PNG images to PDF.

Install it with:

pip install img2pdf

The command line program “img2pdf” can do many tasks, but even more power comes from making a brief Python script. Here are simple command line examples.

Convert a single PNG to PDF:

img2pdf in.png -o out.pdf

Convert multiple PNG images to a single PDF:

img2pdf *.png -o joined.pdf

Rotate each page of a PDF from the command line

A common issue with document scanning is that pages may have been inserted in the wrong orientation, making the PDF difficult to read with upside-down text. This is trivially fixed with the QPDF command line tool.

qpdf --rotate=180 in.pdf out.pdf
in.pdf
input PDF file needing rotation out.pdf
output PDF file with rotated pages
--rotate=180
rotate each page 180 degrees

QPDF can do vastly more operations on PDF files as seen in the QPDF documentation. Poppler is oriented toward rendering and extracting content from PDF files rather than modifying them like QPDF, which does not render PDF files.

Install QPDF

  • Linux: apt install qpdf or similar
  • macOS: brew install qpdf
  • Windows: winget install QPDF.QPDF

On Windows, QPDF is installed under $Env:ProgramFiles/qpdf*/bin/qpdf.exe

Open same PDF multiple times

Opening the same PDF file multiple times is particularly useful when reviewing journal article drafts to view figures simultaneously with text describing the figure.

Okular is a free lightweight PDF viewer for Linux, macOS and Windows that trivially opens the same PDF multiple times by just selecting through the File → Open menu as many times as desired.

Okular is installed by:

  • Linux apt install okular or similar
  • macOS - see Okular download link above
  • Windows winget install KDE.Okular

CTest interactive debug mode

CTest –interactive-debug-mode is particularly useful on Windows to enable/disable debug dumps and error popup modal windows, especially for missing DLL errors. By default this feature is enabled. This default behavior to have interactive debug mode “on” can make problems with modal popups hanging on automated systems like CI or agentic workflows where user interaction is not desired or possible.

To avoid this, one can explicitly disable interactive debug mode by option

ctest --interactive-debug-mode=0

An even better approach is to make CMakePresets.json explicitly set the interactive debug mode for CTest invocation with preset or workflow with "interactiveDebugging": false thats inherited for all CTest preset execution like:

{
  "version": 6,

"testPresets": [
{
  "name": "default",
  "configurePreset": "default",
  "output": {
    "outputOnFailure": true,
    "verbosity": "verbose"
  },
  "execution": {
    "interactiveDebugging": false,
    "noTestsAction": "error",
    "scheduleRandom": true,
    "stopOnFailure": false
  }
}
]
}

In agentic instruction files like CMAKE.instructions.md or similar, consider adding a line like:

CTest on Windows should be run with option --interactive-debug-mode=0 to prevent modal popups from interrupting automated workflows.

NOTE: CTest environment variable CTEST_INTERACTIVE_DEBUG_MODE is read-only and has no effect on setting the interactive debug mode. The mode must be controlled via the --interactive-debug-mode command-line option when invoking CTest.


Related: MSVC exception handling popup disable

Disable Visual Studio exception modal window

Visual Studio executables built in Debug mode by default pop up modal debug windows if an unhandled exception occurs. This can be can be undesirable for reasons including:

  • users having to close nuisance windows manually.
  • CI runs hang if an exception occurs and the modal window appears.
  • AI agents or automated tools may hang with unexpected modal windows.
  • remotely operated systems: modal windows can be difficult to close if the modal window is accidentally off-screen.

Debug popups for exception handling can be disabled by C++ runtime library functions provided by Visual Studio described below. NOTE: missing DLL modal popups are fixed by the calling program such as CTest instead of this C++ method below.

Adding a few lines of code to the C++ program works around this issue by redirecting the error text to stderr console and not popping up the modal window.

This is also relevant to continuous integration systems such as GitHub Actions, which may hang with an unrealized modal dialog otherwise.

#ifdef _MSC_VER
#include <crtdbg.h>
#endif

int main(){
#ifdef _MSC_VER
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
#endif

// rest of program
}