【问题标题】:How to load mruby code from external file in C program?如何在 C 程序中从外部文件加载 mruby 代码?
【发布时间】:2013-10-21 07:05:26
【问题描述】:

我开始使用mruby。我对 C 编程也很陌生,所以我可能不熟悉许多基础知识。我能够compile the example mruby program 从字符串中加载红宝石代码。现在我想从外部文件加载它。这是我正在使用的示例代码:

#include <stdlib.h>
#include <stdio.h>
#include <mruby.h>

static mrb_value my_c_method(mrb_state *mrb, mrb_value value)
{
  puts("Called my C method");
  return value;
}

int main(void)
{
  mrb_state *mrb = mrb_open();

  struct RClass *mymodule = mrb_define_module(mrb, "MyModule");
  mrb_define_class_method(mrb, mymodule, "my_c_method", my_c_method, MRB_ARGS_NONE());

  mrb_load_string(mrb, "MyModule.my_c_method"); // move this to an external file
  return 0;
}

正如您在上面的示例中看到的,我想将 ruby​​ 代码移动到外部文件中。对于是否可以简单地“包含”ruby 文件,或者是否需要将其预编译为.mrb 文件,我有点困惑。即使这样,我也不确定如何在编译时包含 .mrb 文件。

我需要更改什么才能从外部文件加载 ruby​​ 代码?

【问题讨论】:

    标签: c mruby


    【解决方案1】:

    假设您的代码存储在文件foo 中,您只需打开文件并读取它。要打开文件,您需要fopen(),在 stdio.h 中定义。然后您可以使用fgets() 逐行阅读。我不熟悉 mruby,所以我不确定 mrb_load_string 是否期望每个 mruby 代码都在一行中。我会这样假设。你可以这样做:

    #define MAX_CODE_SIZE 128
    FILE *code;
    char code_buf[128]
    code = fopen("foo", "r");
    if (code == NULL) {
        /* File couldn't be opened, handle error case... */
    }
    fgets(code_buf, MAX_CODE_SIZE, code);
    /* Do some work ... */
    
    fclose(code); /* Don't forget to close the file */
    

    这段代码读取文件foo的第一行,最多127个字符(包括换行符),并将其存储在code_buf中。然后你可以拨打mrb_load_string:

    mrb_load_string(mrb, code);
    

    我不确定这是否是您想要的,我从未接触过 mruby,但据我所见,mrb_load_string 期望带有代码的 char *,而您希望它来自文件。你就是这样做的。

    如果你想读取多行代码的文件,你别无选择,只能分配足够大的缓冲区并使用fread()读取它:

    #include <stdio.h>
    #define MAX_LINE_LENGTH 128
    #define MAX_LINES 256
    #define MAX_FILE_SIZE MAX_LINE_LENGTH*MAX_LINES
    
    char code[MAX_FILE_SIZE];
    
    int read_code(char *filepath) {
        FILE *fp = fopen(filepath, "r");
        if (fp == NULL)
            return 0;
        fread(code, 1, MAX_FILE_SIZE, fp);
        fclose(fp);
        return 1;
    }
    

    这个函数读取整个文件(假设它没有超出我们的缓冲区限制)。 code 是全局的,因为如果您分配大的局部变量,您可以轻松达到堆栈容量(另一种选择是使用动态分配)。当您调用read_code() 时,您应该确保检查其返回值,以检查打开文件时可能出现的错误。此外,您可以使用fread() 的返回值来了解缓冲区大小是否不足以读取所有内容。

    请确保完成后不要忘记关闭文件。

    编辑: 对于fgets() 版本,请注意如果该行少于128 个字符,则code_buf 中将保留换行符。如果是这种情况,您可能需要将 code_buf[strlen(code_buf)-1] 设置为 '\0'

    更新:

    根据我们对下面 cmets 的讨论,我正在使用基本解析器更新我的答案,以使您能够在编译时读取 ruby​​ 文件。基本上,解析器将读取您的 ruby​​ 文件并生成一个带有有效 C 代码的输出文件,该文件将文件的内容插入 char 数组中。特殊字符相应地被转义。这里是:

    #include <stdio.h>
    
    void inline insert(int, FILE *);
    
    int main(int argc, char *argv[]) {
        FILE *out, *in;
        int c;
        if (argc != 3) {
            printf("Usage: %s <input_file> <output_file>\n", argv[0]);
            return 1;
        }
    
        in = fopen(argv[1], "r");
        out = fopen(argv[2], "w");
    
        if (out == NULL) {
            printf("Unable to create or write to %s\n", argv[1]);
            return 1;
        }
    
        if (in == NULL) {
            printf("Unable to read %s\n", argv[1]);
            return 1;
        }
    
        fputs("#ifndef MRUBY_CODE_FILE_GUARD\n", out);
        fputs("#define MRUBY_CODE_FILE_GUARD\n", out);
        fputs("char mruby_code[] = \"", out);
        while ((c = getc(in)) != EOF)
            insert(c, out);
        fputs("\";\n", out);
        fputs("#endif\n", out);
        fclose(in);
        fclose(out);
        return 0;
    }
    
    void inline insert(int c, FILE *fp) {
        switch (c) {
            case '\a':
                fputs("\\a", fp);
                break;
            case '\b':
                fputs("\\b", fp);
                break;
            case '\f':
                fputs("\\f", fp);
                break;
            case '\n':
                fputs("\\n", fp);
                break;
            case '\r':
                fputs("\\r", fp);
                break;
            case '\t':
                fputs("\\t", fp);
                break;
            case '\v':
                fputs("\\v", fp);
                break;
            case '\\':
                fputs("\\\\", fp);
                break;
            case '\'':
                fputs("\\'", fp);
                break;
            case '"':
                fputs("\\\"", fp);
                break;
            default:
                fputc(c, fp);
        }
    }
    

    现在,回到你原来的程序并在开头添加以下包含指令:

    #include mruby_code.h
    

    你必须执行以下步骤来编译一个可运行的程序,假设这个解析器被编译成一个名为fileparser.c的文件:

    • 运行./fileparser /path/to/mruby_code_file /path/to/program/mruby_code.h
    • 编译您的原始程序(它将包括mruby_code.h

    mruby 代码在名为mruby_code 的变量中提供。这是一个字符数组,因此您可以将其传递给mrb_load_string。瞧,您在编译时读取了 mruby 文件一次

    【讨论】:

    • 谢谢!这绝对是一个开始并且有助于了解。我遇到了一个使用 mrb_read_irep_file 的 Objective-C 示例。我想知道 mruby 是否有一种方法可以稍微简化这个答案。我不确定如何浏览 mruby 源代码以找到可能有帮助的方法。
    • 好吧,正如我所说,我从来没有看过mruby,所以我不能再帮你了。这是纯粹的 C 方法。也许有更多红宝石知识的人会回答这个问题。
    • 还有一个问题:您的示例是否在运行时读取文件?有没有办法在编译时包含文件以进行打包?
    • 它在运行时读取文件。如果您对每次运行程序时都这样做的开销感到不满意,那么我能看到的唯一替代方法是编写一个打开 mruby 代码文件的 C 程序,生成一个带有适当代码的 C 文件,该代码会推送文件的内容在 char * 中,然后在调用编译器时包含该文件。那真的很酷,我可能会尝试编写这样的程序只是为了好玩。如果我这样做,我会更新我的答案。除此之外,所有其他可能的方法(甚至是 mruby 库函数)很可能会在运行时读取文件。
    • 实际上,这真的很酷,因为您不必担心缓冲区大小。我要编写代码,这是一个非常好的主意!唯一的问题是,如果您更改了 ruby​​ 代码,则必须重新编译您的 C 程序。
    【解决方案2】:

    mruby/compile.h 中有一个名为mrb_load_file 的方法,它允许您加载一个ruby 文件并使用mruby 执行它:

    #include <stdlib.h>
    #include <stdio.h>
    
    #include <mruby.h>
    #include <mruby/compile.h>
    
    static mrb_value my_c_method(mrb_state *mrb, mrb_value value)
    {
      puts("Called my C method");
      return value;
    }
    
    int main(void)
    {
      mrb_state *mrb = mrb_open();
    
      struct RClass *mymodule = mrb_define_module(mrb, "MyModule");
      mrb_define_class_method(mrb, mymodule, "my_c_method", my_c_method, MRB_ARGS_NONE());
    
      FILE *f = fopen("example.rb", "r");
      mrb_load_file(mrb, f);
    
      return 0;
    }
    

    【讨论】:

    • 感谢分享这个例子。如果我有一个方法需要一些参数(在我的 ruby​​ 文件中)例如:ef helloworld(var1, var2),我如何加载文件并从 C 调用这个方法?
    • 另外你知道如何从mrb_value结构中提取信息吗?例如,如何读取字符串、整数、双精度等?
    猜你喜欢
    • 2017-06-17
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多