C++0x lambda for makecontext argument #2

2019-06-21 23:05发布

问题:

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;
}

回答1:

[](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.



回答2:

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.



回答3:

You can use a wrapper function to wrap it. It works for every lambda. The idea behind it is that since you can not pass functor as function pointer, you can pass a wrapper function that calls the lambda function. Another trick part is that I need to pass pointer instead of the whole object, it is because makecontext can only accept int arguments.

template<typename Kernel, typename ...Args>
void wrapper(Kernel *ker, Args*... args)
{
    (*ker)(*args...);
}
template<typename Ker, typename ...Args>
void makectx(Ker ker, Args... args)
{
    makecontext(&ctx, (void (*)(void))wrapper<Ker, Args...>, sizeof...(Args) + 1, &ker, &args...);
}


标签: c++ c++11 lambda