Как сохранить методы как указатели на функции в контейнере карты?

Я хочу иметь возможность вызывать функции на основе данных, которые я читаю из файла. Поэтому для каждого типа элемента я хочу вызвать нужный метод чтения. Я написал этот код, но он не компилируется там, где я хочу добавить указатели функций на карту. Что случилось?

#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)();
  }



};

Я заполняю карту в Конструкторе.

Ответ 1

Вы можете использовать std::function с лямбдой или 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"]();
    }
};

Ответ 2

Вам нужно использовать указатели на функции-члены, например:

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

Ответ 3

Пока есть два ответа, это и это.

Очевидное отличие состоит в том, что один использует std::function а другой использует указатели на функции. Это не важная разница!

Ключевым моментом является то, что функции-члены являются нестатическими функциями-членами. Таким образом, они не относятся к типу void().

Они имеют тип void(reader::*)(). Таким образом, они могут быть вызваны только если задан объект типа читатель; это можно понять как скрытый параметр.

Первый ответ просто решает проблему, указав правильный тип. Это можно сделать с помощью указателей на функции (как представлено) или std::function (последнее намного дороже!).

Второй ответ решает проблему, связывая указатель функции с конкретным экземпляром класса. После связывания тип действительно void(). Это невозможно сделать с помощью необработанных указателей на функции (поскольку они могут указывать только на функцию, а не на пару объект/функция!).

Ответ 4

Я закончил с этим решением. Это делает работу, но у меня есть некоторые сомнения по поводу ее эстетики. В любом случае, чтобы подвести итог, я получил следующий код:

#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();
}

Я передаю имя таблицы читателю, это хорошо делается с помощью привязки и заполнителя.