1

I am trying to call a function using a std::map key value pair. I found this stackoverflow article Calling a function depending on a variable? but the solution

std::map<std::string, std::function<void()>> le_mapo;

does not work and results in a error something like "error: lvalue required as unary ‘&’ operand" using it as so

std::map<std::string, std::function<void()>> le_mapo;
le_mapo["ftp"] = &ftp(); // ftp function is in the same class, this function is the constructor

I am trying to call a function in the same class with this method and it results in a lvalue error any idea what to do

I have also tried using at the top

#define BE_EVIL(map, function, x) map[ #x ] = & function ## x

NOTE: i have all the proper includes such as

#include <iostream>
#include <map>
#include <functional>

here is a Reproducible Example

#include <iostream>
#include <map>
#include <functional>


class stackoverflow
{
    private:
        void ftp();
    
    public:
        stackoverflow();
};

void ftp()
{
    std::cout << "Minimal reproduction" << std::endl;
}

stackoverflow::stackoverflow()
{
    std::map<std::string, std::function<void()>> le_mapo;
    le_mapo["ftp"] = &ftp();
}
Jonathan Coletti
  • 448
  • 4
  • 13

2 Answers2

6

Because ftp is a member function, you are going to have to use a lambda here (well, you could use std::bind, but lambdas have largely superseded that these days).

So you want:

le_mapo["ftp"] = [this](){ ftp(); };

This "captures" this, thus enabling you to call ftp() on the correct object.

&ftp() doesn't work for a number of reasons:

  • appending () means that you are attempting to call the function (and then taking the address of whatever it returns), rather than taking the address of ftp directly.

  • the syntax for taking the address of a member function is &stackoverflow::ftp, and not just &ftp.

  • you are not passing this anywhere, so the caller won't have an object to call ftp on.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
2

This:

le_mapo["ftp"] = &ftp();

is wrong because ftp() is calling the function and only then the address-of operator is applied. Though you cannot take the adress of void. The function pointer is &stackoverflow::ftp.

You need an object to call a member function. std::function has some magic to turn a member function into a callable that takes the object as parameter, hence this works:

stackoverflow::stackoverflow()
{
    std::map<std::string, std::function<void(stackoverflow&)>> le_mapo;
    le_mapo["ftp"] = &stackoverflow::ftp;
}

When calling the functions in the map you must pass a reference to an object of type stackoverflow that will be used to call the member function.

Alternatively you can wrap the object together with the member function in a lambda, as shown in the ohter answer.


PS: I suppose there is a typo in your code, because this:

void ftp()
{
    std::cout << "Minimal reproduction" << std::endl;
}

is a free function completely unrelated to stackoverflow::ftp. You don't need a lambda (other answer) or a std::function with an additional paramter when ftp is a free function. For free functions the answer in the Q&A you link is all you need.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thanks for the more indepth explanation, is lambda better practice or this? – Jonathan Coletti Aug 11 '21 at 17:54
  • @JonathanColetti it depends what you want. The lambda in the other answer can only be used as long as `this` is still alive. The `std::function` does not depend on the object that created it but can be called with other objects. On the other hand, it must get an object passed which can be a minus when you call it with the same object only anyhow – 463035818_is_not_an_ai Aug 11 '21 at 17:56
  • Interesting bit about that... Technically no. `void ftp()` declares (and goes on to define) a free function named `ftp` which is completely different from the `stackoverflow::ftp` that is declared in the `stackoverflow` class. No lambda required, but if you really wanted to use `stackoverflow::ftp` you would need to bundle `this` into something to be able to invoke `stackoverflow::ftp` on `this`. That or make `stackoverflow::ftp` `static`. – user4581301 Aug 11 '21 at 17:59
  • @JonathanColetti oh right. Is that a typo in the question? Do yuo really want to have two different `ftp` functions? – 463035818_is_not_an_ai Aug 11 '21 at 18:01