Reputation: 101
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
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
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
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
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
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