Callbacks (or function pointers) between classes


#1

Hi,
I am trying to implement some kind of callback between classes…so the children have a pointer to a parent class.
I want it to pass events to the parent
So I have something like the following…and i have got an error…how can I send a function pointer from parent to children?

//***********************A.h
#include "B.h"

class A
{ 
public: 
    A(){
        b_object = new B();
        b_object->set_callback(b_callback); //****error 
    }

    ~A(){b_object = nullptr;}

   void b_callback(Component* listenercomponent, String typeoflistener) {
       //Do something as add it as listener: listenercomponent->addlistener(this);
    }
private:

   ScopedPointer<B>  b_object;    
} 

//*********************B.h
class B{
{
public:
    B(){
    //init whatever...buttons, and so on         
    }
    ~B(){
            pointer_callback = nullptr;
};

void set_callback(void(*x)(Component *listenercomponent, String typeoflistener)) {
	pointer_callback = x;
}

private:

    void(*pointer_callback)(Component *listenercomponent, String typeoflistener);
}

//*******************************************
ERROR in Visual Studio: (Note: “eth_panel” would be “A” in the example;
Severity Code Description Project File Line Suppression State
Error C2664 ‘void table_dev::set_gui_listbox_callback(void (__cdecl *)(juce::Component ,juce::String))’: cannot convert argument 1 from 'void (__thiscall eth_panel:: )(juce::Component *,juce::String)’ to ‘void (__cdecl *)(juce::Component *,juce::String)’ Eth1 c:\carlosm\electronica\2-sw\juce\projects\eth1\source\eth_panel_gui.cpp 88

JUCE ERROR pop up WARNING:: argument of type “void(A::)()” is //incompatible with “void(A*)()”


#2

TL;DR
Can you please add the error message the compiler throws.
And if you indent your code by four spaces the forum will show it as code with indents and highlighting (you can edit your original post to save space).
Then somebody might be able to help…
Cheers


#3

If you can do C++11 I would pass a std::function rather than a pointer, then you can define it using a lamdba.

E.g. one of my current projects I have

typedef std::function<void (float)> NodePressCallback;
class ChildComponent : public Component
{
public:
    //...
    void mouseDown (const MouseEvent& event) override
    {
        onPressCallback (someFloatValue);
        repaint();
    }
    void setOnPressCallback (NodePressCallback cb) { onPressCallback = cb; }

    //...

private:
    // member variables for the Component
    NodePressCallback onPressCallback;
}

And then in my MainComponent I have something like:

        childComponent->setOnPressCallback([this](float floatValue)
        {
            // Do something with floatValue
            // [this] allows you to access local members here
        });

#4

Ohh, i didn’t know the 4 space indent!!how I couldn’t!!! ;D
thanks


#5

Thanks, much better to read :slight_smile:

Probably a good idea to learn C++11 and follow adamskis advice. I didn’t yet.
But I usually look, how JUCE is doing it, and an example is ListenerList and it’s usage:

https://www.juce.com/doc/classListenerList#details

Or in Button:

Hope that helps


#6

thanks for your help!, but i can’t makeit yet. I guess I do C++11 since I am using VS2015.

How do you register the parent function to the children?Something like:

@parent:

void getfloat(float value){
}

then:

childrenComponent->tbl_devices->setOnPressCallback(&getfloat);  //??i get errors here: &ilegal

I don’t understant how you define that function:

childComponent->setOnPressCallback([this](float floatValue)
    {
        // Do something with floatValue
        // [this] allows you to access local members here
    });

#7

thanks, but I don’t really get it :cold_sweat:
Any advice related to my simple example?? :innocent:


#8

i’d take a look at how the nested listener classes inside the JUCE components work and just do the same for your own components, it’s a bit cleaner and nice to stick with the designs laid out from the frameworks. Take a look at the button class and the nested JUCE Listener.

You make a nested class and a listener list. then you can add listeners to your component. You inherit from the listener class into the objects you want to receive callbacks and then can call those callbacks from your class. much less coupling.


#9

thanks for your help.

I am taking a look but it doesn’t look as somthing to implement in a few hours without some “more” help…well, I will have to learn :open_mouth:


#10

Anyway I don’t really understand why I couldn’t implement what I was trying…


#11

Sorry, but I don’t understand, what you want to achieve. That’s why I didn’t give any code in the first place…

1.) what is b_object?
if it’s B b_object; then you don't need to call new. Are you aware of the difference between heap memory and stack memory?
if it’s B* b_object; then you create it on the heap with new. But then you don’t call methods with . but with -> operator: b_object->set_callback(…);

2.) you are calling b_object.set_callback(b_callback); But you don’t show, how this method in B is declared.

3.) in C++ a function pointer is not enough, you need the object and the function.

To confuse you even more ;-), here is a concept, how you can wrap object and method together:
http://en.cppreference.com/w/cpp/utility/functional/function

Sorry that I couldn’t solve your problem, but eventually it get’s you a step further…


#12

setOnPressCallback in my example takes a std::function as its argument. Its passed in as a lambda. Look up C++11 lambdas if it doesn’t make sense.


#13

To register this as callback, you would define a function type like in adamskis example:

typedef std::function<void (B&, Component*, String)> B_Callback;
class A {
public:
    void setCallback (B_Callback cb) {
        callback = cb;
    }
    void triggerCallback (B& b, Component* c, String text) {
        callback (b, c, text);
    }
private:
    B_Callback callback;
};

A a;
B b;
Component* c = new Component();
String text ("anything");
a.setCallback (&B::b_callback);
a.triggerCallback (b, c, t);

but I didn’t test this.
Good luck


#14

Sorry but the confusion and maybe I was wrong with b_object. It is a scopedpointer, I know the differences ;D
The real thing is that I have several panels!.

A(let’s say the main component) contains B (and others…C, D…) which is a panel to control ethernet devices.I am using:

  • a listbox to show them:
    This listbox contains several columns to show the device properties, some labels: as name, model, IP, MAC address and so on,
    also buttons to connect and disconnect, and a combo box to choose a set of features. Everything within this panel.
  • It has also several buttons for tcp and udp connections,
  • and a text editor.

I can get in B all the component callbacks from buttons, the list_box and so on. And know I wanted to have notice of those events in A.
Therefore I wanted to pass a kind of function callback from A to B.

So, if in B I have the button callback from the different buttons allocated in the children (list box, and others), I wanted to call a parent method in A
from here…so:

void B_comms_panel::buttonClicked(Button* buttonThatWasClicked)
{

if (buttonThatWasClicked->getComponentID == "discovery"){
	callback_disco(buttonThatWasClicked, "furtherinfo)")
}
else if (buttonThatWasClicked->getComponentID == "dev0"){
	callback_connection(buttonThatWasClicked, "furtherinfo)")

}
else if (buttonThatWasClicked->getComponentID == ...){
	...
}

}

Therefore I wrote:

class A
{ 
public: 
A(){
    b_object = new B();
    b_object->set_callback(b_callback); //****error 
   / b_object->set_callback(&b_callback); //***error to0
}

~A(){b_object = nullptr;}

   void b_callback(Component* listenercomponent, String info) {
   //Do something 
    }
private:

ScopedPointer<B>  b_object;    

}

 //*********************B.h
class B{
{
public:
B(){
//init whatever...buttons, and so on         
}
~B(){
        pointer_callback = nullptr;

};

void set_callback(void(*x)(Component *listenercomponent, String info)) {
pointer_callback = x;

}

private:

void(*pointer_callback)(Component *listenercomponent, String info);
//listbox
//texteditor
//several buttons

}

B(the comms_panel) have a pointer to this kind of function:

void(*x)(Component *listenercomponent, String info)

And also have a function to receive the address of that callback function from parent, so the pointer can points to that function:

void set_callback(void(*x)(Component *listenercomponent, String info));

So in A, I have the object_b which is the comms_panels (with all the paint and resize stuff)

b_object = new B();
    b_object.set_callback(b_callback); //here is where I get the error  using this or with &

and the famous b_callback that I passed to B, so:

void b_callback(Component* listenercomponent, String info) {
   
   if (listenercomponent->getComponentID == "Disco")
		//whatever-> Disco_state = info;
}

#15

So it’s still

b_object->set_callback(b_callback);

You can only access the methods of the ScopedPointer object with .
To access the methods of the wrapped object, you use the -> operator.


#16

Hi, you are right, but I made that mistake when I wrote here, no in my code.
Anyway thanks for pointing it out ;D

ERROR in Visual Studio: (Note: “eth_panel” would be “A” in the example;
Severity Code Description Project File Line Suppression State
Error C2664 ‘void table_dev::set_gui_listbox_callback(void (cdecl *)(juce::Component ,juce::String))’: cannot convert argument 1 from 'void (thiscall eth_panel:: )(juce::Component *,juce::String)’ to ‘void (_cdecl *)(juce::Component *,juce::String)’ Eth1 c:\carlosm\electronica\2-sw\juce\projects\eth1\source\ethpanel_gui.cpp 88


#17

For people as me ;D, i finally found how to do it using std::bind.

I hope is compatible when compiling for Android and IOS.

typedef std::function<int(int, int)> some_function_type;

class B
{
public:
    B() {

}
void setCallBack(some_void_function_type f) {
	f_ = f;
}
void useCallBack() {
	f_(B1, B2);
}



private:
     int B1;
     int B2;
     some_function_type f_;

};

class A
{


public:
A() {
	Bguy = new B();
	Bguy->setCallBack(std::bind(&A::someMethod, this, std::placeholders::_1, std::placeholders::_2));
}

int someMethod(int xx, int xy) {
	return Ar = xx + xy;
}

B * Bguy;
int Ar;
};

hope it is helpfull for someone !