【问题标题】:Pointer to function-member指向函数成员的指针
【发布时间】:2016-05-02 17:04:48
【问题描述】:

我有一个 FreeRTOS 函数 xTaskCreate。简化的声明看起来像

typedef void (*TaskFunction_t)( void* );
unsigned xTaskCreate( TaskFunction_t pxTaskCode, void*params );

并且有两个类:

class Super {
    virtual void task(void*params) = 0;
};
class Derived1 : public Super {
    virtual void task(void*params){ while(1){ blinkLed(1); delay_ms(333); } }
};
class Derived2 : public Super { ... ;}

在函数init() 中,我选择派生类之一并创建它的实例。然后想创建任务

void init(){
    Super *obj = condition ? new Derived1 : new Derived2;
    xTaskCreate( obj->task ); // WRONG.
}

更新。在xTaskCreate 的简化声明中添加错过的void*params

【问题讨论】:

  • 那些类应该继承自 Super 吗?如果不是,什么是派生的?

标签: c++ c++11 member-functions freertos


【解决方案1】:

TaskFunction_t 只是一个指向函数的指针——所以它不能接受指向成员函数的指针。只有一个指向正常功能的指针。或静态成员函数。或者没有捕获的 lambda。这是我们将利用的最后一个。

您从简化声明中删除的参数之一是context

 BaseType_t xTaskCreate(    TaskFunction_t pvTaskCode,
                            const char * const pcName,
                            unsigned short usStackDepth,
                            void *pvParameters,  // <== this one!
                            UBaseType_t uxPriority,
                            TaskHandle_t *pxCreatedTask
                          );

您在参数中提供Super*,并提供一个知道如何处理它的lambda。总而言之:

void init(){
    Super *obj = condition ? new Derived1 : new Derived2;
    xTaskCreate([](void* o){ static_cast<Super*>(o)->task(); },
        ..., // other args here
        obj,
        ... // more args
        );
}

请注意,task() 不应带参数。 void* 是我们要转换为 Super* 的上下文。

【讨论】:

  • static_cast&lt;Super*&gt;(o) 比简单的((Super*)o) 更好?
  • @kyb C++ 转换总是比 C 转换好。显式 > 隐式。
【解决方案2】:

在我自己的几次实验和这里的答案之后,我更喜欢这种更简单的方法,它可以为 RTOS 任务提供面向对象的函数调用。

//These are not full declaration of class IModule which is fully abstarct so //object that are IModule* are always inherited.
      protected:
        virtual int InitModule() = 0;
        virtual bool PreLoop() = 0;
        virtual bool DoLoop() = 0;
        virtual bool PostLoop() = 0;
        virtual bool DoShutdown() = 0;
        //Return if this module implementation requires an RTOS task looping.
        virtual bool isFreeRTOSTaskRequired() = 0;
      private:
        TaskHandle_t *moduleLoopTaskHandle;
        bool CreateRTOSTask();
        static void TaskStart(void* taskStartParameters);
        void TaskLoop();
//END OF PARTIAL decleration

bool IModule::CreateRTOSTask()
{
    xTaskCreate(IModule::TaskStart, "NAME", 2048, this, tskNO_AFFINITY, moduleLoopTaskHandle);

    return true;
}

void IModule::TaskStart(void *taskStartParameters)
{
    IModule *moduleObject = (IModule *)taskStartParameters;
    moduleObject->TaskLoop();
}

void IModule::TaskLoop()
{
    //TODO Buraya ölçüm koyalım ve bir değişkene yazalım
    while (true)
    {
        ESP_LOGD("IModule::TaskLoop", "%s", "I am alive!");
        if (!PreLoop())
        {
        }

        if (!DoLoop())
        {
        }

        if (!PostLoop())
        {
        }
    }

    vTaskDelete(NULL);
}

【讨论】:

    【解决方案3】:

    更新:见下文。

    正如here 所解释的那样,您可能会侥幸逃脱。从你的问题很难判断它是否能满足你的所有要求。

    typedef void (Super::*TaskFunction_t)( void* );
    

    Further Reading

    更新: 我充实了你的例子,结果和代码如下:

    XXXXX:~/scratch/member_function_pointer$ bin/provemeright
    Condition false
    virtual void Derived2::task(void*)
    XXXXX:~/scratch/member_function_pointer$ bin/provemeright foo
    Condition true because of argument foo
    virtual void Derived1::task(void*)
    

    代码(所有一个 cpp 文件,格式错误,但可以证明语法):

    #include <iostream>
    
    class Super;
    typedef void (Super::*TaskFunction_t)(void*);
    unsigned xTaskCreate( TaskFunction_t pxTaskCode, void* params);
    
    bool condition = false;
    
    class Super {
      public: virtual void task(void* params) = 0;
    };
    
    class Derived1 : public Super {
      public: virtual void task(void* params) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        if(params) // Necessary to prevent unused parameter warning
          std::cout << "Not Null" << std::endl;
      };
    };
    
    class Derived2 : public Super {
      public: virtual void task(void* params) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        if(params) // Necessary to prevent unused parameter warning
          std::cout << "Not Null" << std::endl;
      };
    };
    
    void init(){
      Super *obj = condition ? (Super*)new Derived1 : (Super*)new Derived2;
      xTaskCreate( &Super::task , obj);
    }
    
    int main(int argc, char **argv)
    {
      if(argc > 1)
      {
        std::cout << "Condition true because of argument " << argv[1] << std::endl;
        condition = true;
      } else {
        std::cout << "Condition false" << std::endl;
      }
      init();
      return 0;
    }
    
    
    unsigned xTaskCreate( TaskFunction_t pxTaskCode, void* params)
    {
      Super *obj = (Super*) params;
      (obj->*pxTaskCode)(NULL);
      return 0;
    }
    

    如果您担心语法是 &amp;Super::task 而不是 &amp;obj-&gt;task,那么您误解了虚函数的工作原理。 (事实证明,&amp;obj-&gt;task 语法被 ISO C++ 禁止,但 gcc 说它是允许的,所以你不应该但可以强制它编译,并得到完全相同的结果

    关于在对象中调用函数的哪个虚拟版本的信息,而不是类型系统。 (可能会更好,欢迎提出建议,但我认为它可以理解一般观点)没有对象就不可能调用成员函数,所以为了使用函数指针,你必须有一个“调用它”的对象。该对象的类型将决定调用哪个虚函数。所以上面的代码应该可以实现你想要的任何东西,当然,除非这是一种确定 obj 指向的对象类型的迂回方法,在这种情况下,这是一种非常复杂的方法。

    Further Reading 特别是在“Kerrek SB”的回答中。

    【讨论】:

    • Super::* 如何指向 Derived::* 函数?这不是倒退吗?
    • 按照这个答案启发的更简单的模式,我的运气更好。我刚刚添加了一个名为 TaskStart() 的静态函数并将“this”指针传递给它。然后在那个名为 param->TaskLoop() 的静态函数中始终有效并指向层次结构中的正确函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多