DeltaVega
DeltaVega

Reputation: 101

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

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++ ?

Upvotes: 1

Views: 2707

Answers (5)

Kargath
Kargath

Reputation: 528

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

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

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.

Upvotes: 4

yosim
yosim

Reputation: 503

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.

Upvotes: 0

Korchkidu
Korchkidu

Reputation: 4946

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;
}

Upvotes: 0

Sorin
Sorin

Reputation: 11968

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

Upvotes: 2

Related Questions