【C言語】glib2.0を使ってみた〜コマンドライン引数読み込み〜

# はじめに
C言語でアプリケーションを作成するとき、入出力の基本的な処理からコーディングするのは面倒ですし、バグの温床のなりえます。
良い解決策がないか調べたところ、glib2.0というライブラリが
* インストール・利用の容易さ
* 多機能
* ライセンス

という観点で、良さそうであると思いました。
一方、ネット上にあまり情報がなく、公式ドキュメントもわかりにくいということもあり、本記事にて基本的な使い方を紹介したいと思います。

今回は、**コマンドライン引数の読み込み**について記載します。

# 動作環境
本記事は、下記の環境で動作を確認しています。
* Ubuntu18.04
* gcc

# インストール手順
下記コマンドでglib2.0一式をインストールすることができます。
Ubuntu18.04の場合、バージョン2.56.4が入るようです。
```bash
sudo apt install libglib2.0-dev
```

# 使用例
glibを用いたコマンドライン引数の読み込みは、下記のような書き方になるようです。

```c:main.c
#include <glib.h>

#define APP_VERSION "1.0.0"

typedef struct{
    gchar *filepath;
    int int_num;
    double double_value;
    gboolean bool;
}S_COMMAND_ARGS;

int main(int argc, char* argv[])
{
    GOptionContext *context;
    GError *err = NULL;
    int i;

    //コマンドライン引数として読み込む変数群の初期値を定義しておく
    S_COMMAND_ARGS command_args = {
        "./hogehoge",
        127,
        3.141592,
        FALSE
    };

    //{引数のフル名、短縮名、オプション指定フラグ、引数の型、代入先、説明文、ヘルプに記載時の変数名}で列挙
    //最終要素はNULLで終わる必要あり
    GOptionEntry entries[] = 
    {
        { "filepath", 'f', 0, G_OPTION_ARG_FILENAME, &command_args.filepath, "Path of user-specified file", "FILEPATH" },
        { "int_num", 'n', 0, G_OPTION_ARG_INT, &command_args.int_num, "Integer num", "N" },
        { "double_value", 'd', 0, G_OPTION_ARG_DOUBLE, &command_args.double_value, "Double value", "D" },
        { "bool", 'b', 0, G_OPTION_ARG_NONE, &command_args.bool, "bool value", NULL},
        { NULL }
    };

    context = g_option_context_new("test app of command-line arg parser.");
    g_option_context_set_description(context, "Version:\n  "APP_VERSION);
    g_option_context_add_main_entries(context, entries, NULL);

    if (!g_option_context_parse(context, &argc, &argv, &err))
    {
        g_print("option parsing failed: %s\n", err->message);
        g_error_free(err);
    } else {
        g_print("filepath     = %s\n", command_args.filepath);
        g_print("int_num      = %d\n", command_args.int_num);
        g_print("double_value = %f\n", command_args.double_value);
        g_print("bool         = %s\n", command_args.bool ? "true" : "false");

        //g_option_context_parseにてargc, argvが更新されている
        //オプション引数としてパース成功したものは除外されている
        for (i = 1; i < argc; i++) {
            g_print("non-option arg[%d] = %s\n", i, argv[i]);
        }
    }

    //開放処理
    g_option_context_free(context);

    return 0;
}
```
下記のコマンドでコンパイルを行うことができます。
```bash
gcc main.c -o main `pkg-config --cflags --libs glib-2.0`
```

下記のコマンドでヘルプを表示してみます。
自らヘルプを書かなくとも、glibがヘルプ文を自動生成してくれます。
```bash
./main -h
Usage:
  main [OPTION?] test app of command-line arg parser.

Help Options:
  -h, --help                  Show help options

Application Options:
  -f, --filepath=FILEPATH     Path of user-specified file
  -n, --int_num=N             Integer num
  -d, --double_value=D        Double value
  -b, --bool                  bool value

Version:
  1.0.0
```

オプション引数を入れて実行してみます。
下記のようにオプションで指定された引数が正しく読み込まれ、指定されなかった場合はデフォルトの値が採用されます。
```bash
./main -f ./test.txt -n 100 
filepath     = ./test.txt
int_num      = 100
double_value = 3.141592
bool         = false
```

次にオプション引数でない"ADD"と"CHECK"という引数を、それぞれオプション引数群より前と一番最後に加えてみます。
`g_option_context_parse`関数はオプション引数のみパースし、パースしなかった引数のみargc,argvに残す仕様のようです。
したがって、オプション引数でない引数のパース処理は、argc, argvから読み取ります。

```bash
./main ADD -f ./test.txt -d 3.14 CHECK
filepath     = ./test.txt
int_num      = 127
double_value = 3.140000
bool         = false
non-option arg[1] = ADD
non-option arg[2] = CHECK
```

引数の型チェックについても確認します。
int型指定なのに、小数や文字列が与えられた場合や、double型指定なのに文字列が与えられた場合は、エラーメッセージにてエラー理由がわかるようになっているようです。
```bash
./main ADD -f ./test.txt -d aaa
option parsing failed: Cannot parse double value ?aaa? for -d
```

# あとがき
C言語は文字列処理のコーディングに向いておらず、入出力の基本的な処理を作るのは骨が折れます。glib2.0を活用することで、本質ではない部分に割く時間を削減し、本当に重要なことに注力できると思います。


# 参考文献
* [glib2.56manpage](https://www.manpagez.com/html/glib/glib-2.56.0/glib-Commandline-option-parser.php)