The C++ language provides two flavors of functions: member functions and non-member functions. A function is something which is used like this:
result = function(arguments);
By contrast, a member function is used like this:
result = object.function(arguments);
Because of this, it’s impossible to use member functions and non-member functions in the same way: where a non-member function can be called on its own (provided that you have the arguments for it), a member function also requires an object on which it is called.
The Window Procedure Problem
This can be quite confusing to beginners, for instance when defining the window procedure in the Win32 API: this requires defining a non-member function with a certain set of arguments, and then providing a function pointer (referencing that function) to the API for registering it with a window. This usually looks like this:
LRESULT CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM)
{
// Implementation
}
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
WNDCLASS wc;
wc.lpfnWndProc = (WNDPROC) MyWndProc;
// Register the other WNDCLASS properties
RegisterClass(&wc);
}
A question that is often asked on beginner forums is, how do I use a member function of my Window C++ class as a window procedure?
This is, of course, impossible. The main reason why it’s impossible is that it doesn’t make any sense: a window procedure, in the Win32 API, is a procedure that will be called on every window of that window class. By contrast, a user-defined Window C++ class tends to have a “one instance = one window” approach to things, so that even if the same function is called for every window, the object on which it is to be called is different, leading to the question of how those objects are specified to the Win32 API.
A New Hope
Of course, the user could add the constraint “one window = one Win32 window class”, which means that a given window procedure will only ever be called for a single window (the question of whether using a window class to create a single window is an abuse of window classes is best left alone). Then, common sense would imply that a member function could be used as a non-member function as long as the object on which it is called is provided:
class MyWindowClass
{
public:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM)
{
// Implementation
}
};
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MyWindowClass windowInstance;
WNDCLASS wc;
wc.lpfnWndProc = (WNDPROC) windowInstance.WndProc;
// Register the other WNDCLASS properties
RegisterClass(&wc);
}
The hope here is that C++ will somehow detect that “object.function” can be used in the form “result = object.function(arguments)” and therefore behaves just like a normal function. Of course, this doesn’t work: C++ doesn’t support closures.
Closures
By default, a C++ function pointer (the data type used to manipulate functions beyond simply calling them right away) is just a pointer to a function. This means that it can only point at a finite set of values, which happens to be the set of all available non-member functions (of the appropriate type) within your application. It does not carry any other values.
The problem is that “object.function” is not a non-member function defined within the application: it’s a member function being applied to an object. There is no way for the compiler to detect and define this function at compile-time automatically: you could have any number of objects created and destroyed within your application at runtime, and “object.function” will do a different thing for every one of these objects (because this is how member functions work, after all: behave differently on every object).
In short, there’s no workaround for the fact that “object.function” is the association of a member function (”function”) and a piece of data (”object”), and that no built-in C++ type can store both a function and a piece of data (this is commonly known as a closure, and C++ does not support these).
Of course, since closures are a fairly useful tools, a set of replacement tools have been provided: you still can’t use a normal function pointer (or member function pointer) to represent them, but you can use another variable, or another type.
The most elementary example was known from C. C APIs that let you define callbacks usually let you define callback data as well (unless they are brain-dead, which sometimes happens with C APIs). Callback data is the “data” part of the closure, while the callback itself is the “function” part of the closure. It all starts with a callback function that takes an additional “data” argument:
void myCallback(void *callbackData)
{
MyObjectType &object = *reinterpret_cast<MyObjectType*>(callbackData);
object.function();
}
MyObjectType bobby;
hypotheticalAPI(myCallback,&bobby);
MyObjectType timmy;
hypotheticalAPI(myCallback,&timmy);
Here, the ‘hypotheticalAPI’ function stores the callback and data together, then calls the callback function with the associated data as its first argument. This means that the “myCallback” function will call either “bobby.function()” or “timmy.function()” depending on which callback is activated.
This is actually the way the Win32 API is intended to work. The difference here is that you don’t pass another argument this way, but instead associate the callback data to the handle of the window. This works more or less like this:
class MyWindowClass
{
public:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM)
{
// Implementation
}
};
LRESULT CALLBACK MyWindowClassWndProc(HWND h, UINT i, WPARAM w, LPARAM l)
{
MyWindowClass &object =
*reinterpret_cast<MyWindowClass*>
(GetWindowLongPtr(h, DWLP_USER));
return object.WndProc(h,i,w,l);
}
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MyWindowClass windowInstance;
WNDCLASS wc;
wc.lpfnWndProc = (WNDPROC) MyWindowClassWndProc;
// Register the other WNDCLASS properties
RegisterClass(&wc);
HWND h = CreateWindow( /* args */ );
SetWindowLongPtr(h, DWLP_USER, reinterpret_cast<LONG_PTR>(&windowInstance));
}
The SetWindowLongPtr function is used to associate the address of our windowInstance object to the window handle. The window procedure we register then uses the GetWindowLongPtr function to retrieve the windowInstance object, and calls its member function with the correct arguments.
Functors
Aside from the old C trick, C++ also provides the user with the ability to overload “operator()”, which means the ability to have any object behave like a function. And since objects can carry arbitrary amounts of data, having them behave like closures is a very simple task. The most naive implementation of this happens in the standard library itself: instead of having the various algorithm functions (transform, accumulate etc) take function pointers, these functions take an arbitrary type as the operation to be applied. This way, any object with a correctly implemented “operator()” can be used:
struct multiplyBy
{
int lhs;
int operator(int rhs) const { return lhs * rhs; }
multiplyBy(int lhs) : lhs(lhs);
};
std::transform(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
multiplyBy(10),
std::ostream_iterator<int>(std::cout,"\n"));
The implementation is quite simple: instead of defining a function pointer argument, use an arbitrary type parameter:
template<typename F>
int apply_twice(int value, F f)
{
return f( f(value) );
}
apply_twice(5, multiplyBy(2));
// Output : 20
However, this requires compile-time resolving of the function: since the type of the functor is a template argument, the same function template cannot be made to accept, at runtime, both a normal “int (*)(int)” function and a “multiplyBy” instance.
Boost.Function
The obvious solution, then, would be to use runtime polymorphism provided by virtual functions: make a base “function” class where “operator()” is virtual, then create a derived class template which, given a functor (or normal function pointer) wraps around that functor so that it can be used wherever the base class can. The boost library actually goes a little bit further in this respect: it provides a clean way of specifying the type of the wrapped function, and handles the memory allocation of the underlying base class, so that the functions become value types:
int apply_twice(int value, boost::function<int(int)> f)
{
return f( f(value) );
}
boost::function<int(int)> multiplyBy(int i)
{
return boost::bind(std::multiplies<int>, i);
}
apply_twice(5, multiplyBy(2));
Boost.Function also handles member functions out of the box:
struct integer
{
int value;
integer(int value) : value(value) {}
int add(int other) { return value + other; }
};
integer i = 3;
boost::function<int(int)> add3 = boost::bind(&integer::add, i);
apply_twice(5, add3);
The documentation for version 1.37.0 is available here :
http://www.boost.org/doc/libs/1_37_0/doc/html/function.html
http://www.boost.org/doc/libs/1_37_0/libs/bind/bind.html
Recent Comments