cmake基礎

#この記事について
c++の実行方法の備忘録。
具体的には、
・ディレクトリの構成方法
・必要なファイルとその中身の書き方
・使うコマンド
についてまとめる。


#そもそもなんでCMake?
[ここ](https://teratail.com/questions/111977)を参照。
多くのソースでビルドするプロジェクトがあるとする。そのようなプロジェクトにはコンパイルに時間がかかる。

[※ビルドとコンパイルの違い](https://qiita.com/rico/items/9ab8aa110e757a13ef37)
エラーや書き換えによってコードを変更し、改めてビルドする時、変更してないところは以前のビルド結果を使えば良い。
こういうビルド対象を設定するのがMakefile。このMakefileを手動で作るのは大変で、代わりに作ってくれるのがCMake。CMakeでMakefileができたら、make→実行。


# 記事構成
簡単なものから、少しだけ踏み込んだところまで順を追っていくために3つの簡単なプロジェクトを用いる。


#実行環境
MacBookPro(2017) Mojave


#プロジェクト1

###（プロジェクト1）ディレクトリ構成


```terminal
.
├── project1
    ├── CMakeLists.txt
    ├── build
    └── main.cpp
```


###（プロジェクト1）内容
プロジェクト1では、main.cppだけで、”Hello World”を出力する。



###（プロジェクト1）コード中身

```main.cpp
#include <iostream>

int main() {
    std::cout << "Hello World” << std::endl;
    return 0;
}
```

C言語と同じ、まずincludeして、mainを定義してその中に処理を書いている。

※ c++の標準入出力に関する記事
http://wisdom.sakura.ne.jp/programming/cpp/cpp2.html
まとめると、
・c++では、# include\<stdio.h>ではなく、# include\<iostream>を使う
※ サイトによっては\<iostream.h>としているところがあるが、.hをつけるのは昔。今は動くけど、.hはつけないのが主流。
・<<を出力演算子、>>を入力演算子という。
※ これらはシフト演算子としても用いられる。1ビット左シフトとか2ビット右シフトとか。
・iostreamでは、cout（シーアウトと読む？）というオブジェクトがあらかじめ定義されている。
・endlは改行を表す。


```CMakeLists.txt
# CMakeのバージョンを設定。とあるが、どうやって必要最低バージョンを調べるのかはわからない。
cmake_minimum_required(VERSION 2.8)
# プロジェクト名と使用する言語を設定。test_cmakeのところは何でもいい。project1とかとの対応も多分ない。CXXはc++を表す。
project(test_cmake CXX)
# a.outという実行ファイルをmain.cppから作成
add_executable(a.out main.cpp)
```

コメント通り。ファイル内にコメントを入れたいときは多分「#」(シャープ)を使う。一回「//」でコメントアウトしようとしていてcmake通らなくてイラっとした。

###（プロジェクト1）コマンド

```terminal
$ cd {PATH_TO_BUILD} #build内へ移動
$ cmake ..
$ make
$ ./a.out
Hello World
```



#プロジェクト2
###（プロジェクト2） ディレクトリ構成

```terminal
.
├── CMakeLists.txt
├── build
├── hello.cpp
├── hello.hpp
└── main.cpp
```


###（プロジェクト2） 内容
プロジェクト2では、main.cpp内で、別のプログラムで定義した関数を呼び出し、”Hello World”を出力する。



###（プロジェクト2）コード中身
```main.cpp
#include "hello.hpp"

int main() {
    hello();
}
```



```hello.hpp
#ifndef HELLO_H
#define HELLO_H

void hello();

#endif
```


```hello.cpp
#include <iostream>
#include "hello.hpp"

void hello() {
    std::cout << "Hello World” << std::endl;
}
```


```CMakeLists.txt
# CMakeのバージョンを設定
cmake_minimum_required(VERSION 2.8)
# プロジェクト名と使用する言語を設定
project(test_cmake CXX)
# a.outという実行ファイルをmain.cppとhello.cppから作成
add_executable(a.out main.cpp hello.cpp)
```

コメント通り。ファイル内にコメントを入れたいときは多分「#」(シャープ)を使う。一回「//」でコメントアウトしようとしていてcmake通らなくてイラっとした。

###（プロジェクト2）コマンド

```terminal
$ cd {PATH_TO_BUILD} #build内へ移動
$ cmake ..
$ make
$ ./a.out
Hello World
```


#プロジェクト3

###（プロジェクト3） ディレクトリ構成

```terminal
.
├── CMakeLists.txt
├── build
├── image_show.cpp
├── image_show.hpp
└── main.cpp
```


###（プロジェクト3） 内容
プロジェクト3では、main.cpp内で、別のプログラム(image_show.cpp)で定義した関数(image_show)を呼び出し、画像(test.png)を出力する。



###（プロジェクト3）コード中身
```main.cpp
# include <opencv2/opencv.hpp>
# include "image_show.hpp"

int main() {
	image_show();
	return 0;
}
```



```image_show.hpp
# ifndef IMAGE_SHOW_H
# define IMAGE_SHOW_H

int image_show();

# endif
```
もちろん、画像を出力するだけなので、intで返す必要はない。今回コードには書いていないが、0や-1を返すことで、正常動作してプログラムが終了したのか確認できる。(正常動作とはエラーが出ずに実行できたけど、画像が読み取れなくて表示できなくてプログラムが終了しちゃったとか。)

その場合は、int image_show()ではなく、void image_show()をすれば良い。その場合、image_show.hppとimage_show.cppの両方を変更すること。


```image_show.cpp
# include "image_show.hpp"
#include <opencv2/opencv.hpp>

int image_show() {
	cv::Mat img = cv::imread("../sample/test.png", -1);

	if(img.empty()) {
		return -1;
	}

	cv::namedWindow("ex", cv::WINDOW_AUTOSIZE);
	cv::imshow("ex", img);
	cv::waitKey(0);
	cv::destroyWindow("ex");
	return 0;
}
```
opencvを使うのに、上記のようにincludeする必要があります。
・Matについて(https://cvtech.cc/opencv/)
「Mat img」とすることでimgという名前のMat型変数を宣言できる。

・::について(https://teratail.com/questions/55461)
C++での::（コロン2つ）は、名前空間(namespace)の区切りを表します。std::coutは「std名前空間のcoutという変数」といった解釈になります。

・その他imreadとか、関数？ぽいのを使うときにもcv.imread()とかじゃなくてcv::imread()みたいに使っている。この辺は覚えるところというか慣れというか。


```txt:CMakeLists.txt
# ご指摘があったので変更しました。
# 以下、変更前
#cmake_minimum_required(VERSION 3.1)
#project(test_opencv CXX)
#set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
#find_package(OpenCV REQUIRED)  
#include_directories( ${OpenCV_INCLUDE_DIRS} )
#add_executable(a.out main.cpp image_show.cpp)
#target_link_libraries(a.out ${OpenCV_LIBS})

# 以下、変更後
cmake_minimum_required(VERSION 3.8)
project(test_opencv CXX)
find_package(OpenCV REQUIRED)  
add_executable(a.out main.cpp image_show.cpp)
target_compile_features(a.out PUBLIC cxx_std_11)
target_compile_options(a.out PUBLIC -Wall)
target_include_directories(a.out PUBLIC ${OpenCV_INCLUDE_DIRS})
target_link_libraries(a.out PUBLIC ${OpenCV_LIBS})
```
今回はopencvというライブラリを使った。まだopencvしか試してなく、どのライブラリに対しても言えるのかわからないが、opencvを探してリンクするみたいなこともCMakeLists.txtに記述する必要がある。


###（プロジェクト3）コマンド

```terminal
$ cd {PATH_TO_BUILD} #build内へ移動
$ cmake ..
$ make
$ ./a.out
# 画像が別ウインドウで開く
```

# 最後に
ご指摘、コメント等あればお願いします。
読んでくれてありがとうございました！