【初心者向け】CMakeLists.txtを使ってincludeのpathを省略する。

# はじめに
この記事は[前回の記事](https://qiita.com/hi_to_san/items/490f8320900617db9230)の続きになります。
前回の内容はCMakeLists.txtを使ったビルドでした。
今回はソースコードをきれいにまとめる術を書いていきたいと思います。

突然ですが、自作したヘッダーファイルをソースコードに読み込ませる時に`#include`を使うと思います。
`#include`を読み込む時にソースコードとヘッダーファイルの階層が違うと、ディレクトリ名を含めなければいけません。
(含めないと、どのファイルを読み込むのか、ソースコードはわかりません)

しかし、ディレクトリ名をソースコードに書くのは、少しかっちょ悪い。
なので、CMakeLists.txtに書いてしまいましょう。
というのが今回の記事になります。

# 参考サイト
* [かみのメモ](https://kamino.hatenablog.com/entry/cmake_tutorial1)
    * 1番読んだサイト
* [CMakeの使い方](https://qiita.com/shohirose/items/45fb49c6b429e8b204ac)
* [実践C++ 応用講座CMake編](https://theolizer.com/cpp-school3/cpp-school3-1/)

# 目次
* [まずはビルド](#まずはビルド)
* [記述内容の説明](#記述内容の説明)
* [余談](#余談)

## まずはビルド
### ファイルの準備
今回のディレクトリ構成は以下になります。
```shell-session
.
├── CMakeLists.txt
├── sampleApp.cpp
└── util/
    └── file.h
```
sampleApp.cppの他にfile.hを用意しました。
file.hはutilフォルダに置いておきます。


今回のCMakeLists.txtの内容はこちらです。
```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(sampleApp CXX)
add_executable(sampleApp sampleApp.cpp)
target_include_directories(sampleApp PRIVATE ./util)
```
前回と比べて、target_include_directories()が加わりました。

sampleApp.cppとfile.hは以下になります。
```c++:sampleApp.cpp
#include <iostream>
#include "file.h"

int main(void)
{
    Write writer;
    Read reader;
    std::string file_path = "./sample_text.txt";

    writer.AddFile("Hello,World");
    writer.AddFile("Hello,World!");
    writer.AddFile("Hello,World!!");
    writer.WriteFile(file_path);

    reader.ReadFile(file_path);
    std::cout << reader.GetLine(0) << std::endl;
    std::cout << reader.GetLine(2) << std::endl;
    std::cout << reader.GetLine(1) << std::endl;
}
```

```c++:file.h
#pragma once

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

class Read {
public:
    int ReadFile(std::string file_path) {
        std::ifstream ifs(file_path);
        std::string str;

        if (ifs.fail()) {
            std::cerr << "Failed to open file." << std::endl;
            return -1;
        }
        while (getline(ifs, str)) {
            file_data.push_back(str);
        }
        return 0;
    }

    int LineSize() { return file_data.size(); }
    std::string GetLine(int line) { return file_data[line]; }
private:
    std::vector<std::string> file_data;
};

class Write {
public:
    void WriteFile(std::string file_path) {
        std::ofstream ofs(file_path);
        for (int i = 0; i < file_data.size(); i++) {
            ofs << file_data[i] << std::endl;
        }
    }
    void AddFile(std::string data) { return file_data.push_back(data); }
private:
    std::vector<std::string> file_data;
};
```

### ビルド
前回と同様です。
```shell-session
mkdir build
cd build
cmake ..
cmake --build .
```
これにて、buildフォルダにsampleAppが作られます。

### ファイル実行
```shell-session
$ ./sampleApp 
Hello,World
Hello,World!!
Hello,World!
```
```shell-session
$ ls
CMakeCache.txt  CMakeFiles  Makefile  cmake_install.cmake  sampleApp  sample_text.txt
```
coutの内容が出力され、sample_text.txtが生成されます。

## 記述内容の説明
CMakeLists.txtに記述した内容について説明していきます。
```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(sampleApp CXX)
add_executable(sampleApp sampleApp.cpp)
target_include_directories(sampleApp PRIVATE ./util)
```
* 今回、追加したのは`target_include_directories(sampleApp PRIVATE ./util)`です。
    * target_include_directories
        * 実行ファイル名、PRIVATE、リンクするフォルダを指定。
        * PRIVATE以外にPUBLIC、INTERFACEが指定できますが、今はPRIVATEだけで大丈夫です。
* target_include_directoriesを追加したことによる効果は以下になります。
    * 本来、`#include`は以下のようになる。
        ```c++:sampleApp.cpp
        #include <iostream>
        #include "util/file.h"
        ```
    * それをフォルダ名を無くすことができます。
        ```c++:sampleApp.cpp
        #include <iostream>
        #include "file.h"
        ```

## 余談
ここからは余談です。
流してしまっても構いません。

**『`#include "util/file.h"`を`#include "file.h"`にしなければいけない理由とは何か』**
かっちょ悪いから・・・、だけなわけがありません。
理由はメンテナンスの容易さです。

ヘッダーファイルが増えていき、新しくフォルダを追加するとします。
そうすると、ソースコードのフォルダパスも修正しなければいけません。

ソースコードが1ファイルのみなら修正は容易です。
しかし、数ファイルあった場合は少し手間ですよね。

その手間を省くために、この技術が役に立ちます。

**『今はPRIVATEだけで大丈夫です。->他が気になる。』**
簡単に説明します。
target_include_directoriesでは、実行ファイルの後に`PUBLIC`、`PRIVATE`、`INTERFACE`の3つが選べます。
それぞれの説明は以下になります。
* PUBLIC: コマンドの内容が"自分自身"と"自分に依存するターゲット"に反映される
* PRIVATE: コマンドの内容が"自分自身"にのみ反映される
* INTERFACE: コマンドの内容が"自分に依存するターゲット"にのみ反映される

今回、file.hは自分自身のみ使うので、`PRIVATE`を指定しました。
他の実行ファイルから呼ばれることになれば、`PUBLIC`を指定したかもしれません。

**『file.hの実装長くね？』**
すみません。
自分が将来使いそうな実装を残しちゃいました。
C言語出身だったので、fstreamは偉大だと思い、書きました。笑

# さいごに
以上で終了です。
ご参考になれば幸いです。

# 関連記事
* [【初心者向け】CMakeLists.txtを使ってビルドする。](https://qiita.com/hi_to_san/items/490f8320900617db9230)
* 【初心者向け】CMakeLists.txtを使ってincludeのpathを省略する。
* [【初心者向け】CMakeLists.txtを使ってlibraryをリンクする。](https://qiita.com/hi_to_san/items/2b44dd44d3e152594c53)