Reputation: 3
In C++, I am trying to map a user-provided string to a class method invocation for a specific object. I was successful in mapping user-provided strings to function calls for another application, but I do not know how to extend this approach to work for class methods being invoked for a particular object. I would greatly appreciate any assistance in determining how to fix my attempted implementation that is flawed:
void read_input(std::string& input_filename, Class_Name& my_object)
{
// Map string keys to object member invocation
std::map<const std::string, std::function<void (const std::string&)>> argument_map =
{
{"input_one", my_object.method_one},
{"input_two", my_object.method_two},
{"input_three", my_object.method_three}
};
std::ifstream input_file;
input_file.open(input_filename);
std::string line;
while (input_file)
{
std::getline (input_file, line);
std::stringstream argument_read(line);
std::string command;
std::string tmp;
argument_read >> command;
if (command[0] - '#' == 0) continue;
while (argument_read >> tmp)
{
if (tmp[0] - '#' == 0) break;
argument_map[command](tmp);
}
}
return;
}
Upvotes: 0
Views: 209
Reputation: 595782
Since all of the methods belong to the same class, and have the same signature, you can use Pointers to member functions, using the member access operator.*
to call them (no std::function
needed), eg:
void read_input(std::string& input_filename, Class_Name& my_object)
{
// Map string keys to object member invocation
using method_type = void (Class_Name::*)(const std::string&);
static const std::map<std::string, method_type> argument_map =
{
{"input_one", &Class_Name::method_one},
{"input_two", &Class_Name::method_two},
{"input_three", &Class_Name::method_three}
};
std::ifstream input_file(input_filename);
std::string line;
while (std::getline (input_file, line))
{
std::istringstream argument_read(line);
std::string command, param;
if (argument_read >> command && command[0] != '#')
{
auto iter = argument_map.find(command);
if (iter != argument_map.end())
{
method_type method = iter->second;
while (argument_read >> param && param[0] != '#')
(my_object.*method)(param);
}
}
}
}
Otherwise, you can use std::bind()
with std::function
, eg:
void read_input(std::string& input_filename, Class_Name& my_object)
{
// Map string keys to object member invocation
using std::placeholders::_1;
using function_type = std::function<void (const std::string&)>;
std::map<std::string, function_type> argument_map =
{
{"input_one", std::bind(&Class_Name::method_one, &my_object, _1) }
{"input_two", std::bind(&Class_Name::method_two, &my_object, _1) },
{"input_three", std::bind(&Class_Name::method_three, &my_object, _1) }
};
std::ifstream input_file(input_filename);
std::string line;
while (std::getline (input_file, line))
{
std::istringstream argument_read(line);
std::string command, param;
if (argument_read >> command && command[0] != '#')
{
auto iter = argument_map.find(command);
if (iter != argument_map.end())
{
auto &func = iter->second;
while (argument_read >> param && param[0] != '#')
func(param);
}
}
}
}
Or, you can use lambdas with std::function
, eg:
void read_input(std::string& input_filename, Class_Name& my_object)
{
// Map string keys to object member invocation
using function_type = std::function<void (const std::string&)>;
std::map<std::string, function_type> argument_map =
{
{"input_one", [&](const std::string& s){ my_object.method_one(s); } },
{"input_two", [&](const std::string& s){ my_object.method_two(s); } },
{"input_three", [&](const std::string& s){ my_object.method_three(s); } }
};
std::ifstream input_file(input_filename);
std::string line;
while (std::getline (input_file, line))
{
std::istringstream argument_read(line);
std::string command, param;
if (argument_read >> command && command[0] != '#')
{
auto iter = argument_map.find(command);
if (iter != argument_map.end())
{
auto &func = iter->second;
while (argument_read >> param && param[0] != '#')
func(param);
}
}
}
}
Upvotes: 3