mruby-configの設定値取り出しにCの型を指定できるようにした

mruby-configはRuby側で設定した値をC側で取り出す際に、Cの型としてmrb_valueで取り出す仕様にしていました。

の続きです。

で、個人的にもC側で取り出すんだから、intとかcharとか型指定できたほうが楽だよなぁと思っていた時に、ちょうどmatzさんもそっちの方が便利じゃない?と言っていたので、実装することにしました。

基本はmruby APIのmrb_get_argsにならって、同じような仕様で値を参照できるように実装して、C側で使えるmruby-config APIも用意しました。

使い方は以下のようになります。今のところは、C側で値を参照する際は、とりあえず一番使いそうな、

  • int
  • char
  • mrb_value

を指定できるようにしています。

まずは、Rubyで以下のように設定を書きます。

[program lang=’ruby’ escaped=’true’]

#
# global configuration
#

new_config(
    "Listen"            => 80,
    "DocumentRoot"      => "/var/www/html",
    "ExtendedStatus"    => nil,
    "User"              => "apache",
    "Group"             => "daemon",
)

add_config "ExtendedStatus" => "On"

#
# sub configuration
#

new_sub_config "tag1", {
    "Files"         => "index.cgi",
    "AccessLimit"   => 5
}

add_sub_config "tag2", {
    "Files"       => "mt.cgi",
    "AccessLimit" => 1
}

[/program]

そして、それをmruby.confというファイル名で保存しておいて、以下のようにC上で型を指定して値を参照します。

[program lang=’c’ escaped=’true’]

/*
**
** gcc mconfig.c -I ../../mruby/include/ ../../mruby/build/host/lib/libmruby.a
**
*/
#include <stdio.h>
#include "mruby.h"
#include "mruby/variable.h"
#include "mruby/hash.h"

int main() {

  FILE *fp;
  if ((fp = fopen("./mruby.conf", "r")) == NULL)
    return 1;

  // Ruby設定ファイルの値を入れる変数
  int port;
  char *droot;
  mrb_value user;

  char *tag1_file;
  mrb_value tag1_limit; 
  int tag2_limit; 

  // Ruby設定ファイルのロード
  mrb_state* mrb = mrb_open();
  mrb_load_file(mrb, fp);

  /*
  mrb_get_config_value(mrb, key, format, ...)
  mrb_get_sub_config_value(mrb, tag, key, format, ...)
  mrb_config_convert_value(mrb, val, 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 [float]               //Not Implemented
   i: Integer [int]
  */

  // Ruby設定ファイルのグローバルな設定値をCの型指定して取り出し
  mrb_get_config_value(mrb, "Listen",       "i", &port);
  mrb_get_config_value(mrb, "DocumentRoot", "z", &droot);
  mrb_get_config_value(mrb, "User",         "o", &user);

  // Ruby設定ファイルのタグ付の設定値をCの型指定して取り出し
  mrb_get_sub_config_value(mrb, "tag1", "Files",        "z", &tag1_file);
  mrb_get_sub_config_value(mrb, "tag1", "AccessLimit",  "o", &tag1_limit);
  mrb_get_sub_config_value(mrb, "tag2", "AccessLimit",  "i", &tag2_limit);

  // Implement using configuration value in Ruby
  printf("=== global configuration ===¥n");
  printf("port=%d droot=%s¥n", port, droot);
  mrb_p(mrb, user);
  printf("¥n");

  printf("=== tag configuration ===¥n");
  printf("tag1_file=%s¥n", tag1_file);
  mrb_p(mrb, tag1_limit);
  printf("tag2_limit=%d¥n", tag2_limit);

  // 取り出し済みのmrb_valueを任意のCの型に変換もできる
  int tag1_limit_c;
  mrb_config_convert_value(mrb, tag1_limit, "i", &tag1_limit_c);
  printf("tag1_limit=%d¥n", tag1_limit_c);

  mrb_close(mrb);

  return 0;
}

[/program]

値の取り出しは、mrb_get_config_valueやmrb_get_sub_config_valueを使えば良いので、前よりもグッと使いやすくなったと思います。

また、Not Implemetedになってるところで使えそうな型は時間があれば実装しようかなぁ、と思っています。