[日記] Strategy patternをC++のtemplateで書きたい

# 背景
* strategy patternというと通常interfaceを作ってその実装で具体的な振る舞いを定義することになる
* C++でやろうとすると抽象クラスを作ることになると思うが、リソースが限られている環境だとvtableのオーバーヘッドが気になる
* そのためtemplateを使ってstrategyっぽいことを実現したい

# 実装例
* 例としてHttpのリクエストを受け付けるクラスを考える
* StrategyクラスとしてHttpMethodクラスを用意する
    * HttpMethodクラスはHttpGetRequestまたはHttpPostRequestで初期化される
* ConcreateStrategyクラスはなく、初期化時に使われた型によってふるまいを変える

```cpp
#include <string>
#include <iostream>

struct HttpGetRequest
{
    std::string header;
};

struct HttpGetResponse
{
    std::string header;
    std::string body;
};

struct HttpPostRequest
{
    std::string header;
    std::string body;
};

struct HttpPostResponse
{
    std::string header;
    std::string body;
};


// Strategyにあたるクラス
// 個々のメソッドに対応するクラスは作らず、メンバ関数をtemplate化することで振る舞いを変える
template<class T, class U>
class HttpMethod
{
    public:
        HttpMethod(const T &request);
        bool request(U &response);

    private:
        const T mRequest;
        bool run(U &response);
};

template<class T, class U>
HttpMethod<T, U>::HttpMethod(const T &request) : mRequest(request)
{
}

// ConcreateStrategyは作らず、template特殊化で呼び出される関数を変える
template<>
bool HttpMethod<HttpGetRequest,HttpGetResponse>::run(HttpGetResponse &response)
{
    std::cout << "GET request header: " << mRequest.header << std::endl;
    response.header="Test: GET";
    response.body="GET body";
    return true;
}

template<>
bool HttpMethod<HttpPostRequest,HttpPostResponse>::run(HttpPostResponse &response)
{
    std::cout << "POST request header: " << mRequest.header << std::endl;
    std::cout << "POST request body: " << mRequest.body << std::endl;
    response.header="Test: POST";
    response.body="POST body";
    return true;
}

//Contextから実行されるかんｓ
template<class T, class U>
bool HttpMethod<T, U>::request(U &response)
{
   bool res = this->run(response);
   return res;
}

```

```cpp
int main()
{
    //通常のstrategy patternだと各メソッドに対応するクラスをインスタンス化することで
    //振る舞いを変えるがtemplateを使う場合はTemplateの型によって振る舞いを変える

    HttpGetRequest getReq{"Test: GET"};
    HttpGetResponse getResp{"",""};

    HttpMethod<HttpGetRequest,HttpGetResponse> httpGet = HttpMethod<HttpGetRequest,HttpGetResponse>(getReq);
    httpGet.request(getResp);
    std::cout << "GET response header: " << getResp.header << std::endl;
    std::cout << "GET response response: " << getResp.body << std::endl;

    HttpPostRequest postReq{"Test: POST","post it"};
    HttpPostResponse postResp{"",""};
    HttpMethod<HttpPostRequest,HttpPostResponse> httpPost = HttpMethod<HttpPostRequest,HttpPostResponse>(postReq);
    httpPost.request(postResp);
    std::cout << "POST response header: " << postResp.header << std::endl;
    std::cout << "POST response body: " << postResp.body << std::endl;
}
```

### 実行結果
```
$ g++ -std=c++11 TemplateStrategy.cpp -o template
$ ./template
GET request header: Test: GET
GET response header: Test: GET
GET response response: GET body
POST request header: Test: POST
POST request body: post it
POST response header: Test: POST
POST response body: POST body
```

比較用に抽象クラスを用いたテストコードを作る
```cpp
// Request/Response用の構造体は同じ

// Interfaceにあたるクラス
class HttpMethod
{
    public:
        virtual ~HttpMethod() = default;
        virtual bool request(void* request, void* response) = 0;
};

class HttpGetMethod : public HttpMethod
{
    public:
        bool request(void* request, void* response) override
        {
            HttpGetRequest* getRequest = static_cast<HttpGetRequest*>(request);
            HttpGetResponse* getResponse = static_cast<HttpGetResponse*>(response);

            std::cout << "GET request header: " << getRequest->header << std::endl;
            getResponse->header="Test: GET";
            getResponse->body="GET body";
            return true;
        }
};

class HttpPostMethod : public HttpMethod
{
    public:
        bool request(void* request, void* response) override
        {
            HttpPostRequest* postRequest = static_cast<HttpPostRequest*>(request);
            HttpPostResponse* postResponse = static_cast<HttpPostResponse*>(response);

            std::cout << "POST request header: " << postRequest->header << std::endl;
            std::cout << "POST request body: " << postRequest->body << std::endl;
            postResponse->header="Test: POST";
            postResponse->body="POST body";
            return true;
        }
};
```

```cpp
main()
{
    HttpGetRequest getReq{"Test: GET"};
    HttpGetResponse getResp{"",""};
    HttpMethod* httpGet = new HttpGetMethod();
    httpGet->request(&getReq, &getResp);
    std::cout << "GET response header: " << getResp.header << std::endl;
    std::cout << "GET response response: " << getResp.body << std::endl;
    
    HttpPostRequest postReq{"Test: POST","post it"};
    HttpPostResponse postResp{"",""};
    HttpMethod* httpPost = new HttpPostMethod();
    httpGet->request(&postReq, &postResp);
    std::cout << "POST response header: " << postResp.header << std::endl;
    std::cout << "POST response body: " << postResp.body << std::endl;
}
```
### 実行結果
```
$ g++ -std=c++11 NormalStrategy.cpp -o noraml
$ ./noraml
GET request header: Test: GET
GET response header: Test: GET
GET response response: GET body
POST request header: Test: POST
POST request body: post it
POST response header: Test: POST
POST response body: POST body
```

# 評価
## メモリの比較
```
$ size template
   text    data     bss     dec     hex filename
   6250     712     280    7242    1c4a template
```
ROM: 6250+712=6962
RAM: 712+280=992
```
$ size normal
   text    data     bss     dec     hex filename
   6819     728     464    8011    1f4b normal
```
ROM: 6819+728=7547
RAM: 728+464=1192

-> Templateを使った方がROMでは585バイト、RAMでは200バイト節約できた

## 処理時間の比較
シェルから100回実行してみる
```
$ time for i in {1..100}; do ./template > /dev/null 2>&1; done

real    0m0.752s
user    0m0.172s
sys     0m0.422s
```
```
$ time for i in {1..100}; do ./normal > /dev/null 2>&1; done

real    0m0.754s
user    0m0.172s
sys     0m0.297s
```

-> 実時間はほぼ変わらず、システム時間はむしろTemplateを使った場合のほうが長かった

# まとめ
* strategy patternをC++で実装する際に、サブクラスではなくtemplateを使った場合の例を示した
* メモリを調べたところ、一応Templateを使った時のほうが節約はできていそう
* 処理時間はほぼ変わらず
* ただ正直なところ実装次第でどうにでもなりそうな気もする