C++ のプログラムでライブラリを利用する場合の超基本的なこと
スタティックライブラリ, ダイナミックリンクライブラリ
Visual Studio, VC++, Windows
本記事の前提条件は以下の通りです。
- 初心者向け
- とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
- ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
- Visual Studio 2013 くらい~
- Windowsプログラム (CUI, GUI)
- コードの検証
- 開発環境: Visual Studio 2022, x64, Release ビルド
- 実行環境: Windows 10
- 本記事は上から順番に読む前提となっている
- 「C++」と書いてあるが、本記事の内容としては C でもほぼ同じ
- 「Visual Studio 2013 くらい~」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
- 「Windowsプログラム (CUI, GUI)」と書いてあるが、本記事の内容だとどの OS でもほぼ同じ。ただし、ダイナミックリンクライブラリ(共有ライブラリ)の作り方、使い方については OS によって違いがあるので注意
- その他、エラーコードなどについても状況によって異なるので注意
- 「...ライブラリ」「...リンクライブラリ」どちらでもよいと思うが、マイクロソフト的には「スタティックライブラリ」「ダイナミックリンクライブラリ」としているようなのでそれに従っている
- DLL を使う場合、(Visual Studio のテンプレートを使ったとしても)実際にはいろいろと処理が必要なのだが、本記事ではそのあたりの詳細には触れないのであしからず
前提知識として必要と思われる情報
そもそもライブラリとは
「図書館」ではさすがにわかりにくいので、「収集コレクション」と考えるのがよいと思う。
コンピュータープログラムにおいては、同じ処理を使いまわすための仕組みということで、単なるコードスニペット群もライブラリと呼べるかと思うが、ここでは、C++ における *.lib
ファイル、および、*.dll
を対象とする。
静的と動的
ライブラリには、スタティックライブラリ、ダイナミックリックライブラリがある。
以下に、その作り方、使い方を記述し、違いを見てみる。
ライブラリにしたい処理を作る
例えばここに、共通に利用したい関数 int add(int a, int b)
があるとする。
int add(int a, int b);
int add(int a, int b)
{
return a + b;
}
ソースコードを書き、とりあえずコンパイルし、calc.obj
を作成しておく。
スタティックライブラリ
スタティックライブラリを作る
ライブラリアン lib.exe
でスタティックライブラリを作成する。
lib.exe calc.obj /out:calc.lib
ライブラリアンは *.lib
の管理をするツールで、後で別の *.obj
を追加したりできる。
つまり、*.lib
は *.obj
の収集コレクション = ライブラリ、ということである。
スタティックライブラリを使う
ヘッダファイルをインクルードしてコンパイルする。
#include <iostream>
#include "calc.h"
int main()
{
std::cout << add(1, 2) << std::endl;
return 0;
}
cl.exe main.cpp
これで main.obj
が生成される。
リンカに、main.obj
と、calc.lib
を渡してリンクする。
link.exe main.obj calc.lib /out:hogehoge.exe
ライブラリを使わない場合と比べると、リンカに渡すものが calc.obj
から calc.lib
になっただけである。
上記ソースコードを実際にリンクする場合には、VCランタイムなどが追加で必要なのであしからず。
スタティックライブラリを使った場合のポイント
スタティックライブラリを使った場合のポイントとしては、生成された hogehoge.exe
の中に add
関数の本体が埋め込まれている、ということである。
ダイナミックリンクライブラリ
ダイナミックリンクライブラリを作る
リンカで calc.obj
をダイナミックリンクライブラリとしてリンクするのだが、DLL を作成する場合には、どの関数をエクスポートするか、を指定する必要がある。
いくつかの方法があるのだが、それについては本記事では説明しないのであしからず。
なんにせよ、calc.obj
を含んだ calc.dll
ができたとする。
ダイナミックリンクライブラリを使う(Explicit Linking)
ダイナミックリンクライブラリを使う場合には、実行時に DLL から必要な関数を読み込む必要があり、下記のようになる。
#include <windows.h>
#include <tchar.h>
#include <iostream>
int main()
{
// DLL をロードする
HMODULE hLib = LoadLibrary(_T("calc.dll"));
typedef int (*PFUNC_ADD)(int, int);
// add 関数のアドレスを取得する
PFUNC_ADD add = (PFUNC_ADD)GetProcAddress(hLib, "add");
// 使う
std::cout << add(1, 2) << std::endl;
// DLL を解放する
FreeLibrary(hLib);
return 0;
}
とにかく DLL から関数を読み込む必要がある、ということがわかればよいので、上記コードの詳細は割愛する。
ダイナミックリンクライブラリを使う(Implicit Linking)
DLL を作成する場合、(設定によるが、通常)リンカはインポートライブラリ calc.lib
を出力する。
このインポートライブラリには、add
関数の実体は含まれておらず、その代わりに calc.dll
の add
関数を「実行時(exe の起動時)にロードする」処理が含まれている。
したがって、ダイナミックリンクライブラリを使う場合でも、ソースコード上はスタティックライブラリを使う時と同じで、まずはヘッダをインクルードしてコンパイルする。
#include <iostream>
#include "calc.h"
int main()
{
std::cout << add(1, 2) << std::endl;
return 0;
}
スタティックライブラリを使う場合との違いは、リンカに渡す calc.lib
がインポートライブラリである、という点のみである。
インポートライブラリをリンクした exe は、起動時に必要な DLL から必要な関数をロードして利用する。
ダイナミックリンクライブラリを使う場合のポイント
ダイナミックリンクライブラリを使う場合のポイントとしては、生成された hogehoge.exe
の中には add
関数の本体はなく、実行時に見つかった DLL のものを利用する、ということである。