c++ lambda with捕获作为函数指针
C++ lambda with captures as a function pointer
我正在玩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;
}
修改为使用capture后:
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;
}
我得到了编译错误:
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)’
阅读后。我了解到使用捕获的lambda不能隐式地将转换为函数指针。
是否有解决这个问题的方法?它们不能"隐式"转换的事实是否意味着它们可以"显式"转换?(我试过选演员,但没有成功)。修改工作示例以便我可以使用lambdas将条目附加到某个对象的干净方法是什么?
我刚刚遇到这个问题。
如果没有lambda捕获,代码可以很好地编译,但是使用lambda捕获会出现类型转换错误。
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;
}
编辑:当我遇到无法修改原始函数签名,但仍然需要使用lambda的遗留代码时,我不得不重新审视这一点。不需要修改原始函数的函数签名的解决方案如下:
#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;
}
由于捕获lambda需要保持状态,因此没有真正简单的"解决方法",因为它们是而不是只是普通函数。函数指针的意义在于它指向一个单一的全局函数,而这个信息没有给状态留下空间。
最接近的解决方法(本质上是放弃有状态性)是提供某种类型的全局变量,可以从lambda/函数访问。例如,你可以创建一个传统的functor对象,并给它一个静态成员函数,该成员函数引用某个唯一的(全局/静态)实例。
但是这有点违背了捕获lambda的全部目的。
原始
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>;
}
例子int a = 100;
auto b = [&]() { a += 1;};
void (*fp)(void*) = lambda_ptr(b);
fp(&b);
返回值
的示例int a = 100;
auto b = [&]() {return a;};
int (*fp)(void*) = lambda_ptr<int>(b);
fp(&b);
改进版本
距离第一篇关于c++ lambda with capture作为函数指针的文章发布已经有一段时间了。因为它对我和其他人都有用,所以我做了一些改进。
标准函数C指针api使用void fn(void* data)约定。默认情况下使用此约定,并且lambda应该使用void*参数声明。
改进实现
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;
}
};
简单
int a = 100;
auto b = [&](void*) {return ++a;};
将带有捕获的lambda转换为C指针
void (*f1)(void*) = Lambda::ptr(b);
f1(nullptr);
printf("%dn", a); // 101
也可以这样使用
auto f2 = Lambda::ptr(b);
f2(nullptr);
printf("%dn", a); // 102
在需要使用返回值的情况下
int (*f3)(void*) = Lambda::ptr<int>(b);
printf("%dn", f3(nullptr)); // 103
如果使用data
auto b2 = [&](void* data) {return *(int*)(data) + a;};
int (*f4)(void*) = Lambda::ptr<int>(b2);
int data = 5;
printf("%dn", f4(&data)); // 108
使用局部全局(静态)方法可以完成如下操作
template <class F>
auto cify_no_args(F&& f) {
static F fn = std::forward<F>(f);
return [] {
return fn();
};
}
假设我们有
void some_c_func(void (*callback)());
所以用法是
some_c_func(cify_no_args([&] {
// code
}));
这可以工作,因为每个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 typename lambda_traits<F>::pointer cify(F&& f) {
return lambda_traits<F>::cify(std::forward<F>(f));
}
和类似的用法
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;
}));
呵呵,这是一个相当老的问题,但仍然…
#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;
}
我的解决方案,只是使用一个函数指针来引用静态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();
}
有一种简单的方法可以将捕获的lambda转换为函数指针,但是在使用它时需要小心:
https://codereview.stackexchange.com/questions/79612/c-ifying-a-capturing-lambda你的代码看起来像这样(警告:大脑编译):
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;
}
@vladimir-talybin的回答有一点问题:
template <class F>
auto cify_no_args(F&& f) {
static F fn = std::forward<F>(f);
return [] {
return fn();
};
}
也就是说,如果lambda在函数中被调用两次,那么只有第一次调用有效,例如
// only a demo
void call(std::vector<int>& nums) {
static int i = 0;
cify_no_args([&]() {
nums.emplace_back(i++);
})();
}
int main() {
std::vector<int> nums1, nums2;
call(nums1);
call(nums2);
std::cout << nums1.size() << std::endl << nums2.size() << std::endl;
}
您将显示2
和0
的输出,这意味着call
函数的第二次调用使用了第一次调用的lambda闭包。
这是因为解决方案是使用静态来存储闭包的引用,并且一旦存储了引用,它就不会被更改,即使对于新的闭包也是如此。如果闭包被析构(由于超出作用域或其他原因),情况会变得更糟。
我对这个问题的解决方案是简单地将引用转换为指针,并在每次"构造"时更新指针的值。λ:
template <class F>
auto cify_no_args(F&& f) {
static typename std::remove_reference<F>::type* fn;
fn = &f;
return [] {
return (*fn)();
};
}
开销是两个内存访问,一个用于读,一个用于写,但确保了正确性。
在这里找到答案:http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
它将lambda pointer
转换为void*
,并在需要时转换回来。
-
to
void*
:auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));
-
from
void*
:auto function = static_cast<std::函数*> (voidfunction);
- QMetaObject invokeMethod的基于函数指针的语法
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- c++r值引用应用于函数指针
- 模板函数指针和lambda
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 带有类的函数指针
- () 函子后面的括号,而不是函数指针?
- 全局作用域中函数指针的赋值
- 使用"Task"函数指针队列定义作业管理器
- 将成员函数指针作为参数传递给模板方法
- 如何创建对象函数指针C++映射?
- 匹配函数指针作为模板参数?
- 通过函数指针定义类范围之外的方法
- 存储在类中的函数指针
- C++从函数指针数组调用函数
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 整数键映射到头文件中的成员函数指针
- 从类成员函数到类 C 函数指针的转换
- 如何将内联匿名函数分配给C++函数指针
- 将字符缓冲区强制转换为函数指针