C言語でサーバアプリやクライアントアプリを作っていると、じょじょに規模が大きくなり、アプリの設定を外出ししたいなぁ、なんて思いはじめるのはよくある事だと思います。
でも、なんとなくその設定ファイルのParser書いたり参照のインターフェイスを書くのも面倒だし引数で渡すようにするかぁ、なんて思いはじめたりもします。
でも結局引数が大量に増えだして、そのusage表示もカオスになって面倒になり(更新が止まり)、結局もう一度、「設定ファイル作るかー」なんてことになるのはよくある事だと思います。
そこで、そういう人のために,mrubyを使って簡単に設定ファイル(Rubyで書く)を外出しできるmruby-configというmrbgemを作ってみました。こういうことが簡単にできて、かつ、軽量なのがmrubyの良いところですね。
これを使うと、Rubyで書いた設定ファイルを、簡単にCソースの中で呼び出して、その値を参照することができます。
使い方
とりあえず、サーバアプリを実装中と見立てたC側のサンプルとRuby側(設定ファイル)のサンプルを見るのが一番分かりやすいでしょう。
C側は以下のように書きます。
#include <mruby/variable.h>
#include <mruby/hash.h>
static void create_config(mrb_state *mrb, char *key, char *val)
{
mrb_value conf;
conf = mrb_hash_new(mrb);
mrb_hash_set(mrb, conf, mrb_str_new_cstr(mrb, key), mrb_str_new_cstr(mrb, val));
mrb_funcall(mrb, mrb_top_self(mrb), "new_config", 1, conf);
}
int main() {
FILE *fp;
if ((fp = fopen("./mruby.conf", "r")) == NULL)
return 1;
mrb_state* mrb = mrb_open();
// C側で先にconfigを作っておいてその値はRuby側で参照して動的にconfigを生成できるようにする
create_config(mrb, "Version", "2");
// Ruby設定ファイル実行
mrb_load_file(mrb, fp);
// 設定値の参照
int port;
char *droot;
mrb_value user;
/*
mrb_get_config_value(mrb, key, format, ...)
format specifiers (like mrb_get_args):
o: Object [mrb_value]
S: String [mrb_value] //Not Implemented
A: Array [mrb_value] //Not Implemented
H: Hash [mrb_value] //Not Implemented
s: String [char*,int] //Not Implemented
z: String [char*]
a: Array [mrb_value*,mrb_int] //Not Implemented
f: Float [mrb_float] //Not Implemented
i: Integer [mrb_int]
*/
mrb_get_config_value(mrb, "Listen", "i", &port);
mrb_get_config_value(mrb, "DocumentRoot", "z", &droot);
mrb_get_config_value(mrb, "User", "o", &user);
// 設定の値を使って色々と処理を実装
printf("=== global configuration ===¥n");
printf("port=%d droot=%s¥n", port, droot);
mrb_p(mrb, user);
printf("¥n");
mrb_close(mrb);
return 0;
}
このように、設定ファイルであるmruby.confを読み込んで(ここは引数で渡せるようにしておいた方が良いでしょう)そこから、設定ファイルの値をget_config_valueで参照しています。
このサンプルでは、C側で先に最低限の設定をcreate_configで作っておき、その設定に対して、Ruby設定ファイル側から設定値を上書きするような実装をしています。これによって、C側の値を参考に動的にRuby側で設定ファイルを作る事ができます。
では、その設定がかかれたRuby設定ファイルはどうなっているでしょうか。以下がその設定ファイルになります。
"Listen" => 80,
"DocumentRoot" => "/var/www/html",
"ExtendedStatus" => nil,
"User" => "apache",
"Group" => "apache",
)
if get_config("Version").to_i < 2
add_config "ExtendedStatus" => "Off"
else
add_config "ExtendedStatus" => "On"
end
このように、基本はハッシュで渡します。このサンプルでは、C側で先に「Version」に関する設定をnew_configメソッドで作成しておき、その後にこのRuby設定ファイルを呼び出す(mruby的には実行する)事で、設定ファイル内ではC側のバージョンに関する情報を元に、それに対応した設定を生成しています。
また、特にC側で事前に設定を作っておいて、それをRuby設定ファイルで連携する必要がない場合は、以下のようにnew_configメソッドで設定を作成します。
"Listen" => 80,
"DocumentRoot" => "/var/www/html",
"ExtendedStatus" => "On",
"User" => "apache",
"Group" => "apache",
)
C側での設定値の参照はシンプルに以下の様になります。
int main() {
FILE *fp;
if ((fp = fopen("./mruby.conf", "r")) == NULL)
return 1;
mrb_state* mrb = mrb_open();
mrb_load_file(mrb, fp);
int port;
char *droot;
mrb_value user;
mrb_get_config_value(mrb, "Listen", "i", &port);
mrb_get_config_value(mrb, "DocumentRoot", "z", &droot);
mrb_get_config_value(mrb, "User", "o", &user);
// 設定の値を使って色々と処理を実装
mrb_close(mrb);
return 0;
}
これでOKです。簡単ですね!
また、設定を追加・修正したい時は、
と書いたり、設定そのものを削除したい場合は、
と書きます。
また、設定を参照したい場合は、以下のように書きます。
あるいは、設定全てを呼び出したい時は、get_configを引数無しで実行します。get_configメソッドは大抵の場合、上記のサンプルのようにC側で設定ファイルの値を参照したい時に使うと思います。そのためのmruby APIであるmrb_get_config_value()を用意しています。
mrb_get_config_value(mrb, key, format, ...)
format specifiers (like mrb_get_args):
o: Object [mrb_value]
S: String [mrb_value] //Not Implemented
A: Array [mrb_value] //Not Implemented
H: Hash [mrb_value] //Not Implemented
s: String [char*,int] //Not Implemented
z: String [char*]
a: Array [mrb_value*,mrb_int] //Not Implemented
f: Float [mrb_float] //Not Implemented
i: Integer [mrb_int]
*/
int port;
char *droot;
mrb_value user;
mrb_get_config_value(mrb, "Listen", "i", &port);
mrb_get_config_value(mrb, "DocumentRoot", "z", &droot);
mrb_get_config_value(mrb, "User", "o", &user);
mruby-configで定義しているメソッドは全てC側からもRuby側からも値を共有できるように実装しているので、好きなように両者からメソッドを実行すると良いでしょう。
続きも書きました。
mruby-configにtag付のconfigを書けるようにした
最後に
今回はCで書いたサーバアプリやクライアントアプリでパラメータを柔軟に扱いたい場合の一つの手法として、mrubyを用いて簡単に設定値を外部のファイル(Rubyスクリプト)と連携するためのmruby-configを使った手法を紹介しました。これで、単純な設定であれば、ほぼ何も考える事なくCで書かれたアプリケーションの設定ファイルを外出しすることができるでしょう。
個人的にも、設定ファイルを作るのって面倒に思っていたので、今後はmruby-configを使って楽に外出しした設定ファイルからCアプリ内に値をひっぱってくるようにしたいと思います。
0 Comments.