Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++0x lambda for makecontext argument #2

Tags:

c++

c++11

lambda

I'm having trouble passing a C++0x lambda function as the second argument to makecontext (from ucontext.h). The signature of makecontext is:

void makecontext(ucontext_t*, void (*)(), int, ...);

Previously, I was able to apply a C-style (void (*)(void)) cast, to the global scope functions I used. A reinterpret_cast would do the trick in C++. However, with a C++0x lambda function, I get the following error:

error: invalid cast from type ‘main(int, char**)::<lambda(int)>’ to type ‘void (*)()’

I'm using G++ 4.6. The following code is sufficient to produce the compile error:

#include <ucontext.h>

void f1(int i) {}

int main(int argc, char *argv[]) {
  ucontext_t c;
  makecontext(&c, (void (*)(void))f1, 1, 123); // ok
  makecontext(&c, reinterpret_cast<void (*)(void)>(f1), 1, 123); // ok

  auto f2 = [](int i){};
  makecontext(&c, (void (*)(void))f2, 1, 123); // error
  makecontext(&c, reinterpret_cast<void (*) (void)>(f2), 1, 123); // error
  return 0;
}
like image 486
user2023370 Avatar asked Jul 24 '11 21:07

user2023370


2 Answers

[](int i){} is a captureless lambda that has a single int parameter and returns nothing; it is thus implicitly convertible to void(*)(int): a pointer to a function that takes a single int and returns nothing.

Assuming the makecontext function is capable of handling functions with different types (it appears from its documentation that it is, though the documentation is a bit vague), then you'll need to use two casts to use it with a lambda: one to cast the lambda to a function pointer, and one to cast the lambda to the void(*)() type:

auto f2 = [](int i) { };
makecontext(&c, (void (*)(void))(void (*)(int))f2, 1, 123);

This is all rather unsafe (casting between function pointer types isn't exactly the safest thing to be doing), so it would be best to wrap this sort of functionality into a utility library. You can wrap the makecontext function with a function template so that you can be sure that all of your uses of the function are type safe, and all of the unsafe code is encapsulated in one place.

like image 53
James McNellis Avatar answered Nov 05 '22 02:11

James McNellis


Let's say you have already declared f2 as in your example, and want to use it now with makecontext. Rather than trying to cast it, use std::bind (found in the <functional> header) to produce an object which is callable like a void(*)(void):

auto f3 = std::bind(f2, 0);
makecontext(&c, f3, 1, 123);

The object f3 is a function object which is callable without any arguments. The end result is that f2 end up being called with 0 as its argument. If you would rather a different value be passed to f2, you can specify it as the second argument to bind.

Edit

As several commenters have pointed out, this isn't applicable in your case, as makecontext requires an actual function pointer. So, for reference sake, I thought I would point out one caveat that you might run into (in case you don't already know this): using a lambda as a function pointer only works if you don't utilize closures. As soon as you close over variables, the lambda can no longer degenerate to a function pointer, as it can without a closure.

like image 21
Ken Wayne VanderLinde Avatar answered Nov 05 '22 02:11

Ken Wayne VanderLinde