【问题标题】:C Loading Code dynamically in the same way as the Java Compiler Api 7C 以与 Java Compiler Api 7 相同的方式动态加载代码
【发布时间】:2013-01-22 11:00:40
【问题描述】:

我有以下用例,我以前用 Java 解决过,但现在需要将程序移植到 C。

我有一个方法 A,它调用了属于抽象类 Engine 的方法 do_work()。该类的每个具体实现构造如下:

用户将提交 do_work() 方法的定义。如果这个定义是正确的,程序员将使用 Java Compiler API 构造 Engine 类的具体实现。 (此代码包含在下面以供参考)。

如何在 C 中做类似的事情:

我现在有一个结构体引擎,带有一个指向 do_work() 方法的函数指针。我希望用户能够在运行时通过命令行提交此方法(注意:这只发生一次,在启动时,一旦构造了引擎结构,我不想更改它)。

我该怎么做呢?我已经阅读了一些建议,说明我必须使用汇编来执行此操作,其他人则说这是不可能的,但没有一个给出很好的解释或参考。任何帮助将不胜感激。

该解决方案不需要与 32/64 位机器兼容,因为所编写的程序仅适用于 64 位机器。

Java 代码供参考:

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    StandardJavaFileManager stdFileManager = compiler
            .getStandardFileManager(null, Locale.getDefault(), null);
    Iterable<? extends JavaFileObject> compilationUnits = null;

    String[] compileOptions = new String[] { "-d", "bin" };
    Iterable<String> compilationOptions = Arrays.asList(compileOptions);


        SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject(
                "package.adress",getCode());
        JavaFileObject javaFileObjects[] = new JavaFileObject[] { fileObject };
        compilationUnits = Arrays.asList(javaFileObjects);
    }

    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    CompilationTask compilerTask = compiler.getTask(null, stdFileManager,
            diagnostics, compilationOptions, null, compilationUnits);

    boolean status = compilerTask.call();

    if (!status) {// If compilation error occurs
        /* Iterate through each compilation problem and print it */
        String result = ""; 
        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
            result = String.format("Error on line %d in %s",
                    diagnostic.getLineNumber(), diagnostic);
        }
        Exception e = new Exception(result);
        throw e;
    }

    stdFileManager.close();// Close the file manager

    /*
     * Assuming that the Policy has been successfully compiled, create a new
     * instance
     */

    Class newEngine = Class
            .forName("package.name");

    Constructor[] constructor = newPolicy.getConstructors();
    constructor[0].setAccessible(true);
    etc.
}

【问题讨论】:

标签: c dynamic compiler-construction runtime


【解决方案1】:

在 C 语言中,所有代码在使用前都必须编译为本机代码,因此您唯一的方法是使用命令行编译器来构建用户提交的代码。例如,它可能是 GNU C++ 编译器,或 Visual C++ 编译器(但对于 Visual C++,我不知道法律问题是什么,许可证是否允许这样做)。 所以,首先,选择你的编译器,可能是 GNU 的。

接下来,您可以将其编译为可执行程序或 DLL(假设您的软件用于 视窗)。如果您决定将其编译为 DLL,则必须使用 Win32 函数 LoadLibrary 将新构建的 DLL 加载到您的进程中,然后您可以使用 GetProcAddress 函数获取方法地址并从 C++ 动态调用它(您必须实现一个函数包装器并在 DLL 中公开)。

如果你决定将它编译为 EXE 文件,你必须使用 CreateProcess 函数来运行你的代码,通过命令行发送参数和接收数据,可能是管道(参见 CreatePipe 函数),或者可能是临时文件,或 Windows 中可用的任何其他进程间通信方式。

我认为在您的情况下最好编译成 EXE 文件,因为在 DLL 中如果用户代码有错误,您的主程序可能会崩溃。

【讨论】:

  • 该软件不适用于 Windows。
  • 但是对于 Linux 的想法也是一样的(Mac OS 实际上也是 Linux)。您也可以这样做:使用 GCC 将自定义代码编译为可执行文件并使用系统函数运行它,例如,将输出写入临时文件并在您的程序中读取。
  • 这不是用例。这不是运行程序、单独的进程甚至是线程。这是模拟 Java Compiler API 以启用稍后指定的方法定义。它不需要在同一个二进制文件中,但必须在同一个程序中,没有上下文切换。 do_method 的编译定义应该与在编译时指定的定义没有什么不同。
  • 好吧,C 不是 Java,你不能得到完全相同的结果。最类似的方法是使用 GCC 命令行界面将其编译为共享库,并使用linux.die.net/man/3/dlopen 系统调用加载它。如果你不能运行单独的进程(甚至是 GCC 来编译代码),我认为唯一的方法就是实现你自己的 C 编译器。据我所知,目前还没有任何 C 编译器被实现为可以嵌入到代码中的静态或动态库;当然,你可以尝试将 GCC 从命令行工具转换为静态库,但我认为这并不容易。
猜你喜欢
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 2015-11-12
相关资源
最近更新 更多