【问题标题】:How to call in a function a lambda function as a parameter?如何在函数中调用 lambda 函数作为参数?
【发布时间】:2013-03-19 17:13:53
【问题描述】:

不知道如何更好地改写标题。

我想将低级代码移到简单函数之外,并将代码移到类中,以便类处理更复杂的东西。
我想从这里开始:

void a(void) {
    [low level operation];
    //User typed simple operations
    [low level operation];
}

static void AClass::b() {
    register(a); //register(void (__cdecl *func)())
}

int main(void) {
    AClass::b();
    return 0;
}

收件人:

void a(void) {
    //[no low level operation]
    //User typed simple operations
    //[no low level operation]
}

static void AClass::b(void (*func)()) {
    auto funcA = [] (void (*func)()) -> void (*)() { 
        [that first low level operation]; 
        func(); //Which is: void a(void);
        [the second low level operation]; 
    };
    register(funcA(func));
}

int main(void) {
    AClass::b(&a);
    return 0;
}

目前我收到错误“::operator ()' : function must return a value” - 因为指针。我该如何解决这个问题?只返回 void 是行不通的。通过引用 lambda 传递参数(函数“func”)也不起作用(因为在这种情况下,lambda 不再只是一个函数而是一个类) - register(void (__cdecl *func )()) 无法转换 now not a function lambda。

任何想法如何解决我的问题?

【问题讨论】:

  • 您知道register 是C++ 中的关键字吗?已弃用,但仍然存在。
  • @Cheersandhth.-Alf “注册”函数要求“void (__cdecl*)(void)”,这意味着没有参数。而且, a(void) 不是这里的问题。拉姆达是。我尝试让 lambda 返回 void (*)(void) 以便“注册”,但我不能,当然。
  • 很抱歉以第一印象回答这个问题。第二印象:代码没有意义,即使意图是。而不是使用 lambda 函数等。我建议您查找“模板模式”(这与使用 C++ 模板无关)。
  • @JoachimPileborg 我在这里使用它只是为了演示。该函数实际上是在应用程序“glutDisplayFunc”中调用的——来自 Glut 库。此外,错误并不是说我使用了关键字,而是因为指针。

标签: c++ pointers c++11 lambda


【解决方案1】:

有状态的 lambda 不能转换为函数指针。

只有没有状态的 lambda 可以转换为函数指针。

您的州需要通过一些后门滑入。 glutDisplayFunc,只接受一个无状态函数指针。无法修改。

如果你能找到一个后门来存储一些东西,你就可以用它来存储一个任意的 lambda。在这种情况下,glutDisplayFunc 与当前窗口相关联。有没有什么地方可以在当前窗口中推状态,并找出是哪一个?

假设你找到了这样一个void*。然后简单地分配一个std::function<void()>,把它塞进去,然后注册下面的lambda:

void AClass::b(void (*func)()) {
   auto funcA = [func] () { 
     [that first low level operation]; 
     func(); //Which is: void a(void);
     [the second low level operation]; 
   };
   registerPVoidSomewhere( new std::function<void()>( funcA ) );

   register([]() {
     void* pVoid = getPVoidFromWhereIHideItAbove();
     std::function<void()>* pFunc = reinterpret_cast<std::function<void()>*>( pVoid );
     if (pFunc) {
       (*pFunc)();
     };
   } );
}

现在,我敢打赌,当调用 glutDisplayFunc 时,它是从上下文中调用的,此时您可以询问 glut 当前窗口是什么。也许该窗口有一个用户定义的void* 的位置,或者您可以有一个从窗口指针到您管理的void* 的全局映射(在第二种情况下,您甚至可以有一个从窗口指针到@ 的全局映射987654330@,并摆脱那个讨厌的铸造)。

【讨论】:

  • 这变成了丑陋的东西 :) 也许我应该更好地将这些函数保留在“void a”函数中?
  • @LilianA.Moraru 一个签名为void()的函数没有状态来保存东西,除非你把东西塞进全局变量中,或者做一些残忍的黑客行为。因此需要一个后门状态。我敢打赌,在您的情况下存在后门状态,这是处理回调函数状态的预期方式。
  • 没有全局变量或残暴的黑客攻击......这就是为什么我真的不想走这个解决方案的道路。我想让它保持干净和简单。 Glut 可以在初始化后从任何地方执行这两个操作。我没有做任何黑客/全局变量...例如,您可以将其与 Botan 的工作方式进行比较。初始化管道,而不是从您调用 Botan 函数的任何位置,它将影响一个点,您不必传递变量。
  • 用另一种方式解决了这个问题......我的示例代码中没有显示的是,在“void a()”中,AClass 被初始化了一次。我在 C++ 中使用了 RAII 习语:我将第一个低级操作添加到类的构造函数中,将第二个低级操作添加到析构函数中。
【解决方案2】:

有两个问题,一个是类型问题,一个是您声明 lambda 以返回函数指针但实际上不返回任何内容。

第一个可以通过使用模板轻松解决:

struct AClass
{
    template<typename Ft>
    static void b(Ft func)
    {
        auto funcA = [&func]()
        {
            // Do some stuff
            func();
            // Do some other stuff
        };
        some_register_function(funcA);
    }
};

如您所见,我还更改了 lambda 声明,因为在您的版本中您实际上 调用 它,它还将调用您作为参数传入的函数指针,这解决了第二个问题。

请注意,我实际上并不知道它是否会起作用,因为glutDisplayFunc 是纯 C 库中的纯 C 函数,并且对 lambda 或函数对象一无所知。

【讨论】:

  • 这行不通——glutDisplayFunc 接受函数指针,但只有无捕获的 lambda 可以转换为函数指针。
  • 不,不起作用。因为我没有指定 lambda 的类型,所以它在 glutDisplayFunc 要求 void (__cdecl *)() 时返回 void。这仍然是 lambda 的问题...
  • @Lilian :这不是调用 lambda,因此 lambda 的返回类型是什么并不重要。这里的问题是这个 lambda 有一个捕获列表,因此不能像 glutDisplayFunc 想要的那样转换为函数指针。
猜你喜欢
  • 1970-01-01
  • 2018-02-16
  • 2011-12-27
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 2020-11-09
相关资源
最近更新 更多