I've been enjoying the "new" features in C++11. Lambda functions make it much easier to organize code with spurious static functions. I recently realized I can use these for glut's C-style callbacks. For example, I often pass AntTweakBar's callback for passive mouse move events with:
glutPassiveMotionFunc(TwEventMouseMotionGLUT);
But if I want to force a refresh of the display after each mouse move I need to write my own mouse move function. With lambda's this is easy to do inline:
glutPassiveMotionFunc(
[](int x, int y)
{
TwEventMouseMotionGLUT(x,y);
glutPostRedisplay();
});
I had a little trouble using lambda's for glut's timer function. One way to make a time-synced, continuous draw loop is to use a recursive timer. Typically something like this:
static void timer(int ms)
{
glutPostRedisplay();
glutTimerFunc(ms,timer,ms);
}
int main()
{
...
glutTimerFunc(33,timer,33);
...
glutMainLoop();
...
}
With lambda's I hoped I could write something as simple as
auto timer = [&timer](int ms)
{
glutPostRedisplay();
glutTimerFunc(ms,timer,ms);
};
glutTimerFunc(33,timer,33);
However, auto
can't be used like this. I get an error:
error: variable 'auto timer' with 'auto' type used in its own initializer
The problem is that to capture and use timer
the compiler needs to know its type. I thought I could fix this by giving its type as a function object like this:
std::function<void(int)> timer = [&] (int ms)
{
glutTimerFunc(ms, timer, ms);
glutPostRedisplay();
};
glutTimerFunc(33, timer, 33);
But then I get errors because function objects cannot be cast to C-style function pointers:
error: cannot convert 'std::function<void(int)>' to 'void (*)(int)' for argument '2' to 'void glutTimerFunc(unsigned int, void (*)(int), int)'
Interestingly, it seemed that if timer
didn't capture and invoke itself (or anything else in the local scope) then it can be cast to a function pointer (that's why the top example inside glutPassiveMotionFunc
works). This seems to be because the lambda expression lives effectively at the global level.
My friend, Kenshi, managed a work around for my timer problems by using a "bounce" function (what's in a name? "Double callback"? "Leapfrog"?). His works like this:
static std::function<void(int)> timer_bounce;
auto timer = [] (int ms) {
timer_bounce(ms);
};
timer_bounce = [&] (int ms) {
glutTimerFunc(ms, timer, ms);
glutPostRedisplay();
};
glutTimerFunc(33, timer, 33);
Notice a couple important parts. timer_bounce
is defined before timer
so that timer
may call it and then timer may int turn call timerbounce
. timer_bounce
is declared as static
, placing at the global level in terms of lifetime.