代码之家  ›  专栏  ›  技术社区  ›  Trytio

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

  •  1
  • Trytio  · 技术社区  · 7 年前

    我想做一个每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 新注册的 事件 。但现在,它只处理非成员函数。是否有一种方法可以处理非成员函数和成员函数,而不仅仅是从 Handler 但也在 所有其他对象 ?

    如果没有办法,我如何才能做到这一点?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Patrick    7 年前

    使用 std::function 就是要走的路。可以调用的任何内容都可以转换/包装为 std::函数

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

    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方法更具可读性和灵活性。据我记忆所及,建议(是赫伯还是斯科特?)不再使用std::bind,而是使用lambda。

        2
  •  1
  •   MABVT    7 年前

    更新1 在下面添加了“调用任何对象的成员”。

    简介

    我建议使用 std::function std::bind 。但是请记住,由于内部机制的原因,std::函数有一些开销!

    函数非常强大,因为有很多东西可以存储在其中。

    重要事项: 使用仅函数指针的方法是可能的,但如果必须保留简单的统一接口,则会导致一些代码和复杂性。

    示例

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

    如您所见,包括 <functional> 标题将为您提供模板 std::function<R(Args...)> ,其中R是返回类型,Args。。。完全限定参数类型的逗号分隔列表。

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

    这样,您现在几乎可以绑定任何您想要的内容:D

    更新时间: 不能为具有匹配公共方法的任何其他类型调用Schedule函数,例如:

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

    资源

    http://en.cppreference.com/w/cpp/utility/functional http://en.cppreference.com/w/cpp/utility/functional/function http://en.cppreference.com/w/cpp/utility/functional/bind

    工作示例

    http://cpp.sh/7uluut