C++ lambda مع يلتقط كمؤشر الدالة




function-pointers c++11 (5)

Hehe - سؤال قديم جدا ، ولكن لا يزال ...

#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;
}

كنت ألعب مع lambdas 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)’

بعد بعض القراءة. تعلمت أن lambdas باستخدام يلتقط لا يمكن تحويلها ضمنا إلى مؤشرات الدالة.

هل هناك حل لهذا؟ هل تعني حقيقة أنه لا يمكن تحويلها "ضمنيًا" أنه يمكن "بشكل صريح" تحويلها؟ (جربت الصب ، دون نجاح). ماذا ستكون طريقة نظيفة لتعديل مثال العمل حتى أتمكن من إلحاق الإدخالات ببعض الكائنات باستخدام lambdas؟


أنا فقط ركضت في هذه المشكلة.

رمز يجمع غرامة دون التقاط لامبدا ، ولكن هناك خطأ في تحويل النوع مع التقاط لامدا.

الحل باستخدام C ++ 11 هو استخدام std::function (تحرير: يظهر حل آخر لا يتطلب تعديل توقيع الدالة بعد هذا المثال). يمكنك أيضًا استخدام boost::function (والتي تعمل في الواقع بشكل أسرع). كود المثال - تم تغييره بحيث يتم gcc 4.7.1 مع 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;
}

تعديل: اضطررت إلى زيارة ذلك مرة أخرى عندما واجهت رمزًا قديمًا لم أتمكن من تعديل توقيع الوظيفة الأصلي ، ولكن لا تزال هناك حاجة لاستخدام lambdas. الحل الذي لا يتطلب تعديل وظيفة التوقيع على الوظيفة الأصلية هو أدناه:

#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 / الدالة الخاصة بك. على سبيل المثال ، يمكنك إنشاء كائن فضاء تقليدي وإعطائه وظيفة عضو ثابت والتي تشير إلى مثيل فريد (عالمي / ثابت).

ولكن هذا نوع من هزيمة الغرض بأكمله من الاستيلاء على lambdas.


هناك طريقة غير محتوبة لتحويل لامبدا التقاط إلى مؤشر وظيفة ، ولكن عليك أن تكون حذرا عند استخدامه:

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;
}

وظائف Lambda مريحة للغاية وتقليل الكود. في حالتي كنت بحاجة إلى lambdas للبرمجة المتوازية. لكنه يتطلب التقاط وتأثير المؤشرات. الحل هو هنا. ولكن كن حذرا مع نطاق المتغيرات التي قمت بالتقاطها.

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++11