
- OS:Windows11
- Visual Studio 2022 Version 17.9.2

唐突にSDLなるものを使うとだけ言われ、インストールできたものとして話を進められた経験はないでしょうか?
そんな時、C++初心者は初手で詰みます。
本記事では、前半でVisual StudioでのSDLの使い方、後半でどうしてそのようなことをするのかを説明していきます。
なお、後半では私の考察が多く含まれるので、真に受けないほうがよいでしょう。
この記事は「ゲームプログラミング C++」(2018, Sanjay Madhav 著, 吉川 邦夫 訳, 今給黎 隆 監修)のChapter1に関連します。

SDLの公式サイトから「SDL2-devel-2.24.0-VC.zip」というようなフォルダをダウンロードしてきます。バージョンによって「2.24.0」の部分が異なるかもしれません。その場合は以下の操作でも文字列を置き換えてください。
zipファイルを解凍して、その中に「SDL2-2.24.0」というフォルダがあることを確認してください。
「SDL2-2.24.0」フォルダをCドライブ直下に置きます。

Visual Studioを起動し、適当に新規プロジェクトを作成します。
画面上部の「プロジェクト」→「(プロジェクト名)のプロパティ」をクリックします。
「C/C++」→「追加のインクルードディレクトリ」に「C:\SDL2-2.24.0\include;%(AdditionalIncludeDirectories)」と入力します。

「リンカー」→「追加のライブラリディレクトリ」に「C:\SDL2-2.24.0\lib\x64;%(AdditionalLibraryDirectories)」と入力します。

「リンカー」→「入力」→「追加の依存ファイル」に「SDL2.lib;SDL2main.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)」と入力します。

「ビルドイベント」→「ビルド後のイベント」→「コマンドライン」に「xcopy "C:\SDL2-2.24.0\lib\x64\*.dll" "$(OutDir)" /i /s /y」と入力します。

例えばヘッダファイルでSDLをインクルードする場合は以下のように書きます。
#pragma once
#include "SDL.h"
//以下省略
これでSDLが使えるようになりました!

C++ではどのような流れでプログラムを実行しているのかを知る必要があるので、長くなりますがお付き合いください。
大前提としてコンピュータは0と1で構成された(機械語の)命令文を処理することしかできません。
Visual Studioなどで記述したソースコードは単なるテキストファイルであり、実行するためにはこれを機械語に変換する必要があります。

C++はコンパイラ言語と呼ばれていて、実行前に全てのソースコードを機械語へと変換し、機械語のみのファイルを生成します。実行するときは生成した機械語のファイルを呼び出します。
ここで、ソースコードが外部にあるプログラムを参照している場合は、その箇所が実行されるまでのどこかのタイミングで該当する機械語プログラムを入手しなくてはなりません。
よく使う処理は関数化して、それを呼び出すという風にして、何度も同じコードを書かなくてすむようにした経験がある人も多いのではないでしょうか。
その関数を書く場所をヘッダファイルにしたり、別のcppファイルにしたりすれば、外部参照ということになります。
関数の処理内容が記述されていれば上記のファイル以外でも可能で、特にwindowsでは「.lib」や「.dll」という拡張子が、外部から参照されるためのファイルであることを示します。
これらのファイルはライブラリと呼ばれ、「よく使う関数」集と言えます。
先ほどの「変換」はいくつかのステップからなっていて、まずプリプロセッサによってcppファイル内にある「#」から始まる行を処理します。ファイルの結合という面に注目すると、ここでヘッダファイルがcppファイルに結合されます。
次にcppファイルを機械語に翻訳してobjファイルを生成します。これをコンパイルといいます。厳密には、アセンブリ言語(機械語と一対一対応している言語)のファイルを作ってから機械語に直しているそうです。
この段階ではそのcppファイル自体に書かれた機械語プログラムとヘッダファイル内の機械語プログラムを得ることはできていますが、別のcppファイル内、libファイル内やdllファイル内にある機械語プログラムは不明なので保留状態になっています。
外部にあるはずの機械語プログラムを入手できるタイミングは、コンパイル後と実行直前の2つです。
コンパイル後に外部のプログラムを結合して外部参照を解決することをリンクと呼びます。コンパイルした後に続けてリンクが自動で行われるようになっていることが多いです。
リンクによって、使用する全てのobjファイルとlibファイルが結合されて一つになります。これで実際の処理内容がわからなかった関数の中身が得られます。(Visual Studioではプリプロセッサの処理からここまでの作業をビルドと呼んでいるようです)
しかしリンクをしても、まだ一部の関数は実際の処理内容が不明なままです。
その部分については、実行直前にdllファイルからその処理内容を読み取ってすべての外部参照が解決されます。(もし解決できなければエラーとなります。)
流れをざっくりと図にすると以下のようなものになります。

「リンクをコンパイル後が実行直前のどっちか1つにまとめないのか?」という疑問については、長くなるのでここでは触れないことにします。(それぞれに長所と短所があるからです)

上の(長く退屈な)説明から、参照するライブラリの名前と場所を示すことが重要だとわかります。
では、それぞれのライブラリが、どの先述した操作によって指定されていたのかを見ていきましょう。
「#include」に定められた暗黙のルールとして次のものがあります
- 「<>」で囲まれたヘッダファイルはデフォルトのディレクトリ内にあるヘッダファイルを探す
- 「""」で囲まれたヘッダファイルは上から順に
- カレントディレクトリ内(すなわち#includeを書いているファイルがあるディレクトリと同じディレクトリ)にあるヘッダファイルを探す
- インクルードディレクトリを設定するとそのディレクトリ内でヘッダファイルを探す
- デフォルトのディレクトリ内にあるヘッダファイルを探す
このルールにより、「追加のインクルードディレクトリ」を先ほどのように設定すれば、「SDL.h」を見つけることができます。
「追加の依存ファイル」にどのlibファイルをリンクしてほしいかを書き、「追加のライブラリディレクトリ」にどこにあるのかを書きます。
私の予想になってしまいますが、「SDL2.dll」というdllファイルがあることは読み込んだlibファイルに書いてあるのではと考えています。
windowsでは実行ファイルと同じディレクトリにdllファイルがあれば、ディレクトリの設定をしなくても読み込んでくれます。
従って、ビルド後に実行ファイルと同じディレクトリに「SDL2.dll」をコピーすればよいです。
しかし、このコピーを毎回手作業でやるのは面倒ですし、ミスも増えます。
「コマンドライン」に書いた命令文はコマンドプロンプトで自動的に実行されるので、「コマンドライン」にコピーするような命令文を書いて自動化します。