2

I want to use a member function of a CPP Class for a FreeRTOS taskFunction.

Here is my initial solution

#ifndef TaskCPP_H
#define TaskCPP_H

#include "FreeRTOS.h"
#include "task.h"

template<typename T>
class TaskBase {

protected:
        xTaskHandle handle;
        void run() {};

public:

        static void taskfun(void* parm) {
            static_cast<T*>(parm)->run();
        #if INCLUDE_vTaskDelete
            vTaskDelete(static_cast<T*>(parm)->handle);
        #else
            while(1)
            vTaskDelay(10000);
        #endif
        }

        virtual ~TaskBase() {
#if INCLUDE_vTaskDelete
                vTaskDelete(handle);
#endif
                return;
        }

};

#endif /* __TaskCPP_H__ */

#ifndef HTTPSERVER_H_
#define HTTPSERVER_H_

#include "TaskBase.h"
#include <string.h>

#include "lwip/opt.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "lwip/api.h"
#include "lwip/ip_addr.h"

class HttpServer;

class HttpServer : public TaskBase<HttpServer> {

public:
        HttpServer();
        virtual ~HttpServer();

        void run();

        void listen(ip_addr *address, uint8_t port);
        void print();

protected:
        char taskName[50];
        ip_addr address;
        uint8_t port;

};

#endif /* HTTPSERVER_H_ */

#include "HttpServer.h"

HttpServer::HttpServer()
{

}

void HttpServer::listen(ip_addr *address, uint8_t port) {

        char buf[16];
        sprintf(taskName, "%s_%d\r\n", ipaddr_ntoa_r(address, buf, 16), port);

        DEBUGOUT("ADDRESS Listen: %x\r\n", &taskName);

        this->handle = sys_thread_new(this->taskName, &taskfun, NULL, DEFAULT_THREAD_STACKSIZE + 128, DEFAULT_THREAD_PRIO);

}

void HttpServer::run() {

        while(1) {

                print();

                Board_LED_Toggle(LEDS_LED2);
                vTaskDelay(configTICK_RATE_HZ / 14);

        }

}

void HttpServer::print() {

        DEBUGOUT("ADDRESS Listen: %x\r\n", &taskName);

}

HttpServer::~HttpServer() {
        // TODO Auto-generated destructor stub
}

Now my Problem ist that the context is not the same inside the run function. Printing the memory location of taskName is different inside listen(...) and run(...)

My nasty suspicion is that doing a static_cast of the run function looses the class context? Am I wrong?

Is there any other idea to provide a static function pointer to freeRTOS of a Class member function?

Here are the missing functions. The main and the sys_thread_new which is a wrapper only.

int main(void) {

        prvSetupHardware();

        HttpServer server;
        server.listen(IP_ADDR_ANY, 80);

        /* Start the scheduler */
        vTaskStartScheduler();

        /* Should never arrive here */
        return 1;

}

sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
{
xTaskHandle xCreatedTask;
portBASE_TYPE xResult;
sys_thread_t xReturn;

        xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );

        if( xResult == pdPASS )
        {
                xReturn = xCreatedTask;
        }
        else
        {
                xReturn = NULL;
        }

        return xReturn;
}
Pascal
  • 2,059
  • 3
  • 31
  • 52
  • What about the code where you presumably create the object and call `xTaskCreate()` with it somehow? – Notlikethat Apr 13 '16 at 18:50
  • Could you extract a minimal example? In particular the template stuff and the multiple files are not necessary. Also, don't add multiple edit paragraphs and leave it to others to pick the relevant info from them. Instead, clean up your question as required by the site rules and make the relevant obvious. – Ulrich Eckhardt Apr 13 '16 at 20:09
  • Sry, but I was asked about the missing sections (where the object is created and the xTaskCreate) and I do not think that there are much lines out of scope. – Pascal Apr 13 '16 at 20:20
  • 1
    AFAICS, `taskfun()` is expecting an `HttpServer` object to be passed as its argument, so why are you passing a NULL argument to `sys_thread_new()`? – Notlikethat Apr 13 '16 at 22:19

1 Answers1

2

I use something like this:

class MyClass{
public:
    static void run( void* pvParams ){
        ((MyClass*)pcParams)->runInner();
    }
    void runInner(){
        while ( true ){
            // TODO code here
        }
    }
};

then pass pointer to an object when creating task

MyClass* x = new MyClass;
xTaskCreate( MyClass::run, taskName, stackDepth, (void*) x, taskPrio, taskHandle );  

edit:

Assuming you want to use templates:

template<typename T> 
void run ( T* p ){
     ((T*)p)->run();
}

class MyClass{
public:
     void run(){
         while ( true ){
             // task code
         }
     }
}

then creating the task

MyClass* x = new MyClass;
xTaskCreate( run<MyClass>, taskName, stackDepth, x, taskPrio, taskHandle );

Edit:

You need to cast run to (void (*)(void*))

xTaskCreate( (void (*)(void*))run<MyClass>, taskName, stackDepth, x, taskPrio, taskHandle );
mausik
  • 77
  • 5
  • I will try your solution, but why xCreateTask should expect an HttpServer object? It's my decistion to specify something? And doing so whould result in an argument of `void run(void* httpServerObject)` isn't it? – Pascal Apr 14 '16 at 05:56
  • I can't get what you're asking for, could you please try to explain it again or provide an example? – mausik Apr 14 '16 at 08:17