I was playing with C++ lambdas and their implicit conversion to function pointers. My starting example was using them as callback for the ftw function. This works as expecte
Since capturing lambdas need to preserve a state, there isn't really a simple "workaround", since they are not just ordinary functions. The point about a function pointer is that it points to a single, global function, and this information has no room for a state.
The closest workaround (that essentially discards the statefulness) is to provide some type of global variable which is accessed from your lambda/function. For example, you could make a traditional functor object and give it a static member function which refers to some unique (global/static) instance.
But that's sort of defeating the entire purpose of capturing lambdas.
My solution, just use a function pointer to refer to a static lambda.
typedef int (* MYPROC)(int);
void fun(MYPROC m)
{
cout << m(100) << endl;
}
template<class T>
void fun2(T f)
{
cout << f(100) << endl;
}
void useLambdaAsFunPtr()
{
int p = 7;
auto f = [p](int a)->int {return a * p; };
//fun(f);//error
fun2(f);
}
void useLambdaAsFunPtr2()
{
int p = 7;
static auto f = [p](int a)->int {return a * p; };
MYPROC ff = [](int i)->int { return f(i); };
//here, it works!
fun(ff);
}
void test()
{
useLambdaAsFunPtr2();
}
ORIGINAL
Lambda functions are very convenient and reduce a code. In my case I needed lambdas for parallel programming. But it requires capturing and function pointers. My solution is here. But be careful with scope of variables which you captured.
template<typename Tret, typename T>
Tret lambda_ptr_exec(T* v) {
return (Tret) (*v)();
}
template<typename Tret = void, typename Tfp = Tret(*)(void*), typename T>
Tfp lambda_ptr(T& v) {
return (Tfp) lambda_ptr_exec<Tret, T>;
}
Example
int a = 100;
auto b = [&]() { a += 1;};
void (*fp)(void*) = lambda_ptr(b);
fp(&b);
Example with a return value
int a = 100;
auto b = [&]() {return a;};
int (*fp)(void*) = lambda_ptr<int>(b);
fp(&b);
UPDATE
Improved version
It was a while since first post about C++ lambda with captures as a function pointer was posted. As It was usable for me and other people I made some improvement.
Standard function C pointer api uses void fn(void* data) convention. By default this convention is used and lambda should be declared with a void* argument.
Improved implementation
struct Lambda {
template<typename Tret, typename T>
static Tret lambda_ptr_exec(void* data) {
return (Tret) (*(T*)fn<T>())(data);
}
template<typename Tret = void, typename Tfp = Tret(*)(void*), typename T>
static Tfp ptr(T& t) {
fn<T>(&t);
return (Tfp) lambda_ptr_exec<Tret, T>;
}
template<typename T>
static void* fn(void* new_fn = nullptr) {
static void* fn;
if (new_fn != nullptr)
fn = new_fn;
return fn;
}
};
Exapmle
int a = 100;
auto b = [&](void*) {return ++a;};
Converting lambda with captures to a C pointer
void (*f1)(void*) = Lambda::ptr(b);
f1(nullptr);
printf("%d\n", a); // 101
Can be used this way as well
auto f2 = Lambda::ptr(b);
f2(nullptr);
printf("%d\n", a); // 102
In case return value should be used
int (*f3)(void*) = Lambda::ptr<int>(b);
printf("%d\n", f3(nullptr)); // 103
And in case data is used
auto b2 = [&](void* data) {return *(int*)(data) + a;};
int (*f4)(void*) = Lambda::ptr<int>(b2);
int data = 5;
printf("%d\n", f4(&data)); // 108
Found an answer here: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
It converts lambda pointer
to void*
and convert back when needed.
to void*
:
auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));
from void*
:
auto function = static_cast< std::function*>( voidfunction);
There is a hackish way to convert a capturing lambda into a function pointer, but you need to be careful when using it:
https://codereview.stackexchange.com/questions/79612/c-ifying-a-capturing-lambda
Your code would then look like this (warning: brain compile):
int main()
{
vector<string> entries;
auto const callback = cify<int(*)(const char *, const struct stat*,
int)>([&](const char *fpath, const struct stat *sb,
int typeflag) -> int {
entries.push_back(fpath);
return 0;
});
int ret = ftw("/etc", callback, 1);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
I just ran into this problem.
The code compiles fine without lambda captures, but there is a type conversion error with lambda capture.
Solution with C++11 is to use std::function
(edit: another solution that doesn't require modifying the function signature is shown after this example). You can also use boost::function
(which actually runs significantly faster). Example code - changed so that it would compile, compiled with gcc 4.7.1
:
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
int ftw(const char *fpath, std::function<int (const char *path)> callback) {
return callback(fpath);
}
int main()
{
vector<string> entries;
std::function<int (const char *fpath)> callback = [&](const char *fpath) -> int {
entries.push_back(fpath);
return 0;
};
int ret = ftw("/etc", callback);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
Edit: I had to revisit this when I ran into legacy code where I couldn't modify the original function signature, but still needed to use lambdas. A solution that doesn't require modifying the function signature of the original function is below:
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
// Original ftw function taking raw function pointer that cannot be modified
int ftw(const char *fpath, int(*callback)(const char *path)) {
return callback(fpath);
}
static std::function<int(const char*path)> ftw_callback_function;
static int ftw_callback_helper(const char *path) {
return ftw_callback_function(path);
}
// ftw overload accepting lambda function
static int ftw(const char *fpath, std::function<int(const char *path)> callback) {
ftw_callback_function = callback;
return ftw(fpath, ftw_callback_helper);
}
int main() {
vector<string> entries;
std::function<int (const char *fpath)> callback = [&](const char *fpath) -> int {
entries.push_back(fpath);
return 0;
};
int ret = ftw("/etc", callback);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}