Callbacks

Hello

I'm trying to figuring out how Callbacks (pointers to functions) work. I have the following problem (example):

A class TMySerialReader reads the serial input with a while(true) loop. If something is read, a callback should be sent, i.e. a function should be called. Since I don't want this callback to be linked to a specific function, I want to use a pointer to a function (readCallback).

The second class TMyClass starts the reading process, sets the callback pointer and also contains the function that is called (ProcessRead).

The problem is now, how can I set the callback pointer? I tried
caller->readCallback = &TMyClass::ProcessRead;
but this don't work. How can I do this?

thx
Fred


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class TMySerialReader
{
public:

	void (*readCallback)();

	TMySerialReader() {
		readCallback = NULL;
	}

	void read () {
		// read from serial (while(true))
		// ...

		// if something read: callback
		readCallback();
	}
};

class TMyClass
{
public:

	void ProcessRead(){
		printf("Process bytes from serial\n");
	}

	void run() {
		TMySerialReader *caller = new TMySerialReader;
		caller->readCallback = &TMyClass::ProcessRead;
		caller->read();
	}
};

int main() {
	TMyClass instance1;
	instance1.run();

	return 1;
}
The way you have it defined... readCallback is a pointer to a global function, not to a TMySerialReader member function:

1
2
3
4
5
6
7
8
9
10
11
12
13
class TMySerialReader
{
public:
    void (TMySerialReader::*readCallback)();


// to assign it
TMySerialReader *caller = new TMySerialReader;
caller->readCallback = &TMyClass::ProcessRead;

// to call it

(this->*readCallback)();  // you might be able to omit the *this -- I'm not sure.  Try it and see 
Thanks, but this don't work either.

The assignment is still wrong (invalid cast from type ‘void (TMyClass::*)()’ to type ‘TMySerialReader*’).
Shouldn't it be 'void (TMyClass::*readCallback)();' to match the assignment? But then TMySerialReader has to know TMyclass, and I don't want that. And also the "(this->*readCallback)()" don't work.
You need to note that a pointer to a non-static member function needs an object to operate on.
This means that you cannot do this:
1
2
3
4
5
6
7
void read () {
  // read from serial (while(true))
  // ...

  // if something read: callback
  readCallback();//Error need  to provide a  TMyClass object for this function to operate on.
}


Try this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

using namespace std;

class TMyClass; //Forward declaration

class TMySerialReader
{
public:

  void (TMyClass::*readCallback)();

  TMySerialReader() {
    readCallback = 0;
  }

  void read (TMyClass *pMC) //change this function to take a TMyClass pointer
  {
    // read from serial (while(true))
    // ...

    // if something read: callback
    (pMC->*readCallback)();//Call the function pointer
  }

};


class TMyClass
{
public:

  void ProcessRead(){
    cout << "Process bytes from serial\n";
  }

  void run()
  {
    TMySerialReader *caller = new TMySerialReader;
    caller->readCallback = &TMyClass::ProcessRead;
    caller->read(this);	 //Pass a pointer to the TMyClass object
  }
};

int main() {
  TMyClass instance1;
  instance1.run();

  return 1;
}
Oh whoops... I just assumed that was all part of the same class.

That's what I get for not reading.

+1 what guestgulkan said
Great. Still in your version guestgulkan I must know the class TMyClass beforehand. So what I could do is create an abstract base class, which contains a virtual method void ProcessRead().

Like this, right?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class TMyCalledBack // abstract class
{
public:
	virtual void ProcessRead() = 0;
};


class TMySerialReader
{
public:

	void (TMyCalledBack::*readCallback)();


	TMySerialReader() {
		readCallback = NULL;
	}

	void read (TMyCalledBack *pMC) {
		// read from serial (while(true))
		// ...

		// if something read: callback
		(pMC->*readCallback)();
	}
};

class TMyClass : TMyCalledBack
{
public:

	void ProcessRead(){
		printf("Process bytes from serial\n");
	}

	void run() {
		TMySerialReader *caller = new TMySerialReader;
		caller->readCallback = &TMyCalledBack::ProcessRead;
		caller->read(this);
	}
};


int main() {
	TMyClass instance1;
	instance1.run();
	return 1;
}
Last edited on
If you have the boost libraries available to you, the best solution is to use a boost::function<>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <boost/bind.hpp>
#include <boost/function.hpp>

class TMySerialReader {
    typedef boost::function< void(void) > ReadCallback;

   ReadCallback readCallback;   // This will initialize itself to "NULL"

    void read() {
         if( !readCallback )
             throw std::runtime_error( "NULL read callback" );
         readCallback();
    }

    void set_read_callback( ReadCallback cb ) {
         readCallback = cb;
    }
};

int main() {
    TMySerialReader rdr;
    TSomeOtherClass c;

     rdr.set_read_callback( boost::bind( &TSomeOtherClass::SomeMemberFunction, c ) );
}


If you don't have the boost libraries, you can invent poor man's boost::function and boost::bind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct ReadCallbackBase {
   virtual ~ReadCallbackBase() {}
   virtual void operator()() = 0;
};

template< typename Obj, typename Fn >
struct ReadCallback : ReadCallbackBase {
   ReadCallback( Obj* obj, Fn f ) : object_( obj ), fn_( f ) {}

   virtual void operator()() {
       if( !object_ || !fn_ )
           throw std::runtime_error( "Attempt NULL pointer dereference" );
       (object_->*fn_)();
   }

  private:
     Obj* object_;
     Fn     fn_;
};

class TMySerialReader {
   std::auto_ptr<ReadCallbackBase> readCallback;   // This will initialize itself to "NULL"

    void read() {
         if( !readCallback.get() )
             throw std::runtime_error( "NULL read callback" );
         (*readCallback)();
    }

    template< typename Obj, typename Fn >
    void set_read_callback( Obj* obj, Fn f ) {
         readCallback.reset( new ReadCallback<Obj, Fn>( obj, fn ) );
    }
};

int main() {
    TMySerialReader rdr;
    TSomeOtherClass c;

     rdr.set_read_callback( &c, &TSomeOtherClass::SomeMemberFunction );
}

Last edited on
Topic archived. No new replies allowed.