Как сохранить методы как указатели на функции в контейнере карты?
Я хочу иметь возможность вызывать функции на основе данных, которые я читаю из файла. Поэтому для каждого типа элемента я хочу вызвать нужный метод чтения. Я написал этот код, но он не компилируется там, где я хочу добавить указатели функций на карту. Что случилось?
#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();
}
Я передаю имя таблицы читателю, это хорошо делается с помощью привязки и заполнителя.