Я играл с лямбдами C ++ и их неявным преобразованием в указатели на функции. В моем начальном примере они использовались в качестве обратного вызова для функции 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;
}
После модификации для использования захватов:
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)’
После некоторого чтения. Я узнал, что лямбда-выражения, использующие захват, не могут быть неявно преобразованы в указатели на функции.
Есть ли обходной путь для этого? Означает ли тот факт, что они не могут быть преобразованы «неявно», что они могут быть преобразованы «явно»? (Я пробовал кастинг, но безуспешно). Каким будет чистый способ изменить рабочий пример, чтобы я мог добавлять записи к какому-либо объекту с помощью лямбда-выражений?
c++
lambda
function-pointers
c++11
Дункан
источник
источник
void *
). Если используемая вами библиотека допускает этот дополнительный аргумент, вы найдете обходной путь. В противном случае у вас не будет возможности чисто достичь того, что вы хотите делать.Ответы:
Поскольку при захвате лямбда-выражений необходимо сохранять состояние, на самом деле нет простого «обходного пути», поскольку это не просто обычные функции. Суть указателя на функцию заключается в том, что он указывает на единственную глобальную функцию, и в этой информации нет места для состояния.
Ближайший обходной путь (который по существу отбрасывает состояние) - предоставить некоторый тип глобальной переменной, доступ к которой осуществляется из вашей лямбда / функции. Например, вы можете создать традиционный объект-функтор и дать ему статическую функцию-член, которая ссылается на некоторый уникальный (глобальный / статический) экземпляр.
Но это своего рода поражение всей цели захвата лямбд.
источник
namespace
и пометил их какthread_local
, этоftw
подход, который я выбрал для решения чего-то подобного.Я просто столкнулся с этой проблемой.
Код отлично компилируется без лямбда-захвата, но есть ошибка преобразования типа с лямбда-захватом.
Решение на 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; }
Изменить: мне пришлось вернуться к этому, когда я столкнулся с устаревшим кодом, в котором я не мог изменить исходную сигнатуру функции, но все же нужно было использовать лямбды. Решение, которое не требует изменения сигнатуры исходной функции, приведено ниже:
#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; }
источник
ftw
чтобы взятьstd::function
вместо указателя функции ...ftw
бы был аргумент void * userdata, я бы предпочел ответ от @ evgeny-karpov.ОРИГИНАЛ
Лямбда-функции очень удобны и сокращают код. В моем случае мне понадобились лямбды для параллельного программирования. Но для этого требуется захват и указатели функций. Мое решение здесь. Но будьте осторожны с набором переменных, которые вы захватили.
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 ++ с захватами в качестве указателя на функцию. Поскольку это было полезно для меня и других людей, я сделал некоторые улучшения.
API указателя стандартной функции C использует соглашение void fn (void * data). По умолчанию используется это соглашение, и лямбда должна быть объявлена с аргументом 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; } };
Exapmle
int a = 100; auto b = [&](void*) {return ++a;};
Преобразование лямбда с захватами в указатель C
void (*f1)(void*) = Lambda::ptr(b); f1(nullptr); printf("%d\n", a); // 101
Можно использовать и так
auto f2 = Lambda::ptr(b); f2(nullptr); printf("%d\n", a); // 102
Если нужно использовать возвращаемое значение
int (*f3)(void*) = Lambda::ptr<int>(b); printf("%d\n", f3(nullptr)); // 103
И в случае использования данных
auto b2 = [&](void* data) {return *(int*)(data) + a;}; int (*f4)(void*) = Lambda::ptr<int>(b2); int data = 5; printf("%d\n", 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 }));
Это работает, потому что каждая лямбда имеет уникальную подпись, поэтому сделать ее статической не проблема. Ниже приведена общая оболочка с переменным числом аргументов и любым типом возвращаемого значения, использующим тот же метод.
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)); }
И подобное использование
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; }));
источник
=
использования&i
в вашем цикле for.Хе-хе - довольно старый вопрос, но все же ...
#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; }
источник
Есть хакерский способ преобразовать захватывающую лямбду в указатель на функцию, но вы должны быть осторожны при его использовании:
/codereview/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; }
источник
Мое решение, просто используйте указатель на функцию для ссылки на статическую лямбду.
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(); }
источник
Нашел здесь ответ: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
Он преобразует
lambda pointer
вvoid*
и обращенный назад , когда это необходимо.кому
void*
:auto voidfunction = new decltype (to_function (lambda)) (to_function (lambda));
из
void*
:автоматическая функция = static_cast <std :: function *> (voidfunction);
источник