C++ lambda with captures as a function pointer

C++LambdaFunction PointersC++11

C++ Problem Overview


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 expected.

#include <ftw.h>
#include <iostream>

using namespace std;

int main()
{
    auto callback = [](const char *fpath, const struct stat *sb,
        int typeflag) -> int {
        cout << fpath << endl;
        return 0;
    };
    
    int ret = ftw("/etc", callback, 1);
    
    return ret;
}

After modifying it to use captures:

int main()
{

    vector<string> entries;

    auto callback = [&](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 got the compiler error:

error: cannot convert ‘main()::<lambda(const char*, const stat*, int)>’ to ‘__ftw_func_t {aka int (*)(const char*, const stat*, int)}’ for argument ‘2’ to ‘int ftw(const char*, __ftw_func_t, int)

After some reading. I learned that lambdas using captures can't be implicitly converted to function pointers.

Is there a workaround for this? Does the fact that they can't be "implicitly" converted mean s that they can "explicitly" converted? (I tried casting, without success). What would be a clean way to modify the working example so that I could append the entries to some object using lambdas?.

C++ Solutions


Solution 1 - C++

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

Solution 2 - C++

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.

Solution 3 - C++

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

Solution 4 - C++

Using locally global (static) method it can be done as followed

template <class F>
auto cify_no_args(F&& f) {
  static F fn = std::forward<F>(f);
  return [] {
    return fn();
  };
}

Suppose we have

void some_c_func(void (*callback)());

So the usage will be

some_c_func(cify_no_args([&] {
  // code
}));

This works because each lambda has an unique signature so making it static is not a problem. Following is a generic wrapper with variadic number of arguments and any return type using the same method.

template <class F>
struct lambda_traits : lambda_traits<decltype(&F::operator())>
{ };

template <typename F, typename R, typename... Args>
struct lambda_traits<R(F::*)(Args...)> : lambda_traits<R(F::*)(Args...) const>
{ };

template <class F, class R, class... Args>
struct lambda_traits<R(F::*)(Args...) const> {
    using pointer = typename std::add_pointer<R(Args...)>::type;

    static pointer cify(F&& f) {
        static F fn = std::forward<F>(f);
        return [](Args... args) {
            return fn(std::forward<Args>(args)...);
        };
    }
};

template <class F>
inline typename lambda_traits<F>::pointer cify(F&& f) {
    return lambda_traits<F>::cify(std::forward<F>(f));
}

And similar usage

void some_c_func(int (*callback)(some_struct*, float));

some_c_func(cify([&](some_struct* s, float f) {
    // making use of "s" and "f"
    return 0;
}));

Solution 5 - C++

Hehe - quite an old question, but still...

#include <iostream>
#include <vector>
#include <functional>

using namespace std;

// We dont try to outsmart the compiler...
template<typename T>
int ftw(const char *fpath, T callback) {
  return callback(fpath);
}

int main()
{
  vector<string> entries;

  // ... now the @ftw can accept lambda
  int ret = ftw("/etc", [&](const char *fpath) -> int {
    entries.push_back(fpath);
    return 0;
  });

  // ... and function object too 
  struct _ {
  	static int lambda(vector<string>& entries, const char* fpath) {
      entries.push_back(fpath);
  	  return 0;
  	}
  };
  ret = ftw("/tmp", bind(_::lambda, ref(entries), placeholders::_1));

  for (auto entry : entries ) {
    cout << entry << endl;
  }

  return ret;
}

Solution 6 - C++

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

Solution 7 - C++

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

Solution 8 - C++

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.

  1. to void*:

    auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));

  2. from void*:

auto function = static_cast< std::function<void(Args...)>*>(
        voidfunction);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionduncanView Question on Stackoverflow
Solution 1 - C++Jay WestView Answer on Stackoverflow
Solution 2 - C++Kerrek SBView Answer on Stackoverflow
Solution 3 - C++Evgeny KarpovView Answer on Stackoverflow
Solution 4 - C++Vladimir TalybinView Answer on Stackoverflow
Solution 5 - C++egorseView Answer on Stackoverflow
Solution 6 - C++ZhangView Answer on Stackoverflow
Solution 7 - C++user1095108View Answer on Stackoverflow
Solution 8 - C++jesseView Answer on Stackoverflow