Template-struct mit Denkfehler

Ich stehe mal wieder auf der Leitung, denke ich.

Ich möchte eine template-struct für Variablen bauen, die so zu verwenden ist, wie es die Variable alleine wäre, aber zwingend ein weiteres Datenfeld enthält. Also habe ich definiert:

template<typename T> class SetP {
protected:
  T value;
  const char *name;
public:
  SetP() = delete;
  explicit SetP(const char *n) : name(n) {}
  T& operator T() { return value; }
  const char *getName() { return name; }
};
  • es gibt nur den Konstruktor mit dem const char*-Parameter, der leere geht nicht
  • wenn eine Instanz im Kontext des Typs der eingeschlossenen Variablen verwendet wird, soll die Referenz auf diese Variable verwendet werden.

Eine Anwendung wie

SetP<uint8_t> hystSteps("CV5");

bringt den Compiler aber zum Husten - markiert ist dabei das "CV5":

expected a type specifier

Warum?

1 Like

Das hat zwar wahrscheinlich nichts mit der Fehlermeldung zu tun, ich sehe aber nichts, was value setzt.

Gruß Tommy

Da noch nichts, es sollte irgendwo dann hystSteps = 5; stehen können. Theorie: hystSteps im int-Kontext benutzt sollte die am besten passende Konversion benutzen, also uint8_t& operator uint8_t().

Falsch!

Besser:

operator T() { return value; }

Oder:

&operator T() { return value; }

Nach der Änderung kompiliert das bei mir.
UNO C++23

Aber das funktioniert dann nicht für die Verwendung als lvalue, oder? Also hystSteps = 5; geht nicht, nur int werWeissDas = hystSteps;?

[Edit] zu schnell geantwortet: &operator T() könnte es tun.

[Edit 2] Nope, der Fehler bleibt. C++11, übrigens (ESP8266)

Wenn du hystSteps als lvalue nutzen möchtest, wirst du den Zuweisungsoperator überschreiben dürfen.
Der Cast Operator hilft dir nur bei der Verwendung als rvalue.

ESP ist doch auch schon bei C++14 oder C++17 ? ! ?

Okay, verstanden. Bleibt trotzdem die Frage, wieso der Compiler die Deklaration anmotzt?

Tut er bei mir nicht!

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

template<typename T> class SetP {
protected:
  T value;
  const char *name;
public:
  SetP() = delete;
  explicit SetP(const char *n) : name(n) {}
  &operator T() { return value; }
  T operator =(const T&v) 
  { 
    value = v;  
    return value; 
  }
  const char *getName() { return name; }
};

SetP<uint8_t> hystSteps("CV5");


void setup() 
{
  Serial.begin(9600);
  cout << F("Start: ") << F(__FILE__) << endl;
  cout << hystSteps.getName() << endl;

  hystSteps = 5;
}

void loop() 
{

}

Geht sauber durch

1 Like

:open_mouth:
Interessant - wenn ich die Deklaration wie Du auf der globalen Ebene mache, geht es tatsächlich genauso fehlerfrei. Was ich bisher verschwiegen hatte - weil ich dachte, dass das irrelevant sei - ist, dass die Deklaration bei mir innerhalb einer weiteren struct-Definition steht:

template<typename T> class SetP {
protected:
  T value;
  const char *name;
public:
  SetP() = delete;
  explicit SetP(const char *n) : value(0), name(n) {}
  T& operator T() { return value; }
  T operator=(T inv) { return (value = inv); }
  const char *getName() { return name; }
};
SetP<uint8_t> th("bla");   // <===== Das geht durch!
struct SetData {
  uint16_t magicValue;    
  SetP<uint8_t> hystSteps("CV5");  // <=== Das hier aber nicht!
...
} settings;

Darum sagen wir auch immer: Testbares Minimalbeispiel posten!
Genau aus dem Grund.
Wegen den Nebelkerzen.

SetP<uint8_t> hystSteps{"CV5"};  // <=== Das hier schon!

Nochmal :open_mouth: - {} ist doch ein Initialisierungsausdruck, wohingegen der Konstruktor ein Aufrufargument braucht, also ()?

Nein!
Schließlich hat dein Konstruktor eine Initialisierungsliste!

Strukturen und Arrays werden initialisiert, drum die geschweiften Klammern.

:+1:

Danke! (mal wieder)

Kurz nachgesehen, in mich gegangen und überprüft....

Die korrekte Begründung lautet so:
Dein Problem ist, dass

SetP<uint8_t> hystSteps("CV5");

nicht als Definition einer Variablen vom Compiler erkannt wird, sondern als Methoden Prototype, als Vorwärtsreferenz.
Darum wird auch ein Datentype in der runden Klammer verlangt.
Das ist natürlich nicht das, was du dir wünscht.

Bei Verwendung der geschweiften Klammer MUSS es eine Instanziierug einer Variablen sein. Es kann kein Methoden Prototype sein.

Wieder was gelernt!

Ich habe die Idee aber inzwischen wieder ad acta gelegt, denn wenn ich die struct settings binär wegschreibe, wandern natürlich alle name-Einträge mit und blähen das Binärfile auf. Da muss ich doch die Namensliste separat im Code ablegen und Stück für Stück abarbeiten.

Ein paar Infos zum Hintergrund: settings sind die Daten, die mein NodeMCU beim Booten liest, die Einstellung erfolgt aber über eine HTML-Seite im LittleFS, die selbst zwar statisch ist, aber ein Javascript-File inkludiert, in dem die ganzen Einstellungen als document.formular.CVxx.value="..."; abgelegt werden. Ich will also immer beides schreiben, damit beim nächsten Aufruf der Konfigurationsseite die aktuellen settings voreingestellt sind, ich aber nicht immer das Javascript beim Starten neu parsen muss..

Ich auch, zumindest mal wieder etwas interessantes geübt.
Das ist gut.

Schön, mal den Hintergrund zu hören....

Ganz habe ich nicht verstanden, was du da tun möchtest.
Wenn ich das so höre, denke ich an an das "Dependency injection Pattern".

Aber ganz einfach wird das nicht werden, da C++ kein serialize() wie z.B. PHP kennt,

Das serialize()/deserialize() könnte man evtl. über JSON, CSV oder snprintf/sscanf basteln oder wenn man auf der gleichen Architektur bleibt auch binär das Array of struct/class speichern/einlesen.

Gruß Tommy

Und letzteres mache ich. Die Javascriptversion dient dazu, die HTML-Konfigurationsseite statisch ausliefern und trotzdem die Werte einspielen zu können.

Ich mache so was bei der Speicherung der Parameter für meinen Weihnachtsstern.
Evtl. kannst Du ja ein paar Anregungen bekommen.

Gruß Tommy

Danke, ich habe mir das (erstmal oberflächlich) angesehen: viele Wege führen nach Rom! Ich habe meine Variante ausprobiert, weil mir das häufige "String html = ... + ... +..." auf den Keks ging :wink: und ich so Daten, Funktion und Präsentation trennen können müsste. Ich kann das Ergebnis dann gerne mal hier oder nebenan vorstellen, wenn es wie gewünscht geklappt hat.