create member function name and call it at runtime in c++

2.5k views Asked by At

Can someone give me idea on this problem. I have searched on internet about this, but couldn't get much info as I wished to have.

Say there is a class.

class Foo {
  explicit Foo() {}

  int getVar1();
  int getVar2();

  void setVar1(int v);
  void setVar2(int v);

  private:
  int var1, var2;
};

now given a list of tokens {"var1", "var2", ... "varN"}, is there any way I can create the function name at runtime and call those member functions of some object of type Foo. like for e.g

Foo obj;
string input = "Var1,Var2,Var3,...VarN";
vector<string> tokens = splitString(input);
for (vector<string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
  string funName = "get" + *it;
  // somehow call obj.getVar1()....obj.getVarN()
}

using if else is fine for small numbers of variables, but its not good for large number of variables. Using bind and functors also doesn't solve this. One webpage suggested making memory executable at runtime and then using reinterpret_cast, I don't know whether this would work.

UPDATE

Ok, as from the answers and other searches on internet, I see that there is not elegant way of doing this in C++. There is no reflection in C++ as of now. All hacks would require compile time resolution of member function pointers. Could someone give me ideas on alternate class design in these scenario when you have lots of variables and setters and getters functions...or whether getters and setters are good practice in c++ ?

5

There are 5 answers

0
yosim On

why not look at it in a referent way: For each variable assign an index number, starting from 0, 1, 2.... You keep this values in a map (key is the variable name, value is the assigned value). All the values of those variables, you keep in an array, so that the value of the first variable in in cell 0, the next one is in cell 1 etc.

so, when you want to get/set value, all you need to do, is, find it's index in the map, and access the relevant cell in vector.

0
Sorin On

You can't "add" members at runtime. C++ is strongly typed at compile time.

You can get the behaviour you want by having a map<string, func_type> and using it to resolve your string to an actual function. You can create it using macros to make sure that the string names match the function names.

#DEFINE ADD_METHOD(map_var, func) map_var["func"] = &func
0
Korchkidu On

A simple/not perfect solution could be to use a intermediate methods checking the parameter and calling the getVar* method accordingly.

An example like this one maybe:

class Foo 
{
public:
    explicit Foo() {}

    int getVar1() { return 1; }
    int getVar2() { return 2; }

    void setVar1(int v) { var1 = v; }
    void setVar2(int v) { var2 = v; }

    int callGetVar(const std::string &var)
    {
        if (var == "Var1") return getVar1();
        if (var == "Var2") return getVar2();
        else { return -1; }
    }

private:
    int var1, var2;
};

int main()
{
    Foo obj;
    std::string input = "Var1,Var2,Var3,...VarN";
    std::vector<std::string> tokens = { "Var1", "Var2", "Var2", "Var1", "Var1", "Var2", "Var2", "Var1"};
    auto tokensIT = tokens.begin();
    for (; tokensIT != tokens.end(); ++tokensIT) 
    {
        // somehow call obj.getVar1()....obj.getVarN()
        std::cout << obj.callGetVar(*tokensIT);
    }

    return 0;
}
1
Vlad from Moscow On

As an idea consider the following code

struct A
{
    void f1() { std::cout << "A::f1()\n"; }
    void f2() { std::cout << "A::f2()\n"; }
    void f3() { std::cout << "A::f3()\n"; }
    void f4() { std::cout << "A::f4()\n"; }
};

std::map<std::string, void( A::* )()> m = { { "f1", &A::f1 }, { "f2", &A::f2 }, { "f3", &A::f3 }, { "f4", &A::f4 } };

A a;

for ( auto p : m ) ( a.*p.second )();

You can make the map as a data member of your class.

0
Kargath On

You can try this

one example:

template<class C1, class C2, class R, class... A, std::size_t... I>
boost::json::value
call_impl_(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args,
    std::index_sequence<I...>)
{
    return boost::json::value_from(
        (c1.*pmf)(boost::json::value_to< boost::remove_cv_ref_t<A> >(args[I])...));
}

template<class C1, class C2, class R, class... A>
boost::json::value
call_impl(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args)
{
    if (args.size() != sizeof...(A))
    {
        throw std::invalid_argument("Invalid number of arguments");
    }
    return call_impl_(c1, pmf, args, std::index_sequence_for<A...>());
}

template<class C>
boost::json::value
call(C& c, boost::string_view method, boost::json::value const& args)
{
    using Fd = boost::describe::describe_members<C,
        boost::describe::mod_public | boost::describe::mod_function>;
    bool found = false;
    boost::json::value result;
    boost::mp11::mp_for_each<Fd>([&](auto D) {
        if (!found && method == D.name)
        {
            result = call_impl(c, D.pointer, args.as_array());
            found = true;
        }
        });
    if (!found)
    {
        throw std::invalid_argument("Invalid method name");
    }
    return result;
}

//test1 from https://github.com/bytemaster/boost_reflect 
struct calculator {     //need Generic maybe..
    int add(int v, int u) { return u + v; }
    int sub(int v) { return result_ -= v; }
    int result() { return result_; }
private:
    int result_ = 0.0;
};

BOOST_DESCRIBE_STRUCT(calculator, (), (add, sub), (result));

int main(int argc, char** argv) {
    calculator cal;
    std::string line;
    std::string cmd;
    std::string args;

    while (true) {
        std::cerr << "Enter Method: ";
        std::getline(std::cin, line);
        int pos = line.find('(');
        cmd = line.substr(0, pos);
        args = line.substr(pos + 1, line.size() - pos - 2);
        std::cout << "args: " << args << std::endl;
        std::vector<std::string> num_str;
        boost::split(num_str, args, boost::is_any_of(","));
        std::vector<int> nums;
        std::for_each(num_str.begin(), num_str.end(), [&](std::string str) {nums.push_back(std::stoi(str)); });
        // Convert the vector to a JSON array
        const boost::json::value jv = boost::json::value_from(nums);
        std::cout << call(cal, cmd, jv) << std::endl;
    }
    return 0;
}

It can be passed under visual studio 2022 c++17. with cpp20 it will report an error, I don’t know why