Forum: Compiler & IDEs AVR C++ Template Klasse


von markus (Gast)


Lesenswert?

Hallo,

ich brauche eure Hilfe um den Knoten im meinem Kopf zu entwirren.

Ich wage gerade die ersten Schritte mit C++ auf dem AVR. Es stehen zwei 
Klassen für die Ausgabe von Werten zur Verfügung UART und LCD.  Über 
eine Template Klasse soll nun die Ausgabe gesteuert werden. Jede Klasse 
hat den Operator write implementiert. Mit Hilfe der Wrapper Klasse soll 
dann zur Laufzeit die Ausgabe auf das entsprechende Device umgeschaltet 
werden.
Der Compiler beschwert sich beim Bauen mit dieser Fehlermeldung
1
Error: cannot call member function `void UART::write(uint8_t) without object

Ich denke das Problem liegt daran, dass ich in der Wrapper methode die 
Klasse UART angebe anstelle der Instanz von UART. Wie komme ich jetzt an 
die Instanz von UART heran?

Ich hoffe, dass ich es möglichst verständlich erklärt habe. Vielleicht 
gelingt es ja jemanden meinen Knoten im Kopf zu entwirren. Oder gibt es 
noch einen einfacheren Ansatz zur Lösung dieses Problems?

merci für eure Inputs schon einmal!

markus
1
class UART0 {
2
  void write(uint8_t c) {
3
    write to uart
4
}
5
};
6
class LCD {
7
  void write{uint8_t c) {
8
    write to LCD
9
}
10
} ;
11
12
template <class T>
13
class Wrapper {
14
public:
15
  Wrapper(const T& device){}
16
  virtual void write(uint8_t){
17
    T::write();
18
  }
19
};
20
21
void main() {
22
UART uart0(9600);
23
Wrapper<UART> device(uart0);
24
device.write A ;

von Oliver S. (oliverso)


Lesenswert?

Bist du dir mit dem "virtual" in den Template sicher?

Oliver

von Maximilian (Gast)


Lesenswert?

Indem du dir im Konstruktor der Wrapperklasse eine Referenz auf das 
übergebene Objekt merkst.

von Marco M. (marco_m)


Lesenswert?

Du musst die Referenz auf die betreffende Objektinstanz im Konstruktor 
von Wrapper auch speichern, dann kommst du da auch wieder ran.
1
template <class T>
2
class Wrapper {
3
 T &m_device; 
4
public:
5
  Wrapper(T& device) : m_device(device) {}
6
  virtual void write(uint8_t){
7
    m_device::write();
8
  }
9
};

Aber Achtung: Wie du siehst habe ich hier kein 'const T&' mehr stehen. 
Warum? Die Methode write() die du in UART0 und LCD aufrufen willst sind 
nicht als const deklariert, aber nur solche Methoden kannst du mit einem 
konstanten Objekt aufrufen.

Ich verstehe allerdings nicht, warum deine ersten Schritte in C++ gleich 
mit Templates zu tun haben. Das ist so als würde man auf dem Hochseil 
laufen lernen.

von Karl H. (kbuchegg)


Lesenswert?

Marco M. schrieb:

> Ich verstehe allerdings nicht, warum deine ersten Schritte in C++ gleich
> mit Templates zu tun haben. Das ist so als würde man auf dem Hochseil
> laufen lernen.

Ich versteh in erster Linie nicht, wozu dieses Template überhaupt gut 
sein soll. Ob man die virtuelle FUnktion im Template hat, oder ob man 
UART0 und LCD von einer gemeinsamen OutputDevice Klasse herleitet, kommt 
sich aufs gleiche raus.
1
class OutputDevice
2
{
3
  public:
4
    virtual ~OutputDevice() {};
5
6
    virtual void Write( uint8_t value ) = 0;
7
};
8
9
class UART0 : public OutputDevice
10
{
11
  public:
12
    virtual void write( uint8_t c )
13
    {
14
      write to uart
15
    }
16
};
17
18
class LCD : public OutputDevice
19
{
20
  public:
21
    virtual void write( uint8_t c )
22
    {
23
      write to LCD
24
    }
25
};
26
27
28
void foo( OutputDevce* pWhere )
29
{
30
  pWhere->write( 'a' );
31
}
32
33
int main()
34
{
35
  UART0 theUart;
36
  LCD   theLcd;
37
38
  foo( &theUart );    // foo gibt jetzt auf der UART aus
39
  foo( &theLcd );     // und jetzt landet die Ausgabe von Foo auf dem LCD
40
}

stink normale Polymorphie.
Genau das richtige um ...
> ... dann zur Laufzeit die Ausgabe auf das entsprechende
> Device umgeschaltet werden.
Genau dafür ist sie erfunden worden.

von Stefan H. (stefan_h16)


Lesenswert?

markus schrieb:
> Mit Hilfe der Wrapper Klasse soll
> dann zur Laufzeit die Ausgabe auf das entsprechende Device umgeschaltet
> werden.

Ich glaube du Vererbung und virtuelle Methoden sind eher das wonach du 
suchen solltest.
1
class output
2
{
3
   public:
4
   virtual void write(char c);
5
}
6
class Uart : public output
7
{
8
   public:
9
   virtual void write( char c);
10
}
11
class LCD: public output
12
{
13
   public:
14
   virtual void write( char c);
15
}
16
17
18
void main()
19
{
20
 Uart u;
21
 LCD l;
22
 output * o;
23
24
 o = &u;
25
 o->write('a');
26
 o = &l;
27
 o->write('c');
28
}
(Entschuldigt bitte mein C++, ich programmiere zurzeit fast nur noch C).

Damit kannst du wirklich zur Laufzeit was umschalten. Templates sind 
Spielen nur während der Compilierung eine rolle.

Edit: Zu spät

von Klaus W. (mfgkw)


Lesenswert?

Nur, um mal (in diesem Fall) templates schlecht zu machen:
Mit einem Template schreibt man zwar einmal die Schablone hin, das spart 
Quelltext gegenüber zwei sehr ähnliche Klassen komplett zu formulieren.
Es spart aber keinen Programmcode.

Denn: Wrapper<UART> und Wrapper<LCD> sind zwei voneinander unabhängige 
Klassen, die der Compiler bei Bedarf aus der Schablone generiert - und 
zwar vollständig.

D.h. daß auch gemeinsamer Code der beiden zweimal vollständig im 
Programm vorhanden ist, wenn beide Klassen in einem Programm verwendet 
werden.

Zudem sind die beiden nicht wirklich zur Laufzeit umschaltbar, weil sie 
keine gemeinsame Basisklasse haben. Mal das eine und mal das andere z.B. 
als Parameter zu verwenden für dieselbe Funktion, geht nicht.

Das als zusätzliche Erklärung, warum es besser ist, die Gemeinsamkeit in 
eine Basisklasse zu packen und die Unterschiede in eine Ableitung.

Hast du allerdings vor, in je einem Programm immer nur die eine oder die 
andere Version zu verwenden, kann man sich ein template vorstellen.
Dann macht aber ein virtual keinerlei Sinn.

von Marco M. (marco_m)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Ich versteh in erster Linie nicht, wozu dieses Template überhaupt gut
> sein soll. Ob man die virtuelle FUnktion im Template hat, oder ob man
> UART0 und LCD von einer gemeinsamen OutputDevice Klasse herleitet, kommt
> sich aufs gleiche raus.

Oh, das ist im Grunde genommen keine unübliche, wenn auch eher 
fortgeschrittene Technik. Man macht so etwas um Typen die in der 
Klassenhierachie nicht verwandt sind mit einem gemeinsamen Interface 
auszustatten, in etwa analog zu Haskell's Typklassen. Aber das ist nicht 
direkt Anfängerstoff.

von Karl H. (kbuchegg)


Lesenswert?

Marco M. schrieb:

> Oh, das ist im Grunde genommen keine unübliche, wenn auch eher
> fortgeschrittene Technik. Man macht so etwas um Typen die in der
> Klassenhierachie nicht verwandt sind mit einem gemeinsamen Interface
> auszustatten, in etwa analog zu Haskell's Typklassen.

Das sagt mir jetzt nicht viel.
(Und Templates sind einer meiner schwachen Punkte)

Hast du da mal ein Schlagwort dafür?

von Marwin (Gast)


Lesenswert?

Marco M. schrieb:
> Oh, das ist im Grunde genommen keine unübliche, wenn auch eher
> fortgeschrittene Technik.

Karl-Heinz weiss, was Templates sind. Die Frage war, wozu sie in diesem 
konkreten Fall gut sein sollen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Marco M. schrieb:
>
>> Oh, das ist im Grunde genommen keine unübliche, wenn auch eher
>> fortgeschrittene Technik. Man macht so etwas um Typen die in der
>> Klassenhierachie nicht verwandt sind mit einem gemeinsamen Interface
>> auszustatten, in etwa analog zu Haskell's Typklassen.
>
> Das sagt mir jetzt nicht viel.
> (Und Templates sind einer meiner schwachen Punkte)
>
> Hast du da mal ein Schlagwort dafür?


Ist schon ok.
Nach ein paar Minuten nachdenken, denke ich weiß, wo da die Reise 
hingeht. Man will den Klassen keine gemeinsame Basisklasse unterjubeln, 
noch nicht mal in Form eines gemeinsamen Interfaces und macht sich ein 
spezialisierte Interfaceklasse(n) (mit gemeinsamer Basisklasse), welches 
die Umsetzung erledigt. Im Grunde 'so ähnlich wie ein PIMPL Idiom mit 
gemeinsamer Basisklasse'. Und die kann man natürlich dann auch in 
Templates verpacken, so dass man das nicht jedesmal selber neu schreiben 
muss.

Ja ok.
Bringt ihm aber in diesem Falle nichts.
Ich hätte gesagt: Machs erst mal so, wie man das in C++ macht. 
Gemeinsame Basisklasse und Polymorphie.

von Marco M. (marco_m)


Lesenswert?

Marwin schrieb:
> Marco M. schrieb:
>> Oh, das ist im Grunde genommen keine unübliche, wenn auch eher
>> fortgeschrittene Technik.
>
> Karl-Heinz weiss, was Templates sind. Die Frage war, wozu sie in diesem
> konkreten Fall gut sein sollen.

Das ist die Frage die ich beantwortet habe.

von Karl H. (kbuchegg)


Lesenswert?

Streitet euch doch nicht. :-)


Ich denke, wenn man diese Frage hier liest

> Der Compiler beschwert sich beim Bauen mit dieser Fehlermeldung
> Error: cannot call member function `void UART::write(uint8_t)’
> without object
>
> Ich denke das Problem liegt daran, dass ich in der Wrapper methode die
> Klasse UART angebe anstelle der Instanz von UART. Wie komme ich jetzt an
> die Instanz von UART heran?

dann brauchen wir uns nicht lange darüber unterhalten, was eventuell bei 
einem fortgeschrittenem Programmierer ein Template-Problem sein könnte. 
Da haperts schon an ganz anderer Stelle.

Er hat sich einfach nur verrannt und darauf gesetzt, dass Templates 
irgendwie magisch das tun, was er auf seinem Wunschzettel hat.
Das übliche leidige Problem: Im Stroustroup die ersten 400 Seiten 
übersprungen, weil "das brauch ich eh alles nicht".

von Klaus W. (mfgkw)


Lesenswert?

Nix für ungut, aber lies mal den Umschlag: der Mann heisst Stroustrup 
:-)

(nicht böse nehmen, aber die Steilvorlage war zu gut)

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> Nix für ungut, aber lies mal den Umschlag: der Mann heisst Stroustrup
> :-)
>
> (nicht böse nehmen, aber die Steilvorlage war zu gut)

LOL
Danke.

(wenn ich böse wäre, könnte ich jetzt sagen: Ich hab mich mehr um den 
Inhalt gekümmert als um den Umschlag :-)

von Edson (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> (wenn ich böse wäre, könnte ich jetzt sagen: Ich hab mich mehr um den
> Inhalt gekümmert als um den Umschlag :-)

Um ehrlich zu sein, den Eindruck machst du auch. ;)

von Klaus W. (mfgkw)


Lesenswert?

Karl Heinz Buchegger schrieb:
> (wenn ich böse wäre, könnte ich jetzt sagen: Ich hab mich mehr um den
> Inhalt gekümmert als um den Umschlag :-)

Wenn du böse wärst, hättest du gesagt, ICH hätte nur den Umschlag 
gelesen...

von Oliver S. (oliverso)


Lesenswert?

Klaus Wachtler schrieb:
> Hast du allerdings vor, in je einem Programm immer nur die eine oder die
> andere Version zu verwenden, kann man sich ein template vorstellen.

markus schrieb:
> C++ auf dem AVR

Das ist auf einem AVR mit knappen RAM in diesem Fall durchaus sinnvoll.

Oliver

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.