web-dev-qa-db-ja.com

C / C ++でeval( "function(arg1、arg2)")に相当するものはありますか?

名前がevalのような文字列に格納されている関数を呼び出す方法が必要です。手伝ってくれますか?

23
Dipesh KC

C++にはリフレクションがないため、ハックする必要があります。 e .:

#include <iostream>
#include <map>
#include <string>
#include <functional>

void foo() { std::cout << "foo()"; }
void boo() { std::cout << "boo()"; }
void too() { std::cout << "too()"; }
void goo() { std::cout << "goo()"; }

int main() {
  std::map<std::string, std::function<void()>> functions;
  functions["foo"] = foo;
  functions["boo"] = boo;
  functions["too"] = too;
  functions["goo"] = goo;

  std::string func;
  std::cin >> func;
  if (functions.find(func) != functions.end()) {
    functions[func]();
  }
  return 0;
}
25
Hauleth

少なくとも2つの選択肢があります。

  • コマンドパターン
  • Windowsでは、GetProcAddressを使用して名前でコールバックを取得し、* [nix]ではdlopen + dlsymを使用できます。
8
Luchian Grigore
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;


double eval( string expression );


int main( int argc, char *argv[] )
{
    string expression = "";
    for ( int i = 1; i < argc; i++ )
    {
       expression = expression + argv[i];
    }
    cout << "Expression [ " << expression << " ] = " << endl;

    eval( expression );
}


double eval( string expression )
{
    string program = "";
    program = program + "#include <cmath>\n";
    program = program + "#include <iostream>\n";
    program = program + "using namespace std;\n";
    program = program + "int main()\n";
    program = program + "{\n";
    program = program + "   cout << ";
    program = program + expression;
    program = program + " << endl;\n";
    program = program + "}";


    ofstream out( "abc.cpp" );
    out << program;
    out.close();

    system( "g++ -o abc.exe abc.cpp" );
    system( "abc" );
}
5
DavidA

既存のスクリプトエンジンを採用して、これに必要な関数を公開し、これを使用してステートメントを評価することができます。そのようなエンギングの1つはV8エンジンである可能性があります: https://developers.google.com/v8/intro ですが、多くの選択肢と異なる言語から選択できます。

ここではいくつかの例を示します。

4
Tommy Andersen

プログラムで関数マップを使用してMakefileでハッキングする場合を除いて、[〜#〜] elf [〜#〜]を介してアクセスできます。

重複するコードを記述して毎回異なるマシンでコンパイルする必要がなかったので、この方法の方が良いと思います。

これが私のデモです C/C++でeval(“ function(arg1、arg2)”)と同等)

#include<stdio.h>
#include<stdlib.h>
#include<elf.h>
#include<libelf.h>
#include<unistd.h>
#include<fcntl.h>
#include<gelf.h>
#include<string.h>

void my_fun()
{
    int a = 19;
    printf("my_fun is excute, a is %d \n", a);
}

void my_fun2()
{
    printf("my_fun2 is excute\n");
    return;
}

void my_fun3()
{
    return;
}

void excute_fun(char *program_name, char *function_name)
{
    int i, count;
    Elf32_Ehdr *ehdr;
    GElf_Shdr   shdr;
    Elf *elf;
    Elf_Scn *scn = NULL;
    Elf_Data *data;
    int flag = 0;
    int fd = open(program_name, O_RDONLY);
    if(fd < 0) {
        perror("open\n");
        exit(1);
    }
    if(elf_version(EV_CURRENT) == EV_NONE) {
        perror("elf_version == EV_NONE");
        exit(1);
    }
    elf = elf_begin(fd, ELF_C_READ, (Elf *) NULL);
    if(!elf) {
        perror("elf error\n");
        exit(1);
    }
    /* Elf32_Off e_shoff; */
    /* if ((ehdr = elf32_getehdr(elf)) != 0) { */
    /*     e_shoff = ehdr->e_shoff; */
    /* } */
    /* scn = elf_getscn(elf, 0); */
    /* printf("e_shoff is %u\n", e_shoff); */
    /* scn += e_shoff; */
    while ((scn = elf_nextscn(elf, scn)) != NULL) {
        gelf_getshdr(scn, &shdr);
        if (shdr.sh_type == SHT_SYMTAB) {
            /* found a symbol table. */
            break;
        }
    }
    data = elf_getdata(scn, NULL);
    if(!shdr.sh_entsize)
        count = 0;
    else
        count = shdr.sh_size / shdr.sh_entsize;
    for (i = 0; i < count; ++i) {
        GElf_Sym sym;
        gelf_getsym(data, i, &sym);
        char *sym_name = elf_strptr(elf, shdr.sh_link, sym.st_name);
        if(sym_name != NULL && sym_name[0] != '_' && sym_name[0] != '\0' && sym_name[0] != ' ' && sym.st_value != 0)
        {
            /* printf("sym_name is %s\n", sym_name); */
            /* printf("%s = %X\n", elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value); */
            if(!strcmp(sym_name, function_name)) {
                void (*fun)(void) = (void*)sym.st_value;
                (*fun)();
                flag = 1;
            }
        }
    }
    if(!flag)
        printf("can not find this function\n");

    elf_end(elf);
    close(fd);
}

int main(int argc, char *argv[])
{
    char *input = (char*)malloc(100);
    for(;;) {
        printf("input function_name to excute: ");
        scanf("%s", input);
        excute_fun(argv[0], input);
        memset(input, 0, sizeof(input));
        printf("\n");
    }
    free(input);
    return 0;
}

この実装は ELFシンボルテーブルの印刷例 に基づいています。

2
YongHao Hu