Mario Badr

Dec 09, 2016

Creating a Header-Only Library with CMake

It seems like header-only libraries are all the rage in C++ these days. In this post, we'll see a useful way to organize your header only library and generate a CMake target that others can "link" to. We will make use of CMake's INTERFACE library feature to do this:

An INTERFACE library target does not directly create build output, though it may have properties set on it and it may be installed, exported and imported.

Hopefully by the end of this post you will understand why this is so useful!

The Directory Structure

There are a few options here, but in general it makes sense to have an include directory for all the header files in your library. You may then place your headers directly in the include directory, as in cxxopts. Or you may choose to create an additional subdirectory with the same name as your library's namespace, as in type_safe. So for a project that links with your library, a developer can #include in one of two ways:

#include <cxxopts.hpp>
#include <type_safe/strong_typedef.hpp>

Examples and test files should go into their own directory and not pollute the include directory. This provides two benefits:

  1. It is easy to use CMake's target_include_directories feature
  2. It is easy to install the include directory into a destination

Of course, this is not set in stone. For example, nlohmann's json library places its header files in a src directory. But tests and benchmarks are still relegated to their own directories.

Creating a Header-Only CMake Target

Creating an interface library in CMake is very straightforward:

add_library(my-library-name INTERFACE)

By specifying INTERFACE as the second parameter to add_library, we are no longer allowed to provide source files since the library is not meant to generate any build output. However, if you would like your library to be IDE friendly, then you can specify the source files using target_sources, which is done by the type_safe library. target_sources was added in CMake 3.1 and allows headers that are not part of the executable target to be displayed in IDEs. (There are also other benefits to target_sources).

Once you've created a target for your header-only library, you should at the very least specify its target_include_directories. If you followed the directory structure above, then it is as simple as:

target_include_directories(my-library INTERFACE include/)

We specify INTERFACE again here so that future targets that link with our library will inherit our library's include directory. This is extremely convenient and is the main reason we are creating an INTERFACE library. We can continue to use INTERFACE when using target_link_libraries, target_compile_definitions, etc. so that we can propagate dependencies to targets that link with our library. In other words, the only requirement another developer has in using your library is to:

target_link_library(another-users-target my-library)

Doing so will ensure that any INTERFACE-marked include directories, compile definitions, other linked libraries, etc. are also added to another-users-target.

INTERFACE versus PUBLIC versus PRIVATE

It may be handy to understand how INTERFACE, PUBLIC, and PRIVATE behave. If PRIVATE is specified for a certain option/property, then that option/property will only impact the current target. If PUBLIC is specified, then the option/property impacts both the current target and any others that link to it. If INTERFACE is specified, then the option/property does not impact the current target but will propagate to other targets that link to it.

This makes INTERFACE very useful for header-only libraries that aren't compiled (on their own), but rely on other libraries they need to link to. Using target_link_library on our header-only library with INTERFACE specified means that the executable that eventually gets compiled with our library will have the appropriate link flags.

Dec 03, 2016

Using clang-format to Enforce Style

Everyone has their own coding style. Conforming to one particular style or another can be difficult, but with clang-format it doesn't have to be. Given a file (or collection of files), clang-format will modify it so that it meets certain style requirements. Several pre-configured styles already exist, such as llvm, Google, Chromium, Mozilla, and Webkit. For example, suppose we have the main.cpp file:

#include <iostream>

int main() {
std::cout << "Hello world\n";
return 0;
}

We can format the file according to Mozilla's styleguide with:

clang-format -i -style=Mozilla main.cpp

And end up with:

#include <iostream>

int
main()
{
  std::cout << "Hello world\n";
  return 0;
}

If you don't like any of the pre-configured styles, you can create a .clang-format file with your own rules. Simply replace Mozilla with file and clang-format will look for a .clang-format file in your current directory to use. Check out this amazing clang-format configurator to help define your own style.

Using clang-format with CMake

If you are using CMake to build your project, then it probably makes sense to have it handle clang-format for you. The first thing to do is to check if the clang-format executable exists:

find_program(
  CLANG_FORMAT_EXE
  NAMES "clang-format"
  DOC "Path to clang-format executable"
  )
if(NOT CLANG_FORMAT_EXE)
  message(STATUS "clang-format not found.")
else()
  message(STATUS "clang-format found: ${CLANG_FORMAT_EXE}")
  set(DO_CLANG_FORMAT "${CLANG_FORMAT_EXE}" "-i -style=file")
endif()

Using DO_CLANG_FORMAT we can simply pass in the files that need to be formatted. Typically a CMake project will have a list with all the files that are part of a given target:

set(
  MY_PROJECT_SOURCE_FILES
  include/hello_world.hpp
  src/main.cpp
  )

Unfortunately passing these files straight to our DO_CLANG_FORMAT won't work because the path to the files is relative. We need to prefix each file with the full path in order to get it to work. Searching around stackoverflow I came across a handy prepend function to do just that. The first argument passed to the function will contain the result, the second argument is the prefix, and the last argument (not named in the function) is a list of files to be prefixed.

function(prepend var prefix)
  set(listVar "")

  foreach(f ${ARGN})
    list(APPEND listVar "${prefix}/${f}")
  endforeach()

  set(${var} "${listVar}" PARENT_SCOPE)
endfunction()

if(CLANG_FORMAT_EXE)
  prepend(FILES_TO_FORMAT ${CMAKE_CURRENT_SOURCE_DIR} ${MY_PROJECT_SOURCE_FILES})

  add_custom_target(
    clang-format-project-files
    COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${FILES_TO_FORMAT}
  )
endif()

CMake will now generate a custom target that can be executed to format the files. If you use makefiles, it can be invoked easily with:

make clang-format-project-files

And there you have it - free formatting!

Nov 18, 2016

Using clang-tidy with CMake 3.6

In CMake 3.6 a new property was introduced to support clang-tidy. clang-tidy can perform static analysis of your code and provide you with additional information to compiler warnings. In this post we'll take a look at how to make use of the new CMake property to add clang-tidy to our projects. If you're interested in how to use the different features of clang-tidy, I refer you to Google :).

Finding clang-tidy

clang-tidy may not be available on all systems, so in typical cmake fashion we first attempt to find the executable. If the executable is available, I setup a convenient CMake list (DO_CLANG_TIDY) that will help when using the CMake property later. In this case, I have enabled all checks with *, and then subsequently disabled checks that are currently in alpha (using the * wildcard). After you run clang-tidy with your application, you will likely disable additional checks. In that case, you will need to separate removed checks with a comma and ensure they are prefixed with a -.

find_program(
  CLANG_TIDY_EXE
  NAMES "clang-tidy"
  DOC "Path to clang-tidy executable"
  )
if(NOT CLANG_TIDY_EXE)
  message(STATUS "clang-tidy not found.")
else()
  message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
  set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-checks=*,-clang-analyzer-alpha.*")
endif()

Setting the property

With DO_CLANG_TIDY, we can now set the CXX_CLANG_TIDY (assuming you are using C++) property for all targets we want to analyze.

set_target_properties(
  my-target-name PROPERTIES
  CXX_STANDARD 14
  CXX_STANDARD_REQUIRED ON
  COMPILE_FLAGS "${WARNING_FLAGS}"
)

if(CLANG_TIDY_EXE)
  set_target_properties(
    my-target-name PROPERTIES
    CXX_CLANG_TIDY "${DO_CLANG_TIDY}"
  )
endif()

The above will enable C++14 and setup some custom warning flags. If clang-tidy is available, then it will run DO_CLANG_TIDY whenever we build the target. That's basically all there is to it!