Paralleler Funktionsaufruf / Mehr als ein Rückgabewert

Hi zusammen,

ich brauch mal wieder einen Denkanstoß... ich hoffe ich kann mein Problem eindeutig beschreiben.

Angenommen, ich habe folgendes Szenario:

"meinefunktion()" benötigt zwei oder mehr Werte, die nur von "nebenfunktion()" berechnet werden können. Da eine Funktion ja immer nur einen Rückgaberwert haben kann, "nebenfunktion()" aber in dem Fall zwei oder mehrere Rückgabewerte bereitstellen soll, habe ich es aktuell so gelöst, dass die Rückgabewerte in globale Variablen geschrieben werden, um dann wieder in "meinefunktion()" zu Verfügung zu stehen.

Das funktioniert soweit auch prima.
Aber: Ich denk mir gerade, ob es nicht zu Problemen führen kann, wenn zeitgleich 10 Personen den Button 1 drücken und 10 Personen den Button 2? Kommt der liebe ESP dann nicht evtl. durcheinander, weil die globalen Variablen dann zeitgleich mit unterschiedlichen Werten befüllt werden? Ich behaupt jetzt einfach mal ja - und dass das so auch schlecht gelöst ist.

Aber: Ich komme auf keine andere Lösung grad. Am besten wäre es natürlich, wenn eine Funktion auch einen Array als Rückgabewert liefern könnte, aber Google hat mir grad schon "Nein" gesagt.

Ach ja: In meinem Fall wollen wir annehmen, dass "nebenfunktion()" an verschiedenen Stellen des Sketchs benötigt wird und daher die darin enthaltenen Berechnungen nicht direkt in "meinefunktion()" integriert werden sollen. Denn dann wäre es ja einfach :wink:

Hier mal ein stark vereinfachtes Beispiel:

#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);

#include <ArduinoJson.h>

// Verbindungskrimskrams
unsigned long connTimer=0;
unsigned long dauer = 10000;
const char ssid[14] = "Meine SSID";
const char password[21] = "MeinPasswort";

// Hier sehe ich das Problem, dass diese Variablen 
// durch zeitliche Überschneidungen beim Funktionsaufruf u.U. falsch/durcheinander befüllt werden
uint16_t ergebnis_eins = 0;
uint16_t ergebnis_zwei = 0;


static const char indexPage[] PROGMEM = R"=====(
<!doctype html>
<html lang="de">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
  </head>
  <body>
    <button type='button' onclick='meinefunktion(2, 3);'>Button 1</button>
    <button type='button' onclick='meinefunktion(4, 5);'>Button 2</button>
  </body>
</html>

<script>
function meinefunktion(wert_eins, wert_zwei) {
  var xmlhttp;
  var url="/meinefunktion?wert_eins="+wert_eins+"&wert_zwei="+wert_zwei;
  xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function(){
      if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
          let obj = JSON.parse(xmlhttp.responseText);
          console.log(obj.rueckgabewert_eins);
          console.log(obj.rueckgabewert_zwei);     
      }
  }
  xmlhttp.open("GET", url, true);
  xmlhttp.send();
}
</script>

)=====";


void display_root() {
  const char * httpType PROGMEM = "text/html";
  server.send_P(200,  httpType, indexPage);
}


void setup() {
  delay(2000);
  Serial.begin(115200);
  
  // Erstverbindung aufbauen
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  connTimer = millis();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println(WiFi.localIP());
  
  server.on("/", display_root);
  server.on("/meinefunktion", meinefunktion);
  
  server.begin();
}

void loop() {
  server.handleClient();
}


void meinefunktion() {
  StaticJsonDocument<48> doc;
  String output="";
  
  uint16_t wert_eins = server.arg("wert_eins").toInt();
  uint16_t wert_zwei = server.arg("wert_zwei").toInt();
  
  // Berechnungen in Nebenfunktion durchführen
  nebenfunktion(wert_eins, wert_zwei);

  // Mit Ergebnissen aus Nebenfunktion weiterarbeiten
  uint16_t rueckgabewert_eins=ergebnis_eins +10;
  uint16_t rueckgabewert_zwei=ergebnis_eins +20;

  doc["rueckgabewert_eins"] = rueckgabewert_eins;
  doc["rueckgabewert_zwei"] = rueckgabewert_zwei;

  serializeJson(doc, output);
  server.send(200, "application/json", output);
}


void nebenfunktion(uint16_t wert_eins, uint16_t wert_zwei) {
  // Ergebnisse in globale Variablen schreiben, sodass die Werte anschließend 
  // in "meinefunktion()" weiterverwendet werden können
  ergebnis_eins=wert_eins * 10;
  ergebnis_zwei=wert_zwei * 10;
}

Kann man sowas besser/smarter lösen?
Ich bin mit meinem Latein & meinem C++ am Ende.

LG Daniel

Ich denke du suchst Referenzen.

https://de.m.wikibooks.org/wiki/C%2B%2B-Programmierung/_Weitere_Grundelemente/_Referenzen

Ich weiß aber nicht ob ich dich richtig verstanden habe

@Plumps Hast du evtl. Zeit & Lust, mir direkt am geposteten Beispiel-Sketch zu zeigen, wie man das anwendet? Das wäre mega-nett :hugs:

Ansonsten googel ich mich da natürlich auch irgendwie rein.
Also nur, wenn das nicht mit zuviel Aufwand verbunden ist.

Kann ich, aber nicht jetzt. Bin unterwegs und mit Handy im Forum.

Aber vielleicht findet sich ein anderer

Ich will quasi, dass - auch wenn zeitgleich von mehreren Personen Button 1 und Button 2 gedrückt werden - jeder "Drücker" seine richtige Rückgabewerte erhält.

Eben das habe ich nicht verstanden. Wie können mehrere gleichzeitig ein Knopf drücken, bzw. was für ein Unterschied willst du da feststellen?

Warum lernst du nicht die Sprache, welche du verwendest?

Hier ein Vorschlag aus meiner Wühlkiste:

#include <Streaming.h> // die Lib findest du selber ;-)
Print &cout = Serial; // cout Emulation für "Arme"


struct Result
{
  double fläche;
  double umfang;
};

Result kreis(double radius)
{
  return {fläche: pow(radius,2)*PI, umfang: 2*radius*PI};
}


void setup() 
{
  Serial.begin(9600);
  cout << F("Start: ") << F(__FILE__) << endl;
  cout <<    kreis(2.2).fläche  << endl;
  cout <<    kreis(3.22).umfang << endl;


  Result k {kreis(7.7777)};
  cout <<    k.fläche << endl;
  cout <<    k.umfang << endl;
  
}

void loop() 
{

}

Edit:
Formel korrigiert, meinen Dank an den @Rentner

Dem ist nichts hinzuzufügen.

Gruß Tommy

Hi @combie

Danke für deine HIlfe.

Ich tu mich halt immer leichter mit "Learning by doing" und in dem Fall auch mit "asking" :wink:

In deinem Beispiel ist es ja aber so, dass - wenn ich "Fläche" und "Umfang" für einen Kreis mit Radius 2.2 wissen möchte, die Funktion zweimal aufrufen muss. Wenn wir nun aber davon ausgehen würden, dass die Berechnung in "Result kreis" 10 Sekunden dauern würde und ich nicht nur Fläche und Umfang sondern auch noch 10 andere Werte benötigen würde -> Dann müsste ich doch die Funktion kreis 12 mal aufrufen und hätte eine "Wartezeit" von 120 Sekunden.
Oder sehe ich das falsch?

Wenn ich nun aber alle 12 Werte sofort haben möchte, wäre rein performance-technisch dann die Variante mit den globalen Variablen überlegen, oder?

Unfug!
Was der Beweis dafür ist, dass du nicht aus Beispielen lernen kannst wenn dir die Grundlagen fehlen.
Bitte, lerne die C++ Grundlagen, sonst wirst du meine Beispiele nie verstehen.

Übrigens, damit würdest du dir mehr Gefallen tun, als mir.

Das halte ich für eine Lüge, mit der du dich hauptsächlich selber belügst.
Auch mich versuchst du so zu belügen.
Aber damit kommst du nicht bei mir durch.

Globale Variablen sind grundsätzlich, als zu vermeiden, einzustufen.
Man lebt meist besser, ohne sie.

Du hast nicht mal das Beispiel verstanden. Lies es Dir noch mal in Ruhe durch und probiere es aus. Lernen aus Beispielen funktioniert ohne Kenntnisse auch nicht.

Gruß Tommy

1 Like

Nein @combie

Nein?

Hmmm....
Mich deucht, du hättest eben den Nachweis selber geliefert.

Wie kommst du darauf. Sie wird im Beispiel doch nur einmal aufgerufen.

Mess doch mal wie lange es dauert, bei weitem nicht so lang.

Eine Wert Zuweisung dauert immmer gleich lang, ob local oder global. Warum sollte da ein Unterschied bestehen

struct Result
{
  double flaeche;
  double umfang;
};

Result kreis(double radius)
{
  Serial.println("Aufruf");
  delay(2000); // Simulation einer Berechnungszeit/Verzögerung
  return {flaeche: radius*PI/2, umfang: 2*radius*PI};
}


void setup() 
{
  Serial.begin(115200);
  delay(2000); // Dem Serial kurz Zeit geben...
  weiterrechnen(kreis(2.2).flaeche, kreis(2.2).umfang);
}

void loop() 
{

}

void weiterrechnen(double flaeche, double umfang) {
  double ergebnis = flaeche * umfang;
  Serial.print("Ergebnis:");
  Serial.println(ergebnis);
  
}

Ergibt bei mir am Serial Monior folgende Ausgabe

Aufruf
Aufruf
Ergebnis:47.77

D.h. dann mache ich hier irgendwas falsch, oder?

weiterrechnen(kreis(2.2).flaeche, kreis(2.2).umfang); // Hab ich hier einen Fehler drin?

In combies Beispiel gibt es aber keine Ausgabe Aufruf! Probiere doch erst mal das aus und verstehe es, bevor Du darin herum pfuschst.

Gruß Tommy

Ich wollte mit der Ausgabe Aufruf ja nur zeigen, dass die Funktion - wenn ich Fläche und Umfang brauche - doch zweimal aufgerufen wird. Werd doch nicht immer gleich persönlich Tommy :wink:

Ich bin mir ja auch sicher, dass ich bestimmt einen Denkfehler drin hab und man auch mit einmaligem Aufruf auf beide Werte zugreifen kann - aber hier häng ich grad noch gedanklich

Das war nicht persönlich, sondern sollte nur Deine Vorgehensweise kritisieren.
Es bringt nichts, ohne Verständnis darauf los zu ändern. Das nenne ich pfuschen.

Gruß Tommy

Die Logik, an sich, schreit einen doch an...
Wenn man zwei mal kreis aufruft, wird es auch zwei mal aufgerufen!
Wenn man es einmal aufruft, wird es nur ein mal aufgerufen.

Und jetzt die Frage:
Warum rufst du es zwei mal auf, wenn du es doch nur ein mal aufrufen möchtest?

Weil ich nicht weiß, wie ich mit einem einzigen Aufruf an Fläche und Umfang gleichzeitig rankomme.

In deinem Beispiel gibst du die Fläche ja folgendermaßen aus:

kreis(2.2).flaeche

Ich habe daraus schlussgefolgert, dass ich an den Umfang dann folgendermaßen rankomme:

kreis(2.2).umfang

Und schwups hab ich die Funktion kreis doch zweimal aufgerufen, oder?
D.h. ich komm grad nicht drauf, wie ich den Umfang bekomme, ohne nochmal die Funktion kreis() aufrufen zu müssen.