Saya bermain dengan C ++ lambda dan konversi implisitnya ke fungsi pointer. Contoh awal saya menggunakannya sebagai callback untuk fungsi ftw. Ini bekerja seperti yang diharapkan.
#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;
}
Setelah mengubahnya untuk menggunakan tangkapan:
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;
}
Saya mendapat kesalahan kompiler:
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)’
Setelah membaca. Saya belajar bahwa lambda menggunakan penangkapan tidak dapat secara implisit dikonversi ke pointer fungsi.
Apakah ada solusi untuk ini? Apakah fakta bahwa mereka tidak dapat dikonversi "secara implisit" berarti bahwa mereka dapat "secara eksplisit" dikonversi? (Saya mencoba casting, tapi tidak berhasil). Apa cara yang bersih untuk memodifikasi contoh kerja sehingga saya bisa menambahkan entri ke beberapa objek menggunakan lambdas ?.
c++
lambda
function-pointers
c++11
duncan
sumber
sumber
void *
). Jika pustaka yang Anda gunakan memungkinkan untuk argumen tambahan ini, Anda akan menemukan solusinya. Jika tidak, Anda tidak bisa mencapai apa yang ingin Anda lakukan dengan bersih.Jawaban:
Karena menangkap lambda perlu mempertahankan status, sebenarnya tidak ada "solusi" sederhana, karena mereka bukan hanya fungsi biasa. Poin tentang penunjuk fungsi adalah ia menunjuk ke satu fungsi global, dan informasi ini tidak memiliki ruang untuk status.
Solusi terdekat (yang pada dasarnya membuang statefulness) adalah menyediakan beberapa jenis variabel global yang diakses dari lambda / function Anda. Misalnya, Anda dapat membuat objek functor tradisional dan memberinya fungsi anggota statis yang merujuk ke beberapa contoh unik (global / statis).
Tapi itu semacam mengalahkan seluruh tujuan menangkap lambda.
sumber
namespace
dan menandainya sebagaithread_local
, itulahftw
pendekatan yang saya pilih untuk menyelesaikan sesuatu yang serupa.Saya baru saja mengalami masalah ini.
Kode dikompilasi dengan baik tanpa tangkapan lambda, tetapi ada kesalahan jenis konversi dengan tangkapan lambda.
Solusi dengan C ++ 11 adalah dengan menggunakan
std::function
(edit: solusi lain yang tidak memerlukan modifikasi tanda tangan fungsi akan ditampilkan setelah contoh ini). Anda juga dapat menggunakanboost::function
(yang sebenarnya berjalan jauh lebih cepat). Contoh kode - diubah sehingga akan dikompilasi, dikompilasi dengangcc 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; }
Sunting: Saya harus mengunjungi kembali ini ketika saya menemukan kode warisan di mana saya tidak dapat mengubah tanda tangan fungsi asli, tetapi masih perlu menggunakan lambda. Solusi yang tidak memerlukan modifikasi tanda tangan fungsi dari fungsi asli ada di bawah ini:
#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; }
sumber
ftw
menjadi mengambilstd::function
alih-alih penunjuk fungsi ...ftw
memiliki argumen void * userdata, maka saya lebih suka jawaban dari @ evgeny-karpov.ASLI
Fungsi Lambda sangat nyaman dan mengurangi kode. Dalam kasus saya, saya membutuhkan lambda untuk pemrograman paralel. Tapi itu membutuhkan penunjuk menangkap dan fungsi. Solusi saya ada di sini. Tapi hati-hati dengan ruang lingkup variabel yang Anda tangkap.
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>; }
Contoh
int a = 100; auto b = [&]() { a += 1;}; void (*fp)(void*) = lambda_ptr(b); fp(&b);
Contoh dengan nilai kembali
int a = 100; auto b = [&]() {return a;}; int (*fp)(void*) = lambda_ptr<int>(b); fp(&b);
MEMPERBARUI
Versi yang ditingkatkan
Sudah lama sejak posting pertama tentang C ++ lambda dengan captures sebagai penunjuk fungsi telah diposting. Karena itu dapat digunakan untuk saya dan orang lain, saya membuat beberapa peningkatan.
Api pointer C fungsi standar menggunakan konvensi void fn (void * data). Secara default, konvensi ini digunakan dan lambda harus dideklarasikan dengan argumen void *.
Implementasi yang lebih baik
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;};
Mengonversi lambda dengan tangkapan ke penunjuk C.
void (*f1)(void*) = Lambda::ptr(b); f1(nullptr); printf("%d\n", a); // 101
Bisa digunakan dengan cara ini juga
auto f2 = Lambda::ptr(b); f2(nullptr); printf("%d\n", a); // 102
Dalam kasus nilai kembali harus digunakan
int (*f3)(void*) = Lambda::ptr<int>(b); printf("%d\n", f3(nullptr)); // 103
Dan jika data digunakan
auto b2 = [&](void* data) {return *(int*)(data) + a;}; int (*f4)(void*) = Lambda::ptr<int>(b2); int data = 5; printf("%d\n", f4(&data)); // 108
sumber
Menggunakan metode global lokal (statis) dapat dilakukan sebagai berikut
template <class F> auto cify_no_args(F&& f) { static F fn = std::forward<F>(f); return [] { return fn(); }; }
Misalkan kita punya
void some_c_func(void (*callback)());
Jadi penggunaannya akan
some_c_func(cify_no_args([&] { // code }));
Ini berfungsi karena setiap lambda memiliki tanda tangan unik sehingga membuatnya statis tidak menjadi masalah. Berikut ini adalah pembungkus umum dengan jumlah argumen yang bervariasi dan tipe kembalian apa pun yang menggunakan metode yang sama.
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)); }
Dan penggunaan serupa
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; }));
sumber
=
digunakan&i
dalam loop-for Anda.Hehe - pertanyaan yang cukup lama, tapi tetap saja ...
#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; }
sumber
Ada cara hackish untuk mengonversi lambda penangkap menjadi penunjuk fungsi, tetapi Anda harus berhati-hati saat menggunakannya:
/codereview/79612/c-ifying-a-capturing-lambda
Kode Anda kemudian akan terlihat seperti ini (peringatan: kompilasi otak):
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; }
sumber
Solusi saya, cukup gunakan penunjuk fungsi untuk merujuk ke lambda statis.
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(); }
sumber
Menemukan jawabannya di sini: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
Itu mengkonversi
lambda pointer
kevoid*
dan mengkonversi kembali bila diperlukan.kepada
void*
:voidfungsi otomatis = jenis deklarasi baru (fungsi_ (lambda)) (fungsi_ fungsi (lambda));
dari
void*
:auto function = static_cast <std :: function *> (voidfunction);
sumber