指向函数成员的指针

Pointer to function-member

我有一个 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()中我select派生类之一并创建它的实例。然后想创建任务

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

更新。在 xTaskCreate.

的简化声明中添加遗漏的 void*params

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* 的上下文。

更新:见下文。

正如我所解释的那样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;
}

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

有关在对象中调用哪个函数的虚拟版本的信息 'lives',而不是类型系统。 (可能可以更好地表达,接受建议,但我认为它得到了一般点)没有对象就不可能调用成员函数,所以为了使用函数指针,你必须有'call it on' 的对象。它是该对象的类型,它将决定调用哪个虚函数。所以上面的代码应该可以实现你想要的任何东西,当然,除非这是一种确定 obj 指向的对象类型的迂回方式,在这种情况下,这是一种非常复杂的方式。

Further Reading 具体在 "Kerrek SB" 的回答中。

在我自己对此处的答案进行了几次实验之后,我更喜欢这种更简单的方法,它为 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);
}