How do APIs callback to class members

I've been creating an API and I'm now stuck on callbacks. There are many APIs that allow callbacks to class members(e.g. Windows API) using a void pointer to the object. I've searched the internet for hours and I can't find one example of how to use the "hidden object parameter" of an class method pointer that doesn't use std::function/bind or boost::function/bind.

Any information on how API's like Windows API are able to use class methods as callbacks would be greatly appreciated!
Last edited on
I need the API to work with a method that has many arguments and all of those examples only have one.
does it have to be in c? cause other wise you can use std::function
Is there a way to do this without using C++11 or boost? The API needs to be compatible with many different compilers and not all of them support C++11. It also can't rely on boost.

I've created a way to have the option of using both function pointers and class method pointers as callbacks. It's a hybrid between the classic C function pointer method and the static object caller method. I wrote dummy program to demonstrate it.

First I have the caller class that gets registered
Caller.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once

#include <stdio.h>
#include "Callbacker.h"

class Caller
{
public:
	Caller();
	~Caller(void);

	void update(int);
	static void callbackObject(void* object,int value);

};


Caller.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "Caller.h"

Caller::Caller()
{
}

Caller::~Caller(void)
{
}

void Caller::update(int thing)
{
	printf("The value is %d\n",thing);
}


void Caller::callbackObject(void* object,int value)
{
	Caller* This = (Caller*) object;
	This->update(value);
}


In this class, update is the real callback method and callbackObject is the pseudo-callback method that will be registered.

Next there is callbacker class that will call either an objects pseudo-callback or a normal function callback depending on which is registered.

Callbacker.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

class Callbacker
{
public:
	Callbacker(void);
	~Callbacker(void);

	void setCallback(void (*cb)(int));
	void setCallback(void (*cb)(void*,int),void* obj);
	void call();

private:
	void (*callback)(int);
	void (*callback_obj)(void*,int);
	void* object;

};


Callbacker.cpp:
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
#include "Callbacker.h"

Callbacker::Callbacker(void)
{
	callback = NULL;
	callback_obj = NULL;
	object = NULL;
}

Callbacker::~Callbacker(void)
{

}

void Callbacker::setCallback(void (*cb)(int))
{
		callback_obj = NULL;
		object = NULL;
		callback = cb;
}

void Callbacker::setCallback(void (*cb)(void*,int),void* obj)
{
		callback = NULL;
		object = obj;
		callback_obj = cb;
}

void Callbacker::call()
{
	if(callback != NULL)
	{
		callback(9001);
	}
	else if((callback_obj != NULL) && (object != NULL))
	{
		callback_obj(object,9002);
	}
}


In this class there are two setCallback methods. The first one is for registering a C function callback and the other is for registering a classes pseudo-callback for an object. The call function will use the callback that isn't NULL.

Then we have our main function that tests all of it.

main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include <stdio.h>
#include <stdlib.h>

#include "Callbacker.h"
#include "Caller.h"

void callMe(int thing)//For testing a C-style callback
{
	printf("The value is %d\n",thing);
}

int main()
{
	printf("Dummy Callback Test\n");
	Callbacker* back = new Callbacker;
	Caller* caller = new Caller();
	back->setCallback(Caller::CallbackObject,caller);
	back->call();
	back->setCallback(callMe);
	back->call();
	system("pause");//To keep the console open
	return 0;
}


In this file, both the pseudo-callback of an object and a C-style callback are used.

If anyone can think of a circumstance were this method would fail, please let me know and give me a possible solution.
The API needs to be compatible with many different compilers and not all of them support C++11


IMO, you shouldn't worry so much about supporting those. It's already halfway through 2014. A compiler that can't keep up with a basic part of a 3 year old standard (which is already old in the tech world) probably isn't worth supporting.

The compilers that pretty much everyone uses (Clang, gcc, VS) all support std::function.


That said... you can mimic a very simplistic version of std::function with C++03 with use of templates and inheritance. But blech.

Here's an example I whipped up and did only minimal testing:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

class Function
{
private:
    // Abstract interface for all callbacks
    class Callback
    {
    public:
        virtual ~Callback() { }
        virtual Callback* clone() const = 0;
        virtual void call() = 0;
    };
    
    // Implementation for member functions
    template <typename T>
    class ClassCallback : public Callback
    {
    private:
        T*      object;
        void (T::*callback)();
        
    public:
        ClassCallback(T* obj, void (T::*clbk)()) : object(obj), callback(clbk) {}
        virtual Callback* clone() const { return new ClassCallback<T>(object,callback); }
        virtual void call() { (object->*callback)(); }
    };
    
    // Implementation for global functions
    class GlobalCallback : public Callback
    {
    private:
        void (*callback)();
        
    public:
        GlobalCallback( void (*clbk)() ) : callback(clbk) {}
        virtual Callback* clone() const { return new GlobalCallback(callback); }
        virtual void call() { (*callback)(); }
    };
    
private:
    // Data member for the Function class
    Callback*       callback;
    
public:
    // basic construct/destruct
    Function() : callback(0) { }
    ~Function() { delete callback; }

    // copy ctor/assign
    Function(const Function& rhs) : callback(0)
    {
        if(rhs.callback)    callback = rhs.callback->clone();
    }
    Function& operator = (const Function& rhs)
    {
        delete callback;
        if(rhs.callback)        callback = rhs.callback->clone();
        else                    callback = 0;
    }

    // construct with an actual callback
    Function(void (*clbk)())
    {
        callback = new GlobalCallback(clbk);
    }

    template <typename T>
    Function(T* obj, void (T::*clbk)())
    {
        callback = new ClassCallback<T>(obj,clbk);
    }

    // actually calling the function
    void operator () ()
    {
        callback->call();
    }
};


///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// Usage example

#include <iostream>
using namespace std;

class MyClass
{
public:
    void MyCallback()
    {
        cout << "calling MyCallback as a member function" << endl;
    }
};

void MyGlobal()
{
    cout << "calling MyGlobal as a global function" << endl;
}

int main()
{
    cout << "constructing function objects" << endl;

    Function a(&MyGlobal);

    MyClass foo;
    Function b(&foo,&MyClass::MyCallback);

    cout << "calling the callbacks" << endl;
    a();
    b();

    return 0;
}



EDIT:

Looks like you replied with your own solution while I was typing mine. Oh well. =P


EDIT2:

Why are you using new to create your 'back' and 'caller' objects in your example? All that's doing is leaking memory because you never delete them.

Don't use new unless you have to. Just put those on the stack. KISS.
Last edited on
Topic archived. No new replies allowed.