Pitchfork is a Set of C++ Project Conventions

Related tags



Pitchfork is a set of conventions for native C and C++ projects. The most prominent being the project layout conventions.

The layout specification document is available in data/spec.bs.

Why the Name Pitchfork?

The very first public unveiling, drafting, and discussion of these project conventions started with a Reddit thread entitled "Prepare thy Pitchforks". Until that point, I had not chosen any particular name for the conventions, but I felt "Pitchfork" was as apt a name as any.

The pf Tool

This repository also hosts a (currently experimental) tool that helps you create and work with Pitchfork-compliant projects.

This project is still very young and has a while to go before being a useful developer tool. Once ready, this README will be updated with proper user documentation.

The pf Library

The pf tool mentioned above is built upon the pf library, also hosted in this repository. This library can be used to query and manipulate Pitchfork-compliant projects.

  • New subcommand: update source list from source tree

    New subcommand: update source list from source tree

    Adds a subcommand which updates a project's src/CMakeLists.txt from the source tree. Only supports CMake.

    • Automatic detection of project base-dir (for CMake).
      • Ascending filesystem iterator. Moves up a directory with each increment.
      • Use Boost::filesystem as a polyfill rather than std::experimental::filesystem
    • Edit the src/CMakeLists.txt file
    • Add an update subcommand. When the build system is specified as cmake, performs the operation.

    This inserts source files from the tree by scan the CMakeLists.txt file for function calls containing # sources comment. From that comment to the end of the function, everything is replaced with the globbed sources.

    I don't intend on this being a permanent solution. My vision is to parse the CMakeLists.txt file and use some heuristics to determine where to place the source list.

    This is very limited. It only globs for source files and header files; there's currently no option to glob for only one kind. It does skip sources directly under src/; it only grabs sources under a subdirectory of src/. It also only searches src/; it doesn't look at include/ or tests/

    The git history for this branch is kind of messy. The commits are not that clean; there may be some commits which don't build, and there are some commits which are incomplete progress

    opened by Quincunx271 8
  • Configure fails on windows at zlib2 step

    Configure fails on windows at zlib2 step


    I would like to evaluate pitchfork tool. Unfortunately, I can't configure it.

    I use cmake 3.14.4 and conan 1.11.1

    The configure step fails at zlib. When inspecting my folders, I see a file C:\\Users\\MartyLake\\.conan\\data\\zlib\\1.2.11\\conan\\stable\\package\\46fecbf5b55c9a04d9e1ce376246b1f9e619c3b7.dirty of size 0 instead of (I suppose?) a folder that would contain the zlib lib.

    Here is the end of cmake log:

    zlib/[email protected]/stable: 
    Calling package()
    ERROR: zlib/[email protected]/stable: Error in package() method, line 185
    while calling '_rename_libraries', line 133
    	os.rename(current_lib, os.path.join(lib_path, "zlib.lib"))
    	FileNotFoundError: [WinError 2] Le fichier spécifié est introuvable: 'C:\\Users\\MartyLake\\.conan\\data\\zlib\\1.2.11\\conan\\stable\\package\\46fecbf5b55c9a04d9e1ce376246b1f9e619c3b7\\lib\\zlibstatic.lib' -> 'C:\\Users\\MartyLake\\.conan\\data\\zlib\\1.2.11\\conan\\stable\\package\\46fecbf5b55c9a04d9e1ce376246b1f9e619c3b7\\lib\\zlib.lib'
    CMake Error at build/_pmm/1.3.1/conan.cmake:470 (message):
      Conan install failed [1]:
    Call Stack (most recent call first):
      build/_pmm/1.3.1/conan.cmake:495 (_pmm_conan_install_1)
      build/_pmm/1.3.1/conan.cmake:643 (_pmm_conan_install)
      build/_pmm/1.3.1/main.cmake:32 (_pmm_conan)
      build/_pmm/1.3.1/main.cmake:47 (_pmm_project_fn)
      CMakeLists.txt:32 (pmm)
    CMake Error at build/_pmm/1.3.1/conan.cmake:500 (message):
      Conan dependencies were not imported (Expected file
      C:/Users/MartyLake/Dev/pitchfork/build/conanbuildinfo.cmake).  You may need to
      run Conan manually (from the build directory).  Ensure you are using the
      'cmake' generator.
    Call Stack (most recent call first):
      build/_pmm/1.3.1/conan.cmake:643 (_pmm_conan_install)
      build/_pmm/1.3.1/main.cmake:32 (_pmm_conan)
      build/_pmm/1.3.1/main.cmake:47 (_pmm_project_fn)
      CMakeLists.txt:32 (pmm)
    CMake Error at CMakeLists.txt:33 (conan_set_find_paths):
      Unknown CMake command "conan_set_find_paths".
    opened by MartyLake 7
  • Filesystem include issue

    Filesystem include issue

    In the file src/pf/fs.hpp you differentiate between experimental and non-experimental versions of the filesystem include. I have noticed that your *else branch is wrong:

    #include <experimental/filesystem>
    namespace pf {
    namespace fs = std::experimental::filesystem;
    }  // namespace pf
    #include <filesystem>
    namespace pf {
    namespace fs = std::experimental::filesystem;
    }  // namespace pf

    It should be: namespace fs = std::filesystem; instead of: namespace fs = std::experimental::filesystem;

    opened by Randshot 2
  • Test Failed: empty docs and third_party directories under tests/expected/ not included in git repository

    Test Failed: empty docs and third_party directories under tests/expected/ not included in git repository

    The tests fail with this message:

    ../../tests/generate.cpp:38: FAILED:
      CHECK_FALSE( diff )
    with expansion:
      !Unexpected files in output directory:
        + docs
        + third_party
    test cases: 1 | 1 failed
    assertions: 2 | 1 passed | 1 failed

    I'm pretty sure this is because git doesn't commit empty directories; it tracks files, not directories.

    opened by Quincunx271 2
  • Add query subcommand

    Add query subcommand

    opened by Quincunx271 2
  • Extraneous move-from-local in return statements

    Extraneous move-from-local in return statements

    Clang caught some unneeded std::moves which inhibit copy elision:

    fs.hpp line 39

    inline std::fstream open(const fs::path& filepath, std::ios::openmode mode) {
        std::error_code ec;
        auto            ret = open(filepath, mode, ec);
        if (ec) {
            throw std::system_error{ec, "Open file: " + filepath.string()};
        return std::move(ret); // error: moving a local object in a return statement prevents copy elision [-Werror,-Wpessimizing-move]

    fs.cpp line 13

    std::fstream pf::open(const fs::path& filepath, std::ios::openmode mode, std::error_code& ec) {
        std::fstream ret;
        auto         mask = ret.exceptions() | std::ios::failbit;
        try {
            ret.open(filepath.string(), mode);
        } catch (const std::ios::failure& e) {
            ec = e.code();
        return std::move(ret); // error: moving a local object in a return statement prevents copy elision [-Werror,-Wpessimizing-move]

    In both these cases, the return std::move(ret); should simply be return ret;

    opened by Quincunx271 1
  • Fix tests

    Fix tests

    Fixes #3 and fixes #8 , along with some stuff which were necessary.

    Some comments:

    • This fix for #8 is not the best code. It literally copies code from _pf_auto into a new function. Ideally, this new function would share code with _pf_auto rather than copy-paste it.
    • The ignored file is called ignore_in_diff. I don't have a preference for the name
    • The ignored file is ignored in both the expected case and actual case. It might be better to only ignore it in the expected case, just in case the actual generated directories include the ignored file.
    opened by Quincunx271 1
  • Use conan for Catch2

    Use conan for Catch2

    Installing conan is pip install --user conan (I like the --user flag; without it I think you need sudo)

    To use this:

    $ cd build
    $ conan install ..
    $ cmake -DCMAKE_PROJECT_pitchfork_INCLUDE=$(pwd)/conan_paths.cmake ..

    For conan packages which don't properly support non-intrusive cmake integration, it's a bit more work (I write a custom cmake include file that wraps find_package)

    opened by Quincunx271 0
  • Fix compatibility with MSVC

    Fix compatibility with MSVC


    • Use /permissive- for MSVC
    • Add missing #include <cctype> to mustache (https://github.com/kainjow/Mustache/issues/29)
    • Add missing #include <iterator> to tests/compare_fs.cpp
    • Changed class eof in pf.cpp to class reached_eof. MSVC complains that eof is ambiguous at catch (const eof&)
    opened by Quincunx271 0
  • No tests were found!!!

    No tests were found!!!

    Even though the enable_testing() function is getting called by pf_auto(...), CTest cannot find any tests.

    Adding the enable_testing() call before the call to pf_auto() works, though. Adding the enable_testing() call after the call to pf_auto() does not work. This looks like a CMake bug, either in source or in documentation. It looks like the call to enable_testing() must appear before any tests are defined

    To reproduce this, make sure you start with a fresh build directory.

    opened by Quincunx271 0
  • If platform bindings go into `extras/` then extras aren't really

    If platform bindings go into `extras/` then extras aren't really "built upon" the main component(s)

    w.r.t. the first paragraph of 4.4 extras/:

    I get why platform bindings are not in libs/ as they're not always built and that's a requirement for the submodules in libs/. (Some people may never build the Windows bindings, others never build the Linux bindings ...) But typically one or another of them is always required for any given build. They're at the foundation of the project.

    So I'm fine with them in extras but the wording of what goes in extras should be changed ...

    Or maybe it's the requirement that stuff in libs/ is always built that should be relaxed ...

    But actually, in my own project, I'm putting them as submodules in libs/.

    (In fact, I have added another layer inside of libs/ where I group submodules by "category" - I may have multiple implementations of a given service interface, for example, so I put them together under, say, "storage" (one submodule for sqlite, one for filesystem, one for webdav, ...) - and in this way I'll have libs/platform/ and there will be under it windows and linux (macos can go into extras/ with any other contributed stuff...).)

    (Pitchfork is well thought-out IMO, thanks for the effort!)

    opened by david-bakin 0
  • I am uncomfortable with the inconsistency of sometimes having a /src/ folder and sometimes not.

    I am uncomfortable with the inconsistency of sometimes having a /src/ folder and sometimes not.

    If a project with submodules is a directed graph, thus requiring all submodules to be siblings, then simply put all submodules in folders under the /src/ subfolder. For consistency, always place the main submodule in /src/main/, and place the other submodules in their own folders as sibling folders to /main/. If submodules are actually useful as separate libraries, then place them into subfolders of /lib/ and treat /lib/ with the rules currently ascribed to /external/. This, then, makes /external/ redundant, so it can be eliminated.

    When most people think of "Libs" they think of "external libraries." However, within the context of Pitchfork," "Libs" takes on a different meaning, as "private submodules used only within the current project." This is also inconsistent.

    The above suggestion does not take into consideration the debate as to whether external libraries should even be copied to the project folder. If it is decided that external libraries should not be copied to the project folder, the /lib/ folder could be used to hold simlinks to the original folders for those libraries, OR the /lib/ folder could also be eliminated and proper makefiles could be constructed to point to where the external libraries are stored.

    Note: I am also using the singular version for all these folder names.

    opened by GrantRobertson 3
  • Clarification about placing source-code files in source root directory

    Clarification about placing source-code files in source root directory

    The spec reads:


    Doesn't the existence of https://github.com/vector-of-bool/pitchfork/blob/1f1d16549408dc29dae03fc1a479c93556239112/src/pf.cpp go against this rule?

    bug question 
    opened by michaelfranzl 3
  • Minor Fix: Remove duplicate word in spec.bs

    Minor Fix: Remove duplicate word in spec.bs

    Just something I stumbled over

    opened by felixendres 0
  • fix extension name in settings file

    fix extension name in settings file

    VS code extension settings. Old extension name was


    new one is


    opened by m-tosch 0
  • Feedback on project layout guidelines

    Feedback on project layout guidelines

    Hey thanks for writing this list of guidelines for consistent structuring of C++ projects, which are strongly needed imo! They have recently been brought to my attention through an issue in my modern C++ starter template and I wanted to share my thoughts on the guidelines here as well.

    • Inconsistent naming conventions: In the proposition some directory names are singular (include, build, external), some are plural (tests, examples, extras), while others are abbreviated (src, libs, docs). IMO directory naming should be consistent so there is no confusion how to properly name directories.

    • Embedding external projects IMO external projects should never be embedded in a project, as this will create problems for library users downstream. E.g. if they wish to use two libraries A and B which both use C as an embedded library, their project will include C twice, thus breaking the ODR. Projects should always use a dependency management tool that fetches dependencies on demand and can be easily overwritten downstream (such as CPM.cmake) to allow users to resolve any issues.

    • No additional top-level directories the recommendations state that "other directories should not be present in the root directory, except for what is required by other tooling". I think this restriction is very project-dependent and reasonable exceptions should be allowed. E.g. I often use a script directory for non-development related scripts (e.g. files to run certain workflows with the compiled binary). My template even has a top-level standalone target executable that allows to run the library as a command-line utility. This is because the development of the standalone is so strongly coupled to the library that it makes no sense to create an additional project for it.

    • Derived targets I'm not sure if this is in the scope of the guidelines but I'd love to see a paragraph on derived targets (e.g. the tests). In many C++ projects (including my own) I've seen the habbit of adding derived projects from the library's build system (e.g. by including it in from the main CMakeLists.txt). IMO this should be strongly discouraged, as it inverts the dependency tree (the tests should actually depend on the library) and make the library harder to include from other projects, as we usually don't want the tests to be included.

    • Submodules If you are using submodules in a C / C++ project that cannot be modelled as an external dependency or project, it is a strong sign that the library itself needs refactoring. This is why I would avoid mentioning them at all in project guidelines.

    I hope that the feedback makes sense and would love watch this project evolve!

    opened by TheLartians 6
  • Conan hooks

    Conan hooks

    The kind of enforceable project layout rules feel like a perfect fit for a Conan hook. It's more of a note than a feature request right now, but it could be a valuable for people who use Conan and want to enforce the PFL rules in their project.

    opened by Morwenn 0
  • [spec] Add an entry for codegen files

    [spec] Add an entry for codegen files

    Sometimes projects have sources that are not C++ but need to be generated into one. But they're also not data, they're still sources, so it'd be incorrect to put them alongside assets. An example of such could be QML code. According to the [src.layout] section it'd be incorrect to put it alongside the C++ code. What about adding a special section for a code generation folder?

    opened by Minimonium 2
  • Benchmarks



    I was recently changing the structure and tooling of an old project and thought that using a pitchfork layout would be a good enough idea. However, as I was about to handle the benchmarks directory of the project it occurred to me that there wasn't any such thing in PFL.

    It would be interesting if benchmarks were at least discussed: I know that some people put them under either tests, examples or tools, but they often have a dedicated top-level subdirectory too and have their own sets of tooling and libraries.

    opened by Morwenn 1
Type-safe zero-boilerplate interfaces for pure C99, implemented as a single-header library.

Interface99 Type-safe zero-boilerplate interfaces for pure C99, implemented as a single-header library. [ examples/state.c ] #include <interface99.h>

null 77 Sep 11, 2021
A template for modern C++ projects using CMake, Clang-Format, CI, unit testing and more, with support for downstream inclusion.

Modern C++ Template A quick C++ template for modern CMake projects, aimed to be an easy to use starting point. This is my personal take on such a type

Filip Dutescu 826 Sep 12, 2021
🚀 Kick-start your C++! A template for modern C++ projects using CMake, CI, code coverage, clang-format, reproducible dependency management and much more.

ModernCppStarter Setting up a new C++ project usually requires a significant amount of preparation and boilerplate code, even more so for modern C++ p

Lars Melchior 2.1k Sep 13, 2021
Starter project for cross platform WebGPU development in C++

A starter code for cross-platform (i.e., web & native) C++ WebGPU projects.

Will Usher 25 Sep 8, 2021