-2

Knowing how things roll around here somebody is gonna mark this "dupe" right away I'm sure but I sure can't find a good example for "passing member function from constructor". Plenty of passing TO constructors or passing member functions outside, but not from inside a constructor...

Story: We are using a library that uses 'mailboxes', and provides a hook for RX interrupt callbacks. We have our own class for devices. Each object-instance needs to be able to handle one of the libraries mailboxes. Therefore constructor takes a mailbox (maybe > 1 in future), and then registers itself with the comms library.

Simply: I want each objects constructor to pass a pointer to its "Handler" member function to this library.

The problem is of course with member functions vs. standard function pointers. Standard function pointer works fine, a static class function 'works' but we need to work with local object variables not static class variables.

I have read a million threads on this and see the problems between member functions with hidden 'this' arguments vs standard C function pointers etc. etc.

Have attempted to use lambda and std::function<> approaches... Maybe I am fighting the impossible? Is it impossible to pass a pointer to a function before the constructor completes creating the instance?

Example... (there is also a Class hierarchy for code-reuse purposes and there will be some function overrides and extensions in subclasses, but the basic mailbox setup should be in the top base class)


class Base // Object that processes received communication frames
{
public: 
    Base(int);
    bool Initialized;
protected:
    fooframestruct rxframe;
    bool newframe;
    void Handler(const fooframestruct&);
    void DoOtherStuff();
};

Base::Base(int rxmailbox) : 
    Initialized(false),
    newframe(false)
{
    // point comms library to instance function when data received by its mailbox
    somelibraryobject.OnReceive(rxmailbox, this->Handler);  // obviously doesn't work cuz member func
}

void Base::Handler(const fooframestruct &thisframe)
{
    // callback function - just grab the frame and set a flag
    rxframe = thisframe;
    newframe = true;
}

void Base::DoOtherStuff()
{
    cout << "blah";
    Initialized = true;
}

class SubClass : Base
{
public:
    using Base:Base;
    void ExtendWithOtherStuff();
}   


main 
{

Base device1 = new Base(1);
Base device2 = new Base(7);
SubClass device3 = new Base(5);

// etc. 

}

Naturally compiler failure, invalid use of non-static member function, which as described above, I fully understand why, just not what the best way forwared is.

I realize I could do things like create a secondary class function say

Base.RegisterMB() 

and then call that in the main setup such like:

Base device1 = new Base(1);
device1.RegisterMB();

But I hate the '2 step object init'. I suppose a 3rd workaround would be setting the library up outside the Class aka...

somelibraryobject.OnReceive(rxmailbox, device1.Handler); 

or at least something like that , but again, I'd rather encapsulate the object setup if possible.

But maybe I can't?

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 2
    If the library only takes a "standard" function pointer then there is nothing you can do. You have to use a free function or a static function. You cannot pass a statefull function to a regular function pointer. – NathanOliver Jul 29 '19 at 20:29
  • *I fully understand why, just not what the best way forwared is.* -- Ask 10 programmers how to solve this, and you will get more than 10 different answers. You need to come up with the best design to "remember" the instance(s) of your class, so as to make the call through that instance. That can be anything from creating a global object, to sending `this` as a "user defined parameter" (some API's have this), or some other scheme. – PaulMcKenzie Jul 29 '19 at 20:33
  • you could try to use `std::bind(&Base::Handler, this, std::placeholders::_1)` (https://en.cppreference.com/w/cpp/utility/functional/bind) to create a function pointer that will take one argument and invoke `this->Handler` – Zaiborg Jul 29 '19 at 20:35
  • @Zaiborg `bind` does not return a function pointer. – NathanOliver Jul 29 '19 at 20:37
  • 1
    I'm afraid I don't understand the problem. What's the interface that your comm/mailbox library offers? If it has a C API, you can have a `static` function and bind `this` via the `void*` parameter that the C API's callbacks should offer. Otherwise, I don't see why passing a member function pointer to some other place should be a problem, regardless of whether it happens from within the constructor or not. – Daniel Kamil Kozar Jul 29 '19 at 20:46
  • indeed, but std::bind would give a object that is callable but unfortunatly unable to be used with a c api – Zaiborg Jul 29 '19 at 20:48
  • Indeed the interface from the library is (in simplified code example): ```c++ somelibraryobject.OnReceive(int mailbox num, void* handler(const fooframestruct &))```. @Zaiborg are you suggesting I could create a static function that then calls the std::bind object? – vibroverbus Jul 29 '19 at 23:04
  • 1
    The problem with a static function would be to get back to the object. Usually a C API provides you the option to pass in a `void*` for user data (for example the this pointer of the object you want to have available). With a `std::function` in a C++ API you could use the `bind` approach. – Zaiborg Jul 30 '19 at 06:50

4 Answers4

1

Member functions require instance of object to be invoked. While standard components offer ways to do so, none lead to production of function pointer.

In this particular case you can return address only to a static or global function, or captureless lambda.If you have to "capture" state of your object, you have devise such mechanism yourself. E.g. you may have factory\pool\manager which can allow to query existing object based on some parameter, passing pointer in parameters (visitor pattern?) and such.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Yes, more or less. I'm not trying to capture the state of my object externally, but to provide input to my object. The callback hands a packet by reference and we want to hand that to the instance. – vibroverbus Jul 29 '19 at 23:40
0

The answer to the question is: no.

  1. Create a static function that gets registered exactly once.
  2. Create a static list in your class that registers each instance in that list. Have it de-register each instance on that instance's destruction.
  3. When that static function gets called have it call each instance with the relevant function.

Worst case is you have no instances and the static function gets called and has nothing to do.

Demolishun
  • 1,592
  • 12
  • 15
0

Can you make somelibraryobject.OnReceive() accept your callback routine as a std::function?

If so, you can use a capturing lambda to capture this, like so:

somelibraryobject.OnReceive (rxmailbox, [this] mutable () { this->Handler (); });

Live demo

If Handler() is declared const, then you don't need the mutable.

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

Basically answer is: NO can't do it inside a constructor, and, woefully C++-fighting-with-C painful to do it outside the constructor, resulting in just ugly bloated unreadable stupid code.

Outside of the constructor, in the main object setup, I also tried the std::bind approach outlined here: https://embeddedartistry.com/blog/2017/1/26/c11-improving-your-callback-game

Client c;
register_callback(std::bind(&Client::func, &c, std::placeholders::_1));

(I should point out - same as suggested by Zaiborg from the start) But no love there either. no instance of overloaded function matches argument list.

More pain than value here... I'm going to just abandoned ability to use interrupt driven logic and have to clumsily poll for messages all the time.