Boostを使ってコードを書いているときに遭遇したエラーです。
再現可能なコードは以下の通りです。
#include <boost/asio.hpp>
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
#include <boost/asio/ssl.hpp>
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(107): error C2065: 'name': 定義されていない識別子です。
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(110): error C2065: 'name': 定義されていない識別子です。
D:\win\boost\boost_1_84_0\boost/asio/ssl/impl/rfc2818_verification.ipp(112): error C2065: 'name': 定義されていない識別子です。
不可解かもしれませんが、一つでもインクルードの順序が変わったり、コメントアウトしたりするとエラーが発生しなくなります。
なぜ発生する?
なんとなくしか原因を把握できていないのですが、最初の#include <boost/asio.hpp>
を削除すると、エラーが変化します。
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
#include <boost/asio/ssl.hpp>
D:\win\boost\boost_1_84_0\boost/asio/detail/socket_types.hpp(24): fatal error C1189: #error: WinSock.h has already been included
これはBoostが吐いたエラーで、推測するにWinSock.h
があらかじめインクルードされている状況を想定していないと思われます。通常はこのチェックにより、問題の所在を明らかにするはずです。
しかし、最初の例ではこのインクルードチェックが行われていません。どういうことかというと、
#include <boost/asio.hpp>
// boost/asio/detail/socket_type.hppをインクルード、WinSock.hをチェック
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
// WinSock.hなどなどをインクルード
#include <boost/asio/ssl.hpp>
// boost/asio/detail/socket_type.hppをインクルードするも、インクルードガードにより無視される
// -> WinSock.hのチェックをすり抜けてエラー
という流れになっていると思われます。
今回はboost関連のインクルードを分散して書いてしまった結果、間に他のヘッダが入り込むことで不可解なエラーが発生してしまいました。Boost関連のヘッダをまとめてインクルードすれば、こういった問題は起こりません。
example1.cpp
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
example2.cpp
#include <openssl/ssl.h>
#include <windows.h>
#include <wincrypt.h>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
// errorになるが、#error: WinSock.h has already been included となるので解決できる
今回のコードはいささか恣意的にも見えるので、わざわざこんなコードを書こうとは思わないかもしれません。しかし、いろんなライブラリがそれぞれインクルードをした結果、このような構造になってしまうことはしばしばあると思います。
普遍的な対策としては、Boostのような複数のファイルからなるライブラリをインクルードするときに、それ専用のヘッダファイルを作ってしまうということです。
boost_wrapper.hpp
#ifndef MY_BOOST_WRAPPER_HEADER_INCLUDED
#define MY_BOOST_WRAPPER_HEADER_INCLUDED
#include <boost/asio.hpp>
// その他いろいろインクルード
#endif
lib.hpp
#include <boost_wrapper.hpp>
void my_some_func() {}
main.cpp
#include <boost_wrapper.hpp>
#include <lib.hpp>
int main(){}
複数のファイルからBoostなどをインクルードしても、きちんと順序を守ってインクルードされます。が、このような方法がいつもとれるとは限らないし、なにより非効率です。
(#define USE_XXX
などインクルード前に記述が必要な場合などはwrapperを作った方がいいかもしれません)
他の対策としては、別々にコンパイルしてリンクする方法です。今回のケースでは、
boost/asio.hpp
に依存したコード
wincrypt.h
に依存したコード
の両方をinline展開したくてヘッダに書いてインクルードした結果、衝突が起こってしまいました。この二つを別々のオブジェクトとしてコンパイルした後、必要なインターフェースだけヘッダファイルに摘出してリンクすれば衝突を避けることができます。
とはいえ、c++のプリプロセッサは基本的にグローバルスコープである以上、他のライブラリと何らかの要因で衝突する可能性は排除できません。何らかの実装ミスがない限り、こういったエラーが発生するのはごく稀なのであまり気にしなくてよいと思います。