PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Klasse mit Stream-Funktionalität versehen?



7.e.Q
25-10-2006, 08:52
Hi Leute,

ich hab in meinem Programm eine Klasse, die über eine Print-Funktion (ähnlich printf) Texte in eine Datei schreibt (in meinem Fall ist diese Datei ein Socket, auf den sich jemand mit 'nem telnet aufconnected hat).

Diese Print-Funktion möchte ich nun insoweit ersetzen, daß die ganze Klasse als Stream genutzt werden kann:




CInfoServer Inf;

Inf.Print("Hallo Welt\n"); // aktuelle Vorgehensweise
Inf << "Hallo Welt" << std::endl; // erzielte Vorgehensweise



Wie macht man sowas?

Danke!

Grüße,
Hendrik

anda_skoa
25-10-2006, 18:58
Operator Overloading, d.h. man macht zwei operator Funktionen, die bei der Klasse als friend eingetragen sind und daher entweder auf die Daten selbst oder interne Formatierungsfunktionen zugreifen können (falls die Formatierungsfunktionen ohnehin public sind, entfällt das friend natürlich)

Alternativ (wenn man nur Output braucht) kann man auch eine Art toString() Methode implementieren und dann so streamen


stream << object.toString() << endl;


Ciao,
_

7.e.Q
26-10-2006, 08:06
Ich meinte das eigentlich eher so, daß meine Klasse als Ziel eines Streams angegeben werden kann, also als Alternative zu std::cout zum Beispiel.

anda_skoa
26-10-2006, 13:22
Ansich serh ähnlich.
Du brauchst nur anderen Typen in den Operatoren.

Siehe zum Beispiel den Qt Datentyp QStringList bzw dessen Basisklasse QValueList. Dort gibt es den operator<< um praktisch das selbe zu tun wie die Methode append()

Ciao,
_

locus vivendi
26-10-2006, 20:52
Wenn du kompatibel zu den Standard Iostreams sein willst, solltest du einen eigenen Ostream (bzw. Istream) oder einen eigenen Streambuffer schreiben, sprich von den Standard Klassen deine ableiten. Informationen dazu findest du hier:
http://www.angelikalanger.com/
Beispielcode auch hier: http://www.josuttis.com/
Boost.Iostreams hilft beim Implementieren: http://www.boost.org/libs/iostreams/doc/index.html
Hier findest du vielleicht auch Anregungen: http://cholm.home.cern.ch/cholm/misc/#filemm

7.e.Q
27-10-2006, 06:30
Ah Boost, cool, die hab ich eh in Verwendung. Schau ich mir mal an, die Links. Danke.

7.e.Q
30-10-2006, 09:03
Ist es denn nicht möglich, eine Klasse so zu designen, daß man eine Instanz davon direkt als Ziel für einen Stream nutzen kann?

Pseudo-Code:


class Cabc
{
/* */
}

int main()
{
Cabc cAbc;
cAbc << "Hallo" << " " << "Welt" << std::endl;
return 0;
}


Geht sowas nicht irgendwie?

Danke

guardian
30-10-2006, 09:59
Vielleicht suchst du nach den Templates (http://www.google.de/search?q=templates+%2B+c%2B%2B&hl=de&lr=&start=10&sa=N)

7.e.Q
30-10-2006, 11:02
Ich denke nicht. Dann müsste ich ja für jeden Typ, den der Stream bekommen könnte eine eigene Instanz erstellen. Nein, bitte lies noch einmal genau meine Anfrage!

Ich suche wirklich nur nach einer Möglichkeit, meine Klasse als Ziel eines Streams zu nutzen.

So sieht's momentan aus:


std::string Filter = "URGENT";
CInfoServer InfoServer;
InfoServer.MonPrt(Filter, "Hallo Welt\n");


So ist's wie ich's gern hätte:


CInfoServer InfoServer;
InfoServer << "URGENT" << "Hallo Welt" << std::endl;


Filter ist hier nur ein String, in dessen Abhängigkeit die Ausgabe meiner Klasse auf der Konsole eine bestimmte Formatierung erhält; für den eigentlichen Hintergrund meiner Frage also letztendlich irrelevant.
Was muss ich mit der Klasse anstellen, damit das geht?

anda_skoa
30-10-2006, 12:10
Ist es denn nicht möglich, eine Klasse so zu designen, daß man eine Instanz davon direkt als Ziel für einen Stream nutzen kann?


Dein Beispiel hat ansich nichts mit Streams zu tun, es wird lediglich der selbe Operator verwendet.

Wie schon gesagt kann jede Klasse den operator<< implementieren, nicht nur Streams (sind ja im Endeffekt auch nur Klasse).

Die Operatoren >> und << basieren im wesentlichen darauf, daß ihr Rückgabewert der als einer der Parameter übergebene Stream ist, daher kann man sie hintereinandere aufrufen.

Als eines der Beispiele für Klassen, die so einen Operator implementieren, hab ich ja schon QStringList aus Qt genannt. Vermutlich gibt es aber auch in anderen Bibliotheken Klassen, die die Streamsemantik für Daten-IO benutzen

Ciao,
_

7.e.Q
31-10-2006, 07:40
Aber ich müsste dann für jeden Datentyp einen eigenen Operator<< überladen, oder? Wie macht das denn zum Beispiel std::stringstream?

anda_skoa
31-10-2006, 11:53
Ja, das ist korrekt.

Bzw die Datentypen brauchen einen passenden Operator.

Ich glaube bei den Streams sind die Operatoren für die Basisdatentypen als alleinstehende Funktionen implementiert und im Falle von eigenen Klassen implementiert man die Operatoren dann in den Klassen bzw als Friendfunktionen.

Sieht man teilweise auch in der Qt Doku: QDataStream implementiert die Operatoren für die Basistypen (inklusive Qt Basistypern) und Qt Container haben dann meistens zusätzlich in ihren Headern die Operatorenfunktionen mit Ziel QDataStream

Im Grunde sind die beiden "Stream" Operatoren genau so behandelbar wie jeder andere Operator mit zwei Argumenten, also zB operator= usw

Ciao,
_

7.e.Q
31-10-2006, 14:44
Was bekomme ich denn für einen Typ zurück, beispielsweise von dem Operator std::string::operator<<(/* ... */); Das ist doch kein std::string, der da rauskommt, oder?

anda_skoa
31-10-2006, 15:27
Ah! :)

Wie bei jeder Funktion oder Methode legt die Signatur derselben den Rückgabewert fest.

Es hängt also davon ab, in welchem Kontext, bzw. zu welchem Zweck eine Funktion eingesetzt werden soll.

Zum Beispiel gibt der operator= normalerweise eine Referenz auf "this" zurück, damit man so etwas machen kann


a = b = c = d;


Ein operator<<, der für std::ostream implementiert ist, gibt als Resultat die Referenz auf den Stream zurück, den die Funktion als Parmater erhalten hat, damit man so etwas machen kann


cout << a << b << c;


Wenn du also eine Art Stringkonkatenation mit einem operator<< implementieren willst, würdest du vermutlich ein std:string& zurückgeben wenn der Operator das Ziel verändern soll, bzw. ein std::string wenn er ein neues Objekt erzeugen soll, daß eine Verbindung der beiden beteiligten Objekte ist.

Ciao,
_

7.e.Q
31-10-2006, 17:33
Achso. Heißt, um eine Print Funktionalität (s. obiges Beispiel) innerhalb meiner Klasse auf dieses << Format umzubauen, müsste ich einen

std::string& operator<<(const std::string& in);

implementieren, richtig?

locus vivendi
01-11-2006, 08:38
Achso. Heißt, um eine Print Funktionalität (s. obiges Beispiel) innerhalb meiner Klasse auf dieses << Format umzubauen, müsste ich einen

std::string& operator<<(const std::string& in);

implementieren, richtig?
Nein, es muss ein Objekt (oder Referenz auf Objekt) zurückgegeben werden welches den <<-Operator ebenfalls überlädt. Sonst funktioniert das Verketten der Aufrufe ja nicht. Schau dir doch einfach mal an wie die Funktionen für die Standardstreams aussehen. Und wie gesagt, ich würde mir auch überlegen, das so zu machen, das du std::ostream recyclest und nur einen eigenen Streambuffer schreibst. Sei dir zumindest bewusst, das du sonst eine ganze Menge Arbeit duplizierst.

7.e.Q
02-11-2006, 07:48
Gibt std::cout denn auch ein Objekt zurück, das den operator<< überlädt?

locus vivendi
02-11-2006, 21:50
Gibt std::cout denn auch ein Objekt zurück, das den operator<< überlädt?
Kurz gesagt ja, sonst könntest du doch gar "std::cout << x" machen, wenn x ein int ist zum Beispiel. Aber warum guckst du das nicht einfach nach?
http://www.dinkumware.com/manuals/default.aspx?manual=compleat&page=iostream.html

7.e.Q
02-11-2006, 22:47
Dann hab ich da was an der Syntax noch nicht ganz verstanden. Ich dachte, wenn ich std::cout << x; mache, rufe ich den << Operator von std::cout auf und übergebe diesem x.

Mit meiner Frage meinte ich eher ein theoretisches Konstrukt wie dieses:



irgendwas_stream << std::cout << blah << blubb;



Ich hab jetzt in meinem Programm etwas wie dieses versucht:




std::ostream& CBlubb::operator<<(std::ostream& os)
{
os << this->GetName(); // wobei GetName einen std::string liefert;
return os;
}

int main()
{
CBlubb x;

std::cout << "Hallo Welt! Hallo " << x << std::endl;
}


Die Deklaration der Klasse CBlubb ist selbstverständlich entsprechend vorhanden, ich habe sie hier zwecks Übersicht weggelassen.

Die Compiler-Fehlermeldung muss ich morgen raussuchen, die weiß ich jetzt nicht aus dem Kopf. Ich weiß nur, daß er das so nicht übersetzen wollte.

EDIT: Fehlermeldung sieht so aus, kommt aus unserer Software, passt deshalb nicht sauber zum Beispiel oben, spiegelt aber wider, was hier Probleme bereitet:


d:\my\ists\_cgios\ios\main.cpp: In function `void CtrlProc(int, const CIdfRecord &)': d:\\my\ists\_cgios\ios\main.cpp (0,1114)
--- no match for `_IO_ostream_withassign & << const CIdfRecord &'
d:\my\ists\_cgios\\include\g++-3\iostream.h (0,77)
--- candidates are: class ostream & ostream::operator <<(char)
d:\my\ists\_cgios\\include\g++-3\iostream.h (0,78)
--- class ostream & ostream::operator <<(unsigned char)

locus vivendi
03-11-2006, 09:15
Ich hab jetzt in meinem Programm etwas wie dieses versucht:


std::ostream& CBlubb::operator<<(std::ostream& os)
{
os << this->GetName(); // wobei GetName einen std::string liefert;
return os;
}

int main()
{
CBlubb x;

std::cout << "Hallo Welt! Hallo " << x << std::endl;
}
Was versuchst du denn nun eigentlich zu machen? In deinem ersten Post hast du geschrieben, du möchtest deine Klasse wie einen Stream verwenden können. Aber wenn man sich dieses Snippet ansieht würde man eher raten, dass du Objekte deiner Klasse nur in einen Stream ausgeben willst.

Wenn du nur Objekte deiner Klasse ausgeben möchtest, dann schau in die Standard-Header (<ostream> und <string> z. B.) wie das dort gemacht wird, und in die C++-Faq, dort steht das auch drin. In den C++ Annotations gibt es auch Beispiele. Und in diversen Büchern wird es natürlich auch beschrieben.

7.e.Q
03-11-2006, 09:24
Sorry, komme da selbst gerade bissl in den Tüddel...

Primär: Klasse als Stream!
Sekundär: probeweise Klasse IN Stream.

locus vivendi
03-11-2006, 13:55
Wäre folgendes Interface für deine Klasse geignet, um einen Stream bereitzustellen?

class with_stream
{
public:

std::ostream& stream();
};
Die Klasse wäre also nicht selber ein Stream, aber man hätte über sie Zugriff auf einen Stream.

7.e.Q
03-11-2006, 13:58
An sowas hatte ich auch schon gedacht... Ich hätt's halt gern so elegant wie möglich gehabt.

anda_skoa
03-11-2006, 14:20
Sorry, komme da selbst gerade bissl in den Tüddel...


Und wir erst :(



Primär: Klasse als Stream!

Wenn deine Klasse ein Stream ist, kannst du ja von ostream ableiten und die nötigen Operatoren implementieren.

Aber das ist das selbe Problem wie ohne Ableiten, der Unterschied ist, daß dein Objekt bei Funktionen anstelle eines üblichen Streams angegeben werden kann, wenn sie einen Stream erwarten



Sekundär: probeweise Klasse IN Stream.


Probier es mit einer externen Streamfunktion


std::ostream& operator<<(std::ostream& stream, const MyClass& myObject)
{
// whatever, notfalls als friend von MyClass

return stream;
}


Ciao,
_

7.e.Q
06-11-2006, 09:28
Gut, das mit der externen Streamfunktion klappt soweit.

Was heißt, wenn meine Klasse ein Stream ist? Wie bewerkstellige ich das?

anda_skoa
06-11-2006, 14:44
Wie bei jeder "is-a" Relation durch Ableitung von der gemeinsamen Basis :)

Ciao,
_