C++11 lambda functions and glut C-style callbacks

Alec Jacobson

January 05, 2014

weblog/

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.