Skip to content

About

Example Beman project

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

595 Commits

Folders and files

Repository files navigation

How to Use This Template

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.

? Quick Start: Create a New Library

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 install

?? The Beman Project Lifecycle

To 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 bemanproject organization ultimately holds the canonical, unbroken Git history, which prevents downstream forks from breaking later.

Step 1: Push to Your Personal GitHub

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 --push

Option B: Using the GitHub Browser Interface

  1. Create a new repository. Name it exactly what you named your local folder. Leave it completely empty.
  2. 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

Step 2: Incubation & Implementation

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.

Step 3: Transfer to the Beman Project

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.

  1. On your GitHub repository page, go to Settings > General.
  2. Scroll to the bottom to the Danger Zone and click Transfer ownership.
  3. Type bemanproject as the new owner and confirm.

Step 4: Fork It Back for Ongoing Development

Now that bemanproject/my-new-library is the official upstream repository, standard open-source contribution rules apply.

  1. Navigate to the new URL: https://github.com/bemanproject/my-new-library.
  2. Click Fork in the top right corner to create a fork in your personal namespace.
  3. 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

?? Reconfiguring or Renaming Your Project

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_discombobulator

What 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!

Rebasing An Older Exemplar Clone Onto Copier

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" main

After 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 HEAD

If you want to track a fork of exemplar rather than bemanproject/exemplar, edit _src_path in .copier-answers.yml before your first copier update.

Template Maintenance

If you are changing the template itself rather than developing a library, use the Copier workflow directly:

  • Edit copier.yml for template questions, defaults, validators, and post-copy tasks.
  • Edit template/ for files that should be rendered into stamped projects.
  • Run ./copier/check_copier.sh to 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: A Beman Library Exemplar

Library Status Continuous Integration Tests Lint Check (pre-commit) Coverage Standard Target Compiler Explorer Example

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.

License

beman.exemplar is licensed under the Apache License v2.0 with LLVM Exceptions.

Usage

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.

Usage: default projection in constrained algorithms

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/.

Dependencies

Build Environment

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.

Supported Platforms

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

Development

See the Contributing Guidelines.

Integrate beman.exemplar into your project

Build

You can build exemplar using a CMake workflow preset:

cmake --workflow --preset gcc-release

To list available workflow presets, you can invoke:

cmake --list-presets=workflow

For details on building beman.exemplar without using a CMake preset, refer to the Contributing Guidelines.

Installation

Vcpkg

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.

Manual

To install beman.exemplar globally after building with the gcc-release preset, you can run:

sudo cmake --install build/gcc-release

Alternatively, to install to a prefix, for example /opt/beman, you can run:

sudo cmake --install build/gcc-release --prefix /opt/beman

This 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.cmake

CMake Configuration

If 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)

Using 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>

About

Example Beman project

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages