cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure STM32CubeIDE to support C++ development?

TLeaf
Associate II

1. How to configure the IDE to use g++ compiler to compile all the files includes "*.c" files in the project?

Current it compiles the .c files by using gcc and .cpp by using g++ ...

2. If I rename main.c to main.cpp, the cube code generator will create a new main.c file instead of using main.cpp. Any sulotions for this?

Becasue main.c is .c file, so the IDE use gcc to compile this file, and this caused that I can't use any objects in it. But if I changed the main.c to main.cpp, the cube can't generate code into it...:face_screaming_in_fear:

45 REPLIES 45
TMaia.1
Associate II

Just to be sure. There is no way that MX can generate a main.cpp file, right?

Correct, it cannot. None of the auxiliary programs are in C++, all in C.

Generate a routine (I use the name of CPP_LINK.CPP

/*
 * CPP_LINK.cpp
 *
 *  Created on: Jul 12, 2019
 *      Author: madyn
 */
 
#include "configuration.h"
#include "CPP_LINK.hpp"
 
//#ifdef PROJECT_MODULAR_POWER_LK432
//	#include "Power_CPU_APP.hpp"
//#endif
//
//#if (defined PROJECT_MODULAR_CPU_F767)
//	#include "TRICORDER_MK30_APP.hpp"
//#endif
//
//#if (defined PROJECT_TRICORDER_MK30_5)
//	#include "TRICORDER_MK30_5_APP.hpp"
//#endif
//
//
//#if (defined PROJECT_TRICORDER_MK40)
//	#include "TRICORDER_MK40_APP.hpp"
//#endif
//
//
//#if (defined PROJECT_TRICORDER_MK30_4)
//	#include "TRICORDER_MK30_4_APP.hpp"
//#endif
 
	#include "APPLICATION.hpp"
 
#ifdef __cplusplus
	extern "C"
	{
#endif
 
	void cpp_link (void)
		{
			APPLICATION_init();
		}
 
#ifdef __cplusplus
	}
#endif

and then generate CPP_LINK.hpp

/*
 * CPP_LINK.hpp
 *
 *  Created on: Jul 12, 2019
 *      Author: madyn
 */
 
#ifndef CPP_LINK_HPP_
#define CPP_LINK_HPP_
 
	#ifdef __cplusplus
		extern "C"
		{
	#endif
 
		void cpp_link (void);
 
	#ifdef __cplusplus
		}
	#endif
#endif /* CPP_LINK_HPP_ */

From your main routine, call

void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
	  cpp_link();
	  // NEVER GETS HERE, cpp_link goes to application init and then loop
  for(;;)
  {
    osDelay(10000);
  }
  /* USER CODE END 5 */ 
}
 

Everything else in the program that I write is C++. C++ calls C without a problem, C has a problem calling C++.

C++ nature has been added to the project.

The way this is structured, the main.c program can be generated and regenerated as needed. The bridge (CPP_LINK.cpp) never changes as long as you make your program "application.cpp"

Application init simply sets up the main task and goes from there.

Luczia
Associate II

Hi,

I've noticed a problem that is even deeper/weirder:

When creating a project from scratch, it works for me :

  • Create a brand new project (File > New > STM32 Project > Board Selector > F401RE and then Check C++ in the targeted language field)
  • In the CubeIDE navigation tree, right click on main.c and rename to main.cpp
  • Try to create a class in main.cpp => Compilation works :ok_hand:

But when creating a project from an example(File > New > STM32 Project > Board Selector) or opening an existing project, the manipulation (right click > Convert to C++ and then rename the extension of main.c to main.cpp) dosen't change anything. ie : I can see on the compliation log that main.c (even though I renamed it "main.cpp" through Cube IDE) is compiled with gcc and not g++.

Also, very weird : If I double check in the windows explorer, the file is still named main.c. It seems that for some project architectures, CubeIDE and the compiler doesn't take into account the renaming of the file.

Below, a screnshot (top project (My-Project) does compile with g++ and bottom project (UART_printf) doesn't compile with g++) ¯\_(ツ)_/¯

0693W000004JneJQAS.png 

Any tips or thoughts on that ?

main.cpp has to be in the Src folder and not anywhere else, maybe ?

Has anyone ever succeded in converting an ST32 example to compile in C++ ?

Any program source (.c or .cpp) has to be in the SRC directory, and any .h or .hpp file has to be in the inc directory. Having said that, there are some tricks you can pull:

1) I do large projects with common routines acting as libraries

2) There is an application file and a configuration file (as well as screen files) generated by a graphics/project configuration program that handles linking buttons and screens, as well as generating conditional installs for optionally included hardware.

The trick, though, when having common sources to a number of projects is to use virtual folders. If (and when) you add/include a file to the project, the default behavior is to take a copy of that file and put it in the project's .src or .inc directory. Making (and including these) as virtual folders links everything back to a single copy.

Cube (anything) creates only a C file, not C++. Make any changes in configuration, and you add the new file main.c (and main.h). If you already have these, you overwrite the existing copies. If you've renamed your files main.cpp and main.hpp, then you don't change them, you simply overwrite or generate the .c or .h files. The problem is also that I'm using FreeRTOS, which is not (and may never be) C++.

Better to tweak the main file to call your C++ file and leave it at that.

I have the cube generated .c files calling a single statically named .cpp bridge file, which can call the application .cpp file. Any operating system calls are to C routines, and any low level calls to HAL routines are C++ to C, which works.

Since (in my case) the configuration.h file is project unique, it goes into the project's inc directory, as the application.cpp goes into the SRC directory. This also means that each project has an application.cpp and configuration.cpp, and they had better be kept separate.

Since I don't use their examples, other than perhaps reference, I've never really tried converting an example to C++. If all you're doing is examples, then as you create the project, I'd think the INC and SRC directories will load up. Each project needs a separate directory, of course.

So in short. Yes, everything seems to want to be (mostly) in the .src and .inc directories, either direct copies or virtual folders.

If you get to larger projects, all customized from a set of main routines, some of the above may be useful.

VBurs
Associate II

I have the same issue, i can make a cpp project, have the cubemx generate all the files in C, then rename the main.c into main.cpp but when i regenerate with cubemx, it generates a new main.c

To make it work i have to change the main.cpp into main.c, do the cubemx generation, then rename the modified main.c into main.cpp

It is quite anoying to be frank and it feels like this can be easily patched in the cubemx source code.

VBurs
Associate II

For reference for all new people looking to do convert their projects into cpp: https://shawnhymel.com/1941/how-to-use-c-with-stm32cubeide/

If you modify the main.c without renaming it, putting your code in the user defined sections, then you can call a C++ routine from C. This saves the annoyance of having to rename the main.c. I did make the suggestion that if cubemx does find a main.cpp then it ought to just use it instead of a main.c file. Haven't seen that happen at all, though.

I've gone though a lot of changes, project wise, to the main.c and main.h file, and haven't had a problem calling my C++ application loop from the main.c program.

Details are earlier in the thread, involving adding some conditionals for C++.

TMaia.1
Associate II

@Harvey White​ , can you provide some example? I'm not sure if I understand you correctly.

The example is earlier in this thread. Here's the setup, and why I did it like that:

1) I have a main program loop in C++ in file application.cpp. That's what I want to run

2) I have a main.c generated by CubeMX. I don't want to rename main.c to main.cpp

3) I've determined that I will always call the application in my programs application.cpp, even though it may be a different project ( I have a program that writes main application loops for certain types of programs, just to mention it).

4) the main program (main.c) calls the bridge program before it goes into the loop. In main.c it looks like this:

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
	  cpp_link();
	  // NEVER GETS HERE, cpp_link goes to application init and then loop
  for(;;)
  {
    osDelay(10000);
  }
  /* USER CODE END 5 */
}
 

5) note that the main program loop does nothing, and I call my bridge program (C to C++) before the main loop. The main loop executes, but that's a "who cares" and it's going to spend most of its time in a delay. My bridge program is called cpp_link.

CPP_link.hpp looks like:

#ifndef CPP_LINK_HPP_
#define CPP_LINK_HPP_
 
	#ifdef __cplusplus
		extern "C"
		{
	#endif
 
		void cpp_link (void);
 
	#ifdef __cplusplus
		}
	#endif
#endif /* CPP_LINK_HPP_ */

since it's identical for all applications I write, it never needs to change, and the code in main.c can just be copied over.

CPP_LINK.cpp looks like this:

#include "CPP_LINK.hpp"
 
#include "APPLICATION.hpp"
 
#ifdef __cplusplus
	extern "C"
	{
#endif
 
	void cpp_link (void)
		{
			APPLICATION_init();
		}
 
#ifdef __cplusplus
	}
#endif

CPP link never changes because the call to my program (application.cpp) never changes because the main executable routine is always called APPLICATION_init();

Since I'm using FreeRTOS, APPLICATION_init looks like this:

// ********************************************************************************************************************
// ******************************************************* APPLICATION INIT *******************************************
// ********************************************************************************************************************
 
 
   void APPLICATION_init(void)
   {
       #ifdef _FREERTOS
           xTaskCreate((TaskFunction_t) APPLICATION_TASK,
           "APPLICATION",
           24000,
           NULL, 3,
           &APPLICATION_task_handle);
       #endif
   }

Your settings for FreeRTOS will vary, of course.

The first few lines of the application itself look like this....

   #ifdef _FREERTOS
       void APPLICATION_TASK(void const * argument)
       {
           struct address_list_type    A;
           int display_number = 0;         // incrementing display number
// ********************************************************************************************************************
// ******************************************************* APPLICATION VARIABLES **************************************
// ********************************************************************************************************************
 
 
           struct SYSTEM_INSTALL_response install_response[40];
           uint16_t which_install = 0;
           int16_t i;
           int x = 0, y = 0;
           #ifdef _GRAPHICS
               string  main_caption;
           #endif
 
// (more code follows.........)

So:

main.c (regenerated by CubeMX) calls cpp_link. Once the code is inserted in main.c, CubeMX doesn't change that code. CPP_link calls your application code.

That CPP_LINK calls the application_init code in my programs is just how I do things. CPP_link can call your C++ program loop directly. As you see, Application_init sets up my main program task (it then returns, and you get the very long main program loop with the delay of 10000). What you call from CPP_LINK is up to you, or even if it never comes back.

The reason for this design is that:

1) I don't have to change or rename main.c

2) I have a very small program (CPP_LINK) that does exactly what I need and was the experiment to see if it compiled

3) for me (and this may be unique), I have a program that builds application.cpp for me, depending on what project options I pick. So having fixed names (but variable content) makes sense, but that's only for what cpp_link calls.

All the other parts of my code are written to call C routines (if FreeRTOS) or if HAL layer routines. My main code is C++ which can just call C++. No C code calls C++ with the exception of the one call in main.c to cpp_link.

TMaia.1
Associate II

Thank you. I saw your previous code, but I was afraid that I was missing something. I will try again.

Let me ask you a newbie question. Using HAL, many examples work with global variables. How do you handle that without touching main.c?