指向函数成员的指针
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);
}
我有一个 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);
}