Sunday, October 9, 2022

[SOLVED] use std::string to call a function in C++11

Issue

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


Solution

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


Answered By - ILS
Answer Checked By - Clifford M. (WPSolving Volunteer)