同时处理非成员函数指针和成员函数指针

时间:2018-03-09 06:49:40

标签: c++ scheduled-tasks function-pointers member

我想做一个每X秒调用一次的成员函数。我做了一个可以处理非成员函数的小原型,但我不知道我是否做得好,而且我不能处理成员函数和非成员函数。

我有一个Event对象,它使用基本计时器来处理函数和延迟,以检测我们何时需要运行该函数:

typedef void (*ScheduleFunction)(float dt);

class Event
{
private:
    ScheduleFunction m_Func;
    double m_Timer;
    double m_Delay;

public:
    Event(ScheduleFunction function, double delay)
    {
        m_Func = function;
        m_Delay = delay;
    }

    void Call(float dt)
    {
        m_Timer += dt;
        if (m_Timer >= m_Delay)
        {
            m_Func(dt);
            m_Timer = 0.0;
        }
    }
};

然后,我有另一个对象,每个函数调用每个函数为vector<Event>

class Handler
{
private:
    void m_MemberFunction(float dt)
    {
        std::cout << "A member function." << std::endl;
    }

    std::vector<Event> m_ScheduleItems;

public:
    Handler()
    {
        // This will not compile, because the function expect a non member function
        Schedule(&Handler::m_MemberFunction, 1.0);
    }


    void CallScheduledFunctions(float dt)
    {
        for (std::vector<Event>::iterator it = m_ScheduleItems.begin(); it != m_ScheduleItems.end(); ++it)
        {
            it->Call(dt);
        }
    }



    void Schedule(ScheduleFunction func, double delay)
    {
        Event event(func, delay);
        m_ScheduleItems.push_back(event);
    }




    void Unschedule()
    {
        // TODO
    }

};

如您所见,我有一个注册新Schedule的函数Event。但是现在,它只处理非成员函数。有没有办法可以处理非成员函数和成员函数,不仅来自Handler,还来自所有其他对象

如果没办法,我怎么能实现这个目标?

2 个答案:

答案 0 :(得分:2)

使用std::function是可行的方法。任何可以调用的东西都可以转换/包装成std::function

在您的情况下,您可以像这样编写Event构造函数:

Event(std::function<void(float)>, double delay);

你可以用一个独立的函数,一个函子或一个lambda来调用它。 一些例子:

// declaration
auto myDummyFunction (float) -> void;

// Calling the constructor
auto event = Event(myDummyFunction,1.0);

如果我们想传递一个成员函数,只需使用lambda:

// declaration of the class with the member function
class SomeOtherClass
   {
   public:
      auto someMethod(float) -> void;
   };

// Calling the constructor
auto someOtherClass = SomeOtherClass{};
auto event = Event([&someOtherClass](float f){someOtherClass.someMethod(v)},1.0);

一般来说,我发现lambda比std :: bind方法更具可读性和灵活性。据我所知,它被建议(是Herb还是Scott?)不再使用std :: bind,而是使用lambda代替。

答案 1 :(得分:1)

UPDATE 1 Added "call any object's members" below.

BRIEF

I recommend using std::function and std::bind. But remind that std::function has some overhead due to the internal mechanisms!

std::function is very powerful as there are many things you can store in it.

Important: Using a function-pointer only approach is possible, but would cause some code and complexity if you must retain the simple unified interface.

EXAMPLE

#include <functional>

using ScheduleFunction_t = std::function<void(float)>;

class Event {
private:
    ScheduleFunction_t
        m_Func;
    double
        m_Timer,
        m_Delay;

public:
    Event(
        ScheduleFunction_t const&function, 
        double                   delay)
        : m_Func(function)
        , m_Delay(delay)
    { }

    void Call(float dt) {
        m_Timer += dt;
        if (m_Timer >= m_Delay)
        {
            // Important, if you do not assert in the constructor, check if the fn is valid...
            // The ctr shouldn't throw on runtime assert fail... memory leak and incpomplete construction...
            if(m_Func) 
                m_Func(dt); 

            m_Timer = 0.0;
        }
    }
};

As you can see, including the <functional> header will give you the template std::function<R(Args...)>, where R is the return type and Args... a comma separated list of fully qualified argument types.

void g_freeFunction(float f) {
    std::cout << "Globally floating for " << f << "ms" << std::endl;
}

class Handler {
private:
    void m_MemberFunction(float dt) {
        std::cout << "Floating around with " << dt << " m/s" << std::endl;
    }

    std::vector<Event> m_ScheduleItems;

public:
    Handler() {        
        // Bind member function
        Schedule<Handler, &Handler::m_MemberFunction>(this);
        // Or free
        Schedule(&g_freeFunction);
        // Or lambda
        Schedule([](float f) -> void { std::cout << "Weeeeeeeh...." << std::endl; });
    }

    void CallScheduledFunctions(float dt)
    {
        for(Event& e : m_ScheduleItems)
            e.Call(dt);        
    }

    template <typename TClass, void(TClass::*TFunc)(float)>
    void Schedule(
        TClass *const pInstance,
        double        delay = 0.0)
    {
        m_ScheduleItems.emplace_back(std::bind(TFunc, pInstance, std::placeholders::_1), delay); // Create in place at the end of vector.
    }

    void Schedule(
        ScheduleFunction_t fn,
        double             delay = 0.0) 
    {
        m_ScheduleItems.emplace_back(fn, delay); // Create in place at the end of vector.
    }


    void Unschedule() { /* TODO */ }
};

This way you can now bind almost whatever you want. :D

Update: The Schedule-function can not be called for any other type that has a matching public method, e.g.:

struct Test {
    void foo(float f) { 
        std::cout << "TEST ME!" << std::endl;
    }
};   

int main()
{
    Test t={};

    Handler h = Handler();
    h.Schedule<Test, &Test::foo>(&t);

    for(uint32_t k=0; k < 32; ++k)
        h.CallScheduledFunctions(k);
}

RESOURCES

http://en.cppreference.com/w/cpp/utility/functional时未在ios中添加课程 http://en.cppreference.com/w/cpp/utility/functional/function http://en.cppreference.com/w/cpp/utility/functional/bind

工作示例

http://cpp.sh/7uluut