用C写`eval()` [英] Writing `eval()` in C

查看:258
本文介绍了用C写`eval()`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一段时间以来,我一直在尝试在C中创建一个eval函数。

I've been trying to make an eval function in C for a while.

此刻,我的想法是制作一个字符串->函数指针包含所有标准库C函数以及我创建的所有函数,这样我就可以处理函数调用(在已定义的函数上)。

At the moment, my idea is to make a hash String -> function pointer with all the standard library C functions, and all the functions that I make, that way I could handle function invocations (on already defined functions).

但是,用字符串定义函数(即调用 eval( int fun(){return 1;}))仍然是一个问题,我不不知道我该如何在运行时处理这个问题,有人有什么主意吗?

However, defining functions with strings (i.e, calling eval("int fun(){return 1;}")) is still a problem, I don't know how I could handle this on runtime, does anyone have any idea?

变量定义似乎没有太大问题,因为我可以使用另一个哈希 var_name->指针,并在需要变量时使用该指针。

Variable definitions don't seem too much of a problem, as I could just use another hash var_name -> pointer and use that pointer whenever the variable is required.

顺便说一句,我不在乎性能,我想得到这个

By the way, I don't care about performance, I want to get this to work.

推荐答案

试图解析C是一个真正的难题。但是我们已经知道如何解析C了;调用C编译器!在这里,我们将评估代码编译到动态库中并加载它。

Trying to parse C is a real pain in the behind; but we already know how to parse C; invoke the C compiler! Here we compile the eval code into a dynamic library and load it.

您可能会遇到这样的问题,即您的动态代码无法在自己的代码中找到其他函数或变量;一个简单的解决方案是将main()以外的整个程序编译为一个库,然后将动态代码库链接到该程序。您可以通过将库加载地址设置为仅比主加载地址高几K来避免-fpic损失。在Linux上,库中的未解析符号可以通过可执行文件(如果未剥离)来解析,而glibc依赖于此功能。但是,编译器优化有时会遇到障碍,因此可能需要使用全部库方法。

You may run into problems where your dynamic code can't find other functions or variables in your own code; the easy solution for that is to compile your whole program except for main() as a library and link the dynamic code library against it. You can avoid the -fpic penalty by setting your library load address only a few K above your main load address. On Linux, unresolved symbols in a library can be resolved by the executable if not stripped, and glibc depends on this functionality; however, compiler optimizations get in the way sometimes so the total library method may be necessary.

下面的示例代码适用于Linux。只需做少量工作,即可将其应用于包括Mac OSX在内的其他Unix。在Windows上尝试是可以的,但是要加倍努力,因为除非您愿意提供C编译器,否则您将无法保证使用C编译器。在Windows上,关于多个C运行时存在一个令人讨厌的规则,因此您必须使用相同的编译器进行构建,因此也必须使用相同的编译器进行构建。另外,您必须在此处使用全部库技术,否则主程序中的符号将无法在库中解析(PE文件格式无法表达必要的含义)。

Sample code below is for Linux. This can be adopted to other Unix including Mac OSX with minor work. Attempting on Windows is possible, but harder as you have no guarantee of a C compiler unless you're willing to ship one; and on Windows there's the obnoxious rule about multiple C runtimes so you must build with the same one you ship, and therefore must also build with the same compiler you ship. Also, you must use the total library technique here or symbols in your main program just won't resolve in the library (PE file format can't express the necessary).

此示例代码无法为eval()代码提供保存状态的方法;如果您需要这样做,则应该通过主程序中的变量或(最好)按地址传递状态结构来实现。

This sample code provides no way for the eval() code to save state; if you need this you should do so either by variables in the main program or (preferred) passing in a state structure by address.

如果您尝试在其中执行此操作嵌入式环境,不要。在嵌入式世界中,这是个坏主意。

If you are trying to do this in an embedded environment, don't. This is a bad idea in the embedded world.

回答rici的评论;我从未见过eval()块的参数类型和返回类型不是从周围的代码中静态确定的情况;除此以外,您如何称呼它?下面的示例代码可以分解为提取共享部分,因此每个类型的部分只有几行;

In answer to rici's comment; I have never seen a case where the argument types and return type of an eval() block were not statically determined from the surrounding code; besides else how would you be able to call it? Example code below could be cut up extracting the shared part so the per-type part is only a couple of lines; exercise is left for the reader.

如果您没有特定的理由要使用动态C,那么您可以继续练习。尝试使用具有明确定义的接口的嵌入式LUA。

If you don't have a specific reason to want dynamic C; try embedded LUA instead with a well-defined interface.

/* gcc -o dload dload.c -ldl */

#include <dlfcn.h>
#include <stdio.h>

typedef void (*fevalvd)(int arg);

/* We need one of these per function signature */
/* Disclaimer: does not support currying; attempting to return functions -> undefined behavior */
/* The function to be called must be named fctn or this does not work. */
void evalvd(const char *function, int arg)
{
        char buf1[50];
        char buf2[50];
        char buf3[100];
        void *ctr;
        fevalvd fc;
        snprintf(buf1, 50, "/tmp/dl%d.c", getpid());
        snprintf(buf2, 50, "/tmp/libdl%d.so", getpid());
        FILE *f = fopen(buf1, "w");
        if (!f) { fprintf (stderr, "can't open temp file\n"); }
        fprintf(f, "%s", function);
        fclose(f);
        snprintf(buf3, 100, "gcc -shared -fpic -o %s %s", buf2, buf1);
        if (system(buf3)) { unlink(buf1); return ; /* oops */ }

        ctr = dlopen(buf2, RTLD_NOW | RTLD_LOCAL);
        if (!ctr) { fprintf(stderr, "can't open\n"); unlink(buf1); unlink(buf2); return ; }
        fc = (fevalvd)dlsym(ctr, "fctn");
        if (fc) {
                fc(arg);
        } else {
                fprintf(stderr, "Can't find fctn in dynamic code\n");
        }
        dlclose(ctr);
        unlink(buf2);
        unlink(buf1);
}

int main(int argc, char **argv)
{
        evalvd("#include <stdio.h>\nvoid fctn(int a) { printf(\"%d\\n\", a); }\n", 10);
}

这篇关于用C写`eval()`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆