0

I want to use string value to call a function. Is this possible? Can someone please show some implementation. I would appreciate it.

class obj {
    int num1;
    int num2;
}

int func1(obj o) {
   return o.num1 + o.num2;
}

// This is an example data set. This map would be populated with values in the map below with some piece of code.
std::map<std::string, std::string> funcNameMap = {{"func1", "func1"}};

int someFunction(std::string funcName, obj o) {
    // Get the value associated with the string "funcName" i.e. "func1". 
    // Calls function "func1" with value "o" and returns the value.
}

int main(){
   obj o;
   o.num1 = 1;
   o.num2 = 2;
   auto x = someFunciton("func1", o);
   std::cout << x << std::endl;
   return 0;
}


I am writing a program that collect metric data. I want to execute a particular function for a particular metric. I would name all the functions same as the metric name so that when I give metric name as input (string) it would execute a particular function. I don't want if-else for executing function based on the metric name input as my program is collecting over a hundred metric. It would look pretty awkward to have that may if-else. That is why I am trying to do something this way.

Want something that works for both OSs windows/linux

8
  • ...I hope you're not trying for some kind of JS-like eval() for C++... right? Commented Oct 1, 2022 at 4:00
  • yeah i am not. I want to make my code generic. I want to execute a function that would match the string. The function would already exist in my code. Commented Oct 1, 2022 at 4:07
  • In general, the name of a function does not change how a program works in C++. The one exception is that it is used as part of how the program links. If linking is not dynamic, then even this information is lost before execution. So you could either do this with a dynamic library exporting your functions with "C" linkage, or there being code that maintains an association between the string and the name. The association code could be generated, but it must exist. Commented Oct 2, 2022 at 3:09
  • I mean, you could even add the function-name association at the point of function declaration, possibly in a macro. But your requirements are either "you want C++ to have "eval"", or too vague to work out a real solution. C++ does not have "eval". "I want each function name to be named once" is an example of a non-vague requirement, if there aren't hidden requirements hiding somewhere else. Commented Oct 2, 2022 at 3:31
  • @Yakk-AdamNevraumont can u give an example? Can i annotate a function with a name like in webdev a particular function is executed when a particular end point is hit which the function is annotated with. Is there something similar that I can do for my type of case Commented Oct 2, 2022 at 3:58

1 Answer 1

5

One solution is to create a mapping from function name to function itself. We know that a function is a pointer in C/C++, so we can easily do this after confirming the type of the function pointer. In your case, the target function type is int (*)(obj).

Code snippet:

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

class obj {
  public:
    int num1;
    int num2;
};

int func1(obj o) {  // type: int (*)(obj)
    return o.num1 + o.num2;
}

// create the mapping from name to function
std::map<std::string, int (*)(obj)> funcNameMap = {{"func1", func1}};

int someFunction(std::string funcName, obj o) {
    // Get the value associated with the string "funcName" i.e. "func1". 
    // Calls function "func1" with value "o" and returns the value.
    auto fn = funcNameMap.at(funcName);
    return fn(o);
}

int main(){
   obj o;
   o.num1 = 1;
   o.num2 = 2;
   auto x = someFunction("func1", o);
   std::cout << x << std::endl;
   return 0;
}

Edit: Update according to the comment, use dynamic shared library to avoid manually creating the mapping. I assume you are using Linux.

First, move class definition and the type of target functions into a separated header obj.hpp:

class obj {               
  public:                 
    int num1;             
    int num2;             
};                        
// type of target functions
typedef int (*ftype)(obj);

Collect target functions to a separated file functions.cpp to build dynamic shared library.

#include "obj.hpp"

extern "C"
int func1(obj o) {
    return o.num1 + o.num2;
}
extern "C"
int func2(obj o) {
    return o.num1 - o.num2;
}

In the third file main.cpp, we retrieve functions from the library according to the function name.

#include <iostream>
#include <map>
#include <dlfcn.h>
#include <cassert>
#include "obj.hpp"

// name mapping: public name to inner name
std::map<std::string, std::string> funcNameMap = {{"add", "func1"}, {"sub", "func2"}};

int retrieve_fn(void *dl_handler, std::string funcName, obj o) {
    assert(dl_handler != NULL && funcNameMap.count(funcName) != 0);
    auto real_fn_name = funcNameMap[funcName];
    auto fn = (ftype)dlsym(dl_handler, real_fn_name.c_str());
    auto dl_err = dlerror();
    if (dl_err) {
        std::cerr << "Load failed: " << dl_err << std::endl;
        return -999;
    }
    return fn(o);
}

int main(){
    obj o;
    o.num1 = 1;
    o.num2 = 2;
    // open dynamic shared library
    auto dl_name = "./functions.so";
    auto dl_handler = dlopen(dl_name, RTLD_LAZY);
    auto dl_err = dlerror();
    if (dl_err) {
        std::cerr << "Open failed: " << dl_err << std::endl;
        return -1;
    }
    auto x = retrieve_fn(dl_handler, "add", o);
    std::cout << x << std::endl;
    x = retrieve_fn(dl_handler, "sub", o);
    std::cout << x << std::endl;
    dlclose(dl_handler);
    return 0;
}
  1. Build a dynamic shared library from functions.cpp.
$ g++ functions.cpp -shared -fPIC -o functions.so
  1. Build an executable file from main.cpp.
$ g++ main.cpp -ldl -o main
  1. Run and check the results.
$ ./main
3
-1
Sign up to request clarification or add additional context in comments.

8 Comments

It would be nice to add some error checking so you're not calling an invalid pointer if the string isn't in the map.
I don't want to populate the map manually. I want to make my code generic. My program would populate the map (reading an input.json file that contains name of function) with key(string) value(string) where value would match a function name that is already present in file. I would then pass the value (obj) to that function. I already have something similar.
@Anees, let me make it clearer. The real question is how to get a structure that links names to already defined functions automatically, right? If the names are real function names, you can do this with dynamic shared library. First, collect these target functions in a separated file and build it as a dynamic shared library. Then, use dlopen and dlsym to open the library and achieve the mapping from name to function. This answer may be helpful.
@Anees "I don't want to populate the map manually" This is generally not possible. Why do you need that? Please describe a real-world scenario.
@n.1.8e9-where's-my-sharem. I am writing a program that collect metric data. I want to execute a particular function for a particular metric. I would name all the functions same as the metric name so that when I give metric name as input (string) it would execute a particular function. I don't want if-else for executing function based on the metric name input as my program is collecting over a hundred metric. It would look pretty awkward to have that may if-else. That is why I am doing that. Also for the same reason I wanted to populate the map automatically instead of mapping names manually.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.