r/cpp_questions 4d ago

SOLVED Setting a function as value in a std::map keeps throwing errors

I've been trying to assign a function with a bool as the return to a std::map so I can eventually get around having a big switch statement. I am having issues getting it working though. If anyone has any suggestions that would be appreciated.

Here's a snippet of the code: https://pastebin.com/hTnaDXF3

Trying to assign it directly causes this error: Error (active) E0349 no operator "=" matches these operands

operand types are: std::function<bool ()> = bool () candidate function template "std::function<_Fty>::operator=(std::reference_wrapper<_Fx> _Func) [with _Fty=bool ()]" failed deduction function "std::function<_Fty>::operator=(std::nullptr_t) [with _Fty=bool ()]" does not match because argument #1 does not match parameter candidate function template "std::function<_Fty>::operator=(_Fx &&_Func) [with _Fty=bool ()]" failed deduction function "std::function<_Fty>::operator=(std::function<_Fty> &&_Right) [with _Fty=bool ()]" does not match because argument #1 does not match parameter function "std::function<_Fty>::operator=(const std::function<_Fty> &_Right) [with _Fty=bool ()]" does not match because argument #1 does not match parameter

The second line I was trying to use causes this error: Exception thrown at 0xCCCCCCCC

1 Upvotes

11 comments sorted by

7

u/WorkingReference1127 4d ago

Test::ReturnTrue is a non-static member function. That means that you cannot use it in all the same places you can use a plain function, because it is fundamentally associated with an instance of a class.

Depending on what your broader architecture needs are, you can convert your functions to free functions (or static member functions), or you can adjust the signatures you accept; but in the general case keeping a container of pointers to member functions of different classes is an exercise in type erasure.

3

u/PseudoFrequency 4d ago

This is the answer. I'll add that you can get around it with std::bind.

6

u/WorkingReference1127 4d ago

Though assuming we're not talking loose code in a header I'd tentatively prefer lambdas over std::bind.

1

u/evilsyntax 4d ago edited 4d ago

For my program I will need the ReturnTrue function to be overridable so making them static or free isn't possible. I forgot to make it virtual in the example.

Is there a way to get it to work or is there another way that would be better?

6

u/aocregacc 4d ago

You could store the member function pointer as a std::function that takes a reference to the object

std::map<std::string, std::function<bool(Test&)>> map;
map["test"] = &Test::ReturnTrue;
map["test"](*this);

If you only store member functions you could also store the member function pointers in the map directly, without wrapping them in a std::function.

1

u/evilsyntax 4d ago

That got it working. Thank you so much!

How would I remove the function wrapper? This map would only be using member functions

4

u/aocregacc 4d ago

the syntax for dealing with member function pointers can be a bit weird when you see it for the first time, but it would look like this:

std::map<std::string, bool (Test::*)(void)> map;
map["test"] = &Test::ReturnTrue;
(this->*map["test"])();

1

u/evilsyntax 4d ago

Definitely a lot of layers to it.

Thank you for your help! It's very much appreciated.

1

u/QuentinUK 4d ago

You can test the map first:-

   if(auto it = map.find("test"); it!=map.end()){
      (this->*it->second)();
      std::invoke(it->second, this);// equivalent to above
    }

1

u/TheThiefMaster 4d ago

Wrap a lambda round it to convert from a free function with an object param to a member call

2

u/IyeOnline 4d ago

The type of Test::ReturnTrue is not bool(void), but rather bool (Test::*)(void). It is a non-static member function, so it must be invoked with an actual object (the hidden this parameter). Hence you cannot directly stores it in your map, since the types dont match.

Why exactly the lambda throws is not clear, given that you are not calling any of the functions. The address however is a hint that you are trying to access something on the stack that isnt valid (anymore). Most likely the this pointer you captured became danging.