やねうら王ver3.57 USER_ENGINEのコンパイルで躓いたのでメモ

c/c++初心者の自分が、やねうら王のコンパイルで躓いたが、解決できたのでメモ。

最初にしたこと。

  1. Visual c++2015をダウンロード。
  2. やねうら王ver3.57(2016年9月2日時点での最新版)をgithubからダウンロード。
  3. やねうら王のプロジェクトファイル(YaneuraOu.sln)を開く。
  4. shogi.h を開いて、思考エンジンを USER_ENGINEに変更。
  5. extra/config.h を開いて ターゲットCPUをNO_SSEに変更
  6. ビルドを実行

すると次のようなエラーがでてコンパイルに失敗した。

...
1>evaluate_bona_piece.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>evaluate_kpp.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>evaluate_material.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>book.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>misc.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>movegen.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>shogi.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>position.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>thread.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>usi.obj : error LNK2005: "int __cdecl Eval::calc_check_sum(void)" (?calc_check_sum@Eval@@YAHXZ) already defined in user-search.obj
1>..\..\YaneuraOu2016Engine\YaneuraOu.exe : fatal error LNK1169: one or more multiply defined symbols found
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

いつもなら、ここで心が折れるところだが、少し頑張ってみることに。ネットで検索して調べてみたところ、どうやらEval::calc_check_sum(void)が、多重に定義されててリンカーがエラーを起こしているらしい?よく分からん。

ソースコードを検索してみると、calc_check_sum(void)の定義らしきものは evaluate.h と evaluate_kppt.cppに書かれているようだ。

さらにネットで調べてたら、headerファイルに関数の定義を書くべきではない的なことが書いてある。

evaluate.h のソースコードの39〜47行目を見ると次のように書いてある。

  // 評価関数パラメーターのチェックサムを返す。
  s32 calc_check_sum()
#if defined(EVAL_KPPT) || defined(EVAL_KPPT_FAST)
	  ;
#else
  {
	  return 0;
  }
#endif

今は#if内は無視されているので

  s32 calc_check_sum()
  {
	  return 0;
  }

こうなる。これって、ネットに書いてあった、ヘッダに関数定義を書いたらリンカーでエラーになるという状況そのものだ。

しかし、おかしい。

  • ヘッダーに関数定義を書くとエラーが出るらしい。
  • ヘッダーに関数定義が書いてある。
  • よってエラーが出る。

というステップは理解したが、それなら何故ヘッダーに関数定義が書いてあるのか訳がわからない。それに、ヘッダー内で関数定義されている箇所は他にもある。そこでは何故エラーが出ないのか?

更にネットで調べてみたが、どうやら関数がコンパイラーによってinline展開されている場合は、リンカーのエラーは起きないようだ。よって、コンパイラーによって自動的にインライン展開されるような単純な関数はヘッダーに定義を書いてもいいようだ。

そこでさっきの部分を次のように変更した。

  // 評価関数パラメーターのチェックサムを返す。
#if defined(EVAL_KPPT) || defined(EVAL_KPPT_FAST)
  s32 calc_check_sum();
#else
  inline s32 calc_check_sum() {
	  return 0;
  }
#endif

これでようやく実行ファイルの生成に成功した。