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:
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.
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.
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/
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...
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.
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 ....
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.
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.
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.
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!
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.
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.
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.