Ich versuche ein mit der Arduino IDE erstelltes Projekt zu
strukturieren.
Ich programmiere weitestgehend in C, grundsätzlich nutzt Arduino jedoch
C++.
Derzeit sind alle "Module" ausschließlich in Header files untergebracht
(jeweils mit #ifdef MODUL_H) und vermeiden so eine einzelne, elend lange
.ino-Datei.
Der Versuch, diese in Header (.h) und Code (.cpp) aufzuteilen, also etwa
so:
1
project/
2
project.ino
3
module.cpp
4
module.h
5
display.cpp
6
display.h
scheiterte bislang an Compilerfehlern innerhalb eines Arduino-Moduls
(Bibliothek). Daher habe ich es jetzt erstmal rückgängig gemacht.
Konkrete Hinweise finde ich leider von "offizieller" Seite nicht:
https://www.arduino.cc/en/Guide/Environment#toc8> Tabs, Multiple Files, and Compilation> Allows you to manage sketches with more than one file (each of which appears in
its own tab). These can be normal Arduino code files (no visible extension), C
files (.c extension), C++ files (.cpp), or header files (.h).
Aber es dürfte sich ja unter der Haube an die C++/C-Compiler-Regeln
halten.
Für Hinweise oder Verweise auf konkrete Beispiele wäre ich dankbar.
Die .h/.cpp Dateien legste am Besten in User/Dokumente/Arduino/libraries
ab inklusive Unterordner. Danach IDE beenden und neu starten. Wenn du
dann die Lib Dateien zum Bsp. mit Notepad++ bearbeitest, einfach alles
speichern und in der IDE neu kompilieren lassen, dass klappt.
Danke, ich probier's heute abend nochmal.
#pragma once hatte ich nicht, das dürfte ja dem Konstrukt #ifndef /
#define / #endif entsprechen.
Mir ist aufgefallen, dass die IDE ihre temporären Dateien nicht
aufräumt, d.h. wenn ein Modul im Projektverzeichnis entfernt wird, kann
es sein, dass es trotzdem noch compiliert wird.
Der Neustart ist also wohl stets fällig.
Interessant,
pragma once
kannte ich gar nicht.
Ich habe immer die Include-Guards verwendet. In der Wikipedia steht
"widely supported"
https://en.wikipedia.org/wiki/Pragma_once
Wie ist das zu bewerten? Es scheint also nicht von jedem Compiler
unterstützt zu werden, aber der GCC kann's wohl.
Hallo,
wie schon erkannt sind pragma Direktive nicht allgemeiner Standard,
werden aber von den meisten Compilern unterstützt, weshalb der
Verwendung nichts im Weg steht. Erspart einem auf jeden Fall das Kaos
mit #if... im Normalfall wenn man nur einen Header einbinden möchte.
Hänschen K. schrieb:> Mir ist aufgefallen, dass die IDE ihre temporären Dateien nicht> aufräumt, d.h. wenn ein Modul im Projektverzeichnis entfernt wird, kann> es sein, dass es trotzdem noch compiliert wird.> Der Neustart ist also wohl stets fällig.
Manchmal werden Projektdateien im C ... temp Ordner nicht gelöscht nach
beenden der IDE. Das ist aber ein sehr seltener Fehler. Hat aber nichts
mit dem Kompilieren zu tun was du vielleicht meinst.
Kompiliert wird nur das was du als Programmierer eingebunden haben
möchtest. Wenn du irgendwo ein include entfernst bekommste Mecker.
Wenn ich eine Libdatei lösche während die IDE offen ist und diese
benötigt, dann meckert der Compiler. Wenn ich eine Libdatei parallel zur
offenen IDE in Notepad++ bearbeite und nicht neu speichere, kompiliert
die IDE noch mit dem ihr bekannten alten Dateistand. Drücke ich
speichern kompiliert sie umgehend mit dem neuen Dateistand. Die IDE holt
sich demzufolge immer die aktuelle Datei. Deswegen kann ich das nicht
ganz glauben das die IDE eine nicht mehr vorhandene Datei immer noch
fehlerfrei kompilieren kann.
Christoph M. schrieb:> Interessant,>> pragma once>> kannte ich gar nicht.> Ich habe immer die Include-Guards verwendet. In der Wikipedia steht> "widely supported">> https://en.wikipedia.org/wiki/Pragma_once>> Wie ist das zu bewerten? Es scheint also nicht von jedem Compiler> unterstützt zu werden, aber der GCC kann's wohl.
Ich würde eher anders herum fragen: welcher Compiler kann es nicht. Die
großen drei können es alle, der EDG (s.a. MS-Code) auch.
>Ich würde eher anders herum fragen: welcher Compiler kann es nicht. Die>großen drei können es alle, der EDG (s.a. MS-Code) auch.
Es gibt ja schon noch ein paar andere Argumente gegen die Verwendung.
Caveats
Identifying the same file on a file system is not a trivial task.[6]
Symbolic links and especially hard links may cause the same file to be
found under different names in different directories. Compilers may use
a heuristic that compares file size, modification time and content.[7]
Additionally, #pragma once can do the wrong thing if the same file is
intentionally copied into several parts of a project, e.g. when
preparing the build. Whereas include guards would still protect from
double definitions, #pragma once may or may not treat them as the same
file in a compiler-dependent way. These difficulties, together with
difficulties related to defining what constitutes the same file in the
presence of hard links, networked file systems, etc. so far prevented
the standardization of #pragma once.[citation needed]
Christoph M. schrieb:>>Ich würde eher anders herum fragen: welcher Compiler kann es nicht. Die>>großen drei können es alle, der EDG (s.a. MS-Code) auch.>> Es gibt ja schon noch ein paar andere Argumente gegen die Verwendung.>> Caveats>> Identifying the same file on a file system is not a trivial task.
... Mmh, das muss ein ganz altes Zitat sein. Heute sind Hashes üblich
dafür.
Gegen old-school include-guards sprechen ja auch viele Dinge. Vor allem
die Fehleranfälligkeit, weil die Bezeichner selbst gewählt werden. Das
macht gerade beim Umbenennen von Dateien Probleme. Da ist das pragma
m.E. wesentlich einfacher.
- immer schön .cpp nutzen. Umbenennung von Dateien (.c -> .cpp) bringt
die IDE durcheinander, genauer, die IDE sieht die "neue" Datei im
Projektverzeichnis, aktualisiert aber nicht das Temporäre (siehe oben).
- in den Headern die Variablen ggf. extern deklarieren.
- #defines aus der .ino sind nicht global und damit den Modulen nicht
bekannt. Also entweder in die jeweilige Header-Datei oder eine
gemeinsame nutzen.
- #pragma once habe ich jetzt noch nicht benutzt. Gewohnheitssache...
Damit müsste es klappen. Ggf. an der Hierarchie/Struktur feilen.
Danke für die Motivation und den anderen gleichfalls Geduld und
Erfolg...
Hänschen K. schrieb:> - #defines aus der .ino sind nicht global und damit den Modulen nicht> bekannt. Also entweder in die jeweilige Header-Datei oder eine> gemeinsame nutzen.
Präprozessor-Macros sind immer(!) global, sie kennen keinen
Gültigkeitsbereich (Scope). Sie gelten ab dem Punkt der Defintion
überall. Das ist ja gerade eines der fatalen Merkmale dieser Macros.
Hänschen K. schrieb:> ... scheiterte bislang an Compilerfehlern innerhalb eines Arduino-Moduls> (Bibliothek).
Hast du die Fehlermeldung mal durchgelesen und auch versucht zu
verstehen?
Warum hälst du nicht an die Arduino Konvention, gibst deinen Dateien den
Nachnamen ".ino" und packst sie zusammen mit deinen Header Dateien in
deinen Projektordner?
Dann klappt das auch mit dem Kompilieren.
Wilhelm M. schrieb:> Präprozessor-Macros sind immer(!) global, sie kennen keinen> Gültigkeitsbereich (Scope). Sie gelten ab dem Punkt der Defintion> überall. Das ist ja gerade eines der fatalen Merkmale dieser Macros.
Probier das mal aus:
sketch.ino
Wolfgang schrieb:> Hast du die Fehlermeldung mal durchgelesen und auch versucht zu> verstehen?>> Warum hälst du nicht an die Arduino Konvention, gibst deinen Dateien den> Nachnamen ".ino" und packst sie zusammen mit deinen Header Dateien in> deinen Projektordner?> Dann klappt das auch mit dem Kompilieren.
Ja.
Es gibt keine "Arduino Konvention", und ich hatte noch keine "Header
Dateien". ".ino" erhält im temporären Verzeichnis übrigens die Endung
".cpp".
Es klappt ja auch mit dem Kompilieren, wie ich weiter oben schrieb.
Hänschen K. schrieb:> Wilhelm M. schrieb:>> Präprozessor-Macros sind immer(!) global, sie kennen keinen>> Gültigkeitsbereich (Scope). Sie gelten ab dem Punkt der Defintion>> überall. Das ist ja gerade eines der fatalen Merkmale dieser Macros.>> Probier das mal aus:>> sketch.ino>
1
>#defineSTIMMTNICHT
2
>voidsetup(){
3
>}
4
>voidloop(){
5
>}
6
>
>> test.cpp>
1
>#ifndefSTIMMTNICHT
2
>#errorichhatterecht
3
>#else
4
>#errorduhattestrecht
5
>#endif
6
>
Wer inkludiert hier was? Ich sehe nicht, das test.cpp etwas inkludiert.
Hast Du verstanden, was der Präprozessor ist, wann er aufgerufen wird?
Ich denke, das hast Du nicht.
Wilhelm M. schrieb:> Hast Du verstanden, was der Präprozessor ist, wann er aufgerufen wird?> Ich denke, das hast Du nicht.
Er wollte dir mitteilen, dass eine Präprozessor-Definition mitnichten
überall global gilt. Der Compiler muss sie in dieser Translation Unit
auch sehen, sonst tut sie das nämlich nicht.
Eine globale Variable kann ich immer von überall erreichen, ein Makro
nicht. Daraus folgt, dass Makros schonmal nicht global sind.
S. R. schrieb:> Wilhelm M. schrieb:>> Hast Du verstanden, was der Präprozessor ist, wann er aufgerufen wird?>> Ich denke, das hast Du nicht.>> Er wollte dir mitteilen, dass eine Präprozessor-Definition mitnichten> überall global gilt. Der Compiler muss sie in dieser Translation Unit> auch sehen, sonst tut sie das nämlich nicht.
Der Compiler sieht sie gar nicht, sondern der Präprozessor.
Die meisten Leute verstehen eben nicht, dass der Präprozessor ein
nicht-interaktiver Editor ist.
> Eine globale Variable kann ich immer von überall erreichen, ein Makro> nicht. Daraus folgt, dass Makros schonmal nicht global sind.
Nein, das gilt nur für programm-globale Variablen. Für TU-globale gilt
das auch nicht, die kannst Du auch nicht von überall aus Deinem Programm
erreichen, sondern eben nur als der definierenden TU ab
Definitionspunkt.
Und ein Präprozessor-Macro ist in dem Sinne global, dass es natürlich ab
Definitionspunkt überall gilt. Lässt man das #include weg, kann das
natürlich nicht gehen - soviel Erkenntnis hatte ich auch vom TO
erwartet. Man kann zu einem Präprozessor-Macro aber auch nicht TU-global
sagen, weil es eben auch in andere TUs inkludiert werden kann, und dann
auch dort gilt. Deswegen habe ich ja auch unscoped als Erläuterung dazu
geschrieben.
Wilhelm M. schrieb:> Wer inkludiert hier was? Ich sehe nicht, das test.cpp etwas inkludiert.
Habe ich nicht behauptet.
Du hattest dieser Feststellung widersprochen:
Hänschen K. schrieb:> - #defines aus der .ino sind nicht global und damit den Modulen nicht> bekannt.
Deine Annahmen kenne ich nicht.
Schwamm drüber.
Hallo,
ich habe mir gerade mal die Kombination von Visual Studio Code und
platformIO ausprobiert. Nette Kombination.
Vielleicht eine bessere Alternative als die blöde Arduino IDE.
Gruß
Frank
Hallo,
es geht hier im Thread nicht um die IDE und welche jemanden besser
gefällt. Es geht darum wie man seine Libs eingebunden bekommt. Das ist
vollkommen IDE unabhängig.
Wilhelm M. schrieb:> Nein, das gilt nur für programm-globale Variablen.
Richtig. Sowas verstehe ich unter "global".
> Für TU-globale gilt das auch nicht, die kannst Du auch> nicht von überall aus Deinem Programm erreichen,
Was soll denn bitte TU-global sein? Es gibt "global" (von überall aus
erreichbar) und "lokal" (an einen Ort gebunden).
Das sind Gegenstücke: Was nicht global ist, ist lokal.
> sondern eben nur als der definierenden TU ab Definitionspunkt.
Wenn du "innerhalb der TU" meinst, dann ist das lokal. TU-lokal.
Und wenn du "erreichbar in dieser TU sowie allen weiteren TUs" meinst,
dann ist das einfach nur seltsam, ähnlich wie deine Definition.