【问题标题】:Handle non-member function pointer and member function pointer at the same time同时处理非成员函数指针和成员函数指针
【发布时间】:2018-03-09 06:49:40
【问题描述】:

我想做一个每 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,而且还来自 所有其他对象

如果没有办法,我该如何实现呢?

【问题讨论】:

  • std::function 或 C 风格的回调,将指向用户提供的上下文的不透明指针作为额外参数。
  • ScheduledFunction 设为模板参数并使用std::invoke 调用。或者如果你想混合std::function

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


【解决方案1】:

使用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。

【讨论】:

    【解决方案2】:

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

    简介

    我建议使用std::functionstd::bind。但请注意,由于内部机制,std::function 会有一些开销!

    std::function 非常强大,你可以在其中存储很多东西。

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

    示例

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

    如您所见,包含 &lt;functional&gt; 标头将为您提供模板 std::function&lt;R(Args...)&gt;,其中 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

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-14
      • 1970-01-01
      • 1970-01-01
      • 2018-09-12
      • 2012-10-03
      • 2011-04-29
      • 2010-11-02
      • 1970-01-01
      相关资源
      最近更新 更多