Void() mittels Variable aufrufen

Hallo liebes Forum!
Gibt es eine Möglichkeit, eine Fuktion mit Hilfe einer Variable aufzurufen?
Ich weiß nicht einmal ganau, wie man die Frage richtig stellt, aber ich versuche es mal.

byte = i;

void Programm"i"()

Bisher habe ich das entweder gelöst, in dem ich die Variable an eine Funktion übergeben haben

void Programm(byte i)
und dann mit
if() oder case i

Geht das auch, in dem man das "i" in den Programmnamen einbindet?
Ich hoffe das war irgendwie verständlich...

Liebe Grüße!
Tiff

Hallo Tiff

Nimm eine Suchmaschine deiner Wahl und befrage das WWW nach "function pointer array".

Nein!
Interpretersprachen können das, wenigstens manche.
C++ ist eine Compilersprache. Da gehen alle Funktionsbezeichner bei der Übersetzung dem linken verloren.

Ich empfehle dir ein schönes dickes modernes C++ Grundlagenbuch.

Ja!
Über Funktionszeiger.

1 Like

Dankesehr! :kissing:

Man kann das natürlich über Pointer (Funktionszeigern) machen
Pointer sind aber nicht so leicht verständlich und leider auch leicht falsch zu verwenden.

Man kann das aber leicher verständlich mit Switch case machen.

Außerdem kann man mehrere Funktionen mit gleichem Namen und verschiedener Anzahl von Parametern haben und je nach anzahl der übergebenen Parameteranzahl wird eine oder die andere Funktion ausgeführt.

Grüße Uwe

1 Like

Mit Funktionspointern kann man nicht viel falsch machen
Der Compiler kann das recht gut überwachen

Zudem ist der Umgang damit recht einfach. Wobei natürlich notwendig ist, dass man weiß was man tut. Aber das ist ja immer so.

using FuncPtr = void (*)();

void test()
{
  Serial.println('D');
}

FuncPtr func[]
{
  [](){Serial.println('A');},
  [](){Serial.println('B');},
  [](){Serial.println('C');},
  test,
};

void setup() 
{
  Serial.begin(9600);
  Serial.println("Start");
  
  func[2]();
  func[1]();
  func[2]();
  func[0]();
  func[3]();

  Serial.println("------------");

  for(FuncPtr f : func) f();

  Serial.println("------------");

  FuncPtr myFunc = func[1];
  myFunc();

  myFunc = test;
  myFunc();
  

}

void loop() {}
1 Like

ich fürchte du verrennst dich da. Und auch wenn die Antworten bisher gut waren, rate ich dir zeig dein "Problem" in einem vollständigen Sketch - dann bekommst du auch eine Antwort die du gut umsetzen kannst.

1 Like

< nitpicking>
Das stimmt so nicht ganz. Im Gegenteil erzeugt der C++ Compiler lange "mangled names" mit den Namen, der Aufrufkonvention und den Parameter- und Ergebnistypen bei Funktionen. Damit kann der Linker prüfen, ob alle Aufrufe kompatibel sind und dann erst werden die Namen weggeworfen.
< /nitpicking>

OK, wenn man Compilerlauf und Linken unterscheidet, hast du recht.
Nach dem Linken sind die Bezeichner allerdings weg, so dass ein void Programm"i"() zur Laufzeit nicht möglich ist, nicht möglich sein kann.

Ja, wenn man den Buildprozess nicht als ganzes betrachtet.

Ich habe das oben korrigiert

Hallo Tiff

Ich habe auch noch eine schnelle und einfache Lösung.

//make names
enum TaskName {Hund,Katze,Maus};
// make variables 
void (*task[])(){hund,katze,maus};
//make tasks 
void hund() 
{
  Serial.println(__func__);
}
void katze() 
{
  Serial.println(__func__);
}
void maus() 
{
  Serial.println(__func__);
}
// make application
void setup() 
{
  Serial.begin(115200);
  task[Hund]();
  task[Katze]();
  task[Maus]();
}
void loop(){}

... ist ein Makro , die beim (vor dem) Übersetzen gebildet wird, und --wie @paulpaulson zeigt-- den Satz

als unzutreffend brandmarkt. Das sind ähnliche Dinger wie die wie FILE DATE Makros.

Aber ob das eine Lösung für die Originalfrage ist, weiß ich nicht.

Und übrigens:
task[Hund]();
übersetzt bei mir nicht, aber
task[0]();
schon, und erzeugt wie erwartet die Ausgabe "hund"
Weil so die Funktion

void hund() {
  Serial.println(__func__);
}

definiert ist, die schon der Präprozessor in

void hund() {
  Serial.println("hund");
}

vorübersetzt. Das ist jetzt nix besonderes, und __func__ macht keine Zauberei, die man nicht auch von Hand hinkriegen könnte.

@combie's Lösung zeigt übrigens, dass man sogar in C++ Funktionen, die gar keinen Namen haben, in einem Array sammeln und und über den Index im Array aufrufen kann.

Ich brandmarke hier nix !!

Hier möchte ich mal auf __PRETTY_FUNCTION__ hinweisen.
Beim debuggen sinnvoll, ins Besondere, wenn man mit Template Funktionen oder sonstigen überladenen Funktionen/Methoden zu tun hat.

Nein, ist es nicht.
In C++ gibt es kein eval() Statement, auch nichts vergleichbares, mit dem man eine Funktion mit einem dynamisch erzeugten Namen aufrufen kann.
z.B. PHP als Interpretersprache kann das.

https://www.php.net/manual/de/function.eval.php

Hmmm....
Kein Disassembler oder Decompiler ist in der Lage, aus einem fertig gebautem C++ Programm die Funktion/Klassen/Variaben oder was auch immer für Bezeichner zu extrahieren. Eben weil nicht mehr im Maschinencode enthalten.

So ist es auch nicht möglich, zur Laufzeit einen Funktionsbezeichner zusammenzubasteln und die dazugehörige Funktion aufzutreiben.

Zudem:

Das durchnummerieren von Dingen ist eigentlich immer ein Kandidat für Arrays oder andere Container.

const char* thisFunctionName = __func__; // eigentlich unnötig, macht aus einem Macro eine typisierte Konstante 

Wenn es nicht wegen Nichtbenutzung wegoptimiert wird, ist der Funktionsname zur Laufzeit schon Teil des Maschinencodes.

Das ist allerdings richtig.

Aber, wie gesagt, hat mit der eigentlichen Frage nicht viel zu tun.

Doch, genau das war die Eingangsfrage!


Ich gönne dir deine statischen Zeichenketten!
Wenn sie wegoptimiert werden ist mir das auch recht.

Nutzlos sind sie!
Zumindest im Falle das TO sind sie nutzlos.
Die Zeichenketten stecken in der für Daten zuständigen Section und nicht in der Code Section.
Es gibt keine Möglichkeit von der jeweiligen Zeichenkette auf den Ort der Funktion zu schließen.
Einzig in der Funktion ist die Zeichenkette nutzbar.

Danke, sind aber nicht meine, sondern die von @paulpaulson.

Dass man mit dem Namen einer Funktion als Text nicht viel anfangen kann, außer ihn auszudrucken, stimme ich dir völlig zu.
Habe nur --speziell bei dir, lieber @combie-- etwas pedantisch auf deine Aussage reagiert, dass der Name einer Funktion zur Laufzeit nicht mehr vorhanden ist.

Dein Array von namenlosen Funktionen, die also nur über ihren Index im Array erreichbar sind, halte ich für die Lösung, wenn nicht "was willst du eigentlich?" die richtige Antwort ist.

Das war der Sinn und Zweck.

Speziell bei mir.... OK, warum auch nicht....

Vielleicht ist dir ja als selbsternannter Pedant aufgefallen, dass ich von Anfang an das Wort Bezeichner verwendet habe. Oder auch nicht aufgefallen...

Leider verwendest du durchgehend das Wort Namen, oder Name.

Hier möchte ich dich darauf hinweisen, dass das, je nach Grad der Pingeligkeit, nicht das gleiche ist, selbst wenn es ähnlich ist. Selbst die gleiche Zeichenfolge.

Über den Bezeichner haben wir Zugriff auf die gewünschte Funktion, zur Compilezeit, können sie nutzen, oder einen Zeiger darauf ermitteln.

Über den Namen, welcher in irgendeinem C-String rum dümpelt, können wir zur Laufzeit nichts ermitteln. Wir können ihn ausgeben und ihn in den Programmquelltexten suchen. Aber wir haben keinen direkten Bezug zur Funktion. Keinen Zeiger, kein Aufruf.

Hier sieht der Combie Pingel den Unterschied zwischen Name und Bezeichner.
Das ist auch der Grund warum ich über Bezeichner rede und geredet habe.

Nein das wurde nicht gebrandmarkt.
Die Bezeichner gehen beim Build Prozess verloren, selbst wenn die Namen in irgendwelche Strings kopiert werden.
Es geht die Möglichkeit verloren, vom Bezeichner aus, die Funktion zu erreichen, zu nutzen.

Der Bezeichner ist der "Anfass", der Name ist Schall und Rauch.

Die Frage ist beantwortet, allerdings habe ich noch nicht alle Lösungen durchprobiert um zu sagen, welche mir an meisten zusagt.
Und das mehrere richtig sind, wird das mit dem markieren schwer....

Und....
Auf den Handy einen anderen Account als auf den PC.
Und ich erinnere mich schon...

Trotzdem

Liebe Grüße!
Tiff