I want to create a map whose key is a string and whose value is one of the member functions of a class. I originally had the functions outside the class and that could be called from a function in the class.
void nvmRead(unsigned char* msg, unsigned char* resp_frame);
void nvmWrite(unsigned char* msg, unsigned char* resp_frame);
void nvmProvision(unsigned char* msg, unsigned char* resp_frame);
void nvmStatus(unsigned char* msg, unsigned char* resp_frame);
std::map<std::string, void (*)(unsigned char* msg, unsigned char* resp_frame)> command_table = {
{ "NVM_READ", nvmRead },
{ "NVM_WRITE", nvmWrite },
{ "NVM_PROVISION", nvmProvision },
{ "NVM_STATUS", nvmStatus }
};
Then from a function in my class that receives a command request corresponding to one of the keys in the s variable it jumps to the appropriate function. I call the function using:
command_table.at(s)(parm1, parm2);
That got me to the appropriate function when they were defined external to the class.
I am trying to do the same thing but I want the above functions to be within the class, so that it can access private members of the class.
If I move the above lines of code into the class in the public section it seems to complain about not being able to convert the functions:
error: could not convert '{{"NVM_READ", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmRead}, {"NVM_WRITE", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmWrite}, {"NVM_PROVISION", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmProvision}, {"NVM_STATUS", ((myna::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmStatus}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, void (*)(unsigned char*, unsigned char*)>'
85 | };
I have tried various combinations that I won't go into here. Including making the assignments in a constructor doing something along the lines of
command_table["NVM_READ"] = nvmRead
which generates this error:
src/tests/raid_dev_comm_in_task.cpp:40:31: error: cannot convert 'myns::RaidDevCommInTask::nvmRead' from type 'void (myns::RaidDevCommInTask::)(unsigned char*, unsigned char*)' to type 'std::map<std::__cxx11::basic_string<char>, void (*)(unsigned char*, unsigned char*)>::mapped_type' {aka 'void (*)(unsigned char*, unsigned char*)'}
40 | command_table["NVM_READ"] = nvmRead;
| ^~~~~~~
Again trying lots of different combinations that I won't go into here.
I can't seem to find the correct combination.
How can I make a map where a string is used as the key and the value is a class' member function within the class?
CodePudding user response:
Instead of simple function pointers, you could store pointers-to-member-functions. Note that the syntax for calling them is a bit different, since they require an object to work on:
using MemFnT = void(RaidDevCommInTask::*)(unsigned char*, unsigned char*);
std::map<std::string, MemFnT> command_table = {
{ "NVM_READ", &RaidDevCommInTask::nvmRead },
{ "NVM_WRITE", &RaidDevCommInTask::nvmWrite },
{ "NVM_PROVISION", &RaidDevCommInTask::nvmProvision },
{ "NVM_STATUS", &RaidDevCommInTask::nvmStatus }
};
Then when you call them, you'll do something like:
MemFnT command = command_table.at(s);
(this->*command)(param1, param2);
CodePudding user response:
You could make a lookup table of member functions:
struct RaidDevCommInTask {
void call(std::string cmd, unsigned char* msg, unsigned char* resp_frame) {
static std::unordered_map<std::string,
void(RaidDevCommInTask::*)(unsigned char*, unsigned char*)> command_table
{
{ "NVM_READ", &RaidDevCommInTask::nvmRead },
{ "NVM_WRITE", &RaidDevCommInTask::nvmWrite },
{ "NVM_PROVISION", &RaidDevCommInTask::nvmProvision},
{ "NVM_STATUS", &RaidDevCommInTask::nvmStatus }
};
// call the found member function:
(this->*command_table.at(cmd))(msg, resp_frame);
}
private:
void nvmRead(unsigned char*, unsigned char*){}
void nvmWrite(unsigned char*, unsigned char*){}
void nvmProvision(unsigned char*, unsigned char*){}
void nvmStatus(unsigned char*, unsigned char*){}
};
If you need the map accessible from other functions, make it static
to the class instead of inside call
.
CodePudding user response:
If you can use C 11, the simplest answer would be to use std::bind
and std::function
.
Something like this:
void nvmRead(unsigned char* msg, unsigned char* resp_frame);
void nvmWrite(unsigned char* msg, unsigned char* resp_frame);
void nvmProvision(unsigned char* msg, unsigned char* resp_frame);
void nvmStatus(unsigned char* msg, unsigned char* resp_frame);
std::map<std::string, std::function<void(unsigned char*, unsigned char*)>> command_table = {
{ "NVM_READ", std::bind(&RaidDevCommInTask::nvmRead, this, _1, _2) },
{ "NVM_WRITE", std::bind(&RaidDevCommInTask::nvmWrite, this, _1, _2) },
{ "NVM_PROVISION", std::bind(&RaidDevCommInTask::nvmProvision, this, _1, _2) },
{ "NVM_STATUS", std::bind(&RaidDevCommInTask::nvmStatus, this, _1, _2) }
};
Note that this creates an instance of command_table
for each instance of the class, which is often undesirable.
Also, std::function
can be more expensive, so if you need performance, it'd be better just to have something like:
enum class Command {
NVM_READ,
NVM_WRITE,
NVM_PROVISION,
NVM_STATUS,
};
static std::map<std::string, Command> command_table = {
{ "NVM_READ", Command::NVM_READ },
{ "NVM_WRITE", Command::NVM_WRITE },
{ "NVM_PROVISION", Command::NVM_PROVISION },
{ "NVM_STATUS", Command::NVM_STATUS },
};
void nvmCommand(const std::string& command, unsigned char* msg, unsigned char* resp_frame)
{
switch (command)
{
case Command::NVM_READ:
return nvmRead(msg, resp_frame);
case Command::NVM_WRITE:
/* ... */
}
}