How to store methods as function pointers in a map container?

Viewed 3040

I want to be able to call functions based on the data I read from file. So for each item type, I want to call the desired reader method. I wrote this code, but it does not compile where I want to add function pointers to the map. What is wrong?

#include <vector>
#include <map>
#include <iostream>
class reader
{

  std::map< std::string, void(*)()> functionCallMap; // function pointer
  void readA(){ std::cout << "reading A\n";};
  void readB(){ std::cout << "reading B\n";};;
public:
  reader()
  {
    *functionCallMap["A"] = &reader::readA;*
    *functionCallMap["B"] = &reader::readB;*
  }

  void read()
  {
   auto (*f) = functionCallMap["A"];
   (*f)();
  }



};

I am filling the map at Constructor.

4 Answers

You can use std::function with a lambda or std::bind :

class reader
{
    std::map<std::string, std::function<void()>> functionCallMap;

    void readA() { std::cout << "reading A\n"; };
    void readB() { std::cout << "reading B\n"; };

public:
    reader()
    {
        functionCallMap["A"] = [this]() { readA(); };
        functionCallMap["B"] = std::bind(&reader::readB, this);
    }

    void read()
    {
        functionCallMap["A"]();
        functionCallMap["B"]();
    }
};

You need to use pointers to member functions, like this:

class reader
{
    using FuncPtr = void(reader::*)(); // function pointer
    std::map< std::string, FuncPtr> functionCallMap;
    void readA(){ std::cout << "reading A\n"; }
    void readB(){ std::cout << "reading B\n"; }
public:
    reader()
    {
        functionCallMap["A"] = &reader::readA;
        functionCallMap["B"] = &reader::readB;
    }

    void read()
    {
        auto f = functionCallMap["A"];
        (this->*f)();
    }
};

int main()
{
    reader r;
    r.read();
}

There are two answers so far, this and this.

The obvious difference is that one uses std::function and other uses function pointers. This is not the important difference!!

The key point is that the member functions are non-static member functions. So, they are not of type void().

They are of type void(reader::*)(). Thus, they can be only called if an object of type is reader is given; one can understand this somewhat as a hidden parameter.

The first answer just fixes the problem by specifying the correct type. This can be done using function pointers (as presented) or using std::function (The latter is much more expensive!).

The second answer fixes the problem by binding the function pointer to the particular instance of the class. After binding, the type is then indeed void(). This cannot be done using raw function pointers (because they can only point to a function and not an object/function pair!).

I ended up with this solution. It does the job, but I have some doubts over its aesthetics. Anyway, to sum up, I ended up with this code:

#include <map>
#include <iostream>
#include <functional>
class reader
{
    std::map< std::string, std::function<void(std::string tableName)>> functionCallMap; // function pointer
    void readA(const std::string tableName){ std::cout << "reading:" << tableName<< "\n"; }
    void readB(const std::string tableName){ std::cout << "reading:" << tableName <<"\n"; }
public:
    reader()
    {
      functionCallMap["A"] = std::bind(&reader::readA, this, std::placeholders::_1);
      functionCallMap["B"] = std::bind(&reader::readA, this, std::placeholders::_1);
    }

    void read()
    {
      const std::string table_name = "A";
      functionCallMap[table_name](table_name);
    }
};

int main()
{
    reader r;
    r.read();
}

I pass the table name to the reader, it is nicely done with the bind and placeholder.

Related