Forum: Compiler & IDEs STM32CubeIDE und C++


von Franz W. (woyzeck)


Lesenswert?

Hallo, ich habe Probleme mit dem Compilieren von C++ Code in der 
STM32CubeIDE - muss gleich am Anfang gestehen, dass ich ein echter C++ 
Anfänger bin.

Was habe ich bisher gemacht ?
- Projekt von C auf C++ konvertiert ("convert to C++)
- alle meine eigenen Klassen haben die Extensions *.hpp bzw. *.cpp
- main.h/c auf main.hpp/cpp umbenannt
- die von der STM32CubeIDE generierten Files haben weiterhin ihre 
originalen Extensions, also z.B. stm32xx_hal_conf.c

Sobald ich builde ist zu sehen, dass die hpp/gpp-Files korrekt mit *g++* 
compiliert werden, während die h/c-Files gcc anstoßen. Das 
funktioniert soweit auch.

Das Problem ist jetzt mit den Includes - stm32xx_hal_conf.c inkludiert 
main.hpp, was wiederum screen.hpp inkludiert, die wiederum mein 
Config.File config.hpp inkludiert. Alles bis auf stm32xx_hal_conf.c ist 
also C++.

Bloß kann das natürlich nicht funktionieren, da ich in den C++-Headern 
die C++-Versionen der Standard-Lib inkludiere (also z.B. cstdlib statt 
stdlib). Diese Header werden von gcc aber nicht gefunden:
1
arm-none-eabi-***g++*** "../Core/Src/main.***cpp***" -mcpu=cortex-m4 -std=gnu++14 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F303xE -c -I../Core/Inc -I"STM32CubeIDEWorkspace/SensorBoardFirmware/CppUTest/include" -I../Drivers/STM32F3xx_HAL_Driver/Inc -I../Drivers/STM32F3xx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32F3xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -Wall -fstack-usage -MMD -MP -MF"Core/Src/main.d" -MT"Core/Src/main.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Core/Src/main.o"
2
arm-none-eabi-***g++*** "../Core/Src/screen.***cpp***" -mcpu=cortex-m4 -std=gnu++14 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F303xE -c -I../Core/Inc -I"STM32CubeIDEWorkspace/SensorBoardFirmware/CppUTest/include" -I../Drivers/STM32F3xx_HAL_Driver/Inc -I../Drivers/STM32F3xx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32F3xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -Wall -fstack-usage -MMD -MP -MF"Core/Src/screen.d" -MT"Core/Src/screen.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Core/Src/screen.o"
3
arm-none-eabi-***gcc*** "../Core/Src/stm32f3xx_hal_msp.***c***" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F303xE -c -I../Core/Inc -I"STM32CubeIDEWorkspace/SensorBoardFirmware/CppUTest/include" -I../Drivers/STM32F3xx_HAL_Driver/Inc -I../Drivers/STM32F3xx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32F3xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"Core/Src/stm32f3xx_hal_msp.d" -MT"Core/Src/stm32f3xx_hal_msp.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Core/Src/stm32f3xx_hal_msp.o"
4
arm-none-eabi-***gcc*** "../Core/Src/stm32f3xx_it.***c***" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F303xE -c -I../Core/Inc -I"STM32CubeIDEWorkspace/SensorBoardFirmware/CppUTest/include" -I../Drivers/STM32F3xx_HAL_Driver/Inc -I../Drivers/STM32F3xx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32F3xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"Core/Src/stm32f3xx_it.d" -MT"Core/Src/stm32f3xx_it.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Core/Src/stm32f3xx_it.o"
5
In file included from ../Core/Inc/screen.hpp:8,
6
                 from ../Core/Inc/main.hpp:35,
7
                 from ../Core/Src/stm32f3xx_it.c:22:
8
../Core/Inc/config.hpp:8:10: fatal error: cstdlib: No such file or directory
9
    8 | #include <cstdlib>
10
      |          ^~~~~~~~~
11
compilation terminated.

Wie löst man diesen Knoten ?

: Bearbeitet durch User
von Johannes S. (Gast)


Lesenswert?

Man kann mit einem #if abfragen ob cpp kompiliert wird, aber warum soll 
der low level hal_conf ein main.h includieren? Wenn da gemeinsame 
Konstanten drin sind, dann würde ich die eher in einen C Header 
auslagern.
Und Mbed-OS benutzen, dann hast du gleich ein main und ein API in C++. 
Und HAL Funktionen kann man auch direkt aufrufen weil die für die STM32 
targets benutzt werden.

Und nach interfacing C/C++ suchen, da gibts noch mehr Fallstricke. Ganz 
übel das ‚weak‘ in dem Zusammenhang.

von Franz W. (woyzeck)


Lesenswert?

Johannes S. schrieb:
> ... aber warum soll der low level hal_conf ein main.h includieren?

Soweit ich den Code verstehe, initialisiert stm32fxx_hal_msp.c das MCU 
Support Package, das wieder irgendein Layer um die Hardware ist,. 
Jedenfalls wird dieses File von der STM32CubeIDE generiert - samt 
"#include <main.hpp>". Und damit nimmt das Schicksal seinen Lauf.

von Klaus W. (mfgkw)


Lesenswert?

Wenn ich ein neues STM32-Projekt in der STM32CubeIDE als C++ erzeuge, 
wird erstmal gar kein main.cpp und main.hpp erzeugt, sondern main.c und 
main.h (warum auch immer, eben nochmal mit 1.5.1 probiert).

Die main.c kann man dann in main.cpp umbenennen, Die main.h kann man so 
lassen, wie sie ist.

Im ganzen Untergrund, so wie er beim Generieren vorgegeben wird, ist 
wohl eh nichts in C++. Das sind ja dieselben Funktionen wie auch in C, 
und können aus C++ aufgerufen werden (sind passend mit extern "C" 
deklariert).

Erst wenn du in deinem eigenen Code C++ verwendest, schreibst du das in 
die main.cpp rein und inkludierst ggf. noch mehr *.hpp, von denen aber 
die automatisch generierten Dateien doch nichts mitbekommen.

PS: https://shawnhymel.com/1941/how-to-use-c-with-stm32cubeide/

: Bearbeitet durch User
von Klaus W. (mfgkw)


Lesenswert?

PPS:

Beim Umkonfigurieren wird mit dem Regenerieren des Codes dummerweise 
immer eine main.c erzeugt, auch wenn man schon auf main.cpp umgeschwenkt 
ist (zumindest bei mir, 1.5.1).
Da scheint das CubeMX zu doof zu sein und C++ zu ignorieren.

https://community.st.com/s/question/0D50X0000At0EJ3/how-to-configure-stm32cubeide-to-support-c-development

Dann muß man sich wohl behelfen, indem man die interessanten Sachen 
(C++) aus der generierten main.c heraushält und in eigene C++-Quelltexte 
auslagert.

Krank...

von Harry L. (mysth)


Lesenswert?

Laß doch die main.c in Ruhe, und erzeug dir ein xyz.cpp mit deiner 
"main-funktion", die du aus der eigentlichen main.c heraus aufrufst.
Ab hier gehts dann mit C++ weiter.

Das ist die ursprüngliche Idee dahinter.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

Harry L. schrieb:
> Laß doch die main.c in Ruhe, und erzeug dir ein xyz.cpp mit deiner
> "main-funktion", die du aus der eigentlichen main.c heraus aufrufst.
> Ab hier gehts dann mit C++ weiter.
>
> Das ist die ursprüngliche Idee dahinter.

Genau so mache ich das auch. Alles von dem IDE genierte wird nicht 
angefasst oder umbenannt. Aber mein Code ist dann C++, und die 
Hauptklasse wird dann aus Main heraus aufgerufen.

Achja, dieses Umbenennen habe ich mir auch erspart. Ich habe mit 
Makefile gebaut, und darin einfach nur gcc durch g++ ersetzt. Das 
erspart die ganzen Änderungen der Includes in hpp usw ....

von Rolf M. (rmagnus)


Lesenswert?

Franz W. schrieb:
> Das Problem ist jetzt mit den Includes - stm32xx_hal_conf.c inkludiert
> main.hpp, was wiederum screen.hpp inkludiert, die wiederum mein
> Config.File config.hpp inkludiert. Alles bis auf stm32xx_hal_conf.c ist
> also C++.
>
> Bloß kann das natürlich nicht funktionieren, da ich in den C++-Headern
> die C++-Versionen der Standard-Lib inkludiere (also z.B. cstdlib statt
> stdlib). Diese Header werden von gcc aber nicht gefunden:

Es ist Absicht, dass die nicht gefunden werden und soll auch so sein. 
Die würden ja sowieso nicht funktionieren, da es C++-Header sind, du 
aber C compilierst.

Wenn du ein C-Projekt auf C++ umstellst, solltest du entweder 
ausnahmslos alles auf C++ umstellen oder den C-Teil und den C++-Teil 
sauber von einander trennen. Dabei kann man die in C implementierten 
Dinge in der Regel problemlos auch von C++ aus nutzen, aber nicht 
umgekehrt. Ein Include eines C++-Headers hat in einem C-File nichts 
verloren.

: Bearbeitet durch User
von Klaus W. (mfgkw)


Lesenswert?

Mit den Headerdateien hast du recht, die sollte man bei .h lassen soweit 
sie auch für *.c nutzbar sind.

Aber trotzdem wäre es sinnvoll, gleich im eigenen main.c(pp) C++ nutzen 
zu können.
Nachdem der Codegenerator zu doof ist und immer main.c erzeugt, müsste 
man in den Einstellungen von main.c dafür sorgen, daß die Datei trotz 
des Namens als C++ behandelt wird.
Ich sehe leider keine Möglichkeit, in Properties of main.c -> Resource 
-> Type von "C Source file" auf C++ zu ändern.
Sehr wohl kann man aber dort (Properties of main.c -> C/C++ Build -> 
Settings ->  Tool Settings -> MCU GCC Compiler -> Command:) das Kommando 
zum Übersetzen von gcc auf g++ ändern, nur für main.c.
Dann wird auch als C++ kompiliert, obwohl die Datei main.c heißt und so 
auch vom Codegenerator erzeugt wird. Das heißt, man kann sich in main.c 
schon in C++ austoben und trotzdem klappt es mit Änderungen durch 
CubeMX.
Habe das eben probiert, funktioniert soweit.

Allerdings werden die Compileroptionen erstmal noch wie bei C-Dateien 
gesetzt.
Wenn man da etwas spezielles möchte (bspw. -std=c++17 oder sowas) muß 
man es manuell in den Einstellungen machen.

Damit sollte man das ursprüngliche Problem umschiffen können, bis STM 
auf die Idee kommt,es richtig zu machen.

: Bearbeitet durch User
von Johannes S. (Gast)


Lesenswert?

Hier steht in den Antworten unten das man main über die IDE umbenennen 
soll:
https://community.st.com/s/question/0D50X0000At0EJ3/how-to-configure-stm32cubeide-to-support-c-development

von Franz W. (woyzeck)


Lesenswert?

Harry L. schrieb:
> Laß doch die main.c in Ruhe, und erzeug dir ein xyz.cpp mit deiner
> "main-funktion", die du aus der eigentlichen main.c heraus aufrufst.
> Ab hier gehts dann mit C++ weiter.
>
> Das ist die ursprüngliche Idee dahinter.

Ich verstehe zwar die Idee, sehe aber hier keine Lösung. Ich kann 
natürlich neben die generierte main.c eine main.cpp stellen und dann aus 
main.c Code über main.hpp aufrufen. Bloß wird main.hpp lauter Imports 
haben, die C++ Konstrukte aufweisen (Namespaces etc.) und spätestens 
dann gibt es doch wieder das gleiche Problem, oder ?

Die einzige wirkliche Lösung sehe ich darin, entweder eine gefakte 
main.h neben der main.hpp hinzustellen, die cann von gcc verwendet wird 
und nur auf C-Code referenziert und in main.cpp per Preprocessor den C++ 
Teil auszukommentieren. Das ist aber schon eine arge Krücke.

Alternativ könnte man versuchen, den Build ausschließlich mit g++ zu 
compilieren.

von Harry L. (mysth)


Lesenswert?

Klaus W. schrieb:
> Aber trotzdem wäre es sinnvoll, gleich im eigenen main.c(pp) C++ nutzen
> zu können.
Warum?

> Nachdem der Codegenerator zu doof ist und immer main.c erzeugt,
Das ist genau so gewollt.

> müsste
> man in den Einstellungen von main.c dafür sorgen, daß die Datei trotz
> des Namens als C++ behandelt wird.

Und warum sollte man sowas tun?
Es gibt ein Regelwerk, das aufgrund der Extension entscheidet, wie eine 
Datei zu behanden ist.
Warum sollte man deiner Meinung nach diesen Mechanismus aushebeln?

main.c ist eine klassische C-Datei, und die muß man auch so behandeln, 
was dich ja nicht daran hindert aus der C-Datei heraus C++ Komponenten 
aufzurufen.

Franz W. schrieb:
> Ich verstehe zwar die Idee, sehe aber hier keine Lösung.
Danm musst du noch ein Wenig C(++) lernen!

Franz W. schrieb:
> und spätestens
> dann gibt es doch wieder das gleiche Problem, oder ?
Mein, gibt es nicht.

Franz W. schrieb:
> Die einzige wirkliche Lösung sehe ich darin, entweder eine gefakte
> main.h neben der main.hpp hinzustellen, die cann von gcc verwendet wird
> und nur auf C-Code referenziert und in main.cpp per Preprocessor den C++
> Teil auszukommentieren. Das ist aber schon eine arge Krücke.

Das ist vom Ansatz her bereits grundverkehrt.
S.o.: C lernen!

: Bearbeitet durch User
von Klaus W. (mfgkw)


Lesenswert?

Harry L. schrieb:
> Klaus W. schrieb:
>> Aber trotzdem wäre es sinnvoll, gleich im eigenen main.c(pp) C++ nutzen
>> zu können.
> Warum?

Dein Vorschlag ist sicher machbar.
Aber richtig toll finde ich ihn nun auch nicht.
Der Programmlauf beginnt in main(), da kommen allerlei Initialisierungen 
und dann die übliche Endlosschleife.
Wenn ich etwas in C++ mache, habe ich eines oder mehrere Objekte, die 
vor der Schleife erzeugt werden, und in der Schleife passiert dann 
irgendwas mit denen.
Würde main.c(*) als C++ kompiliert, dann hätte man diese Objekte in eben 
diesem Quelltext liegen, entweder als static globale Objekte oder gleich 
lokal in main().
Wenn du jetzt einen zusätzlichen Quelltext xyz.cpp einführst, damit 
main.c ein C-Quelltext bleiben kann: wo liegen dann solche Objekte?
Wohl in xyz.cpp als globale Variablen, was man vielleicht nicht haben 
will. Und wie soll ich die von main() aus initialisieren? main() kennt 
kein C++. also müsste ich für jede Initialisierung einen C-Wrapper 
schreiben, der das Ding anlegt, und für jeden Zugriff nochmal einen. 
Implizit beziehen diese Wrapper dann auf ein einziges vorhandenes 
globales Objekt in xyz.cpp? Oder indem man für alle solchen Objekte 
irgendwelche Handles bastelt, damit main() nichts von C++ wissen muß?
Objektorientiert sieht anders aus...

Oder anders formuliert:
Es werden von CubeMX ja verschiedene Bereiche user 1, user 2 etc. 
vorgegeben, in denen man ungestört herumprogrammieren kann.
Wenn main.c jetzt nur C kann und nicht C++, muß man ziemliche Klimmzüge 
machen, um von allen diesen Bereichen aus auf die selben C++-Objekte 
zugreifen zu können.

Warum soll ich mir von der IDE vorgeben lassen, ob ich C++ nehmen darf?
Ich erzeuge ein Projekt, in dem ich von vornherein C++ auswähle und 
nicht C. Anschließend erzeugt das Ding mir lauter C-Quellen, STM schlägt 
selber vor das Projekt komplett nach C++ zu konvertieren, die main.c 
umzubenennen in .cpp, dann kommt der Codegenerator beim 
Nachkonfigurieren und macht das zunichte (wenn ich nicht manuell wieder 
in .c zurück benenne und nach dem Generieren wieder in .cpp).

>
>> Nachdem der Codegenerator zu doof ist und immer main.c erzeugt,
> Das ist genau so gewollt.

Vielleicht von CubeMX, aber nicht von mir.
Wenn ich in C++ arbeite, muß ich nach deinem Vorschlag mindestens einen 
zusätzlichen Quelltext einfügen, nur weil die IDE zu schusselig ist.

Kann man machen, finde ich aber nicht erstrebenswert.

Ich glaube auch nicht, daß das von STM wirklich so gewollt ist - eher 
haben sie es bislang verbaselt und bisher nicht korrigiert, weil eh 
nicht viele Leute ernsthaft mit C++ hantieren (zumindest in der 
Vergangenheit).

>
>> müsste
>> man in den Einstellungen von main.c dafür sorgen, daß die Datei trotz
>> des Namens als C++ behandelt wird.
>
> Und warum sollte man sowas tun?
> Es gibt ein Regelwerk, das aufgrund der Extension entscheidet, wie eine
> Datei zu behanden ist.
> Warum sollte man deiner Meinung nach diesen Mechanismus aushebeln?

Ich sage ja auch nicht, daß das der tollste Weg ist.
Aber es ist ein workaround, wenn CubeMX kein main.cpp erzeugen will, 
obwohl ich ein C++-Projekt angelegt habe.

Abgesehen davon ist dein Regelwerk mit der Erweiterung auch nicht so in 
Stein gemeiselt, daß man gleich ein Denkverbot aussprechen muß, mal eine 
main.c als C++ zu kompilieren.

Ja, es ist eher ungewöhnlich und evtl. erklärungsbedürftig, warum man 
gegen die üblichen Konventionen C++ in einer main.c hat und mit g++ 
kompiliert.
Aber alle anderen Lösungen sind doch auch nicht das Grüne vom Ei, außer 
einem Fix von STM.


Bevor jetzt der Verdacht aufkommt: ich will den ganzen IDE-Kram von STM 
nicht wirklich verteufeln. Im Gegenteil finde ich es klasse gemacht, 
auch wenn ich eigentlich sonst kein Freund von integriertem Zeug bin 
(zumindest nicht über den EMACS hinaus).
Aber den Punkt haben sie halt verbockt, das muß man nicht schönreden.
Und ich gehe davon aus, daß sich das irgendwann erledigt und STM 
nachbessert.

: Bearbeitet durch User
von Harry L. (mysth)


Lesenswert?

Betrachte den generierten Code wie eine Art Bootloader, der deine 
Peripherie initialisiert!

Die nächster Stufe ist dann dein C++-Code, und da gibt es keinerlei 
unlösbare Konflikte mit dem generierten C-Code.

Mit der Platzierung von globalen Variablen hat das schon mal gar nichts 
zu tun.
Und, auch, wenn es im Code überall diese UserCode-Sections gibt, muß man 
die nicht benutzen.

Ich schreibe meinen Code immer in eigene Dateien, und in der main.c gibt 
es 1 oder 2 Aufrufe zu meinem Code.

Klaus W. schrieb:
> Wenn ich in C++ arbeite, muß ich nach deinem Vorschlag mindestens einen
> zusätzlichen Quelltext einfügen, nur weil die IDE zu schusselig ist.
Ja und? - das ist doch täglich Brot eines jeden Programmierer.

Klaus W. schrieb:
> Ich glaube auch nicht, daß das von STM wirklich so gewollt ist - eher
> haben sie es bislang verbaselt und bisher nicht korrigiert, weil eh
> nicht viele Leute ernsthaft mit C++ hantieren (zumindest in der
> Vergangenheit).
Da irrst du gewaltig!

TouchGFX z.B. ist nahezu vollst. C++-Code und kann sogar autom. von 
CubeMX eingebunden werden.

Der aktuelle Weg ist generisch - d.h.: man kann so sowohl C wie auch 
C++-Projekte mit dem selben Unterbau nutzen und generieren - und das ist 
genau so gewollt.

Du siehst Probleme, die es so nicht gibt.

von Franz W. (woyzeck)


Lesenswert?

Danke für eure Antworten - gibt es vielleicht irgendwo ein Beispiel für 
ein Projekt, das C++ in der STM32CubeIDE verwendet ? Ich finde leider 
nur triviale Beispiele auf github, die nicht wirklich weiterhelfen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.