====== Konstanten in Arduino/C++ ====== **Für wen ist dieser Artikel gedacht?** In einfachen Arduino-Sketches ist es eigentlich egal, wie man seine Konstanten definiert, wichtig ist zunächst nur Konsistenz. Wenn man jedoch auch etwas professionelleren Code schreiben möchte ist es gut, die Vor- und Nachteile der verschiedenen Optionen zu kennen. Arduino-Sketches sind C++-Code, welcher um Arduino-spezifische Funktionen erweitert wurde. Sämtliche C++(11)-Sprachfeatures sind im Code erlaubt. Daher gibt es oft viele Wege, einen bestimmten Effekt zu erzielen. Je nach Anwendungsfall gibt es dann manchmal idiomatische und nicht-idiomatische Wege. Dieser Artikel behandelt drei verschiedene Möglichkeiten, konstante Werte im Code zu definieren und zu verwenden. ===== Option 1: #define ===== #define LED_PIN 13 #define DELAY_TIME 500 void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { digitalWrite(LED_PIN, HIGH); delay(DELAY_TIME); digitalWrite(LED_PIN, LOW); delay(2 * DELAY_TIME); } ''#define'' ist eine Präprozessor-Anweisung. Alle nachfolgenden Verwendungen von ''LED_PIN'' werden vor dem Kompilieren durch ''13'' ersetzt. ==== Vorteile ==== * Kein zusätzlicher Speicher benötigt, da Ersetzung direkt beim Funktionsaufruf ==== Nachteile ==== * Man kann die Definition mittels ''#undef'' später im Programm wieder zurücknehmen und neu definieren. Die Konstante hätte dann im ganze Programm nicht überall denselben Wert. Klar kann man sich sagen, dass man das einfach nicht macht, aber sich solch ein Regelwerk aufzusetzen und dann womöglich im Team noch durchzusetzen ist immer eher suboptimal. Es ist **immer** besser, wenn man so programmiert, dass schon der Compiler unsinnigen Code erkennt. * Diese Option bringt keine Typinformationen mit sich und führt nur triviale Textersetzung durch. Man könnte ja auch ''#define DELAY_TIME 400 + 100'' schreiben, was dann später im Programm zu nicht sofort offensichtlichen Fehlern führt (''2 * 400 + 100'' ist definitiv nicht dasselbe wie ''2 * 500''). Man kann in ''#define''s auch Code verstecken der bei jeder Verwendung ausgeführt wird, diese Möglichkeiten verleiten oft zu schwer lesbarem Code. ===== Option 2: const ===== const int led_pin { 13 }; void setup() { pinMode(led_pin, OUTPUT); } void loop() { digitalWrite(led_pin, HIGH); delay(500); digitalWrite(led_pin, LOW); delay(500); } ''const int led_pin { 13 };'' ??? Diese Schreibweise nennt sich C++ uniform initialization. Sie ist fast immer **besser** als ''led_pin = 13'', weil C++ auch folgenden Code erlauben würde: ''const int led_pin = 13.5;'' Das ist natürlich definitiv falsch. Daher gibt es seit C++11 die erstgenannte Schreibweise, bei der kein implizites type-narrowing vorgenommen wird. ''const int led_pin { 13.5 };'' gibt nämlich einen Kompilierfehler. ==== Vorteile ==== * Typsicherheit: Konstanten haben auch einen Typ * Sicherheit gegen Veränderungen: Konstanten können nicht verändert/undefiniert werden (Ja, man kann manchmal das ''const'' weg-casten, aber das betrachten wir hier nicht) ==== Nachteile ==== * Globale Variablen und Konstanten landen im heap Speicher und belegen für die gesamte Programmlaufzeit diesen Platz. Ob und wie der Compiler sie wegoptimiert hängt stark von den verwendeten Compileroptimierungen ab. ===== Option 3: constexpr ===== constexpr int led_pin { 13 }; constexpr int delay_time { 500 }; void setup() { pinMode(led_pin, OUTPUT); } void loop() { digitalWrite(led_pin, HIGH); delay(delay_time); digitalWrite(led_pin, LOW); delay(2 * delay_time); } ''constexpr'' vereint die Vorteile der beiden vorherigen Optionen. ==== Vorteile ==== * Typsicherheit: Konstanten haben auch einen Typ * Sicherheit gegen Veränderungen: Konstanten können nicht verändert/undefiniert werden * Kein zusätzlicher Speicher benötigt, da Ersetzung durch Compiler * Man kann sogar ''constexpr int delay_time { 400 + 100 };'' schreiben und es funktioniert trotzdem immer noch alles wie erwartet, da der Compiler die Anweisung zur Compilezeit auswertet ==== Nachteile ==== * Das Syntax-Highlighting der Arduino-IDE kennt ''constexpr'' nicht als Schlüsselwort * Nicht jeder kennt die "neue" C++11-Syntax (die zum Zeitpunkt dieses Artikel ja auch erst offiziell 7 Jahre alt ist) Man kann sogar einige Funktionen ''constexpr'' machen, um diese schon zur Compilezeit auszuwerten, darauf gehe ich hier aber jetzt nicht ein. Im Arduino-Kontext wird das meistens auch nicht gebraucht.