C++ lambda 捕获作为函数指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7852101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ lambda with captures as a function pointer
提问by duncan
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.
我在玩 C++ lambdas 和它们到函数指针的隐式转换。我的起始示例是将它们用作 ftw 函数的回调。这按预期工作。
#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 convertedto function pointers.
经过一番阅读。我了解到使用捕获的 lambda不能隐式转换为函数指针。
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?.
有解决方法吗?它们不能“隐式”转换的事实是否意味着它们可以“显式”转换?(我尝试铸造,但没有成功)。修改工作示例以便我可以使用 lambda 将条目附加到某个对象的干净方法是什么?
采纳答案by Kerrek SB
Since capturing lambdas need to preserve a state, there isn't really a simple "workaround", since they are notjust 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.
由于捕获lambda表达式需要保存的状态,确实没有一个简单的“解决方法”,因为它们不只是普通的功能。关于函数指针的要点是它指向一个单一的全局函数,并且这个信息没有状态的空间。
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.
最接近的解决方法(基本上丢弃状态)是提供某种类型的全局变量,它可以从您的 lambda/函数访问。例如,您可以创建一个传统的函子对象,并为其提供一个静态成员函数,该函数引用某个唯一(全局/静态)实例。
But that's sort of defeating the entire purpose of capturing lambdas.
但这有点违背了捕获 lambda 的全部目的。
回答by Jay West
I just ran into this problem.
我刚刚遇到了这个问题。
The code compiles fine without lambda captures, but there is a type conversion error with lambda capture.
代码在没有 lambda 捕获的情况下编译得很好,但是 lambda 捕获存在类型转换错误。
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
:
使用 C++11 的解决方案是使用std::function
(编辑:本示例后显示了另一个不需要修改函数签名的解决方案)。您也可以使用boost::function
(实际上运行速度明显更快)。示例代码 - 更改为可以编译,编译时使用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:
编辑:当我遇到无法修改原始函数签名但仍需要使用 lambdas 的遗留代码时,我不得不重新审视这个问题。不需要修改原始函数的函数签名的解决方案如下:
#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;
}
回答by Evgeny Karpov
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.
Lambda 函数非常方便,并且减少了代码。就我而言,我需要 lambda 来进行并行编程。但它需要捕获和函数指针。我的解决方案在这里。但是要注意您捕获的变量范围。
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.
自从第一次发布关于 C++ lambda 并将捕获作为函数指针发布以来,已经有一段时间了。因为它对我和其他人有用,所以我做了一些改进。
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.
标准函数 C 指针 api 使用 void fn(void* data) 约定。默认情况下使用此约定,并且 lambda 应使用 void* 参数声明。
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
将带捕获的 lambda 转换为 C 指针
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
回答by Vladimir Talybin
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.
这是有效的,因为每个 lambda 都有一个唯一的签名,因此使其成为静态不是问题。以下是具有可变参数数量和使用相同方法的任何返回类型的通用包装器。
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 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;
}));
回答by egorse
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;
}
回答by user1095108
There is a hackish way to convert a capturing lambda into a function pointer, but you need to be careful when using it:
有一种将捕获的 lambda 转换为函数指针的黑客方法,但是在使用它时需要小心:
https://codereview.stackexchange.com/questions/79612/c-ifying-a-capturing-lambda
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;
}
回答by Zhang
My solution, just use a function pointer to refer to a static lambda.
我的解决方案,只需使用函数指针来引用静态 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();
}
回答by jesse
Found an answer here: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
在这里找到答案: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.
它会在需要时转换lambda pointer
为void*
并转换回来。
to
void*
:auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));
from
void*
:auto function = static_cast< std::function*>( voidfunction);
到
void*
:auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));
来自
void*
:自动函数 = static_cast<std::function*>(voidfunction);