開発を行っていると、簡便な方法でライブラリを扱えればよいのに…という場面が少なくありません。
ヘッダーオンリーライブラリの仕組みを使用することで簡便にライブラリを作成して扱うことが可能になります。
本記事では配布前提でヘッダーオンリーライブラリの作成方法と使用方法について紹介したいと思います。
前提知識
-
C++の基礎構文を理解している -
Visual Studio2022からファイルを作成する程度の操作を行える
想定環境
- Visual Studio2022
ヘッダーオンリーライブラリとは
ヘッダーオンリーライブラリとは、ソースコードをすべてヘッダファイルにまとめて提供したライブラリのことです。
配布時には単一ファイルのみで完結し、導入側もインクルードのみで使用できるため非常に簡便である反面、ライブラリのソースコードが丸見えになったり、ビルド時間が増えるといったデメリットが存在します。
よって、DLLや静的リンクライブラリよりも、小規模なツールやユーティリティの実装に向いている配布形式といえます。
| 項目 | ヘッダーオンリー | DLL (動的リンクライブラリ) | 静的リンクライブラリ |
|---|---|---|---|
| 導入方法 |
#include するだけ |
DLLファイルを配置し、リンク設定が必要 |
.lib をリンク設定に追加 |
| 配布 | ヘッダーファイルのみ |
DLLファイル+インポートライブラリ |
.lib ファイル |
| ビルド時間 | 長くなりやすい(全ソースに展開) | 比較的短い(共有コードはDLL側) |
中程度(リンク時に結合) |
| 実行時の依存 | なし(ソースに展開済み) |
DLLが存在しないと実行不可 |
なし(実行ファイルに埋め込み済み) |
| 更新・差し替え | 再コンパイルが必要 |
DLLを差し替えるだけで更新可能 |
再リンクが必要 |
| デバッグ | ソースが見えるので容易 |
DLL内部は別途デバッグが必要 |
ソースがあれば容易 |
| 実行ファイルのサイズ | 大きい | 小さい | 大きい |
ライブラリ側セットアップ方法
配布を行う際などに使用するプロジェクトのセットアップを行います。
まずは、Visual Studio 2022を開き、新しいプロジェクトの作成をクリックしてください。

ヘッダーオンリーライブラリの場合は.dllや.libなどの生成を必要としないため、空のプロジェクトで作成を行います。
空のプロジェクトを選択したら、画面右下の次へをクリックしてください。

プロジェクト名をつけて、右下の作成をクリックします。
ソリューションとプロジェクトを同じディレクトリに配置するの項目は、本記事ではチェックしたままにします。

プロジェクトを作成できたら、ソリューションエクスプローラーのGUIにあるすべてのファイルを表示のボタンをクリックしてください。
この操作を行うことで、Visual Studioからフォルダ構成を確認しながらフォルダを作成することが出来ます。

まずは配布するヘッダーファイルを作成します。
新しいフォルダはプロジェクトを右クリックし、追加->新しいフォルダで追加することが出来ます。

本記事では、include->mylib->MyHeaderLib.hppという構成にしました。
includeというフォルダを作成することで、導入側がインクルードを行うべきファイルを迷わなくて良いようになります。
ライブラリ名(本記事ではmylib)のフォルダを用意することで、他ライブラリとのファイル名衝突を事前に避ける意味があります。

次に、サンプルコードを作成します。
導入に直接の関係はありませんが、導入する人のためのガイド的な役割があります。
本記事でのフォルダ構成はexamples->main.cppとしました。
examplesという名前でガイドがここに存在することを示しつつ、main.cppという名前でエントリポイントの位置を示しています。

最後に、main.cppがヘッダーファイルを開けるようにするための設定を行います。
上部GUIからプロジェクト->プロパティをクリックして、プロパティウィンドウを開きます。

プロパティウィンドウの左から構成プロパティ->C/C++->全般を選択し、右の追加のインクルードディレクトリにサンプルコードで使用したいディレクトリ(本記事では$(ProjectDir)include)を追加してOKをクリックします。

プロパティウィンドウの右下の適用ボタンをクリックしてください。

これでセットアップが完了しました。
実際に配布する際にはREADME.mdなど、導入する人への説明書などを配布するのがベストですが、本記事では省略します。
ライブラリ側のコード例
HelloWorldを出力するサンプルコード
//-------------------------------------------------------------
//! @file MyHeaderLib.hpp
//! @brief "Hello, World!"を出力する関数のみが入ったヘッダーオンリーのライブラリ
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include <iostream>
//-------------------------------------------------------------
//! @namespace mylib
//! @brief ヘッダーオンリーライブラリの名前空間
//! @details "Hello, World!"を出力する関数のみを提供したライブラリ
//-------------------------------------------------------------
namespace mylib {
//-------------------------------------------------------------
//! @brief "Hello, World!"を出力するだけの関数
//-------------------------------------------------------------
inline void PrintHelloWorld() {
std::cout << "Hello, World!" << std::endl;
}
} // namespace mylib
//-------------------------------------------------------------
//! @file main.cpp
//! @brief MyHeaderLib.hppを使用するサンプルコード
//! @author つきの
//-------------------------------------------------------------
#include <mylib/MyHeaderLib.hpp>
//エントリポイント
int main() {
// ライブラリの関数を呼び出す
mylib::PrintHelloWorld();
return 0;
}
Hello, World!
サンプルコードが正常に動きました。
ヘッダーオンリーライブラリでは、その性質上ヘッダーファイルに定義を書かざるを得ません。
そのため、複数の.cppから呼ばれた際の多重定義エラー対策として、関数の頭にinlineとつけ、インライン関数にします。
また、実際の開発では名前の衝突が考えられます。
名前空間にライブラリの名前を使用しておくことで、他ライブラリなどとの衝突を防いでおくことを私は推奨します。
//-------------------------------------------------------------
//! @namespace mylib
//! @brief ヘッダーオンリーライブラリの名前空間
//! @details "Hello, World!"を出力する関数のみを提供したライブラリ
//-------------------------------------------------------------
namespace mylib {
//-------------------------------------------------------------
//! @brief "Hello, World!"を出力するだけ関数
//-------------------------------------------------------------
inline void PrintHelloWorld() {
std::cout << "Hello, World!" << std::endl;
}
} // namespace mylib
導入側のセットアップ
ライブラリが作成できたので、導入側プロジェクトのセットアップを行います。
プロジェクトの作成は先ほど紹介したので省略します。
同じように空のプロジェクトを作成してください。
本記事ではソリューションとプロジェクトは同じディレクトリに配置し、プロジェクト名はHeaderLibTestとしました。

エクスプローラーでソリューションディレクトリを開き、externalという名前のディレクトリを作成します。
externalは外部から取り込んだファイルであることを明示する名前です。

externalに、追加するライブラリ名(本記事ではMyHeaderLib)のディレクトリを作成します。

追加するライブラリ名のディレクトリに、追加したいヘッダーオンリーライブラリのincludeディレクトリをコピーしてください。
これでライブラリをソリューションに配置できました。

Visual Studio2022に戻り、あらかじめエントリポイントとなるファイルを作成しておきます。
ファイルが存在しないとインクルードディレクトリを追加することが出来ません。

右上のGUIからプロジェクト->プロパティをクリックしてプロパティウィンドウを表示し、プロパティウィンドウ左から構成プロパティ->C/C++->全般を選択し、右側の追加のインクルードディレクトリから、使用したいライブラリのパス(本記事では$(SolutionDir)external\MyHeaderLib\include)を追加します。
OKをクリックし、プロパティウィンドウの適用をクリックしてください。

これでヘッダーオンリーライブラリの導入が完了しました。
コードでは#includeを行うだけで使用ができます。
導入側のコード例
//---------------------------------------------------------
//! @file main.cpp
//! @brief 外部ライブラリの導入サンプル
//! @author つきの
//---------------------------------------------------------
#include <mylib/MyHeaderLib.hpp> // 外部ライブラリのヘッダーをインクルード
//エントリポイント
int main() {
// ライブラリの関数を呼び出す
mylib::PrintHelloWorld();
return 0;
}
Hello, World!
特に追加で説明することもないかと思います。
#includeを行うだけで、非常に簡便にライブラリを使用することが可能です。
追加のインクルードディレクトリでは$(SolutionDir)external\MyHeaderLib\includeと追加したので、includeディレクトリ配下のmylibからを#includeで指定してください。
#include <mylib/MyHeaderLib.hpp> // 外部ライブラリのヘッダーをインクルード
総括
-
ヘッダーオンリーライブラリとはソースコードをすべてヘッダーファイルにまとめて提供する形式のこと。 - 配布や導入が非常に簡便
- ビルド時間が長くなるデメリットがあるため、小規模なライブラリに向いている。
- ヘッダーファイル内の定義はインラインにすることで多重定義エラーを回避することが必須となる。