Building a header-only library — Hello World

Installing a header-only library

I recently set out to publish my first header-only library, but I found most of the existing examples to be extremely dense. They were all well maintained and supported dozens of linters, formatters, and package managers. Some were even skeleton projects for quickly stamping out the boilerplate necessary to create a new library. I could have used hpp-skel as my template and been off to the races adding my own code. But I wanted to understand every line of code in my new repository and to do that I knew I would need to start small. I needed hello world.

If you’re in the same boat this article is for you.

Directory Structure

I’ve observed two common layouts. Anything is possible with cmake, but these two archetypes hold true in most cases.

The first is the single header structure which features an include folder at the root of the repository with a single header file named after the library itself. Consumers of the library can include that file once and it will include all of the necessary implementation files. I tend to lean toward this structure because the projects I am working on right now are mostly small and closely coupled enough that you need to include the entire code base. Programs using hello need only #include <hello.hpp> and they get access to everything. This is what the directory structure looks like in hello.

3 directories, 5 files

The second common layout is a nested multi-header structure which flattens the dependency tree and allows consumers to include individual headers. In the following example you’ll see that programs using type_safe can pick and choose which files to include as long as they prefix the filename with type_safe/.

If you’re planning to follow along, now is your chance to build your own directory structure. It might look something like this initially.

Just the code, Thanks

Let’s start with our implementation file.

Next create the main interface header file which includes all implementation files.

And while we are here let’s write a test as well.

Thats it. Next up is the hard part for those new to cmake. Getting it to build.


We will start with just enough cmake to build the project.

Now try building under /build/ which should be git ignored.


Start by installing Catch2 if you don’t already have it. Then we will show cmake how to discover catch tests.

Try running tests.


This part could theoretically work with about 2 lines, but I also want it to work with find_package, which you’ll encounter later. And to do that things get a bit verbose.

First our config.

Then the final section of CMakeLists.txt.

I’m not even going to try to break that down line by line. Let’s just try it out so you can see what it does.


There are a few different ways to skin this cat. But let’s keep it simple. We’re going to build a command line app that uses our library to print out hello world. It will use a cmake builtin called find_package to locate our library where we previously installed it on our system rather than supplying an include path or copying our lib into our new directory structure. Speaking of directory structure, this is what it will look like.

Then our code. Note that the header file needs no path prefix. This is because find_package will supply the correct include directory.

And our build scripts. The message calls are unnecessary, but I found them useful while I was wrapping my head around the interaction between this file and the config for the installed library.

Then test it out.


You shouldn’t use any of this as a blueprint for what good looks like. But hopefully it can help with wrapping your head around the basic building blocks before you get started with something more advanced.

If you want to browse the code again, check it out on Github.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store