31

it need a way to call function whose name is stored in a string similar to eval. Can you help?

Dipesh KC
  • 3,195
  • 3
  • 32
  • 50
  • 3
    Nope, your best bet is the command pattern. – Khaled Alshaya Jun 18 '12 at 07:07
  • If you are willing to bow to the power of qt, yes: http://qt-project.org/doc/qt-4.8/QMetaObject.html – Ferenc Deak Jun 18 '12 at 07:10
  • 1
    There are some C++ interpreters: http://stackoverflow.com/questions/69539/have-you-used-any-of-the-c-interpreters-not-compilers/ – Anderson Green Mar 01 '13 at 04:37
  • -1 This is a terrible idea and I cannot in good conscience encourage it. If you get in the habit of solving problems with `eval`, you are getting in a very, very bad habit. Your code will be slow, inefficient, vulnerable, and difficult to understand. – Parthian Shot Aug 16 '16 at 15:23
  • 13
    So because it's bad practice, one shouldn't know of it's existence and the possibilities? Way to go buddy! I couldn't ignore your lecture in good conscience. – dystopiandev Dec 20 '16 at 22:44

5 Answers5

33

C++ doesn't have reflection so you must hack it, i. 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;
}
Wagner Patriota
  • 5,494
  • 26
  • 49
Hauleth
  • 22,873
  • 4
  • 61
  • 112
  • is functional a separate class? Please can you elaborate? Thanks – Dipesh KC Jun 18 '12 at 07:43
  • `function` is new class in C++11 that is in `functional` header. – Hauleth Jun 18 '12 at 07:44
  • Thanks so much . My compiler could not compile the line std::map> functions; But it runs when i change it to this map functions; It works now. I've not used map before and know little about functional pointers. Have I made the right thing or could it be fixed it a better way? Is it possible to pass parameters using this way? – Dipesh KC Jun 18 '12 at 08:16
  • Why not? Pass it to the call. About C++11: in GCC you must use `-std=c++11` flag to get this compile and work. – Hauleth Jun 18 '12 at 09:51
  • If I have `void foo(x){std::cout< – Beta Decay Aug 28 '14 at 10:46
  • How is this concerned with reflection? I would like to know if C++ has reflection, how does the pseudo code would look like. Storing all the global functions inside a class and then try to get the function call as in Java ? https://stackoverflow.com/a/37632/5983841 – Rick Nov 26 '21 at 17:04
  • C++ do not have OOTB project-wide reflection. Most of the reflection libraries require explicite code to mark functions and structures that will provide reflection. – Hauleth Nov 26 '21 at 23:24
10

There are at least 2 alternatives:

  • The command pattern.
  • On windows, you can use GetProcAddress to get a callback by name, and dlopen + dlsym on *nix.
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
8
#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" );
}
DavidA
  • 81
  • 1
  • 1
  • 4
    A compiled program that compiles code. Should note that this doesn't work on all platforms. – Mossarelli Jan 09 '17 at 11:42
  • @DavidA I don't see why that would work. I tried something similar in swift, and even with a 10 second delay, due to the way that compilers work, it executes files the way they were at compile time. – Sapphire_Brick Nov 08 '19 at 01:32
5

You could try to adopt an existing scripting engine, expose the functions you like to this and then use this to evaluate your statements. One such enging could be the V8 engine: https://developers.google.com/v8/intro but there are many alternatives and different languages to choose from.

Here are some examples:

Tommy Andersen
  • 7,165
  • 1
  • 31
  • 50
2

Except using the function map in the program and hack it on the Makefile, you can access it through ELF.

I think this method is better as it did not need to write duplicate code and compile it every time on different machine.

Here is my demo C/C++ equivalent of 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;
}

This implementation is based on Example of Printing the ELF Symbol Table

chris
  • 2,761
  • 17
  • 24