This repository uses Copier as its templating engine to generate, manage, and update Beman library boilerplate. The template inputs are defined in copier.yml, and the rendered project files live under template/.
Note
?? stamp.sh is deprecated. If you are used to the legacy workflow of forking this repository and running ./stamp.sh, please transition to the natively supported copier workflow below. The script remains for legacy CI compatibility but will not receive future updates.
If you are already familiar with the Beman project lifecycle, you can generate a new library immediately using uvx (via the uv package manager):
uvx --from copier copier copy --trust --vcs-ref HEAD gh:bemanproject/exemplar.git my-new-library
cd my-new-library
git init
git add .
git commit -m "Initial commit from Beman Exemplar template"
uvx pre-commit installTo create a new library and get it officially adopted into the Beman project, you will generate the project locally, push it to your personal GitHub for active development, and eventually transfer it to the bemanproject organization.
Why this specific workflow? Developing on your personal account first gives you a sandbox to experiment and clean up your Git history. Transferring the repository (rather than creating it directly in the org) ensures the
bemanprojectorganization ultimately holds the canonical, unbroken Git history, which prevents downstream forks from breaking later.
After running the Quick Start commands above, push the fresh template to your personal account to begin development.
Option A: Using the GitHub CLI (gh)
gh repo create my-new-library --public --source=. --remote=origin --pushOption B: Using the GitHub Browser Interface
- Create a new repository. Name it exactly what you named your local folder. Leave it completely empty.
- Link and push your local code:
git remote add origin [https://github.com/](https://github.com/)<your-username>/my-new-library.git git branch -M main git push -u origin main
Before transferring the project to the Beman organization, take the time to build out the actual proposal.
- Expand beyond the default placeholder headers (e.g.,
my-new-library.hpp). - Stub out the necessary files for your library's domain.
- Write out the skeleton of the library's design and documentation.
- Optional: Once you have a working sandbox, feel free to squash or clean up your Git history using an interactive rebase (
git rebase -i) before handing it over.
Once the repository has enough substance to be reviewed or collaborated on, hand it over to the organization to establish it as the canonical source.
- On your GitHub repository page, go to Settings > General.
- Scroll to the bottom to the Danger Zone and click Transfer ownership.
- Type
bemanprojectas the new owner and confirm.
Now that bemanproject/my-new-library is the official upstream repository, standard open-source contribution rules apply.
- Navigate to the new URL:
https://github.com/bemanproject/my-new-library. - Click Fork in the top right corner to create a fork in your personal namespace.
- Update your local repository's remotes so you can pull from upstream and push to your new fork:
# Rename your current remote (the org repo) to 'upstream' git remote rename origin upstream # Add your new personal fork as 'origin' git remote add origin [https://github.com/](https://github.com/)<your-username>/my-new-library.git
One of the biggest advantages of using copier is that it actively maintains your project's boilerplate. If you start out with a placeholder name and later decide to rename your library (or change your minimum C++ version), you can instruct Copier to update the structural boilerplate using the --data flag.
For example, to rename your project to range_discombobulator:
uvx --from copier copier update --trust --vcs-ref HEAD --skip-answered --data project_name=range_discombobulatorWhat this does:
--data project_name=...: Overrides your previous answer for the project name.--skip-answered: Skips the interactive prompt for all the questions you aren't changing.
Copier will calculate the difference and automatically update the template-provided files�renaming CMake configurations, GitHub Actions matrices, standard folder paths, and core macros.
?? Important Caveat: Copier only manages the files it originally generated. If you have added custom files (like detail/internal.hpp), or manually typed the old project name into new C++ namespaces or include guards, you will need to update those manually. Always review the changes with git diff, and use your IDE or grep to hunt down any lingering references to the old name before committing!
If your repository predates the Copier migration and was created as a plain GitHub template copy, bootstrap a fresh stamped baseline at the original exemplar split point, then rebase your project commits onto that baseline.
Start from a clean worktree and make sure you can identify the branch you want to migrate; the examples below assume main.
git remote add exemplar [https://github.com/bemanproject/exemplar.git](https://github.com/bemanproject/exemplar.git)
git fetch exemplar
base=$(git merge-base main exemplar/main)
git branch pre-copier-backup main
git switch --detach "$base"
tmpdir=$(mktemp -d)
python3 -m venv "$tmpdir/venv"
"$tmpdir/venv/bin/python3" -m pip install copier pre-commit
"$tmpdir/venv/bin/copier" copy --trust --vcs-ref HEAD --overwrite \
-d project_name=your_project_name \
-d maintainer=your_github_username \
-d minimum_cpp_build_version=20 \
-d paper=PnnnnRr \
-d description="Short project description." \
-d unit_test_library=gtest \
-d generating_exemplar=false \
-d owner=bemanproject \
[https://github.com/bemanproject/exemplar.git](https://github.com/bemanproject/exemplar.git) \
.
"$tmpdir/venv/bin/pre-commit" run --all-files || true
git add .
git switch -c stamp
git commit -m "Bootstrap Copier template"
git switch main
git rebase --rebase-merges --onto stamp "$base" mainAfter the rebase, commit the generated .copier-answers.yml. That file is what enables future template updates with:
uvx --from copier copier update --trust --vcs-ref HEADIf you want to track a fork of exemplar rather than bemanproject/exemplar, edit _src_path in .copier-answers.yml before your first copier update.
If you are changing the template itself rather than developing a library, use the Copier workflow directly:
- Edit
copier.ymlfor template questions, defaults, validators, and post-copy tasks. - Edit
template/for files that should be rendered into stamped projects. - Run
./copier/check_copier.shto verify exemplar self-regeneration and non-exemplar templating. - Create a new project locally using the Quick Start commands when you want to use the template for real work.
The consistency check renders from a .git-free temporary snapshot of the repository. That keeps local validation aligned with the current worktree contents, rather than only the last committed Git state.
What follows is an example of a Beman library README.
beman.exemplar is a minimal C++ library conforming to The Beman Standard.
This can be used as a template for those intending to write Beman libraries.
It may also find use as a minimal and modern C++ project structure.
Implements: std::identity proposed in Standard Library Concepts (P0898R3).
Status: Under development and not yet ready for production use.
beman.exemplar is licensed under the Apache License v2.0 with LLVM Exceptions.
std::identity is a function object type whose operator() returns its argument unchanged.
std::identity serves as the default projection in constrained algorithms.
Its direct usage is usually not needed.
The following code snippet illustrates how we can achieve a default projection using beman::exemplar::identity:
#include <beman/exemplar/exemplar.hpp>
namespace exe = beman::exemplar;
// Class with a pair of values.
struct Pair
{
int n;
std::string s;
// Output the pair in the form {n, s}.
// Used by the range-printer if no custom projection is provided (default: identity projection).
friend std::ostream &operator<<(std::ostream &os, const Pair &p)
{
return os << "Pair" << '{' << p.n << ", " << p.s << '}';
}
};
// A range-printer that can print projected (modified) elements of a range.
// All the elements of the range are printed in the form {element1, element2, ...}.
// e.g., pairs with identity: Pair{1, one}, Pair{2, two}, Pair{3, three}
// e.g., pairs with custom projection: {1:one, 2:two, 3:three}
template <std::ranges::input_range R,
typename Projection>
void print(const std::string_view rem, R &&range, Projection projection = exe::identity>)
{
std::cout << rem << '{';
std::ranges::for_each(
range,
[O = 0](const auto &o) mutable
{ std::cout << (O++ ? ", " : "") << o; },
projection);
std::cout << "}\n";
};
int main()
{
// A vector of pairs to print.
const std::vector<Pair> pairs = {
{1, "one"},
{2, "two"},
{3, "three"},
};
// Print the pairs using the default projection.
print("\tpairs with beman: ", pairs);
return 0;
}
Full runnable examples can be found in examples/.
This project requires at least the following to build:
- A C++ compiler that conforms to the C++17 standard or greater
- CMake 3.30 or later
- (Test Only) GoogleTest
You can disable building tests by setting CMake option BEMAN_EXEMPLAR_BUILD_TESTS to
OFF when configuring the project.
You can disable building examples by setting CMake option BEMAN_EXEMPLAR_BUILD_EXAMPLES to
OFF when configuring the project.
| Compiler | Version | C++ Standards | Standard Library |
|---|---|---|---|
| GCC | 16-13 | C++26-C++17 | libstdc++ |
| GCC | 12-11 | C++23-C++17 | libstdc++ |
| Clang | 22-19 | C++26-C++17 | libstdc++, libc++ |
| Clang | 18 | C++26-C++17 | libc++ |
| Clang | 18 | C++23-C++17 | libstdc++ |
| Clang | 17 | C++26-C++17 | libc++ |
| Clang | 17 | C++20, C++17 | libstdc++ |
| AppleClang | latest | C++26-C++17 | libc++ |
| MSVC | latest | C++23 | MSVC STL |
See the Contributing Guidelines.
You can build exemplar using a CMake workflow preset:
cmake --workflow --preset gcc-releaseTo list available workflow presets, you can invoke:
cmake --list-presets=workflowFor details on building beman.exemplar without using a CMake preset, refer to the Contributing Guidelines.
The preferred way to install exemplar is via vcpkg. To do so, after installing vcpkg
itself, you need to add support for the Beman project's vcpkg
registry by configuring a
vcpkg-configuration.json file (which exemplar provides).
Then, simply run vcpkg install beman-exemplar.
To install beman.exemplar globally after building with the gcc-release preset, you can
run:
sudo cmake --install build/gcc-releaseAlternatively, to install to a prefix, for example /opt/beman, you can run:
sudo cmake --install build/gcc-release --prefix /opt/bemanThis will generate the following directory structure:
/opt/beman
├── include
│ └── beman
│ └── exemplar
│ ├── exemplar.hpp
│ └── ...
└── lib
└── cmake
└── beman.exemplar
├── beman.exemplar-config-version.cmake
├── beman.exemplar-config.cmake
└── beman.exemplar-targets.cmakeIf you installed beman.exemplar to a prefix, you can specify that prefix to your CMake
project using CMAKE_PREFIX_PATH; for example, -DCMAKE_PREFIX_PATH=/opt/beman.
You need to bring in the beman.exemplar package to define the beman::exemplar CMake
target:
find_package(beman.exemplar REQUIRED)You will then need to add beman::exemplar to the link libraries of any libraries or
executables that include beman.exemplar headers.
target_link_libraries(yourlib PUBLIC beman::exemplar)To use beman.exemplar in your C++ project,
include an appropriate beman.exemplar header from your source code.
#include <beman/exemplar/exemplar.hpp>