1

I have tried to build code which passes a function to be used within another function. Below is the code I tried, and it works OUTSIDE the function (so if I delete everything related to class Foo, it works), but I have no idea how to get this to work within the class itself. How can I pass one function within the class along? I tried with this->part1_math_class, Foo::part1_math_class and part1_math_class, none worked.

// Example program
#include <iostream>
#include <string>
#include <stdint.h>
#include <functional>

int64_t loop(std::string partialMath, std::function<int64_t(std::string)> recurse)
{
    while (partialMath.find("(") != std::string::npos)
    {
        auto posClose = partialMath.find(")");
        auto posOpen = partialMath.rfind("(", posClose);

        std::string sub = partialMath.substr(posOpen + 1, posClose - posOpen - 1);

        int64_t resultInner = loop(sub, recurse);
        partialMath.replace(posOpen, sub.size() + 2, std::to_string(resultInner));
    }

    return recurse(partialMath);
}

int64_t part1_math(std::string math)
{
    return 0;
}

int64_t part2_math(std::string math)
{
    return 1;
}

class Foo {
public:
    Foo() { }
    
    int64_t loop_class(std::string partialMath, std::function<int64_t(std::string)> recurse)
    {
        while (partialMath.find("(") != std::string::npos)
        {
            auto posClose = partialMath.find(")");
            auto posOpen = partialMath.rfind("(", posClose);
    
            std::string sub = partialMath.substr(posOpen + 1, posClose - posOpen - 1);
    
            int64_t resultInner = loop_class(sub, recurse);
            partialMath.replace(posOpen, sub.size() + 2, std::to_string(resultInner));
        }
    
        return recurse(partialMath);
    }
    
    int64_t part1_math_class(std::string math)
    {
        return 2;
    }
    
    int64_t part2_math_class(std::string math)
    {
        return 3;
    }
    
    int64_t runLoop()
    {
        return loop_class("(1 + 2)", Foo::part1_math_class);
    }
};

int main()
{
  std::cout << loop("(1 + 2)", part1_math) << std::endl;  // this one works
  Foo bar;
  std::cout << bar.runLoop() << std::endl;  // this one does not even compile
  return 0;
}
3
  • Do you want to send a member function as a parameter to another function ? Commented Dec 18, 2020 at 10:53
  • Yes, but within the same class. I could use some sort of if logic to do the same stuff, but I wanted to just pass along the function (within the same class) to run. Commented Dec 18, 2020 at 10:54
  • Why don't you use a lambda? return loop_class("(1 + 2)", [this](std::string math) { return part1_math_class(math); }); std::function can be used with functions as well as functors. In this case, the lambda provides the matching functor (correct signature) and binds (captures) this as member. Commented Dec 18, 2020 at 10:58

4 Answers 4

1

Concerning

    int64_t runLoop()
    {
        return loop_class("(1 + 2)", Foo::part1_math_class);
    }

Foo::part1_math_class() has the matching signature for the std::function<int64(std::string)> when called with the correct object. In this case, this would be the pointer to the intended object. Hence, it must be "cheated" somehow into the call.

An easy way is to use a functor instead of a function. A functor is a class which overloads the operator() and can act like a function but with additional context.

std::function arguments can accept functors as well as functions.

The IMHO easiest way to write a functor is a lambda:

    int64_t runLoop()
    {
        return loop_class("(1 + 2)",
          [this](std::string math) { return part1_math_class(math); });
    }

this is captured. The signature matches as the parameter is compatible and the return type is auto-detected from the lambda's body and matching as well.

Live Demo on coliru

Sign up to request clarification or add additional context in comments.

1 Comment

Nice, that worked exactly as I wanted it to. I really need to read up properly on lambdas on how to use them.
1

First, to form a member function pointer you need &Foo::part1_math_class; note the &.

Second, when an n-ary non-static member function pointer is stored in a std::function, it acts as an n+1-ary function because it requires an implicit first argument that becomes the this pointer. Since your non-static member function is unary, it would be suitable for a binary std::function. But your std::function is also only unary. Therefore, you cannot use a non-static member function pointer.

Declare part1_math_class static to make it work.

5 Comments

And how would I do it with std::bind then? I tried that as well, but couldnt get it to work either.
That should be a topic of a different question.
The advantage of std::function is that it can be used with functors also. Hence, you can provide additional context by the function members. this could be part of this context. Hence, I don't see why you recommend Declare part1_math_class static to make it work.
Well, assuming I use member variables within part1_math of the current class, I cannot use a static function (which is the case for my real scenario, I just thought the example would illustrate the issue)
Then you must do as @Sheff says: Bind the first parameter of your non-static member function to an object. In modern code, the easiest to do that is using a lambda expression.
1

You can parameterize your functions taking callbacks so that they don't require an std::function and can instead get other types of callable objects.

Then, two options are to bind this to the member function pointer, or to use a lambda:

// ...
template<typename F>
int64_t loop(std::string partialMath, F recurse)
{
    // ...
}

// ...

class Foo {
public:
    Foo() { }
    
    template<typename F>
    int64_t loop_class(std::string partialMath, F recurse)
    {
        // ...
    }
    
    // ...
    
    int64_t runLoop()
    {
        using namespace std::placeholders;
        return loop_class("(1 + 2)", std::bind(&Foo::part1_math_class, this, _1));
    }
    
    int64_t runLoopLambda() {
        return loop_class("(1 + 2)", [this](auto&& ...args) {
            return this->part1_math_class(std::forward<decltype(args)>(args)...);
        });
    }
};

// ...

Comments

0

You can make loop_class a template and then use simple bind or lambda. (General, lambda's should be prefered over bind, but in this case I find using bind simpler). For example:

int64_t loop_class(std::string partialMath, std::function<int64_t(std::string)> recurse)
{...}

or template

template <typename F>
int64_t loop_class(std::string partialMath, F recurse)
{...}

and then

int64_t runLoop()
{
    return loop_class("(1 + 2)",  bind(&Foo::part1_math_class, this, std::placeholders::_1));
}

See live

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.