勝手に作るCMake入門の4本目です。
1本目の記事ではCMakeのhello_worldプロジェクトを作成し、2本目の記事では静的ライブラリを利用してプロジェクトを階層化し、3本目の記事ではキャッシュ変数を利用したプロジェクト設定の方法を紹介しました。
今回はCMakeで外部ライブラリを利用する方法についてまとめていきます。
主にOSSで公開されているライブラリをインストールする方法や、find_package()
コマンドの使い方について解説します。
全体のもくじ
- 基本的な使い方
- プロジェクトの階層化
- プロジェクトの設定
- 外部ライブラリを利用する【今ここ】
この記事のもくじ
スポンサーリンク
7. find_packageとは
具体的な外部ライブラリの使い方を説明する前にfind_package()
コマンドについて解説しておきます。
find_package(<PackageName>)
は、特定の名前のCMakeスクリプトを探し出し、それを実行するコマンドです。
対象となるCMakeスクリプトはその役割に応じてModule, Configの2種類に分類されます。
find_packageはModule, Configの順で探索を行い、目的のスクリプトファイルが見つかったらその中身を実行してコマンドを終了します。
具体的な動作は以下のとおりです。
- Moduleモード:以下のパスで
<PackageName>.cmake
もしくはFind<PackageName>.cmake
を探す- CMakeキャッシュ
CMAKE_MODULE_PATH
に指定されたパス - CMakeにデフォルトで付いてくるモジュールの置き場(
/usr/local/share/cmake/Modules
など)
- CMakeキャッシュ
- Configモード:以下のパスとそれに適当なサフィックスを付けたパスで
<PackageName>Config.cmake
もしくは<lower-case-package-name>-config.cmake
を探す- CMake変数
<PackageName>_DIR
で指定されたパス - CMakeキャッシュ変数
<PackageName>_ROOT
で指定されたパス - 環境変数
<PackageName>_ROOT
で指定されたパス - CMakeキャッシュ変数
CMAKE_PREFIX_PATH
,CMAKE_FRAMEWORK_PATH
,CMAKE_APPBUNDLE_PATH
で指定されたパス - 環境変数
<PackageName>_DIR
,CMAKE_PREFIX_PATH
,CMAKE_FRAMEWORK_PATH
,CMAKE_APPBUNDLE_PATH
で指定されたパス - 環境変数
PATH
で指定されたパス(bin
,sbin
で終わるパスはその親ディレクトリに読み替える) - (他にもあるが省略)
- CMake変数
なお、コマンド終了時に<PackageName>_FOUND
というCMake変数に0 or 1 (FALSE or TRUE)が格納されるので、発見できたかどうかによって分岐処理を書くこともできます。
「適当なサフィックス」には./cmake/
, ./<name>*/
, ./lib/x64/cmake/<name>*/
などが含まれます。
詳しいことは公式ドキュメントを参照してください。
以上がfind_packageの概要です。 それではModuleとConfigについて解説していきましょう。
7.1. Module
まずはModuleと呼ばれるCMakeスクリプトについてです。
Moduleは以下の2種類に分類されます。
- よく使うCMake関数をまとめたUtility Modules
- 外部ライブラリの情報を収集するFind Modules
CMakeでは、インストール時にデフォルトで多くのモジュールがインストールされています。 モジュールの一覧は公式ドキュメントから確認できます。
また、自分でCMakeスクリプトを書いて自作Moduleを作ることもできます。
Utility Modules
Utility Modulesはよく使うCMake関数をまとめたものです。
例えば、CheckLanguage.cmake
(あるプログラミング言語の開発ツールがインストールされているか確認するための機能をまとめたもの)やBundleUtilities.cmake
(主にMacの*.app
の中身にアクセスするための機能をまとめたもの)などです。
これらのスクリプトを読み込むとcheck_language()
, get_bundle_main_executable()
などの関数が定義されるので、それらを呼び出すことで機能を利用できます。
Find Modules
一方、Find ModulesはPC内にインストールされているライブラリを発見するためのスクリプトです。
ファイル名はFind<PackageName>.cmake
です。
ご存知の通り、C++のライブラリを自分のプログラムにリンクするときには①ヘッダファイルのディレクトリ, ②静的リンクライブラリファイルのディレクトリ, ③静的リンクライブラリファイルの名前, の3つの情報が必要になりますが、これらの情報をConfigureの度に手入力するのは非常に面倒です。 そこで、これらの情報を自動で収集してくれるのがFindモジュールです。
多くの開発環境では、ライブラリをインストールするときにapt, yum, brewなどのパッケージマネージャーや開発元が配布しているインストーラーを使います。 そのため「このOSならこのパスにライブラリがインストールされているだろう」というのが推測できます。 Findモジュールはそのパスを探索して、①, ②の情報とライブラリのバージョン情報などを取得します。 ③はほぼ固定なのでFindモジュールのスクリプト内で決め打ちされていることが多いです。
CMakeではBoost, BLAS, GLEW, OpenGL, X11, Qt4などのライブラリのFindモジュールがデフォルトで用意されています。
大抵のFindモジュールは、以下のようなCMake変数に値をセットします。
<PackageName>_INCLUDE_DIRS
:インクルードディレクトリのパス<PackageName>_LIBRARY_DIRS
:ライブラリファイルがあるディレクトリのパス<PackageName>_LIBRARIES
:ライブラリファイル名のリスト<PackageName>_DEFINITIONS
:コンパイル時のdefinitionフラグ
find_packageコマンドでこれらの変数をセットさせ、target_include_directories(main PUBLIC Boost_INCLUDE_DIRS)
などのように適宜設定を反映させていくわけです。
ただし、変数名は必ずしもこの通りになっているわけではないですし、ライブラリの情報を変数にまとめるのではなくターゲット(ライブラリ)を作成しそのプロパティにまとめるケースもあります。
親切なFindスクリプトは冒頭にコメントで仕様を書いてくれているので、それを確認しながら自分のスクリプトを書いていきます。
自作のModule
CMakeに付属しているModuleの他にも、自分で書いたCMakeスクリプトを自作Moduleとして読み込ませることもできます。
書いたスクリプトを<PackageName>.cmake
やFind<PackageName>.cmake
のファイル名で適当なパスに保存しておき、Configure時にCMake変数CMAKE_MODULE_PATH
にそのパスを指定してやれば、find_package(<PackageName>)
で読み込めるようになります。
多くのOSSではプロジェクト直下にcmake
というフォルダを作って自作Moduleをまとめておき、set(CMAKE_MODULE_PATH './cmake')
のようにしてからfind_packageで読み込む方法が使われています。
7.2. Config
次に、Configと呼ばれるCMakeスクリプトについてです。
Configファイルは、CMakeプロジェクトをビルドしてライブラリを作成したときに一緒に作成できる設定ファイルです。
ファイル名は<PackageName>Config.cmake
もしくは<lower-case-package-name>-config.cmake
です。
ライブラリの情報を提供するという点ではFindモジュールと同じですが、Configファイルはライブラリの開発元が直接提供している設定ファイルであるという違いがあります。
ライブラリをインストールしたとき、cmake
やshare
という名前のディレクトリの中に含まれていることが多いです。
Findモジュールと同じく、大抵のConfigファイルは以下のようなCMake変数に値をセットします。
<PackageName>_INCLUDE_DIRS
:インクルードディレクトリのパス<PackageName>_LIBRARY_DIRS
:ライブラリファイルがあるディレクトリのパス<PackageName>_LIBRARIES
:ライブラリファイル名のリスト<PackageName>_DEFINITIONS
:コンパイル時のdefinitionフラグ
また、これらの情報を1つのターゲットにまとめているケースもあります。 Configファイルを使うときは、開発元が公開しているドキュメントやConfigファイルの中身を読み、Configファイルによってどのような変数やターゲットが作成されるのかを確認しながら、自分のスクリプトを書いていくことになります。
*nix系のConfigファイル事情
*nix系のOSにライブラリをインストールするときは、/usr/local/lib/
の中に静的ライブラリファイル, /usr/local/include/
の中にヘッダファイル, /usr/local/bin/
に実行ファイルを設置するのが標準的です。
パッケージマネージャによっては、ライブラリ自体は別のディレクトリにインストールしておき/usr/local/*/
にシンボリックリンクだけ貼っておくというパターンもあります。
どちらにせよ、/usr/local/*/
にライブラリ関係のファイルが配置されるというわけです。
ライブラリのConfigファイルは/usr/local/share/<PackageName>/
や/usr/local/lib/cmake/<PackageName>/
に配置されます。
大抵の場合/usr/local/bin/
はPATHに追加されているので、CMakeはここに置かれたConfigファイルを発見することができます。
つまり*nix系のOSではパッケージマネージャを通してライブラリをインストールするだけで(ほぼ)find_packageで発見できるようになるということです。
もしライブラリにConfigファイルが付属していなかった場合は、自分でFindモジュールを用意する必要があります。
また、変則的な場所にライブラリをインストールしている場合は、Configure時にキャッシュ変数<PackageName>_DIR
にそのパスをセットすることでCMakeにConfigファイルを発見させることができます。
WindowsのConfigファイル事情
Windowsにライブラリをインストールするときは、C:\Program Files\
やC:\Program Files(x86)\
以下に<ライブラリ名>
もしくは<ベンダー名\ライブラリ名>
のようなフォルダを作ってその中に配置することが多いです。
面倒なことに、*nix系OSの/usr/local
のようにパスが通っている場所にまとめるという文化はないので、個別にパスを通さない限りfind_package()
で発見できるようになりません。
とはいえ、PATH
にどんどんライブラリのパスを追加していくのは環境が汚染されるので遠慮したいところです。
ライブラリをCMakeから利用する前提なら環境変数<PackageName>_DIR
を作成するか、CMAKE_PREFIX_PATH
にConfigファイルへのパスを追加していくのがよいでしょう。
8. CMakeで外部ライブラリを利用する
それでは、find_package()
の挙動を踏まえた上で外部のCMakeライブラリを利用する方法を見ていきます。
ここでは一例として、Googleが開発している数理最適化ライブラリceres-solver
を利用する手順を紹介していきます。
最終目標はceres-solver
のサンプルコードを動かして、非線形の最小二乗問題を解いてみることです。
ceres-solver
は線形代数ライブラリEigen
とglog
に依存しているので、作業手順は以下のようになります。
- Eigenとglogをインストールする
- CMakeを使ってceres-solverをインストールする
- 自分用のプロジェクトを作りceres-solverのサンプルコードを動かす
8.1. Eigenとglogをインストールする
Eigenとglogはかなり有名なOSSですので、色々なインストール方法が用意されています。
*nix系OSの場合
*nix系のOSを使っているなら、パッケージマネージャーを通してインストールすることができるはずです。
例えば、apt
からインストールするにはapt install libeigen3-dev libgoogle-glog-dev
を実行すればいいです。
前述のように、OSパッケージマネージャーを通してインストールされたライブラリは/usr/local
以下に適切にシンボリックリンクが作成されるので、そのままfind_package()
で発見できるようになります。
Windowsの場合
一方、WindowsなどこれというOSパッケージマネージャーに乏しい環境では、CMakeを使ってソースからビルドするのが無難だと思います。
ビルドとインストールの手順はこれまでのCMakeプロジェクトと同じです。
git clone https://github.com/eigenteam/eigen-git-mirror.git cd eigen-git-mirror git checkout 3.3.7 mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX='C:/lib/eigen3.3.7' cmake --build . --target install cd .. git clone https://github.com/google/glog.git cd glog git checkout v0.4.0 mkdir build_dir cd build_dir cmake .. -DCMAKE_INSTALL_PREFIX='C:/lib/glog0.4.0' cmake --build . --target install
デフォルトではC:\Program Files(x86)\
にインストールされますが、インストーラーを使わず手動で入れたことをわかりやすくするため、C:\lib\eigen3.3.7
とC:\lib\glog0.4.0
に変えておきます。
また、glogではbazel用のBUILD
というファイルが存在するのでbuild_dir
というディレクトリの中でConfigureしています。
他にも色々ビルドオプションを設定できるので、必要に応じてccmakeやcmake-guiでキャッシュ変数を編集してからビルドしてください。
Eigenはヘッダオンリーライブラリなのでビルドは行われず、所定の場所にファイルがコピーされるだけですが、これでインストールは完了です。 glogもビルド後に諸々のファイルがコピーされます。
インストール後は、Configファイルのパスを通しておきましょう。
Eigenとglogはどちらも親切にConfigファイルを作成してくれます。
例えば、EigenはC:/lib/eigen3.3.7/share/eigen3/cmake/
の中にEigen3Config.cmake
が用意されているはずです。
これらのファイルをCMakeLists.txtのfind_package()
から発見できるよう設定しておきます。
いくつか方法はありますが、環境変数CMAKE_PREFIX_PATH
を作成し、そこにC:/lib/eigen3.3.7/;C:/lib/glog0.4.0;
を設定しておくのがスタンダードな方法だと思います。
(このあたりの手順が煩雑なのが、Windowsで開発しづらくしてますよね…)
これでEigenとglogをCMakeから利用するための環境が整いました。
8.2. ceres-solverをインストールする
次に、ceres-solver
をインストールします。
まずは、適当なディレクトリにGitHubのリポジトリをクローンします。
ceres-solverは1.9から2.0に移行するに当たってbazelをサポートしたりCMakeスクリプトを改善したりしているようなので、開発中の2.0.0
を使うためにmasterをビルドします。
執筆時のHEADは059bcb7f
です。
git clone https://github.com/ceres-solver/ceres-solver cd ceres-solver mkdir build_dir cd build_dir cmake .. -DCMAKE_INSTALL_PREFIX='install_dir' -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF cmake --build . --target install
先ほども述べたように、CMakeはデフォルトでは/usr/local/
やC:/Program Files(x86)/
にインストールしようとします。
しかし、今回はceres-solverを試してみたいだけなので、本格的にインストールするのは避けたいところです。
そこで、ceres-solver/build_dir/install_dir
に仮インストールするよう設定しています。
また、ceres-solverのサンプルとテストのビルドは必要ないのでOFFにしておきます。
ビルドに数分かかりますが、ceres-solver/build_dir/install_dir
以下にceres-solverのライブラリがインストールされます。
8.3. サンプルプロジェクトを作る
次に、ceres-solverを利用するCMakeプロジェクトを書いてみます。
適当なディレクトリにmain.cpp
を作成し、以下のコードをコピーしましょう。
ceres-solverのサンプルcurve_fitting.cc
を流用しています。
main.cpp
のソースコード
// Ceres Solver - A fast non-linear least squares minimizer // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. // http://code.google.com/p/ceres-solver/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Google Inc. nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: sameeragarwal@google.com (Sameer Agarwal) #include "ceres/ceres.h" using ceres::AutoDiffCostFunction; using ceres::CostFunction; using ceres::Problem; using ceres::Solver; using ceres::Solve; // Data generated using the following octave code. // randn('seed', 23497); // m = 0.3; // c = 0.1; // x=[0:0.075:5]; // y = exp(m * x + c); // noise = randn(size(x)) * 0.2; // y_observed = y + noise; // data = [x', y_observed']; const int kNumObservations = 67; const double data[] = { 0.000000e+00, 1.133898e+00, 7.500000e-02, 1.334902e+00, 1.500000e-01, 1.213546e+00, 2.250000e-01, 1.252016e+00, 3.000000e-01, 1.392265e+00, 3.750000e-01, 1.314458e+00, 4.500000e-01, 1.472541e+00, 5.250000e-01, 1.536218e+00, 6.000000e-01, 1.355679e+00, 6.750000e-01, 1.463566e+00, 7.500000e-01, 1.490201e+00, 8.250000e-01, 1.658699e+00, 9.000000e-01, 1.067574e+00, 9.750000e-01, 1.464629e+00, 1.050000e+00, 1.402653e+00, 1.125000e+00, 1.713141e+00, 1.200000e+00, 1.527021e+00, 1.275000e+00, 1.702632e+00, 1.350000e+00, 1.423899e+00, 1.425000e+00, 1.543078e+00, 1.500000e+00, 1.664015e+00, 1.575000e+00, 1.732484e+00, 1.650000e+00, 1.543296e+00, 1.725000e+00, 1.959523e+00, 1.800000e+00, 1.685132e+00, 1.875000e+00, 1.951791e+00, 1.950000e+00, 2.095346e+00, 2.025000e+00, 2.361460e+00, 2.100000e+00, 2.169119e+00, 2.175000e+00, 2.061745e+00, 2.250000e+00, 2.178641e+00, 2.325000e+00, 2.104346e+00, 2.400000e+00, 2.584470e+00, 2.475000e+00, 1.914158e+00, 2.550000e+00, 2.368375e+00, 2.625000e+00, 2.686125e+00, 2.700000e+00, 2.712395e+00, 2.775000e+00, 2.499511e+00, 2.850000e+00, 2.558897e+00, 2.925000e+00, 2.309154e+00, 3.000000e+00, 2.869503e+00, 3.075000e+00, 3.116645e+00, 3.150000e+00, 3.094907e+00, 3.225000e+00, 2.471759e+00, 3.300000e+00, 3.017131e+00, 3.375000e+00, 3.232381e+00, 3.450000e+00, 2.944596e+00, 3.525000e+00, 3.385343e+00, 3.600000e+00, 3.199826e+00, 3.675000e+00, 3.423039e+00, 3.750000e+00, 3.621552e+00, 3.825000e+00, 3.559255e+00, 3.900000e+00, 3.530713e+00, 3.975000e+00, 3.561766e+00, 4.050000e+00, 3.544574e+00, 4.125000e+00, 3.867945e+00, 4.200000e+00, 4.049776e+00, 4.275000e+00, 3.885601e+00, 4.350000e+00, 4.110505e+00, 4.425000e+00, 4.345320e+00, 4.500000e+00, 4.161241e+00, 4.575000e+00, 4.363407e+00, 4.650000e+00, 4.161576e+00, 4.725000e+00, 4.619728e+00, 4.800000e+00, 4.737410e+00, 4.875000e+00, 4.727863e+00, 4.950000e+00, 4.669206e+00, }; struct ExponentialResidual { ExponentialResidual(double x, double y) : x_(x), y_(y) {} template <typename T> bool operator()(const T* const m, const T* const c, T* residual) const { residual[0] = T(y_) - exp(m[0] * T(x_) + c[0]); return true; } private: const double x_; const double y_; }; int main(int argc, char** argv) { double m = 0.0; double c = 0.0; Problem problem; for (int i = 0; i < kNumObservations; ++i) { problem.AddResidualBlock( new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>( new ExponentialResidual(data[2 * i], data[2 * i + 1])), NULL, &m, &c); } Solver::Options options; options.max_num_iterations = 25; options.linear_solver_type = ceres::DENSE_QR; options.minimizer_progress_to_stdout = true; Solver::Summary summary; Solve(options, &problem, &summary); std::cout << summary.BriefReport() << "\n"; std::cout << "Initial m: " << 0.0 << " c: " << 0.0 << "\n"; std::cout << "Final m: " << m << " c: " << c << "\n"; return 0; }
次にCMakeLists.txt
を書いていきます。
ライブラリのリンクの基本
通常、C++のライブラリはヘッダファイル, 静的リンクライブラリファイル(, 動的リンクライブラリファイル)の組み合わせで配布されます。 これを自分の実行ファイルにリンクするときには①ヘッダファイルのディレクトリ, ②静的リンクライブラリファイルのディレクトリ, ③静的リンクライブラリファイルの名前, の3つが必要になります。
CMakeでこの3つを行うときの基本構文は以下のようになっています。
# ライブラリディレクトリの指定 link_directories("xxxx/lib") add_executable(main_app main.cpp) # インクルードディレクトリの指定 target_include_directories(main_app PRIVATE "xxxx/include") # ライブラリファイル名の指定 target_link_libraries(main_app xxxx)
ライブラリディレクトリの指定にはlink_directories()
コマンドを使います。
この設定はこのプロジェクト全体で共有されます。
インクルードディレクトリの指定にはtarget_include_directories()
コマンドを使います。
ライブラリファイル名の指定にはtarget_link_libraries()
コマンドを使います。
以前の記事で登場したときは自分で作成したライブラリをリンクするために利用していましたが、このように外部ライブラリのファイル名を指定するためにも利用されます。
この構文が外部ライブラリを利用するときの基本となります。
CeresConfig.cmakeを利用したリンク
しかし今回のケースでは、main.cpp
をビルドするときにceres-solver
のヘッダファイル群を参照させ、ライブラリファイルlibceres.a
をリンクする必要があり、またEigenやglogといった間接的に参照しているライブラリもリンクする必要があります。
これはかなり面倒な作業です。
そこで、この辺りの面倒な作業は全てCeresConfig.cmake
に任せます。
CeresConfig.cmakeは必要な設定を詰め込んだターゲットceres
を作成してくれるので、我々はこれをmain_app
にリンクするだけで必要なセッティングが完了します。
ターゲットceresに対しては、以前の記事で紹介したのと同じように、target_include_directories()
などのコマンドを駆使して依存ターゲットへのインクルードやリンクの設定が行われているので、我々はtarget_link_libraries(main_app ceres)
を呼び出すだけで必要な設定が行われるというわけです。
ちなみに、この辺のConfigファイルの仕様はライブラリ開発者次第なので、ドキュメント要確認です。 大抵の場合、Configファイルの先頭にコメントで使い方を書いてくれています。
cmake_minimum_required(VERSION 3.1) find_package(Ceres REQUIRED) add_executable(main_app main.cpp) target_link_libraries(main_app ceres) if(MSVC AND ${MSVC_VERSION} GREATER_EQUAL 1915) target_compile_definitions(main_app PRIVATE _ENABLE_EXTENDED_ALIGNED_STORAGE) endif()
最後の部分は、MSVCでのみ必要なオプションの設定です。
CeresではC++のalignof()
を利用していますが、過去のMSVCではこの挙動にバグがありバグ修正の前後でMSVCの挙動が変わっています。
この挙動の変化を把握していることを明示するため_ENABLE_EXTENDED_ALIGNED_STORAGE
を指定しています。
このCMakeプロジェクトをビルドします。 今回はceres-solverを変則的な場所にインストールしているので、以下のようにCeresConfig.cmakeの場所を教えてやります。 ここではフルパスを指定する必要があります。
mkdir build cd build cmake .. -DCeres_DIR='<path to directory of CeresConfig.cmake>' cmake --build .
作成されたmain_app
を実行してみます。
main_appは区間 において
にノイズを混ぜたサンプル点に対してカーブフィッティングを行うことで
の値を復元するプログラムです。
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time 0 1.211734e+02 0.00e+00 3.61e+02 0.00e+00 0.00e+00 1.00e+04 0 4.41e-04 5.31e-04 1 2.334822e+03 -2.21e+03 0.00e+00 7.52e-01 -1.87e+01 5.00e+03 1 1.50e-04 7.78e-04 2 2.331438e+03 -2.21e+03 0.00e+00 7.51e-01 -1.86e+01 1.25e+03 1 9.20e-05 9.23e-04 3 2.311313e+03 -2.19e+03 0.00e+00 7.48e-01 -1.85e+01 1.56e+02 1 8.68e-05 1.07e-03 4 2.137268e+03 -2.02e+03 0.00e+00 7.22e-01 -1.70e+01 9.77e+00 1 1.12e-04 1.44e-03 5 8.553131e+02 -7.34e+02 0.00e+00 5.78e-01 -6.32e+00 3.05e-01 1 1.27e-04 1.69e-03 6 3.306595e+01 8.81e+01 4.10e+02 3.18e-01 1.37e+00 9.16e-01 1 7.88e-04 2.62e-03 7 6.426770e+00 2.66e+01 1.81e+02 1.29e-01 1.10e+00 2.75e+00 1 6.55e-04 3.33e-03 8 3.344546e+00 3.08e+00 5.51e+01 3.05e-02 1.03e+00 8.24e+00 1 6.38e-04 4.00e-03 9 1.987485e+00 1.36e+00 2.33e+01 8.87e-02 9.94e-01 2.47e+01 1 7.29e-04 4.79e-03 10 1.211585e+00 7.76e-01 8.22e+00 1.05e-01 9.89e-01 7.42e+01 1 6.92e-04 5.51e-03 11 1.063265e+00 1.48e-01 1.44e+00 6.06e-02 9.97e-01 2.22e+02 1 7.33e-04 6.31e-03 12 1.056795e+00 6.47e-03 1.18e-01 1.47e-02 1.00e+00 6.67e+02 1 8.05e-04 7.17e-03 13 1.056751e+00 4.39e-05 3.79e-03 1.28e-03 1.00e+00 2.00e+03 1 9.01e-04 8.13e-03 Ceres Solver Report: Iterations: 14, Initial cost: 1.211734e+02, Final cost: 1.056751e+00, Termination: CONVERGENCE Initial m: 0 c: 0 Final m: 0.291861 c: 0.131439
に近い値が復元されていますね。
以上、CMakeで外部ライブラリを利用するために必要な知識を解説しました。
とりあえず、この記事でCMake連載は完結のつもりです。 まだ紹介していないトピックとして、プロジェクトのインストール設定, Configファイルの作成, CMake関数の挙動などが残っていますが、このあたりの書き方はケースバイケースなのでうまくまとめるのが難しいかな、と感じています。
紹介したサンプルが動かない、間違いを見つけた等ありましたらお知らせいただけると幸いです。
もしよければ↓の☆を1クリックお願いします!