3

I have a FreeRTOS function xTaskCreate. Simplified declaration looks like

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

And there are two classes:

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 { ... ;}

In function init() I select one of derived classes and create its instance. Then want to create task

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

Upd. Add missed void*params in Simplified declaration of xTaskCreate.

kyb
  • 7,233
  • 5
  • 52
  • 105

3 Answers3

10

TaskFunction_t is just a pointer to a function - so it can't take a pointer to a member function. Only a pointer to normal function. Or a static member function. Or a lambda with no capture. It's that last one that we'll take advantage of.

One of the arguments you removed from your simplified declaration is the context:

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

You provide the Super* in the parameters and provide a lambda that knows what to do with it. Altogether:

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

Note that task() should take no arguments. The void*is the context that we're converting to a Super*.

Barry
  • 286,269
  • 29
  • 621
  • 977
2

After several experiements of my own with answers here I prefered this simpler method giving Object oriented function calls to RTOS tasks.

//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);
}
Ali Akdurak
  • 3,841
  • 1
  • 18
  • 16
-1

UPDATED: See below.

As explained better than I can here, you might get away with this. Hard to tell from your question if it will cover all of your requirements.

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

Further Reading

UPDATE: I fleshed out your example, and the results and code are below:

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*)

code (all one cpp file, bad form, but proves syntax):

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

If you're concerned that the syntax is &Super::task instead of &obj->task, then you're misunderstanding how virtual functions work. (It turns out that the &obj->task syntax forbidden by ISO C++, but gcc says it's permissive, so you shouldn't but could force it to compile, and get exactly the same result)

The information about which virtual version of a function to call 'lives' in the object, not the type system. (Could probably phrase that better, open to suggestions, but I think it gets the general point across) It is impossible to call a member function without an object, so in order to make use of the function pointer, you'll have to have an object to 'call it on'. It is the type of that object which will determine which virtual function gets called. So the code above should achieve whatever you're going for, unless of course, this is a round-about way to determine the type of the object pointed to by obj, in which case, it's an awfully convoluted way of going about it.

Further Reading specifically in "Kerrek SB"s answer.

Community
  • 1
  • 1
Jfevold
  • 422
  • 2
  • 11
  • How would Super::* point to a Derived::* function? isn't that backwards? – xaxxon May 02 '16 at 17:18
  • I had a better luck following a simpler pattern inspired by this answer. I just added a static function called TaskStart() and passed it "this" pointer. Then in that static fucntion called param->TaskLoop() Always works and points to correct function in hierarchy. – Ali Akdurak Jul 29 '18 at 17:07