简体   繁体   English

将 C++ 方法作为函数指针传递

[英]Passing C++ method as function pointer

I have a class method:我有一个类方法:

    bool sendRequest(DataFieldList& requestParams,
                     DataFieldList& responseParams,
                     std::string context, void(*sendFailure)() = NULL);

The sendFailure is an optional function pointer, in my C++ class in one instance I have: sendFailure 是一个可选的函数指针,在我的 C++ 类中,有一个实例:

    sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", HBCMaxBrightnessLimitSendFailure);

HBCMaxBrightnessLimitSendFailure is declared in the class as a private method: HBCMaxBrightnessLimitSendFailure 在类中声明为私有方法:

    void HBCMaxBrightnessLimitSendFailure();

When I build this application I get error:当我构建这个应用程序时,我收到错误:

    lightingsystemcomponent.cpp(969): error C3867: 'Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure': function call missing argument list; use '&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure' to create a pointer to member

I also tried:我也试过:

    sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", HBCMaxBrightnessLimitSendFailure());

This changed the error to:这将错误更改为:

    lightingsystemcomponent.cpp(969): error C2664: 'bool Lighting::SystemComponent_impl::sendRequest(DataFieldList &, DataFieldList &,std::string,void (__cdecl *)(void))' : cannot convert argument 4 from 'void' to 'void (__cdecl *)(void)'

Expressions of type void cannot be converted to other types void 类型的表达式不能转换为其他类型

I've had a bit of a move around and tried to implement the proposal put forward by Spencer, new prototype:我做了一些改变,并尝试实施 Spencer 提出的提案,新原型:

    bool sendRequest(DataFieldList& requestParams,
                     DataFieldList& responseParams,
                     std::string context,
                     std::function<void()> sendFailure = NULL);

In the sendRequest function:在 sendRequest 函数中:

    void LightingEngine::sendRequest(DataFieldList& requestParams,
                                     DataFieldList& responseParams,
                                     std::string context,
                                     std::function<void()> sendFailure) {
    if ( sendFailure != NULL ) {
        sendFailure();
    }

} }

Of course this isn't all the code, just enough to illustrate the new layout and call.当然这还不是全部代码,仅仅足以说明新的布局和调用。

When I try to build this I get:当我尝试构建它时,我得到:

    error C2064:  term does not evaluate to a function taking 0 arguments

Here is an example of the class thats calling this routine and the callback function I'm passing:这是调用此例程的类和我传递的回调函数的示例:

    void component::onFailure() {
        ...Do something...
    }

    void component::update() {
        ... Some set-up to build paramlist ...
        LightingEngine* engine = getLightingEngine();
        assert(engine != nullptr);
        engine->sendRequest(&paramList, &responseList, "Some context text", [this]()->void { onFailure(); });            
    }

A nonstatic class member function is different from a non-class function, in that it requires an instance of the class to work.非静态类成员函数与非类函数不同,它需要类的实例才能工作。 In your declaration在您的声明中

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, void(*sendFailure)() = NULL);

sendFailure is declared as a non-member function. sendFailure被声明为非成员函数。 If it has to be this way, you will never be able to bind to a nonstatic member function.如果必须这样,您将永远无法绑定到非静态成员函数。

The simplest course would be to declare HBCMaxBrightnessLimitSendFailure as static or even move it out of the class altogether, but that will only work if it doesn't depend on the class's state.最简单的方法是将HBCMaxBrightnessLimitSendFailure声明为static ,甚至将其完全移出类,但这只有在它不依赖于类的状态时才有效。

Presumably you have MANY DIFFERENT failure functions that get passed in at different times.大概你有许多不同的失败函数,它们在不同的时间被传入。 If there's only the one, don't pass in a function pointer .如果只有一个,不要传入函数指针 Instead pass in a bool:而是传入一个布尔值:

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 bool sendFailure = false)
{
  // ...
  if (sendFailure) HBCMaxBrightnessLimitSendFailure();
  // ...
}

You can use a pointer-to-member function but you need an instance of the class.您可以使用指向成员函数的指针,但您需要该类的实例。 If sendRequest and HBCMaxBrightnessLimitSendFailure are in the same class, use this .如果sendRequestHBCMaxBrightnessLimitSendFailure在同一类中,请使用this

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, 
                 void(lightingsystemcomponent::*sendFailure)()= 0);
{
 if (sendFailure !=0) this ->*sendFailure();
}

...
sendRequest (request, response, ctxt, &HBCMaxBrightnessLimitSendFailure); 

The most flexible way to do it (at the cost of a little overhead) is to redeclare the sendFailure parameter as a std::function :最灵活的方法(以一点开销为代价)是将sendFailure参数重新声明为std::function

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, std::function<void()> sendFailure);

You would then call sendRequest in a class method using a lambda:然后,您将使用 lambda 在类方法中调用sendRequest

DataFieldList request;
DataFieldList response;
sendRequest (request, response, "context", [this]()->void 
                             { HBCMaxBrightnessLimitSendFailure(); })

If the function parameter needs to be a bare function, eg in order to pass to a C API call, things get messy.如果函数参数需要是一个裸函数,例如为了传递给 C API 调用,事情就会变得一团糟。 A lambda with captures will never work.带有捕获的 lambda 永远不会工作。 Such a "stateful" lambda is a class created on the fly with the function pointer and the capture values as data members.这种“有状态”的 lambda 是使用函数指针和捕获值作为数据成员动态创建的类。 And you can't pass a C++ class into a C function.并且您不能将 C++ 类传递给 C 函数。

But you need an instance of the class to use the member function!但是你需要一个类的实例来使用成员函数! Arrgh!啊! The only thing for it is a global instance of the class to access from within the function:它唯一的事情是从函数内部访问的类的全局实例:

class lightingsystemcomponent
{
    static lightingsystemcomponent*api_context;
    static void HBCMaxBrightnessLimitSendFailure()
    {
      api_context-> something();
    }
    bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, void(*sendFailure)() = NULL);
};
// ...
api_context = this;
sendRequest(request, response, "context", HBCMaxBrightnessLimitSendFailure());

This is analogous to the stuff a stateful lambda does for you, but you can pass it to a C library at the cost of some elegance.这类似于有状态 lambda 为您做的事情,但您可以将它传递给 C 库,但要付出一些优雅的代价。

You could declare sendFailure as a member function pointer.您可以将sendFailure声明为成员函数指针。

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, 
                 void(Lighting::SystemComponent_impl::*sendFailure)() = nullptr);

This would allow you to write a really long line:这将允许你写一个很长的行:

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", &Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure);

Another option is to use std::function .另一种选择是使用std::function

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, 
                 const std::function<void()> &sendFailure = nullptr);

And call sendRequest like this:并像这样调用sendRequest

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [this]{ HBCMaxBrightnessLimitSendFailure() });

The idiomatic "C++ way" of doing callbacks is to use a function template.进行回调的惯用“C++ 方式”是使用函数模板。 This is similar to using std::function but with far superior performance这类似于使用std::function但性能要好得多

template <typename Func>
bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context, 
                 Func sendFailure);

The only minor issue with this is that you'd have to have another overload for when sendFailure isn't passed.唯一的小问题是,当sendFailure未通过时,您必须有另一个重载。 For this reason, it's probably not ideal for your scenario.因此,它可能不适合您的场景。

bool sendRequest(DataFieldList& requestParams,
                 DataFieldList& responseParams,
                 std::string context);

Then call sendRequest like this:然后像这样调用sendRequest

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [this]{ HBCMaxBrightnessLimitSendFailure() });

If you change your void(*sendFailure)() function pointer to an std::function<void(void)> , then I think you can do what you want:如果您将void(*sendFailure)()函数指针更改为std::function<void(void)> ,那么我认为您可以执行所需的操作:

void printFunc(int a, std::function<void(void)> f)
{
    std::cout << a << '\t';
    if (f)
        f();
    else
        std::cout << "f is null" << std::endl;
}

void testFunc()
{
    std::cout << "testFunc" << std::endl;
}

class A
{
private:
    void privatePrint() { std::cout << "privatePrint " << a << std::endl; }
    int a;

public:
    A(int i) : a { i } {}
    void print()
    {
        printFunc(a, [this]() {this->privatePrint();} );
    }
};

class B
{
private:
    static void staticPrint() { std::cout << "staticPrint" << std::endl; }
    int b;

public:
    B(int i) : b{ i } {}
    void print()
    {
        printFunc(b, staticPrint);
    }
};

struct C
{
    void operator()() { std::cout << "operator()" << std::endl; }
};

int main()
{
    printFunc(1, testFunc);

    A a{ 2 };
    a.print();

    B b{ 3 };
    b.print();

    printFunc(4, nullptr);

    C c;
    printFunc(5, c);
}

Here I pass a non-class function, a non-static member function (wrapped in a lambda), a static member function, a nullptr , and a functor to the same printFunc and they all work.在这里,我将一个非类函数、一个非静态成员函数(包装在 lambda 中)、一个静态成员函数、一个nullptr和一个函子传递给同一个printFunc ,它们都可以工作。 Output is:输出是:

1       testFunc
2       privatePrint 2
3       staticPrint
4       f is null
5       operator()

Edit:编辑:

If you can change the the signature of sendRequest and are using a fairly modern c++ (11 or higher), i would opt for std::function .如果您可以更改 sendRequest 的签名并使用相当现代的 C++(11 或更高版本),我会选择std::function

#include <functional>

bool sendRequest(DataFieldList& requestParams,
                     DataFieldList& responseParams,
                     std::string context, std::function<void()> sendFailure = nullptr);

Then you can call it with a lambda like proposed:然后你可以用一个像提议的 lambda 来调用它:

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", [&](){HBCMaxBrightnessLimitSendFailure();});

or without lambda but with bind:或者没有 lambda 但有绑定:

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", std::bind(&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure,this));

Old post:旧帖:

Try what the compiler tells you试试编译器告诉你的

&Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure

so:所以:

sendRequest(paramList, responseList, "Could not set background lighting control brightness limit", &Lighting::SystemComponent_impl::HBCMaxBrightnessLimitSendFailure);

A member function cannot be directly converted to a function pointer since it has an implicit parameter for the object calling it (you see it as the this pointer in the member).成员函数不能直接转换为函数指针,因为它具有调用它的对象的隐式参数(您将其视为成员中的this指针)。 Working around this is not as easy, as I thought it is see here .解决这个问题并不像我认为的那样容易, 见这里

What's the best solution is, depends on your use case.最好的解决方案是什么,取决于您的用例。 If both caller and callee are member functions and also really depend on the object they are called from (that is the same for both), add another member function pointer as parameter.如果调用者和被调用者都是成员函数,并且确实依赖于调用它们的对象(两者相同),则添加另一个成员函数指针作为参数。 If your passed member function does not depend on its object, make it static and you have no problem.如果您传递的成员函数不依赖于其对象,请将其设为静态,您就没有问题。 For everything in between it gets tricky.对于介于两者之间的所有事情,它都会变得棘手。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM