Using the queue with C++ classes

Hi,

I am kinda new to C++ and FreeRTOS and have some struggles with bridging C to C++. I cant get my head around C linkage and C++ name mangling.

I want to encapsulate the queue and task handle inside a class so I can create multiple Instances of this class.

I did a research and found out that POD-Classes are one way to achieve the kind of behaviour I want. But I do not like the idea of stuffing a whole class into one abstraction layer (public). This feels bad to me and makes things complicated to comprehend. At least to me. Furthermore I want to use C++ features like virtual functions, inheritance and so on. This would not be possible with POD-Classes (as far I know).

Would it work to pass a pointer to a c-linked variable like so:

:warning: Code beneath is pseudo code. It should give you the idea what I want to achieve but may contain mistakes :warning:

class MyClass
{
private:
	TaskHandle_t** handle;
	QueueHandle_t* queue;

public:
	MyClass(char* const task_name, UBaseType_t priority, TaskHandle_t** handle, QueueHandle_t* queue);

	[...]
}

MyClass::MyClass(char* const task_name, UBaseType_t priority, TaskHandle_t** handle, QueueHandle_t* queue)
{
	xTaskCreate(call_task, task_name, 400, this, priority, *this->handle);
	*this->queue = xQueueCreate(2, sizeof (int));
}

int main(void){
	/*as far as I know static is a pendant to 'extern "C"' correct me if I am wrong*/
	static TaskHandle_t* myHandle;
	static QueueHandle_t myQueue;

	MyClass("My task name", tskIDLE_PRIORITY, &myHandle, &myQueue)
	[...]
}

Is this the way? This way it would be possible to use virtual methods and all those features not available inside a POD class, wouldn’t it?

Is this a considerable method to achieve this kind of behavaior or is there another easier way I am just not aware of?

Thanks for you answers and reading my question

Non POD-Classes can not be use in queue (but pointers to them can be) because queue transfer the data with memcpy, and only POD-classes are promised to work under that method. In practice you can push that limit a bit, but I don’t like to do that.

As to your code (yes, you said it might contain mistakes, but these seem more fundamental), first your members handle and queue were never initialized, just because the constructor takes a parameter of the same name, doesn’t make that construction copy the value into the class.

Second your TaskHandle_t* myHandle is never set to point to a valid location of memory, and I see no reason it needed to be declared as pointer in the first place.

Third, there is no issue with classes USING queue from using any of the features of C++ you want, it is only trying to place a class inside a queue, which would start with construction the queue using sizeof(class name).

Lastly “static” doesn’t convert things from C++ to C. For a member function, it makes it look like a free function, since it doesn’t need the automatically added this parameter. It still isn’t technically a “C” function, but most ABIs make it equivalent, since C++ code might use a different base calling convention if the system ABI so defined it.

As far as C linkage and C++ name mangling, the simple rule to remember is that functions just declared in C++ (outside an extern “C” declaration) won’t have a “name” that is just the function name, so C code that calls a function by that name won’t find the C++ function. Thus all functions that need the “simple” name, need to be declared as extern “C”. This doesn’t make that code “C” code, only changes the naming convention (and ABI) to be that of C, but the function can fully use C++.

Here is a version that will be closer to what you want to do:

class MyClass
{
private:
//  typedef struct tskTaskControlBlock * TaskHandle_t;
    TaskHandle_t handle;
//  typedef struct QueueDefinition   * QueueHandle_t;
    QueueHandle_t queue;

public:
    void callTask();
    MyClass( char* const task_name, UBaseType_t priority );
};

void MyClass::callTask()
{
    // queue can be used in this function
    for( ;; )
    {
        vTaskDelay( 1000U );
    }
}

extern "C" void call_task( void *pvParameter )
{
    MyClass * myClass = ( MyClass * ) pvParameter;
    myClass->callTask();
    vTaskDelete (NULL);
}

MyClass::MyClass( char* const task_name, UBaseType_t priority )
{
    queue = xQueueCreate( 2, sizeof ( int ) );
    xTaskCreate( call_task, task_name, 400U, ( void * ) this, priority, &handle );
}

int main(void)
{
    MyClass myClass( "My task name", tskIDLE_PRIORITY );
}

I was not able to test this code, but it does compile.

I would recommend not to worry too much about the extern "C" function.

C and C++ can be mixed easily as shown in the above example.

As I mention, to point for extern “C” would be if you are implementing a function that the C code is calling by name, like vApplicationIdleHook, that function needs to be implemented as extern “C”, or the linker won’t match up the two. I think newer version of the code will add this declaration in the FreeRTOS headers.

The other big place you need it is for ISR handlers, if the ISRs are called by name, like is done with the Cortex-M, verses Controllers where you build a table or assign with a function call.

If you want to see some C++ wrappers I have done, they are at GitHub - richard-damon/FreeRTOScpp: FreeRTOS C++ Wrappers (I do need to get my next update out)

1 Like

Thanks a lot. This clearified a lot of my questions