BattleWalrus
BattleWalrus

Reputation: 3

C++ How to map string keys to class method invocations for a specific object?

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

Answers (1)

Remy Lebeau
Remy Lebeau

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

Related Questions