erweiterte Funktionsparameter Übergabe

Hallo, ich habe mir einige Funktionen in meinem Code ausgelagert. Ich weiß das ich einer Funktion Variablen übergeben kann

void foo(uint_8 uebergabebyte, char woerter[10])

aber wie kann ich einen "Befehl" ähnlich der Topologie von z.B. der pinMode Funktion nutzen indem ich direkt einen "Befehl" oder eine "Anweisung" übergebe

pinMode(A0, INPUT)

ich suche nach der "Variante" wie ich in meiner selbst geschrieben Funktion zwischen mehreren Methoden wählen kann, also ähnlich dem Auswahlmenü was mir die pinMode Funktion mit INPUT, OUTPUT, INPUT_PULLUP bietet.

Ich hoffe ich konnte mich verständlich machen. Dankeschön schon mal im voraus

Im einfachsten Fall so:

enum Variante {Bli,Bla=42,Blub=4711};



void tuwas(Variante variante)
{
  if(variante == Blub) Serial.println("Blub");
  switch(variante)
  {
    case Bli: Serial.println("Bli"); break;
    case Bla: Serial.println("Bla"); break;
  }
}


void setup() 
{
  Serial.begin(9600);      
  Serial.println("Start");
  tuwas(Blub);
}

void loop() 
{

}

INPUT ist nur ein Platzhalter für eine Zahl (s.h. Arduino.h) und pinMode() erwartet dementsprechend auch eine (s.h. wiring_digital.c)

Rintin:
INPUT ist nur ein Platzhalter für eine Zahl (s.h. Arduino.h) und pinMode() erwartet dementsprechend auch eine (s.h. wiring_digital.c)

Dass das im Arduino Core so gelöst ist, ist nicht unbedingt ein Qualitätsmerkmal.
Allerdings typisch für die etwas ältere C Welt.
Zumindest hat es den Vorteil, dass man pinMode() auch in C Programmen nutzen kann.

Arduino *.ino Dateien sind allerdings C++, da kann man auf die alten Zöpfe ganz entspannt verzichten.

Und sogar einen Schritt weiter gehen, und die Sache vollständig Typesicher und weniger Seicherintensiv gestalten:

enum class Variante:byte {Bli=0,Bla=3,Blub=42};



void tuwas(Variante variante)
{
  if(variante == Variante::Blub) Serial.println("Blub");
  switch(variante)
  {
    case  Variante::Bli: Serial.println("Bli"); break;
    case  Variante::Bla: Serial.println("Bla"); break;
  }
}


void setup() 
{
  Serial.begin(9600);      
  Serial.println("Start");
  tuwas(Variante::Blub);
}

void loop() 
{

}

Die direkte Konvertierung von klassischen enums und Integer hat hier aber auch Vorteile. Vor allem bei digitalWrite(). Da kann man dann sowas machen:

digitalWrite(led, var1 > var2);

Wenn man da nur die typisierte enums übergeben könnte, ginge das nicht mehr

Ihm sprach von "Auswahl".
Wenn an der Stelle allerdings auch Ausdrücke gewünscht sind, dann hast du Wahr.
Das kann ich mir bei pinMode() allerdings kaum vorstellen

und Integer

Gerade bei digitalWrite() , wäre bool vermutlich der passendere Datentype.
Und nicht eine enum Konstante, oder gar ein int

Aber auch hier wieder:
Damit es in C funktioniert, ist das Original schon ok


Wie auch immer...
Bis jetzt habe ich das eigentliche Problem von arduino-wasser noch nicht verstanden!

Also können meine Vorschläge, nur Vorschläge sein, keine festen Empfehlungen.

Ich baue gerade aus 8Stück 4-fach 7Segment Anzeigen (also 32 7Segmente) eine Matrix mit meinen noch übrigen 74HC595. Die Segmente sind in 2 Zeilen a 16 Digits angeordnet. (was ich aber später auch noch flexibel in die große Funktion einbinden will, sodass Länge x Breite beliebig werden)

Dazu brauch ich nun eine "dicke" Funktion die mir eine simple Print-Befehl Ausgabe ermöglicht. Ich will so Sachen lösen wie ALIGN_LEFT, ALIGN_RIGHT als auch die eventuelle Zahlenbasis (DEC, HEX, OCT).
Die einzelnen Funktionsbrocken habe ich schon fertig. Weitere grundliegende Textformatierungen kommen noch. Zur Zeit funktioniert schon die ersten beiden Formatierungsanweisungen, jedoch man muss halt zur Zeit immer noch separat geschachtelte Funktionen aufrufen.

Den zweiten Lösungsvorschlag von combie finde ich sehr interessant. Bis dato hatte ich noch keinerlei Kontakt mit "echtem" C++ und somit auch weder mit enum noch mit Klassen (deutscht man das eigentlich ein?)

Gibt es da eventuell zu einen Verständlichen Artikel um sich dem Thema langsam zu widmen und danach auch zu kapieren was man denkt zu tun? Vielleicht sogar auf Deutsch?

Da sollte es ein richtiges C++ Buch tun.

Gruß Tommy

Dazu brauch ich nun eine "dicke" Funktion die mir eine simple Print-Befehl Ausgabe ermöglicht. Ich will so Sachen lösen wie ALIGN_LEFT, ALIGN_RIGHT als auch die eventuelle Zahlenbasis (DEC, HEX, OCT).

Zum Teil ist das schon fertig.
Du könntest von Print erben, so wie es auch Serial und die LCD Klassen tun.

Enums gibt es auch in C. Die C enums haben aber ein paar Probleme:
1.) sie sind nur schwach typisiert und direkt in Integer konvertierbar. Das kann zwar auch Vorteile haben (siehe oben), aber erlaubt auch allerlei Unsinn.
2.) in Standard C/C++ nicht möglich den darunter liegenden Typ anzugeben (z.B. byte oder int). Einige Compiler wie GCC erlauben das aber
3.) die einzelnen Konstanten landen im gleichen Scope. Das heißt z.B. wenn zwei enums eine Konstante namens "None" haben gibt es einen Fehler.
4.) es war nicht möglich ein enum mit dem vollen Namen (d.h. Enumeration::Konstante) anzugeben (in C++11 geht das)

Letztlich sind C enums daher nicht viel anders als einzelne #define Konstanten (und die Arduino Software verwenden für solche Sachen auch #define Makros)

Deshalb gibt es seit C++11 strongly typed enums. Das gibt man mit "enum class" an, aber mit Klassen hat das eigentlich nichts zu tun. Hier hat jetzt jede Konstante nur den Scope ihrer eigentlichen Enumeration (das ist auch der Grund weshalb man "class" als Schlüsselwort genommen hat. Die Konstanten sind so gekapselt). Man kann Konstanten so mehrfach verwenden. Und sie haben ihren eigenen Typ der nicht in Integer konvertierbar ist

Ok, also das ganze denke ich ist genau das was ich gesucht habe. Nur eines habe ich noch nicht wirklich verstanden, in deinem Beispiel definierst du

enum class Variante:byte {Bli=0,Bla=3,Blub=42};

und rufst dann die Funktion (nennt man das dann noch Funktion, oder Beschreibt man das nun anders?) via Variante::Blub, Variante::Bla etc. auf, das hab ich soweit verstanden.

Aber wird dann den "Namen" in dieser Funktion beim Deklarieren ein byte Wert zugewiesen? Ist die Zuweisung der Werte in der geschweiften Klammer in etwa das was ich auch mit #define mache, nur in diesem Fall lokal auf meine jeweilige Funktion begrenzt (also nur in diesem Scope). Könnte ich somit auch die Funktion so aufrufen Variante::42?

Wie rufe ich dann "tuwas" richtig auf
tuwas(42) oder tuwas(Blub) .....ich denke ich versuche mich gerade selbst zu verwirren. Ich will so eine Art von Übergabe einer Funktion haben ala
tudass(zahlzumdarstellen, ALIGN_LEFT, ROWx, COLUMNx, HEX) und da wäre halt gut wenn man den byte Wert bereits mit dem "Namen" der Variante substituieren könnte.

Und noch eine Frage, wie gebe ich einen Standartwert für eine solche Funktion vor wenn kein Wert übergeben wird?

//Es wird übrigens ein 32-Digit 7-Segment Anzeige Modul mit modularer Länge x Breite. Darum kann ich die .print Funktionen eigentlich nicht wirklich beerben, da ich alles via 74HC595 ausshifte und meine eigene Bitmap habe zum Darstellen aller Möglichen Symbole der Matrix. Und genau dafür brauch ich die einfache Auswahl der Methode der Anzeige beim direkten übergeben.

Du kannst auch

enum class Variante:byte {Bli,Bla,Blub};
schreiben.
Dann überlässt du dem Kompmiler, welche Werte er vergibt.
Er wird mit Null beginnend aufwärts zählen

Standartwertf

So wie immer....

void tuwas(Variante variante = Variante::Bli)
{}

tudass(zahlzumdarstellen, ALIGN_LEFT, ROWx, COLUMNx, HEX) und da wäre halt gut wenn man den byte Wert bereits mit dem "Namen" der Variante substituieren könnte.

Darum habe ich dir das Beispiel in Posting #1 gezeigt.

Wie rufe ich dann "tuwas" richtig auf

Nicht ohne Grund, habe ich dir voll funktionsfähige, testbare, Beispiele gezeigt.

Das sind keine Funktionen! Enum steht für Enumeration. Oder auf deutsch "Aufzählung". Da wird einfach jeder Konstante sequentiell ein Wert zugewiesen. Aber gerade bei den strongly typed enums interessiert dich der Wert dahinter nicht wirklich. Deshalb ist es besser man lässt die direkte Zuweisung weg und lässt das den Compiler erledigen

Ok nach einigem herumprobieren habe ich nun herausgefunden das man es nur über den folgenden aufruf klappt
tuwas(Variante::Blub);
nun würd mich aber interessieren wie denn die "eigentlichen" Arduino Funktionen bzw. dessen Librarys das ganze ohne den Zusatz von
Variante::
vor dem Konfigurationsparameter schaffen?
als Beispiel nur mal diese PID Library Arduino Playground - PIDLibraryConstructor dieser werden zur Konfiguration die Parameter
"Direction" und "POn"
übergeben. Wie wird das gemacht? Ich hab mir die Library selbst schon angeschaut, stehe aber scheinbar ein wenig auf dem Schlauch.

OK, noch mal in einfachen Worten:
Die meisten core libraries verwenden defines
gehe dazu in Arduino/hardware/avr/cores/arduino und schau dir z.b. die print.h an
#define DEC 10
#define HEX 16
#define OCT 8

also kannst du das gleiche tun, bzw. wenn du etwas findest, dass du brauchen kannst auch reusen. D.h. deine Optionen für DEC, HEX, OCT sind schon da. Du brauchst in deinen Funktionen nur entsprechend darauf reagieren. Eigentlich musst du nicht einmal wissen welcher numerische wert hinter dem define steckt, denn du kannst ja auch beispielsweise auf case HEX abfragen und dann dein 0x... so wie du es haben möchtest selber ergänzen.

oder du verwendest enumerationen.

Auf Basis dessen was dir schon geschrieben wurde zwei Beispiele:

// mittels precompiler defines - so wie in vielen core libraries:
#define NO_BLINKING 0
#define BLINKING 1

// enumerationen können die die manuelle Durchnummerierung abnehmen:
// enumeration/Aufzählung in der einfachsten Art:   
enum {ALIGN_LEFT,               // die Aufzählung zählt die Elemente einfach durch: 0
      ALIGN_RIGHT,              // = 1
      ALIGN_MIDDLE,             // = 2
      ALIGN_MAGIC = 48          // du kannst aber auch einen Wert vergeben
     };          
// eigentlich soll dich der numerische Werte gar nicht interessieren, 
// du verwendest ab jetzt deine Aufzählung ähnlich wie Konstanten.


void blinken(byte variante)
{
  switch (variante)
  {
    case  NO_BLINKING: Serial.println(F("no blinking")); break;
    case  BLINKING: Serial.println(F("blinking"));
  }
}

void tuwas(byte variante)
{
  switch (variante)
  {
    case  ALIGN_LEFT: Serial.println(F("Linksbündig")); break;
    case  ALIGN_RIGHT: Serial.println(F("Rechtsbündig")); break;
    case  ALIGN_MIDDLE: Serial.println(F("Zentriert")); break;
    case  ALIGN_MAGIC: Serial.println(F("blabla Irgendwas"));
  }
}


void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start"));
  tuwas(ALIGN_LEFT);
  blinken(BLINKING);
}

void loop()
{

}

so, wenn nun der Groschen gefallen ist, und du dir die erste Funktion zur Ausgabe erstellt hast, beherzige den Rat von Combie und schau dir mal die Print.h/Print.c an. Die sollst du wirklich beerben, denn die nimmt dir schon einiges ab: einen string in einzelne zeichen zerlegen, dec/hex/bin etc.

Ja, so wollte ich die Struktur von den Funktionsübergabewerten.
Ich denke ich höre das klicken in der Ferne.... den Zähler/Wert braucht es also in der "enum" sodass C weiß mit was da überhaupt herumgeworfen wird Dateitypmäßig.

Und wegen der Print.h/Print.cpp - ich war bis dato nur in der Lage Zahlen auf meinem MatrixDisplay auszugeben und wollte mich nun an die String Ausgabefunktion machen. Und ihr habt beide recht, da sind interessante Teile drin die ich gut gebrauchen kann. Nun muss ich nur noch richtig verstehen wie ich das passend aufrufe.
Ich werde mich dann mal Versuchen da reinzulesen und die Print.h/Print.cpp zu verstehen.

Danke

sodass C weiß

nein, Vorsicht. Brauchst du nicht zwingend in c++

Blättere noch mal zurück und kopier dir das mal in deine IDE.

du könntest auch so schreiben:

enum {ALIGN_LEFT, ALIGN_RIGHT, ALIGN_MIDDLE, ALIGN_MAGIC };

dann ist ALIGN_MAGIC halt 3.
Das was ich im Sketch daneben geschrieben habe sind Kommentare und hätten dir helfen sollen zu verstehen, welche Aufzählung welchen Wert erhält, bzw. wie du explizit Werte angeben kannst. Das sind keine Wertezuweisungen, das wurde nur bei ALIGN_MAGIC gemacht!

Ich werde mich dann mal Versuchen da reinzulesen und die Print.h/Print.cpp zu verstehen.

Eigentlich brauchst du die nicht zu verstehen...
:o :o :o

Einfach nur erben und die virtuelle Methode write() implementieren.
Schon steht dir das ganze Print Interface für deine Matrix zur Verführung.

Und das Interface kennst du ja schon, von Serial usw.

Und diese write() Methode ist für ein Zeichen. Du musst also nur beschreiben was mit einem Zeichen geschehen soll. Alles darüber nimmt dir die Print Klasse ab.

Dinge wie die Ausrichtung kann man dann noch selbst implementieren (indem vorne oder hinten entsprechend Leerzeichen ausgibt), aber es gibt keinen Grund wegen HEX oder DEC noch mal das Rad neu zu erfinden.