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

Anees
  • 49
  • 5
  • ...I hope you're not trying for some kind of JS-like `eval()` for C++... right? – Dai Oct 01 '22 at 04: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. – Anees Oct 01 '22 at 04: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. – Yakk - Adam Nevraumont Oct 02 '22 at 03: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. – Yakk - Adam Nevraumont Oct 02 '22 at 03: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 – Anees Oct 02 '22 at 03:58
  • @Anees No, I can't give an example unless you are specific about what you want an example of. Can I give an example of what? I gave a pile of plausible solutions, I do not know what you are asking for an example of in your latest comment. I asked for clarification about requirements, without clarification I can't help you. As an example, ILS answered you below with an answer that handles ALL of your requirements in the question; you added new requirements to rule it out. And REQUIREMENTS, not "I would like", what do you need. – Yakk - Adam Nevraumont Oct 02 '22 at 04:27
  • @Yakk-AdamNevraumont I don't think I have added new requirements. I have just added some text which is an example of why I need this, which I have posted in one of the comments below when a person asked for a real world example. So, I thought it would be better to put that in the original post. ILS posted something which I already had, and the solution for that was already available here https://stackoverflow.com/a/19473354/18774119. And my main question was "how to call a particular function when a particular string is passed". – Anees Oct 02 '22 at 19:40
  • @Yakk-AdamNevraumont The reason of having a map was to populate it with string values, and I wanted to associate those string values with function. I was looking to automate the entire process instead of manually writing string and function name in map manually. I wasn't sure if the solution to this problem will be possible or not. – Anees Oct 02 '22 at 19:47

1 Answers1

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
ILS
  • 1,224
  • 1
  • 8
  • 14
  • 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. – Mark Ransom Oct 01 '22 at 04:12
  • 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 Oct 01 '22 at 04:22
  • 1
    @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](https://stackoverflow.com/a/497158/10017662) may be helpful. – ILS Oct 01 '22 at 06:34
  • @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. m. could be an AI Oct 01 '22 at 06:51
  • @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. – Anees Oct 02 '22 at 02:31
  • @Anees There are many functions in your program. How do you plan to distinguish a function that is callable by a string name from a function that is not? I think you better have a list somewhere. – n. m. could be an AI Oct 02 '22 at 04:00
  • @ILS Thanks for the solution, ur second solution is what i was looking for. I need to achieve this for windows as well. I found a wrapper for dlfcn.h here https://github.com/dlfcn-win32/dlfcn-win32 and this works as well for windows, but i am getting this error "Open failed: ".\functions.so": The specified module could not be found.". How do I create function.dll using any command on Windows OS that would work like funciton.so? – Anees Oct 02 '22 at 19:58
  • @Anees. Glad it helps you. Your further question about `How do I create dll on Windows` depends on the specific development environment. If you use an IDE, such as visual studio, follow its guide to create a DLL. If you use a CLI environment, first determine what compiler you are using, and then search for its options to create a DLL. I think you may try search engine first. :) – ILS Oct 03 '22 at 03:27