Wie ruft man ein Array mit template Klasse mit Objekten auf?

Hallo,

Ich habe mich in den letzten Tagen fast nur noch mit template Klassen beschäftigt und endlich die Initialisierung eines Arrays hinbekommen. "Dummerweise" gleich mit 3 verschiedenen Syntaxen, nirgends meckert der Compiler. Was mich ehrlich gesagt etwas verwundert.

Die Einzelinitialisierung in Zeile 51,52 mit ihren Aufrufen in Zeile 75,76 funktionieren. Ich möchte jedoch alles in einem Array behandeln. Der Aufruf in Zeile 78 des Array scheitert. Ich weiß auch nicht wie ich dem noch einen unbekannten Datentyp mitgeben soll. Er meckert das ihm ein template Argument fehlt. Aber was für eins?

error: missing template arguments before 'Steuerung'

   array2[0].inits();      // Syntax?

Sketch:

/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.8
  avr-gcc 7.3.0 mit -std=gnu++17
  Arduino Mega2560
  29.04.2019

  GPA.0 ... GPA.7 => Pinnummer 0-7
  GPB.0 ... GPB.7 => Pinnummer 8-15
*/

#include <Wire.h>
#include <docMCP23017.h>

const byte MCP_I2C_ADR = 0x20;

InputPullup Taster_11 (MCP_I2C_ADR, 0);
InputPullup Taster_22 (MCP_I2C_ADR, 1);
Output Led_11 (MCP_I2C_ADR, 8);
Output Led_22 (MCP_I2C_ADR, 9);

// *******************************************************************************

template <typename I, typename O, typename T>
class Steuerung
{    
  private:
  I objT;
  O objL;
  T intervall;
    
  public:
  // Konstruktor
  Steuerung(I &pT, O &pL, T iv) :
  // Initialisierungsliste
  objT{pT},
  objL{pL},
  intervall{iv}
  {}
    
  // Methoden 
  void inits ()
  {
    objT.init();
    objL.init();
  }
};

// *******************************************************************************

//Steuerung steuerung1 {Taster_11, Led_11, 500};   // funktioniert
//Steuerung steuerung2 {Taster_22, Led_22, 500};   // funktioniert
/*
template <typename T>
T array1[] = {
  {Steuerung (Taster_11, Led_11, 500)},   // Parameter laut Initialisierungsliste
  {Steuerung (Taster_22, Led_22, 250)},   // Taster, Led, Blinkintervall
};
*/
template <typename T>
T array2[] = {
  {T (Taster_11, Led_11, 500)},   // Parameter laut Initialisierungsliste
  {T (Taster_22, Led_22, 250)},   // Taster, Led, Blinkintervall
};
/*
template <typename T>
T array3[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};
*/
void setup(void) {
  Wire.begin();
  
  //steuerung1.inits();   // funktioniert
  //steuerung2.inits();   // funktioniert

  array2[0].inits();      // Syntax?
  
  /*
  // das ist das Ziel //
  template <typename T>
  for (T &k : array2) {
    k.inits();
  }
  */
}

void loop(void) {

  if (Taster_11.read() ) {
    Led_11.setHigh();
  }
  else {
    Led_11.setLow();
  }

  if (Taster_22.read() ) {
    Led_22.setHigh();
  }
  else {
    Led_22.setLow();
  }
}

docMCP23017.zip (3.54 KB)

ach so, falls jemand denkt

Steuerung array2[0].inits();      // Syntax?

habe ich natürlich als erstes probiert. ... error: expected initializer before '.' token

template <typename T>
T array2[] = {
  {T (Taster_11, Led_11, 500)},   // Parameter laut Initialisierungsliste
  {T (Taster_22, Led_22, 250)},   // Taster, Led, Blinkintervall
};

Mir war bisher nicht klar, dass man Templates auch für Arrays verwenden kann...

Und ja, ich sehe so keine Chance den Type zu spezifizieren.

Also wohl ein Nogo!

Hallo,

laut dem was ich in letzter Zeit alles gelesen habe kann man wohl templates für alles mögliche und unmögliche verwenden. :wink: Ob template Arrays wirklich funktionieren kann ich nicht sagen. Aufrufen kann man sie wie man sieht noch nicht. Aber erstellen kann man sie scheinbar erstmal. :grin: Aber gut, hier sind verschiedene Objekte im Spiel. Mit üblichen Datentypen mag das funktionieren.

Woran ich Anfangs gescheitert bin und bis heute nicht verstehe ist folgendes.
Für Einzelinitialisierungen benötige ich kein template. Das frisst er auch so.

//Steuerung steuerung1 {Taster_11, Led_11, 500};   // funktioniert
//Steuerung steuerung2 {Taster_22, Led_22, 500};   // funktioniert

Also denkt man, okay, dann muss damit auch ein normales Array funktionieren

Steuerung array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

Das mag er jedoch nicht. Das wäre zu einfach gewesen. Das wurmt mich.

docMCP23017_Lib_Entwicklungstestcode_Steuerung_005:66:1: error: template placeholder type 'Steuerung' must be followed by a simple declarator-id

Steuerung array2[] = {

...\docMCP23017_Lib_Entwicklungstestcode_Steuerung_005.ino:25:7: note: 'template<class I, class O, class T> class Steuerung' declared here

class Steuerung

docMCP23017_Lib_Entwicklungstestcode_Steuerung_005:66:18: error: 'array2' declared as array of 'Steuerung'

Steuerung array2[] = {

Bibliothek Wire in Version 1.0 im Ordner: ...\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.209\libraries\Wire wird verwendet
Bibliothek docMCP23017 im Ordner: ...\libraries\docMCP23017 (legacy) wird verwendet
exit status 1
template placeholder type 'Steuerung' must be followed by a simple declarator-id

Hallo,

vielleicht kannst du mir noch folgende Frage beantworten die auch offen geblieben ist. Wenn man template Klassen erstellt hat, dann muss man doch beim Aufruf die Parameter in spitzen Klammern angeben. Darf man hier nicht machen. Auch die Reihenfolge bleibt gleich zu Initialisierungen mit Klassen ohne template.

Erwartet habe ich folgenden erforderlichen Syntax mit template Klassen:

Steuerung <Taster_11, Led_11, 500> steuerung1; 

// wie gesagt, gültig ist nur der
Steuerung steuerung1 (Taster_11, Led_11, 500);

Liegt das an unserem c++17 Compiler? Kann auch sein ich sehe momentan den Wald nicht mehr.

Das mag er jedoch nicht. Das wäre zu einfach gewesen. Das wurmt mich.

Natürlich mag er das nicht!

Denn du spezifizierst die Template Parameter nicht!
Hier bitte für a,b,c die richtigen Typen einsetzen.

Steuerung<a,b,c> array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

KA, ob das in deinem Sinne ist, oder gar funktioniert.

Oder mit Alias:

using Strg = Steuerung<a,b,c>;
Strg array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

Das müsste auch gehen:

Steuerung array2[] = {
  Steuerung(Taster_11, Led_11, 500),   // Parameter laut Initialisierungsliste
  Steuerung(Taster_22, Led_22, 250),   // Taster, Led, Blinkintervall
};

Gruß Tommy

Steuerung <Taster_11, Led_11, 500> steuerung1;

Das ist ja auch unfug!

denn 500 ist ganz sicher kein typename!
Der müsste an der Stelle unsigned long oder uint32_t heißen.

Hallo,

Tommy, leider nicht. :confused: Hatte ich die letzten Tage schon versucht

error: template placeholder type 'Steuerung' must be followed by a simple declarator-id

note: 'template<class I, class O, class T> class Steuerung' declared here

error: 'array2' declared as array of 'Steuerung'

combie, das funktioniert

Steuerung <InputPullup, Output, unsigned long> array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

-------------------------------
 
  array2[0].inits();
  array2[1].inits();

Nur ist das nicht das was ich wollte. Den Datentyp vom Objekt wollte ich nicht mit angeben, den soll er selbst bestimmen. Wenn ein Taster zwischendurch nicht vom Typ/Klasse InputPullup sondern nur Input ist, dann scheitert der gesamte Aufruf.

Der Traum war wie hier, wo er es selbst bestimmt

template <typename T>
void init_Ref (T &pL)
{
  pL.init();
}

---------------------------------

init_Ref(Taster_22);
init_Ref(Led_11);

Die eigentlich Frage lautet. Warum möchte er bei Arrays den Datentyp wissen und sonst nicht`?

Doc_Arduino:
ok.

Nur ist das nicht das was ich wollte. Den Datentyp vom Objekt wollte ich nicht mit angeben, den soll er selbst bestimmen. Wenn ein Taster zwischendurch nicht vom Typ/Klasse InputPullup sondern nur Input ist, dann scheitert der gesamte Aufruf.

Du könntest InputPullup von Input erben lassen oder beiden einen gemeinsamen Vorfahren geben. Über virtuelle Methoden könnte es unterschieden werden.

Gruß Tommy

Hallo,

wenn du einmal meine Lib anschauen würdest?
Ich habe darin eine gemeinsame Klasse "Basis".
Davon erben alle. Input, InputPullup und Output.
Alle 3 haben unterschiedliche init Methoden und paar andere die sich unterscheiden.

Das sieht eher nach C++14 variable templates als nach Klassen aus:

Willst du decltype um den Typ einer Variablen zu bekommen? Damit kannst do was machen:

template <typename T>
class Test
{
public:
 Test(T test) : test(test)
 { }

 T test;
};

Test<int> test1(5);
Test<int> test2(10);

decltype(test1) arr[] = { test1, test2 }; //<---- Magie hier

void setup() 
{
 Serial.begin(9600);

 for (auto &e : arr)
 { 
 Serial.println(e.test);
 }
}

void loop() 
{
}

@Doc_Arduino: Sorry, ich hatte das Zip übersehen.

Aber dann sollte doch auch folgendes mit Polymorphy gehen:

Steuerung <ReadInput, Output, unsigned long> array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

Gruß Tommy

Hallo,

Tommy,
den Anhang übersieht man leicht. Alles gut. Allerdings hat die "Zwischenbasisklasse" ReadInput (komischer Name ich weiß) keine init Methode. Die darf es erst in den davon vererbten Klassen Input und InputPullup geben.
Oder muss ich die init in der Basis erstellen und dann in den Vererbten überladen?
Aber ob er dann die Richtige init aufruft ist fraglich?
Weil ReadInput kann nicht auf Input oder InputPullup zugreifen. Klappt nur umgekehrt.
Oder setzen hier die mir noch nicht geläufigen virtuellen Methoden an? Mit dem eigentlich nicht möglichen Vorwärtszugriff.

Ich schaue mir mal Sereniflys Antwort an ...

Aber ob er dann die Richtige init aufruft ist fraglich?

Dafür wurden die schon genannten virtuellen Methoden erfunden.

Basis hat doch eine init(). Damit hat ReadInput die auch. Schief geschaut.
Ich würde die in Basis aber virtuell ergänzen:

virtual  void init (void) = 0;

Damit würden Basis und ReadInput abstrakte Klassen werden und von Output, Input und InputPullup können dann Instanzen erzeugt werden.

Gruß Tommy[/code]

Hallo,

Serenifly,
wenn ich einen Datentyp ändere kompiliert es nicht mehr.
Das würde laut meinem Verständnis auch bei meinem Objekten so sein.

Test<byte> test1(5);
Test<int> test2(10);

Ich schaue mir mal virtuelle Methoden an ...

Nervig wäre dann immer noch die Datentypen für das template mitschleppen zu müssen.

#include <Wire.h>
#include <docMCP23017.h>

const byte MCP_I2C_ADR = 0x20;

InputPullup Taster_11 (MCP_I2C_ADR, 0);
InputPullup Taster_22 (MCP_I2C_ADR, 1);
Output Led_11 (MCP_I2C_ADR, 8);
Output Led_22 (MCP_I2C_ADR, 9);

// *******************************************************************************

template <typename I, typename O, typename T>
class Steuerung
{    
  private:
  I objT;
  O objL;
  T intervall;
    
  public:
  // Konstruktor
  Steuerung(I &pT, O &pL, T iv) :
  // Initialisierungsliste
  objT{pT},
  objL{pL},
  intervall{iv}
  {}
    
  // Methoden 
  void inits ()
  {
    objT.init();
    objL.init();
  }
};

// *******************************************************************************

Steuerung <InputPullup, Output, unsigned long> array2[] = {
  {Taster_11, Led_11, 500},   // Parameter laut Initialisierungsliste
  {Taster_22, Led_22, 250},   // Taster, Led, Blinkintervall
};

void setup(void) {
  Wire.begin();
   
  for (Steuerung <InputPullup, Output, unsigned long> &k : array2) {
    k.inits();
  }
}

void loop(void) {

  if (Taster_11.read() ) {
    Led_11.setHigh();
  }
  else {
    Led_11.setLow();
  }

  if (Taster_22.read() ) {
    Led_22.setHigh();
  }
  else {
    Led_22.setLow();
  }
  
}

Ich muss deswegen nochmal auf die Kleine template Funktion zurückkommen.
Warum benötigt die keine extra Datentypangabe?

template <typename T>
void init_Ref (T &pL)
{
  pL.init();
}
---------------------------------
init_Ref(Taster_22);
init_Ref(Led_11);

und das benötigt auch keine extra Datentypangabe

Steuerung steuerung1 (Taster_11, Led_11, 500);

Warum benötigt dann ein Array eine Datentypangabe für die template Klasse?
Das ist der Knackpunkt den ich absolut nicht verstehe! Hier scheitert all meine Logik.

Virtuelle Methoden und Polymorphie musst du dir unbedingt anschauen wenn du das bisher nicht gewusst hast. Das sollte eigentlich vor Templates kommen und ist ein zentralen OOP Thema

wenn ich einen Datentyp ändere kompiliert es nicht mehr.

Klar. Das sind ja dann zwei verschiedene Klassen und die kannst du nicht in ein Array packen

Warum benötigt dann ein Array eine Datentypangabe für die template Klasse?

Wie gesagt das ist keine Klasse sondern eine Template Variable

Hallo,

den Unterschied verstehe ich leider immer noch nicht. Ich würde meine Fragen ab hier nur wiederholen.
Vielleicht komme ich später nochmal darauf zurück.
Ich mache mich erstmal virtuell schlau.

Zum Zeitpunkt ein großes Zwischendanke für alle Beteiligten. Immerhin gibts erstmal eine Lösung.

Du willst wahrscheinlich eher sowas in der Tat:

class Base
{
public:
  virtual void run() = 0;
};

class Derived1 : public Base
{
public:
  void run()
  {
    Serial.println("Klasse 1");
  }
};

class Derived2 : public Base
{
public:
  void run()
  {
    Serial.println("Klasse 2");
  }
};


Derived1 obj1;
Derived2 obj2;

Base* arr[] = { &obj1, &obj2 };

void setup()
{
  Serial.begin(9600);

  for (auto &obj : arr)
    obj->run();
}


void loop() 
{
}

Ich hatte halt gedacht dass Polymorphie inzwischen bekannt ist nachdem du dich hier einige Zeit mit OOP beschäftigt hast