ThriftをC言語で使ってみる

#この記事について
C言語でThriftを使いたいと思ったのですが、結構面倒だったので備忘録＆情報共有のため投稿します。
#なぜThrift?
C++だけでなくC言語にも対応しているからです。
最近がgRPC(Protocol Buffers)の方が勢いがある気もしますが、今回はThriftを使ってみます。

#前提
dockerとThriftの知識が少し必要です。
dockerに関しては、dockerイメージの部分を純粋なCentOSと読み替えれば大丈夫です。
この記事で使用するソース等は[ここ](https://github.com/Be-cricket/thrift_c_sample_qiita)に配置していますので参考にしてください。

#なぜC言語？
筆者はここ２０年くらい、Java中心の仕事をしていますが久しぶりにC言語が使いたくなったのです。そしてこれがやりたい！

![スクリーンショット 2019-05-12 23.00.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338832/a0c7f54e-874d-e943-3816-db53789d3f0b.png)

Berkeley DBのフル機能を使うためにはJava EditionではなくC言語版が必要でした。
そして、最終的これやってみたい。
![スクリーンショット 2019-05-19 14.40.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338832/cf3d5615-9d8c-fcd8-566e-10819795f56c.png)

やるとは言ってません。やってみたいだけです。

#開発環境の準備
C言語やC++を使う時に一番面倒なのはこれです。
多くの依存モジュールをインストールする必要があります。

以前はMacに直接環境を作っていましたが、今はContainerが便利ですね。
CentOSのDockerイメージで環境を作ります。もちろん純粋なCentOSでも構いません。

```shell
docker run -it -d --name="mycentos" centos /bin/bash
docker attach mycentos
```
基本的に、[Thriftのマニュアル（https://thrift.apache.org/docs/install/centos）](https://thrift.apache.org/docs/install/centos)通りにインストールすればよいのですが、C言語で動作させる場合は少し追加が必要です。
以下に追加の環境設定手順を記載します。
同じことを行っている[Dockerfile](https://github.com/Be-cricket/thrift_c_sample_qiita/blob/master/docker/Dockerfile)もご活用ください。

#### 不足モジュールのインストール
※テストを簡単に行うため、Pythonも使えるようにしておきます。

```shell
#Install glib2
yum -y install glib2-devel

#Install Pip（Pythonを使う場合）
wget https://thrift.apache.org/docs/install/centos
python get-pip.py
python -m pip -V

#Install six (Python modules)
pip install six
```

#### ビルド時にpythonとc_glibのライブラリが使えるよに指定します。

``` shell
#Build and Install Thrift Compiler
git clone https://github.com/apache/thrift.git
cd thrift
RUN ./bootstrap.sh
RUN ./configure --with-lua=no --with-python=yes --with-c_glib=yes 
RUN make
RUN make install
```
#### ライブラリパスも忘れずに！
```shell
export LD_LIBRARY_PATH=/usr/local/bin
```

# サンプルコード
ping() と hello() のインターフェースを実装します。
[Thrift公式ページのTutorial（https://thrift.apache.org/tutorial）](https://thrift.apache.org/tutorial/c_glib)を参考にしていますが、もっとシンプルにしました。
ソースは[こちら](https://github.com/Be-cricket/thrift_c_sample_qiita/tree/master/src)

インターフェースはsample.thriftに定義する必要があります。
仕様は[こちら](https://thrift.apache.org/docs/idl)

```json:sample.thrift
namespace c_glib  sample 
namespace py sample

# hello              :Sample implementation
service Basic
{
      void ping(), 
      string hello(1:string arg)
}
```

## サーバ側の実装
![サンプル３.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338832/de0d9ba8-690a-f66e-78a3-ff8d8b6992bf.png)


```shell
|-- Makefile
|-- sample.thrift
|-- server.c
```

### スケルトンの生成

- まず、I/Fが定義されているsample.thriftをThriftコンパイラでコンパイルし、必要なスケルトンソースを生成します。

```shell
thrift --gen c_glib sample.thrift
```

gen-c_glibディレクトリが作成されています。

```shell
|-- Makefile
|-- sample.thrift
|-- server.c
|-- gen-c_glib
|   |-- sample_basic.c
|   |-- sample_basic.h
|   |-- sample_sample_types.c
|   `-- sample_sample_types.h
```

### 関数の実装
-　生成されたsample_basic.cに ping(),hello()の処理を記述します。

```c
gboolean sample_basic_handler_ping (sampleBasicIf * iface, GError ** error)
{
  /* TODO: Implement your own code. */
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);
  puts ("ping()");
  return TRUE;
}

gboolean sample_basic_handler_hello (sampleBasicIf * iface, gchar ** _return, const gchar * arg, GError ** error)
{
  /* TODO: Implement your own code. */
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);
  GString *retValue;
  retValue = g_string_new( NULL );
  g_string_printf( retValue ,"Server received: %s \n" , arg );
  puts( retValue->str );
  *_return = retValue->str;
  return TRUE; 
}
```

###サーバ実行
dockerイメージで実行してみます
/root/dev/c_thrift_sample の下にソースがあることを想定しています

```shell
docker run -it mycentos /bin/bash
# cd /root/dev/c_thrift_sample
# make idl　（or #thrift --gen c_glib sample.thrift）
# make 
# ./server
Starting the server...
``` 

9090ポートでListenしています


## クライアント側
クライアントのコードはpythonで記述します。
![サンプル4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338832/cf918e37-664c-24cb-077a-0b4f7032dcf7.png)

###クライアント実装
ソースコードは[こちら](https://github.com/Be-cricket/thrift_c_sample_qiita/blob/master/test/python/client.py)

```python:client.py
print "Call ping() = %s " % (client.ping(),)
print "Call hello() = %s " % (client.hello("Hey guys!"),)
```


###必要なPythonモジュールの生成

```shell
thrift --gen py sample.thrift  #<---　C言語のスケルトン生成時に使用したファイル
```

###クライアント実行
```shell
docker exec -it 232db73154c2  /bin/bash  #<---  232db73154c2 はサーバ動作中のCONTAINER IDです
# cd /root/dev/c_thrift_sample/test/python  #<---  テストコードはここにある想定
# python client.py
Call ping() = None
Call hello() = Server received: Hey guys!
```
pythonクライアントから送った文字列がC言語で作成したサーバで受信できました。

### おまけ
thriftコンパイラで生成したソースを変更しましたが、もう一度生成すると、上書きされて消えてしまいます。
そこで、変更したhandler関数を外出ししました。
ソース類を[ここ](https://github.com/Be-cricket/thrift_c_sample_qiita/tree/handler)に置きます。興味のある方はご覧ください。

# まとめ
今更ですがC言語は難しい!
BerkeleyDBも少し使ってみたのですが、久しぶりに使ってみて痛感しました。
最近の言語では、あまり意識する必要がない「メモリ管理」や「UNICODE文字列の操作」などを、自分で作るか外部ライブラリに頼ることになります。
モジュールやライブラリの管理も微妙で難易度がさらに上がります。
しかし、C言語はとてもシンプルなので、性能や実行モジュールサイズはC言語に敵うものはありません。
これからも職人のツールとして残るでしょう。
最後に
　**C言語は計算機の仕組みを理解するには一番良い言語です**
　**これを理解していれば、新しい言語や技術に対応できる能力が身につきます**
筆者のようなおじさんだけでなく、若い技術者にもトライしてほしいと思います