mapやjsonを引数で渡すのをやめた方がいい(c++)


##はじめに

学生の頃からプログラムを書いていますが、
当時は自分はわからなかったことが、今になってわかったことがありました。

それは、関数の引数と返り値をしっかりと設計することです。

それについて書いてみようと思います。

タイトルにはmapと書いていますが、mapも引数や返り値にするのを控えたいです。


##jsonを引数、または返り値にしてしまった時の代償

API叩いたり、システムの内部パラメーターとして保存するとき、
よく使うフォーマットがjsonです。

json読み込みの関数を書いてみます。
boostのptreeを使います。

```json
{
  id:"1",
  data:"I am kikochan"
}
```

###　よくない方法

```c++

void read_json_file(std::string jsonFile, boost::property_tree::ptree& jsonPtree) {
    boost::property_tree::read_json(jsonFile, jsonPtree);
  }

void main(){
//呼び出し
   boost::property_tree::ptree pt;
   read_json_file("test.json",&pt);
   std::string id = pt.get_optional<std::string>("id");
   std::string data = pt.get_optional<std::string>("data");


//また別の処理時に使いたいので呼び出し
//この例では同じ関数内で呼び出しているけど、
//別のファイル、別の処理で呼び出していると仮定してください。
//結果、jsonのキーはなんだっけとか、同じ処理を書いてしまうことが増えてしまいます。
   boost::property_tree::ptree pt;
   read_json_file("test.json",&pt);
   std::string id = pt.get_optional<std::string>("id");
   std::string data = pt.get_optional<std::string>("data");
}

```

この実装はその時はいいかもしれませんが、
後々、ptreeで値渡しを行なっていると、パッとみた時に、
型がわからなくなるのと、取得する際に都度jsonのキーを入力する必要が出ます。
これは良くないです。

###　まだマシな方法
```c++

void read_json_file(std::string jsonFile, boost::property_tree::ptree& jsonPtree) {
    boost::property_tree::read_json(jsonFile, jsonPtree);
  }

//サブルーチンにして、あちこちから呼び出す。
void getParameter(std::string& id, std::string& data){
   boost::property_tree::ptree pt;
   read_json_file("test.json",&pt);
   id = pt.get_optional<std::string>("id");
   data = pt.get_optional<std::string>("data");
}

void main(){
   std::string id;
   std::string data;
   //jsonのキーの心配はなく、IDEのコード補完も効くから上の例より良いです。
   getParameter(&id,&data);
}

```
main関数でptreeを取得しパースするのではなく、
一度、間に関数を挟みます。
その関数にptreeの取得とパースを一任する方法です。

こうすると、idとdataは型が決まっているので、IDEのコード補完が効きますし、
都度、jsonから情報を読み込むのにパースの処理を書く必要がありません。




###　一番いい方法
```c++

//jsonをクラスとして定義
//コンストラクタ内では、自動的にjsonを読み込みます。
//今回のクラスはjsonの読み込みのみになります。
//jsonのwriteを行いたい場合は、setterを定義し実装してください。
class TestJsonObject{
  std::string id_;
  std::string data_;
  void read_json_file(std::string jsonFile, boost::property_tree::ptree& jsonPtree) {
    boost::property_tree::read_json(jsonFile, jsonPtree);
  }

public:
  TestJsonObject(){
    boost::property_tree::ptree pt;
    read_json_file("test.json",pt);
    id_ = *pt.get_optional<std::string>("id");
    data_ = *pt.get_optional<std::string>("data");
  }
  std::string id(){return id_;}//getter
  std::string data(){return data_;}//getter
};

void main(){
  TestJsonObject json;
  std::string id = json.id();
  std::string data = json.data();
}

```
jsonはデータですので、それを包括するデータクラスたるものを作成することをお勧めします。
そのクラスには、自動的にjsonのデコード、または外部からjson stringを入力した時に、
メンバ変数に格納をする仕組みを実装します。

jsonのパラメータが変更された場合は、そのデータクラスを修正すれば大丈夫ですし、
IDEでももちろんコード補完は行われます。

c++にはデコード、エンコードライブラリはないので手実装しましたが、
SwiftではCodableがありますので、そのようなライブラリがあればそちらを使うことをお勧めします。


### 最後に
プログラムの実装はなるべく分離度を高くすることが、
効率的な開発に繋がります。

今回は、jsonにフォーカスしての分離の高めかたになります。
少しでもラップしてあげることで、わかりやすくなります。


### リストの場合

```json
{
  "id" : "0"
  "lists": [
    {
      "id":"1",
      "data":"I am kikochan"
    },{
      "id":"2",
      "data":"I am kikochan 2"
    }
  ]
}
```


```c++

class TestJsonObject{
public:
  //コンストラクタをprivateにすることで外部からオブジェクトの生成は禁止する。
  //friendでTestJsonObjectはprivateのアクセスを許可する。
  //なので、TestJsonObjectはTestJsonObjectIdDataを作れる
  class TestJsonObjectIdData{
    friend TestJsonObject;
    std::string id_;
    std::string data_;
    TestJsonObjectIdData() = default;
  public:
    std::string id(){return id_;}
    std::string data(){return data_;}
  };
private:
  std::string id_;
  std::vector<TestJsonObjectIdData> lists_;
  void read_json_file(std::string jsonFile, boost::property_tree::ptree& jsonPtree) {
    boost::property_tree::read_json(jsonFile, jsonPtree);
  }

public:
  TestJsonObject(){
    boost::property_tree::ptree pt;
    read_json_file("test.json",pt);
    id_ = *pt.get_optional<std::string>("id");

    for (ptree::value_type& list : pt.get_child("lists")) {
      TestJsonObjectIdData testJsonObjectIdData;
      auto info = list.second;
      testJsonObjectIdData.id_ = *info.get_optional<std::string>("id");
      testJsonObjectIdData.data_ = *info.get_optional<std::string>("data");
      lists_.push_back(testJsonObjectIdData);
    }
  }
  std::string id(){return id_;}
  std::vector<TestJsonObjectIdData> lists(){return lists_;}
};


int main(){
    TestJsonObject testJsonObject;
    auto k = testJsonObject.lists();
    std::cout << testJsonObject.id() << std::endl;
    for(auto &&s : k){
      std::cout << s.id() << std::endl;
      std::cout << s.data() << std::endl;
    }
}
```