Search code examples
c++c++11templatesfunction-pointersfreertos

Call template provided (static) function


I am trying to create a C++ wrapper for a Task. The challenge here is that freertos needs to take a function pointer as described here https://www.freertos.org/xTaskCreateStatic.html

Now I came up with this

template<typename Functor_T, uint32_t u32StackSize>
class CFreeRTOS_Task
{
public:
    ///Constructor
    CFreeRTOS_Task(const std::string& strTaskNameArg);
    ///Destructor
    ~CFreeRTOS_Task();

private:
    ///the Name of this task
    const std::string strTaskName;
    ///the task handle
    TaskHandle_t task;
    ///the task control block
    StaticTask_t task_tcb;
    ///is the task currently running (can be accessed from multiple threads => atomic)
    std::atomic<bool> bTaskRunning;
    ///the actual stack
    StackType_t stack[u32StackSize] = {};
    ///the task function to pass to freertos
    static void TaskFunction(void* pvParameters);
};

//+++++++++++++++++++++++++  Implementation +++++++++++++++++++++++++++++++++++++++++
template<typename Functor_T, uint32_t u32StackSize>
CFreeRTOS_Task<Functor_T, u32StackSize>::CFreeRTOS_Task(const std::string& strTaskNameArg) :
    strTaskName(strTaskNameArg)
{
    task = xTaskCreateStatic(
        TaskFunction, /* Function that implements the task. */
        strTaskName.c_str(), /* Text name for the task. */
        u32StackSize, /* Number of indexes in the xStack array. */
        (void*)1, /* Parameter passed into the task. */
        tskIDLE_PRIORITY,/* Priority at which the task is created. */
        stack, /* Array to use as the task's stack. */
        &task_tcb); /* Variable to hold the task's data structure. */
    bTaskRunning = true;
}
template<typename Functor_T, uint32_t u32StackSize>
CFreeRTOS_Task<Functor_T, u32StackSize>::~CFreeRTOS_Task()
{
    if (bTaskRunning)
    {
        //terminate task...
        bTaskRunning = false;
    }
}
template<typename Functor_T, uint32_t u32StackSize>
void CFreeRTOS_Task<Functor_T, u32StackSize>::TaskFunction(void* pvParameters)
{
    //do some initilisation
    for (;;)
    {
        //call the user provided task function
        Functor_T();
        osDelay(10);
    }
    //shutdown this task (common to all freertos tasks)
}

Now my instantiation looks like this

///test task function
static void TestTaskFunc();


///Test task instance
static CFreeRTOS_Task<TestTaskFunc,10> testTask("test_task");


static void TestTaskFunc()
{
    volatile uint32_t test = 0;
}

However I am getting 2 Compiler errors

error: type/value mismatch at argument 1 in template parameter list for 'template<class Functor_T, long unsigned int u32StackSize> class NRTOS_Wrapper::CFreeRTOS_Task'
static CFreeRTOS_Task<TestTaskFunc,10> testTask("test_task");
                                      ^
note:   expected a type, got 'NRTOS_Wrapper::TestTaskFunc'
error: invalid conversion from 'const char*' to 'int' [-fpermissive]

could you help me find out what I am missing here?


Solution

  • The way you have your class template written, Functor_T is a type not a value. There are a couple of places you've gotten tripped up by that:

    static CFreeRTOS_Task<TestTaskFunc,10> testTask("test_task");
    

    Here, you're trying to pass a value where CFreeRTOS_Task is expecting a type.

    template<typename Functor_T, uint32_t u32StackSize>
    void CFreeRTOS_Task<Functor_T, u32StackSize>::TaskFunction(void * pvParameters)
    {
        //do some initilisation
        for(;;)
        {
            //call the user provided task function
            Functor_T();   // <---- HERE
            osDelay(10);
        }
    
        //shutdown this task (common to all freertos tasks)
    }
    

    Here you are value-initializing a temporary object of type Functor_T, not calling an existing functor object as the comment implies. Something like Functor_T()() could make sense for function-objects, but if Functor_T is a simple function pointer type (as in your example) that wouldn't make any sense.


    It seems likely that what you actually need to do is store an object of the functor type in your class and then pass a pointer to that object to TaskFunction. For example:

    template<typename Functor_T, uint32_t u32StackSize>
    class CFreeRTOS_Task
    {
    public:
        ///Constructor
        CFreeRTOS_Task(std::string strTaskNameArg, Functor_T functor);
    private:
        //...
    
        using Func_T = std::decay_t<Functor_T>;
        ///the functor to call
        Func_T mFunctor;
    
        ///the task function to pass to freertos
        static void TaskFunction(void* pvParameters);
    
    };
    
    template<typename Functor_T, uint32_t u32StackSize>
    CFreeRTOS_Task<Functor_T, u32StackSize>::CFreeRTOS_Task(
        std::string strTaskNameArg,
        Functor_T functor
    )
        : strTaskName{std::move(strTaskNameArg)},
          mFunctor{std::move(functor)}
    {
        task = xTaskCreateStatic(
            TaskFunction, /* Function that implements the task. */
            strTaskName.c_str(), /* Text name for the task. */
            u32StackSize, /* Number of indexes in the xStack array. */
            &mFunctor, /* The functor to call, passed as a parameter into the task. */
          //^^^^^^^^^^ <---- HERE, pass a pointer to the functor as the task arg
            tskIDLE_PRIORITY,/* Priority at which the task is created. */
            stack, /* Array to use as the task's stack. */
            &task_tcb /* Variable to hold the task's data structure. */
        );
        bTaskRunning = true;
    }
    
    template<typename Functor_T, uint32_t u32StackSize>
    void CFreeRTOS_Task<Functor_T, u32StackSize>::TaskFunction(void * pvParameters)
    {
        //do some initilisation
        for(;;)
        {
            //cast the parameter back to a pointer to the correct functor type
            Func_T* pFunctor = reinterpret_cast<Func_T*>(pvParameters);
            //call the user provided task function
            (*pFunctor)();
            osDelay(10);
        }
    
        //shutdown this task (common to all freertos tasks)
    }
    

    Then at your call site, give the type of the functor as your CFreeRTOS_Task's template parameter and pass the value to its constructor:

    ///test task function
    static void TestTaskFunc();
    
    
    ///Test task instance
    static CFreeRTOS_Task<decltype(TestTaskFunc),10> testTask("test_task", TestTaskFunc);
                        //^^^^^^^^^^^^^^^^^^^^^^                           ^^^^^^^^^^^^
                        //Pass the type as a template parameter            And the value as a constructor parameter
    
    
    static void TestTaskFunc()
    {
        volatile uint32_t test = 0;
    }
    

    Live Demo