Is it possible create freertos task in c++?

cynergizer
2010-07-20
2013-09-16
  • cynergizer
    cynergizer
    2010-07-20

    I am having trouble trying to call xTaskCreate(myTask, …) from within MyClass where myTask is also a c++ class member.  Is it possible to do this?  myTask just needs to be a function pointer, right?  It works fine in c, and I'm having no trouble using a mix of c++ and c in my app.  My particular app could really benefit from object-oriented.  I'm using the Rowley compiler (almost identical to GNU compiler) on the LPC2148.  myTask compiles within MyClass as either

    portTASK_FUNCTION(MyClass::myTask, pvParameters)
    {

    }

    or as

    void MyTask::myTask(void* pvParameters)
    {

    }

    The compile error that I'm getting is "argument of type 'void (MyClass::)(void*)' doesn't match 'void (*)(void*)', nor will it let me cast myTask as a function pointer. 

     
  • Richard Damon
    Richard Damon
    2010-07-20

    Yes, you can write tasks in C++, the key thing to remember is that the task function needs to be a function with "C" linkage, and takes 1 void* parameter. This can be a static member function, or a global function.

    If you want a member function to act as the task, you need to wrap it with wrapper that looks something like

     void taskfun(void* parm) {
        static_cast<classname *>(parm)->memberfun();
    }
    

    I have a header file (below) that automatically builds these wrappers.
    Derive your class from TaskClass, and a task will be created that calls the member function task.
    Or, for a free-function task, create a item of type Task, and it will create the task TCB for you.

    #ifndef TaskCPP_H
    #define TaskCPP_H
    #include "FreeRTOS.h"
    #include "task.h"
    class TaskBase {
    public:
      xTaskHandle handle;
      ~TaskBase() {
    #if INCLUDE_vTaskDelete
        vTaskDelete(handle);
    #endif
        return;
      }
    };
    class Task : public TaskBase {
    public:
      Task(char const*name, void (*taskfun)(void *), unsigned portBASE_TYPE priority,
           unsigned portSHORT stackDepth=configMINIMAL_STACK_SIZE) {
        xTaskCreate(taskfun, (signed char*)name, stackDepth, this, priority, &handle);
      }
    };
    class TaskClass : public TaskBase {
    public:
      TaskClass(char const*name, unsigned portBASE_TYPE priority,
               unsigned portSHORT stackDepth=configMINIMAL_STACK_SIZE) {
        xTaskCreate(&taskfun, (signed char*)name, stackDepth, this, priority, &handle);
      }
      virtual void task() = 0;
      static void taskfun(void* parm) {
        static_cast<TaskClass *>(parm)->task();
    #if INCLUDE_vTaskDelete
        xTaskDelete(handle);
    #else
        while(1)
          vTaskDelay(10000);
    #endif
      }
    };
    
     
  • Richard
    Richard
    2010-07-20

    Excellent answer - thanks.

    This question comes up from time to time, and its not something I have particular experience of, although I know people do use C++.  I really should add some information to the web site about it.  In the mean time, would somebody be willing to add a little bit of information to the FreeRTOS Interactive site?  Maybe even post this header file.  This forum might be a good candidate: http://interactive.freertos.org/forums/135282-any-other-files-any-other-manufacturers-any-other-business .

    I know the data hiding in the FreeRTOS source prevents it linking completely as C++, so generally people either have to compile the FreeRTOS source as C and their own application as C++ (note 1: the extern "C" code is already present in the header files.  note 2: taking note of richard_damon's comments above) or alternatively move the currently private FreeRTOS data structure definitions into the public header files.

    Regards.

     
  • Richard Damon
    Richard Damon
    2010-07-20

    I'll look at posting my C++ wrappers over there, I have wrappers for Tasks, Queues, Semaphores and Mutexs, letting C++ code call the FreeRTOS kernel in a mostly object oriented way.

    Richard, would you be interested in some mods that would move the structures into the public header files, but still (by default) keep the data hiding in place, even if the compiler can't handle the normal forward declaring of the struct?

     
  • Richard
    Richard
    2010-07-20

    Richard, would you be interested in some mods that would move the structures into the public header files, but still (by default) keep the data hiding in place, even if the compiler can't handle the normal forward declaring of the struct?

    I would definitely be interested in seeing your ideas and discussing it.  Whether or not I ended up including it would be a different matter, I tend to be a bit anal about these things.  Also, testing changes with so many different (and sometimes obscure!) compilers can be tricky - not that the more obscure ones seem to know much about C++ in any case.

    Also, it might be interesting to distribute some C++ wrappers, or complete example projects, in the main FR download (if you were happy with that).  What do you think?

    The data hiding seems to provoke strong feelings in users, some for it and some against it.  Also any mods have to maintain backward compatibility.

    Regards.

     
  • cynergizer
    cynergizer
    2010-07-21

    Richard Damon,
    Thanks so much for your great and detailed response!  Your TaskClass concept works wonderfully and solved my static linkage problem for task creation.  I am delighted to be able to use C++ with freeRTOS, as it will allow for huge design improvements. 

     

  • Anonymous
    2010-07-27

    Hi all,
    I think that Object Oriented programming (C++) in a multitasking environment is next step of the embedded word. I'm working on this topic. The first idea is to write a set of FreeRTOS wrapper class. Today I have a simple blinking led demo running on STM32 the shows the concept. I would update this demo by adding all the standard FreeRTOS demo task.
    I'd like to contribute to this discussion, so I have published my work in progress and source code on this web page: http://sites.google.com/a/stf12.net/developer-sw-fw/cpp-demo

    I will appreciate any comment (what about the chosen architecture?) and suggestion for further improvement,

    Regards,
    Stefano

     
  • Richard Damon
    Richard Damon
    2010-07-27

    Stefano,
    A few quick comments on what I see you have done, (not to say they are "wrong", as in programming if it works it can't truly be wrong).

    First, your classes really don't wrap the FreeRTOS structures, but more wrap the FreeRTOS handles. For example, your CTask class does not inherently actually create a FreeRTOS class, but either needs for a task to have already been created and have its handle passed in, or after creation, have an additional call made to create the real task within. In the wrappers I have been working on (which I am doing final clean up for public distribution, and working on getting permission to publish), the constructor of my Task class actually creates the task. What I like about my design (and why some don't like it) is that it allows me to create a file in my program for a give operation, and it can automatically create at global scope all the tasks, queue, etc that are needed for that operation, and the program main function doesn't need to do anything to set it up. In fact, often my main function is just a call to the function to start up the task system.

    A second comment is the hierarchy of semaphores, while FreeRTOS may use the same type as a handle and reuses some functions for the various types of semaphores and mutexes, there usage conditions are enough different that I am not sure they should have a common base (at least that is public). The issue is that the fundamental meaning of give and take is different, so I can't think of way that a routine might be written to be able to use either one without knowing what type it had. Semaphores typically indicate the availability of a resource between producers and consumers, with the producers giving the semaphore and consumers taking it. Mutexes control access to common data and users take it to get access and give it back when done.

    A third comment, is that your Task wrapper doesn't do anything (that I can easily see) to connect a task with a class. In my code I posted earlier, I created a derived class from my class task, that if derived from would make objects of that class have a task running on them as a member function. It is also possible if you are willing to use templates (which are not always available for embedded compilers) that would you use member functions as tasks.

     

  • Anonymous
    2010-07-28

    Hi Richard,
    thanks for your time.

    First, your classes really don't wrap the FreeRTOS structures, but more wrap the FreeRTOS handles.

    You are right. It depends on some architectural choices I made. Let me explain. FreeRTOS internal structures are hidden by design, and I don't want to modify the FreeRTOS source code because this choice is not maintainable in the long time:  when R.Barry releases a new version I have to work in order to expose the FreeRTOS internal structures again.

    The first target I have is:
    1. I want to add OO features to FreeRTOS and this set of class must be a layer built on top of FreeRTOS.
    In each case I can add a behavior like your class by adding another constructor that call xTaskCreate function. I'm not sure that this means "wrapper" something :-) What is the benefit other than a different programming style?

    The second key point is:
    2. I want to use a CTask object (or a CQueue object, etc.) as parameter of the standard procedural FreeRTOS API.
    The class layout and the cast operator make it possible. Vice versa, the class constructor that take an existing FreeRTOS handle as parameter, makes easier the interoperability between the two programming model.  For example image a scenario where I have to add some new features to an existing FreeRTOS project. This is a real case in my organization where FAE with a strong experience on C++ programming are not enough.

    A second comment is the hierarchy of semaphores, while FreeRTOS may use the same type as a handle and reuses some functions for the various types of semaphores and mutexes, there usage conditions are enough different that I am not sure they should have a common base

    I use the abstract class ASemaphore only to group the common part of the implementation according to the principle of code reusing of OO. An application can't directly instantiate an object of type ASemaphore due to the pure virtual methos Take and Give. And that super class avoid to duplicate the same code in each subclass and it will help me for further development.

    A third comment, is that your Task wrapper doesn't do anything (that I can easily see) to connect a task with a class.

    I'm not sure to have well understood this point. Do you means that a better design would be to have an abstract ATask base class that forces the user to implement the task control loop (for example by overridding a void run(void *params) =0; method)? This is interesting! I will review my CTask class.

    Another key point is:
    3. The memory overhead must be as less as possible
    So, have we to use all C++ features? Or only a subset, waiting for the next generation of MCU? At the moment I think to not force the user to enable STL and Run time type detection, this means that I don't use these features in the "FreeRTOS wrapper" layer even if I can enable them in my final application. But I want to better analyze this problem later when I have more information from the map file of some concrete use case.

    I'm curious to look your code, if possible, because if it meet all my need I'm happy to use it and avoid to make all by myself. :-)

    Best regards,
    Stefano

    P.S. During this design I was inspired by Microsoft MFC: a C++ super-set of class that wrapper the WIN32 API.

     
  • Richard Damon
    Richard Damon
    2010-07-28

    Stefano,
    First, let me repeat, that this is mostly stylistic and for many meanings of works, your method works.

    As to my first comment, if you look closely at the abstraction of the classes, you classes fundamentally deal with "handles", in that you need to explicitly put the handle into the class, you also have an explicit state of "not connected" for an object that hasn't had a handle imbued into it. In my model, the class represents the FreeRTOS structure, and the fact that it deals with those structures via a handle is invisible. This also means that I have a clearer ownership relationship to the FreeRTOS structure, my class "owns" the structure and is responsible for it. In your case, a simple error could imbue the same handle into multiple wrappers leading to life time control issues.

    Again, your second point makes it clear that in your mind you are treating the classes as wrappers for the handles, not the concepts of tasks/queue/semaphores. In my opinion, taking an existing program, and within that program take an existing task/queue/semaphore etc and suddenly add an additional variation of API is normally not productive and is error prone. If an existing task has enough "C" style references to it that you do not want to convert them, adding additional references as "C++" style would be an obfuscation, breaking the link between them mentally. From my experience, half conversions are a nightmare, either change too a C++ style or stay in C style, the mixed model loses much of the advantages of each of the style and adds few of its own. As an example, you are stuck with the C style allocation model, and can not implement RAII as it is incompatible with the C side of the mixture.

    As to the duplication of code in your abstract base, the give and take routines are very small since they are mere wrappers to the FreeRTOS calls. What the abstract base allows is to write a routine that takes an ASemaphore by pointer/reference that is polymorphic, but because these are two very different type of object in usage, I don't think it is possible to write a routine that can really take both types of synchronization primitives unless your intent is to allow the (ab)use of a semaphore as a mutex without priority inheritance. In the code that I have been working on, I keep semaphores and mutexes as separate concepts,  and this doesn't seem to have caused problems. If your base had been something like SyncronizationBase, I might think of this differently, but the name ASemaphore implies that the hierarchy implements a semaphore usage which really isn't true. I mutex is really more than a specialization of a semaphore.

    For my third comment, i don't REQUIRE the user to derive and specialize, but allow it. One usage is to create a Task object, and pass it a pointer to a free function, and that creates a task very much as a standard FreeRTOS task, or one can derive a class from TaskClass, and this will automatically (from the provided info) create a task based on a void classname::task() member function (note that the member function doesn't take an explicit parameter as that is used to specify the object it is called on, and that object can contain any other needed information to pass to the function).

    As far as memory overhead, I believe it is negligible, my basic Task Class is the size of a task handle, which would be needed to be stored somewhere anyway (and I don't need it stored elsewhere first). My Queue class stores a handle and a pointer to simplify the usage of the FromISR versions with the need to store the flag for the need to do a task switch. My Semaphore similarly is just a handle and pointer. My Mutex, since I did implement a common base for recursive and non-recursive mutexes to implement a RAII lock class, has a handle and a vtable pointer. All my member functions are inline and not much more than a call to the corresponding FreeRTOS function. There is no need at all for the STL, and I do not use Run Time Type Detection, and in fact, except for the mutex classes which have virtual give and take function, don't even bring any of these into play.

    I am currently working on getting the code ready for publishing, by adding proper documentation, and hopefully I will add it to the article I wrote on using FreeRTOS in C++ shortly.

    The one thing it might not provide is a easy support for a mixed C/C++ model the way you are doing so. You will need to create the objects C++ style (and the C++ style will "own" the object and be in charge of deleting it if needed) and you can then extract the handle to give to the C style access to the object.

    As to using MFC as a design guideline, my personal opinion is it wasn't all that well done, and Microsoft has mostly dropped it as a design method too. It also wraps a much bigger interface than FreeRTOS.

     

  • Anonymous
    2010-08-03

    Ok Richard,
    your opinion is very clear.

    Regards,
    Stefano