0

I'd like to be able to bind a function with set parameters within itself - I've been given an implementation of scheduled callbacks wherein there's a multimap from std::chrono time signatures to std::function<void(void)>, and I want to have this method do some stuff and then schedule to call itself again in the future. Thus, creating a std::bind of itself to ultimately get into this multimap with an associated time.

I'm having a devil of a time actually trying to get the syntax right here, and I'm not really able to parse the error messages / see why things aren't working. For example,

#include <functional>
#include <iostream>

class x {
public:
  void testBind(char y);
};

void x::testBind(char y) {
    std::cout<<"Blah! " << y << "\n";
    auto boundItself = std::bind(&x::testBind, &x, std::placeholders::_1);

    boundItself('h');
}

produces the following error on the line with std::bind:

error C2275: 'x': expected an expression instead of a type

https://godbolt.org/z/rncfchvPb

As a toy example, I should be able to get recursive printouts of Blah etc., but the syntax around Bind aren't being cooperative.

https://en.cppreference.com/w/cpp/utility/functional/bind From the std::bind documentation, here's an example that DOES work:

struct Foo {
    void print_sum(int n1, int n2)
    {
        std::cout << n1+n2 << '\n';
    }
    int data = 10;
};
int main()
{
    using namespace std::placeholders; 

    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);

  return 0;
}

Now, I notice that in this example, there's first &Foo:print_sum, e.g. capital F, e.g. the class definition, while the second argument is &foo, a lowercase, e.g. a specific instance of the class. But how do I get this argument into my bind definition? Do I need to declare a static global instance of my class to use as a sort of placeholder here?

JaMiT
  • 14,422
  • 4
  • 15
  • 31
DSiegmeyer
  • 17
  • 3
  • 3
    instead of `&x` you probably want `this` or `*this`. Your question seems to be how to get the current instance as a pointer/reference and the answer is `this` – user253751 Oct 26 '22 at 04:48
  • *"I'm having a devil of a time actually trying to get the syntax right here"* -- this is one reason many people prefer lambdas to `std::bind`. Still it is generally a good idea to include your error message in your question. – JaMiT Oct 26 '22 at 05:42
  • *"how do I get this argument into my bind definition?"* -- Let's look at the design: *"I want to have this method do some stuff and then schedule to call itself again in the future."* Apparently, you want the same object as used in the initial call. So I think we can rule out "declare a static global instance of my class to use as a sort of placeholder here". – JaMiT Oct 26 '22 at 05:47
  • @JaMiT - So I was triumphant and I got this to compile and run using exactly that - but I now suspect that the default values of the static global are interfering with the future calls that I'm trying to schedule - When it's time to execute the first scheduled call, some of my member values have suddenly hard set to 0!! Very frustrating!! – DSiegmeyer Oct 26 '22 at 07:25
  • 1
    **Rule of thumb:** whenever you think the solution to a syntax error is to introduce a global variable, reject that idea and go back to the drawing board. More generally, avoid global variables unless your initial design calls for them (and even then, have second thoughts). – JaMiT Oct 26 '22 at 07:31

1 Answers1

0

You came close by noticing that the second parameter had to be a specific instance of the class. This is an application to std::bind of a more general principle – every call to a non-static member function must be associated with an object of the class. In std::bind, this principle takes the form of providing the object as the second parameter. In more common cases, this principle takes the form of providing an object in front of the function call, as in obj.testBind('h'), where obj had been earlier declared as x obj;.

So the question is which object should be associated with the bind? Since the context is inside a member function, one likely possibility is the object associated with that call. Remember, every call to testBind() must be associated with an x object. This applies to the call you hope to make in the future (via the bind) and to the call that is currently executing. Since the goal is to repeat the current call, it makes sense to use the current object.

The name given to the address of the current object is this, so the bind you are looking for is probably

std::bind(&x::testBind, this, std::placeholders::_1)
//                      ^^^^

Alternatively, you could abandon std::bind and use a lambda.

auto boundItself = [this](char yy) { testBind(yy); };

Wait a minute! Where is the object for the call to testBind() in the lambda version? What happened to the principle driving this answer? (If you were already asking that before reading this far, good observation skills.)

Nothing happened to the principle. We have simply stumbled into a different context. Inside a member function, there is a shorthand syntax that obscures the principle. Accessing members of the current object is so common that the compiler will assume the current object if none is specified. This assumption spills into lambdas that capture this. The statement testBind(yy); in the lambda is merely shorthand for this->testBind(yy);. There is still an object associated with the new call to testBind().

JaMiT
  • 14,422
  • 4
  • 15
  • 31