【问题标题】:C++ function pointer and member function pointerC++函数指针和成员函数指针
【发布时间】:2018-09-12 21:49:09
【问题描述】:

有没有办法同时为函数和成员函数使用函数指针?

我想创建一种事件句柄,它可以保存类的成员函数,同时它应该能够具有“普通”函数指针。

编辑:我找到了解决方案。可能不是最好的,而是一个;

事件.h

#include <map>

class BasicEvent {

public:

    BasicEvent(int id) : eventId(id)
    {}

    int GetId() const
    { 
        return eventId;
    }

    virtual void ObjectMethode() {}

protected:

    const int eventId;

};

class BasicEventHandler {

public:

    void Trigger(BasicEvent* event)
    {
        if (this->eventMap.find(event->GetId()) != this->eventMap.end()) {
            eventMap[event->GetId()](event);
        }
    }

    void AddEvent(int id, std::function<int(BasicEvent*)> eventFunction)
    {
        eventMap[id] = eventFunction;
    }

    void RemoveEvent(int id)
    {
        this->eventMap.erase(id);
    }

private:

    std::map<int, std::function<int(BasicEvent*)>> eventMap;

};

main.cpp

class CustomEvent : public BasicEvent {

public:

    CustomEvent(int id) : tq::BasicEvent(id)
    {
    x = 9;
    y = 8;
    }

    struct {
    int x;
    int y;
};

    void ObjectMethode()
    {
    std::cout << this->eventId << " Custom Event: X: " << x << " Y: " << y << '\n';
    }

};


int main() {
    BasicEventHandler myHandler;

    myHandler.AddEvent(0, [&](BasicEvent* event) {
        std::cout << " EVENT <" << event->GetId() << "> Triggered!\n";
    return 0;
    });

    myHandler.AddEvent(1, [&](tq::BasicEvent* event) {
    event->ObjectMethode();
    return 0;
    });

    myHandler.Trigger(&BasicEvent(0));
    myHandler.Trigger(&CustomEvent(1));

    return 0;
}

【问题讨论】:

  • 你不能用std::function吗?另请参阅 std::bindstd::invoke
  • 我不这么认为。我现在将提供两种解决方案。一个用于班级成员,一个用于功能

标签: c++ function pointers


【解决方案1】:

C++11 和 Lambdas .. 耶!!

所以我们开始..

让我们有一个函数,你的事件处理程序将接受一个谓词(一个返回真或假的函数)和两个函数,即on_sucesson_error

如果test_predicate 返回true,则应调用on_success,否则将调用on_error

template<typename Predicate, typename OnSucess, typename OnError>
void single_event_handler (Predicate && test_predicate,
                           OnSucess  && on_success,
                           OnError   && on_error)
{
    if( test_predicate() )
        on_success();
    else
        on_error();
}

现在是我们的调用方。 在这里,我们有 3 个函数想要适应这种模式。

bool is_even(int num) { return !(num % 2); }
void on_even_found() { std::cout << "Number passed is even\n"; }
void on_odd_found() { std::cout << "Number passed is odd\n"; };

但正如您所见,is_even 函数不适合test_predicate 的签名。 is_even(int) 接受单个参数,而 test_predicate() 不接受任何参数。

那么我们该如何解决这个问题呢?

我们将它包装成另一个函数和/或 lambda,然后传递它。

指导原则:每一个问题都可以通过增加一层来解决。

// wrap it in another function or lambda
auto is_5_even = []() { return is_even(5); };
auto is_4_even = []() { return is_even(4); };

现在,一旦拼图的所有部分都准备好了,我们就可以创建自己的事件处理程序了。

// caller side - pair the final structure
single_event_handler(is_5_even, on_even_found, on_odd_found);
single_event_handler(is_4_even, on_even_found, on_odd_found);

现在这段代码可以在 pre C++11 中实现,通过使用绑定和函数指针。 遗憾的是,std::bindstd::function 是在 C++11 中引入的,您必须使用其他替代方案,例如 boost 库。

此外,可以在single_event_handler 之上编写另一个包装器,它可以一次管理多个事件,但我认为我们必须创建另一个函数register_event 才能有效地使用它。

这是完整的工作代码,供您参考(使用g++ -std=c++11编译)

#include <iostream>

template<typename Predicate, typename OnSucess, typename OnError>
void single_event_handler (Predicate && test_predicate,
                           OnSucess  && on_success,
                           OnError   && on_error)
{
    if( test_predicate() )
        on_success();
    else
        on_error();
}

bool is_even(int num) { return !(num % 2); }
void on_even_found() { std::cout << "Number passed is even\n"; }
void on_odd_found() { std::cout << "Number passed is odd\n"; };

int main()
{
    // caller side
    auto is_5_even = []() { return is_even(5); };
    auto is_4_even = []() { return is_even(4); };

    single_event_handler(is_5_even, on_even_found, on_odd_found);
    single_event_handler(is_4_even, on_even_found, on_odd_found);

    return 0;
}

【讨论】:

  • 真的,非常感谢。但实际上我的需求看起来有点不同。这是我想到的概念void (*eventClose)(EventType&amp;); // &lt;- Shall be capable of holding function and member function pointer
  • 那么你最好使用 lambdas 或 bind,使用你指定的接口。 :) 是的,现在我的回答似乎与您的需求无关。
【解决方案2】:

成员函数从哪里得到它的“this”指针? 这两个函数不能具有相同的签名。 可能,您最好的选择是创建一个 lambda,它将捕获“this”,并将其发送到您的事件处理程序。

std::function 可以为您保管。

【讨论】:

    【解决方案3】:

    接受您的处理程序作为std::function,然后客户端可以传入他们想要的任何内容。无论是函数、lambdas 还是其他仿函数,您都可以命名。

    模板将为您处理详细信息,客户可以选择他们希望被调用的方式。

    【讨论】: