Bawiłem się lambdami C ++ i ich niejawną konwersją na wskaźniki funkcji. Moim początkowym przykładem było użycie ich jako wywołania zwrotnego dla funkcji ftw. Działa to zgodnie z oczekiwaniami.
#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;
}
Po zmodyfikowaniu go w celu użycia przechwytywania:
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;
}
Otrzymałem błąd kompilatora:
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)’
Po lekturze. Dowiedziałem się, że lambdy używające przechwytywania nie mogą być niejawnie konwertowane na wskaźniki funkcji.
Czy istnieje obejście tego problemu? Czy fakt, że nie można ich „niejawnie” przekonwertować, oznacza, że można je „jawnie” przekonwertować? (Próbowałem rzucać, ale bez powodzenia). Jaki byłby czysty sposób zmodyfikowania działającego przykładu, aby móc dołączyć wpisy do jakiegoś obiektu za pomocą lambd?
c++
lambda
function-pointers
c++11
duncan
źródło
źródło
void *
). Jeśli używana biblioteka pozwala na ten dodatkowy argument, znajdziesz obejście tego problemu. W przeciwnym razie nie będziesz mieć możliwości osiągnięcia czysto tego, co chcesz zrobić.Odpowiedzi:
Od przechwytywania lambdas trzeba zachować stan, nie jest naprawdę proste „obejście”, ponieważ są one nie tylko zwykłe funkcje. Punkt dotyczący wskaźnika funkcji polega na tym, że wskazuje on pojedynczą, globalną funkcję, a ta informacja nie ma miejsca na stan.
Najbliższym obejściem (które zasadniczo odrzuca stan) jest zapewnienie pewnego typu zmiennej globalnej, do której można uzyskać dostęp z poziomu lambda / funkcji. Na przykład, możesz stworzyć tradycyjny obiekt funktora i nadać mu statyczną funkcję składową, która odwołuje się do jakiejś unikalnej (globalnej / statycznej) instancji.
Ale to w pewnym sensie pokonuje cały cel przechwytywania lambd.
źródło
namespace
i oznacz je jakothread_local
, to jestftw
podejście, które wybrałem do rozwiązania czegoś podobnego.Właśnie napotkałem ten problem.
Kod kompiluje się dobrze bez przechwytywania lambda, ale podczas przechwytywania lambda występuje błąd konwersji typu.
Rozwiązaniem z C ++ 11 jest użycie
std::function
(edycja: inne rozwiązanie, które nie wymaga modyfikacji podpisu funkcji jest pokazane po tym przykładzie). Możesz także użyćboost::function
(który faktycznie działa znacznie szybciej). Przykładowy kod - zmieniony tak, żeby się kompilował, skompilowany zgcc 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; }
Edycja: musiałem to ponownie odwiedzić, gdy natknąłem się na starszy kod, w którym nie mogłem zmodyfikować oryginalnej sygnatury funkcji, ale nadal potrzebowałem użyć lambd. Poniżej znajduje się rozwiązanie, które nie wymaga modyfikowania sygnatury funkcji oryginalnej funkcji:
#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; }
źródło
ftw
aby wziąćstd::function
zamiast wskaźnika funkcji ...ftw
miał argument void * userdata, wolałbym odpowiedź z @ evgeny-karpov.ORYGINAŁ
Funkcje lambda są bardzo wygodne i redukują kod. W moim przypadku potrzebowałem lambd do programowania równoległego. Ale wymaga przechwytywania i wskaźników funkcji. Moje rozwiązanie jest tutaj. Uważaj jednak na zakres zmiennych, które przechwyciłeś.
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>; }
Przykład
int a = 100; auto b = [&]() { a += 1;}; void (*fp)(void*) = lambda_ptr(b); fp(&b);
Przykład ze zwracaną wartością
int a = 100; auto b = [&]() {return a;}; int (*fp)(void*) = lambda_ptr<int>(b); fp(&b);
AKTUALIZACJA
Poprawiona wersja
Minęło trochę czasu od opublikowania pierwszego postu o lambdzie w C ++ z przechwytywaniami jako wskaźnikiem funkcji. Ponieważ był użyteczny dla mnie i innych osób, dokonałem pewnej poprawy.
Standardowy interfejs API funkcji C używa konwencji void fn (void * data). Domyślnie używana jest ta konwencja, a lambda należy zadeklarować z argumentem void *.
Ulepszona implementacja
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;};
Konwersja lambda z przechwytywaniami do wskaźnika C.
void (*f1)(void*) = Lambda::ptr(b); f1(nullptr); printf("%d\n", a); // 101
Może być również używany w ten sposób
auto f2 = Lambda::ptr(b); f2(nullptr); printf("%d\n", a); // 102
W przypadku, gdy należy użyć wartości zwracanej
int (*f3)(void*) = Lambda::ptr<int>(b); printf("%d\n", f3(nullptr)); // 103
I w przypadku wykorzystania danych
auto b2 = [&](void* data) {return *(int*)(data) + a;}; int (*f4)(void*) = Lambda::ptr<int>(b2); int data = 5; printf("%d\n", f4(&data)); // 108
źródło
Używając lokalnie globalnej (statycznej) metody można to zrobić w następujący sposób
template <class F> auto cify_no_args(F&& f) { static F fn = std::forward<F>(f); return [] { return fn(); }; }
Załóżmy, że mamy
void some_c_func(void (*callback)());
Więc użycie będzie
some_c_func(cify_no_args([&] { // code }));
To działa, ponieważ każda lambda ma unikalną sygnaturę, więc uczynienie jej statyczną nie stanowi problemu. Poniżej znajduje się ogólne opakowanie z różną liczbą argumentów i dowolnym typem zwracanym przy użyciu tej samej metody.
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)); }
I podobne użycie
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; }));
źródło
=
używać&i
w pętli for.Hehe - dość stare pytanie, ale wciąż ...
#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; }
źródło
Istnieje hakerski sposób na przekonwertowanie przechwytywanej lambdy na wskaźnik funkcji, ale podczas jej używania należy zachować ostrożność:
/codereview/79612/c-ifying-a-capturing-lambda
Twój kod wyglądałby wtedy tak (ostrzeżenie: kompilacja mózgu):
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; }
źródło
Moje rozwiązanie, po prostu użyj wskaźnika funkcji, aby odwołać się do statycznej lambdy.
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(); }
źródło
Znalazłem odpowiedź tutaj: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
W razie potrzeby konwertuje
lambda pointer
dovoid*
i konwertuje z powrotem.do
void*
:auto voidfunction = new decltype (to_function (lambda)) (to_function (lambda));
od
void*
:funkcja auto = static_cast <std :: function *> (voidfunction);
źródło