Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ lambda with captures as a function pointer

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

like image 590
duncan Avatar asked Oct 21 '11 16:10

duncan


People also ask

Can you pass lambda to a function pointer?

A lambda expression with an empty capture clause is convertible to a function pointer. It can replace a stand-alone or static member function as a callback function pointer argument to C API.

How do you capture variables in lambda?

Capture clause A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.

Is there lambda function in C?

Significance of Lambda Function in C/C++ Lambda Function − Lambda are functions is an inline function that doesn't require any implementation outside the scope of the main program. Lambda Functions can also be used as a value by the variable to store.


2 Answers

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; } 
like image 69
Jay West Avatar answered Sep 27 '22 18:09

Jay West


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.

like image 37
Kerrek SB Avatar answered Sep 27 '22 17:09

Kerrek SB