C++ lambda avec captures comme pointeur de fonction
je jouais avec C++ lambdas et leur conversion implicite en pointeurs de fonction. Mon exemple de départ était de les utiliser comme callback pour la fonction ftw. Cela fonctionne comme prévu.
#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;
}
après l'avoir modifié pour utiliser des 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;
}
j'ai eu l'erreur du compilateur:
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)’
après quelques lectures. J'ai appris que lambdas utilisant des captures ne peut pas être converti implicitement en des pointeurs de fonction.
est-ce qu'il y a une solution? Le fait qu'ils ne peuvent pas être "implicitement" converti signifie qu'ils peuvent "explicitement" convertis? (J'ai essayé le casting, sans succès). Quelle serait une façon propre de modifier l'exemple de travail pour que je puisse ajouter les entrées à un objet en utilisant lambdas?.
7 réponses
depuis la capture lambdas besoin de préserver un État, il n'y a pas vraiment une simple "solution", car ils sont pas juste des fonctions ordinaires. Le point sur un pointeur de fonction est qu'elle pointe vers une seule fonction globale, et cette information n'a pas de place pour un état.
la solution la plus proche (qui écarte essentiellement l'état) est de fournir un certain type de variable globale qui est accessible à partir de votre lambda/fonction. Pour par exemple, vous pouvez créer un objet fonctionnel traditionnel et lui donner une fonction de membre statique qui se réfère à une instance unique (globale/statique).
mais ça va à l'encontre du but de la capture de lambdas.
je viens de rencontrer ce problème.
le code compile fine without lambda captures, mais il y a une erreur de conversion de type avec lambda capture.
Solution avec C++11 est d'utiliser std::function
(éditer: une autre solution qui ne nécessite pas de modifier la signature de la fonction est montré après cet exemple). Vous pouvez également utiliser boost::function
(qui fonctionne en fait beaucoup plus rapidement). Exemple de code-modifié pour qu'il soit compilé, compilé avec 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: J'ai dû revisiter cela quand je suis tombé sur le code d'héritage où je ne pouvais pas modifier la signature de la fonction originale, mais j'avais encore besoin d'utiliser lambdas. Une solution qui ne nécessite pas de modifier la signature de la fonction d'origine est ci-dessous:
#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;
}
ORIGINAL
les fonctions Lambda sont très pratiques et réduisent le code. Dans mon cas, j'avais besoin lambdas pour la programmation parallèle. Mais cela nécessite des points de capture et de fonctionnement. Ma solution est ici. Mais faites attention avec la portée des variables que vous avez capturées.
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>;
}
exemple
int a = 100;
auto b = [&]() { a += 1;};
void (*fp)(void*) = lambda_ptr(b);
fp(&b);
exemple avec une valeur de retour
int a = 100;
auto b = [&]() {return a;};
int (*fp)(void*) = lambda_ptr<int>(b);
fp(&b);
Mise à jour
version améliorée
cela faisait longtemps que le premier post sur C++ lambda avec des captures comme pointeur de fonction n'avait pas été publié. Comme Il n'était utilisable pour moi et d'autres personnes j'ai fait quelques améliorations.
fonction Standard C pointeur api utilise void fn(void* data) de la convention. Par défaut cette convention est utilisée et lambda doit être déclarée avec un argument void*.
amélioration de la mise en œuvre
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;};
conversion lambda avec captures En pointeur C
void (*f1)(void*) = Lambda::ptr(b);
f1(nullptr);
printf("%d\n", a); // 101
peut aussi être utilisé de cette façon
auto f2 = Lambda::ptr(b);
f2(nullptr);
printf("%d\n", a); // 102
dans le cas où la valeur de retour doit être utilisée
int (*f3)(void*) = Lambda::ptr<int>(b);
printf("%d\n", f3(nullptr)); // 103
et dans le cas où des données sont utilisées
auto b2 = [&](void* data) {return *(int*)(data) + a;};
int (*f4)(void*) = Lambda::ptr<int>(b2);
int data = 5;
printf("%d\n", f4(&data)); // 108
en utilisant localement la méthode globale (statique), il peut être fait comme suit
template <class T>
auto wrap(T t) {
static T fn = t;
return [] { fn(); };
}
supposons que nous ayons
void some_c_func(void (*callback)());
donc l'usage sera
some_c_func(wrap([&] {
// code
}));
cela fonctionne parce que chaque lambda a une signature unique donc le rendre statique n'est pas un problème. Suivant un wrapper générique avec un nombre variadique d'arguments et n'importe quel type de retour utilisant la même méthode.
template <class T>
struct lambda_traits : lambda_traits<decltype(&T::operator())>
{ };
template <class T, class R, class... Args>
struct lambda_traits<R(T::*)(Args...) const> {
typedef R (*pointer)(Args...);
static pointer cify(T t) {
static T fn = t;
return [](Args... args) {
return fn(args...);
};
}
};
template <class T>
inline typename lambda_traits<T>::pointer cify(T t) {
return lambda_traits<T>::cify(t);
}
et usages similaires
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;
}));
Hehe-une vieille question, mais quand même...
#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;
}
il y a une façon hackish de convertir un lambda de capture en pointeur de fonction, mais vous devez être prudent lorsque vous l'utilisez:
Trouvé une réponse ici: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
il convertit lambda pointer
en void*
et de convertir en arrière si nécessaire.
-
à
void*
:auto voidfunction = new decltype(to_function (lambda)) (to_function(lambda));
-
de
void*
:fonction auto = static_cast< std::function*>( voidfunction);