 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Guest
|
Posted: Mon Feb 27, 2006 8:06 pm Post subject: Designfrage: Wie Downcast vermeiden ? |
|
|
Hi Group,
ich habe folgende Klassenhirachie einer Checksummen Bestimmung.
class Checksum
{
Checksum()
{
pStrategy=new StrategyCrc32();
}
bool operator==(const Checksum& checksum)
{
return *m_pStrategy == checksum.m_pStrategy;
}
private:
ChecksumStrategy* m_pStrategy;
}
class Strategy
{
virtual bool operator==(const ChecksumStrategy strat) = 0;
}
class StrategyCrc32
: public Strategy
{
bool operator==(const ChecksumStrategy strat)
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( strat )
if( p != NULL )
{
...Vergleich....
return true;
}
return false;
}
}
Ich denke das sollte soweit funktionieren.
In Wirklichkeit hat die Checksum Klasse noch ne Factory
um unterschiedliche Checksum Strategien (crc8, 16 o.ä. )
zu erzeugen. Das habe ich mal alles weggelassen. Was mich
stört ist der downcast in der StrategyCrc32 Implementierung.
Meine Frage: Wie kann ich das geschickter lösen bzw.
wie würdet ihr die Klassenhirachie gestalten ?
Gruss
Markus
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Thomas Maeder Guest
|
Posted: Mon Feb 27, 2006 10:06 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
krk (AT) gmx (DOT) net writes:
| Quote: | Hi Group,
ich habe folgende Klassenhirachie einer Checksummen Bestimmung.
class Checksum
{
Checksum()
{
pStrategy=new StrategyCrc32();
}
bool operator==(const Checksum& checksum)
{
return *m_pStrategy == checksum.m_pStrategy;
}
private:
|
Das ist überflüssig - die beiden obigen Members sind auch private.
| Quote: | ChecksumStrategy* m_pStrategy;
|
Und für was steht ChecksumStrategy?
| Quote: | }
class Strategy
{
virtual bool operator==(const ChecksumStrategy strat) = 0;
}
|
Dieser Operator sollte wohl const sein. Ich erwarte jedenfalls von
einem Vergleich mit ==, dass die beiden Operanden nicht verändert
werden.
Zudem ist das const beim Parameter ein reiner Kommentar.
| Quote: | class StrategyCrc32
: public Strategy
{
bool operator==(const ChecksumStrategy strat)
|
Das ist jetzt sehr verwirrlich. Stehen Strategy und ChecksumStrategy
in einer Beziehung zueinander?
| Quote: | {
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( strat )
|
Das wird nicht kompilieren - strat ist ja kein Zeiger.
[snip]
| Quote: | Ich denke das sollte soweit funktionieren.
|
Ich denke das nicht.
| Quote: | In Wirklichkeit hat die Checksum Klasse noch ne Factory
um unterschiedliche Checksum Strategien (crc8, 16 o.ä. )
zu erzeugen. Das habe ich mal alles weggelassen. Was mich
stört ist der downcast in der StrategyCrc32 Implementierung.
Meine Frage: Wie kann ich das geschickter lösen bzw.
wie würdet ihr die Klassenhirachie gestalten ?
|
Poste mal den wirklichen Code, dann hast Du Chancen auf einen
Vorschlag.
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Guest
|
Posted: Tue Feb 28, 2006 9:06 am Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Sorry habe in der Eile wohl ein paar "Kleinigkeiten" vergessen )
Ich hoffe meine eigentliche Frage wird nun klarer ?!
Gruß
Markus
class Checksum
{
public:
Checksum()
{
pStrategy=new StrategyCrc32( );
}
bool operator==(const Checksum& checksum) const
{
return *m_pStrategy == checksum.m_pStrategy;
}
private:
ChecksumStrategy* m_pStrategy;
}
class ChecksumStrategy
{
virtual bool operator==(const ChecksumStrategy& strat) const = 0;
}
class StrategyCrc32
: public ChecksumStrategy
{
bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
if( p != NULL )
{
...Vergleich....
return true;
}
return false;
}
}
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Bob Hairgrove Guest
|
Posted: Tue Feb 28, 2006 10:06 am Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
On 27 Feb 2006 22:24:10 -0800, krk (AT) gmx (DOT) net wrote:
| Quote: | Sorry habe in der Eile wohl ein paar "Kleinigkeiten" vergessen )
Ich hoffe meine eigentliche Frage wird nun klarer ?!
Gruß
Markus
|
Zuerst eine wichtige Fragen, dann "meckere" ich ein wenig weiter
unten... ;)
Willst Du die Gleichheit oder die Äquivalenz der zwei Objekte prüfen?
Es wäre viel einfacher, die Zeiger selbst zu vergleichen. Das wäre
aber Gleichheit (identity) und nicht Äquivalenz (equivalence).
Was heisst es, eine "Strategy" zu sein? Kann man diese z.B. als
statische Singletons implementieren, und einen Zeiger auf diese
Instanz (oder Instanzen verschiedener Typen) herumreichen, oder kann
es mehrere Instanzen einer spezifischen Strategy geben? Im ersten Fall
würde ja eine Überprüfung auf Gleichheit ausreichen; sonst braucht man
Äquivalenz.
Falls Äquivalenz und nicht Gleichheit gewünscht wird, müsste man einen
(oder mehrere) "gemeinsamen Nenner" finden, um Äquivalenz
festzustellen. Polymorphisch zu implementieren ist ein wenig
kompliziert, das hast Du bereits gesehen. Was ich machen würde, ist
nicht operator== virtuell zu machen, sondern eine (oder auch mehrere)
zusätzliche virtuelle Funktion(en) zu schreiben, die einen Kennwert
zurückgibt. Jede abgeleitete Klasse "weiss", welchen Wert eine Instanz
dieser Klasse zurückgeben sollte. Dann kann man in der Basisklasse in
der Implementierung von operator== diese Funktionen aufrufen und deren
Rückgabewerte vergleichen, um die Äquivalenz festzustellen.
Überlegenswert wäre es auch, die Klasse Checksum als Template-Klasse
zu implementieren, wobei das Template-Argument die Strategy-Klasse
wäre (s. Andrei Alexandrescu's "Modern C++ Design").
Nun zum Code:
| Quote: | class Checksum
{
public:
Checksum()
{
pStrategy=new StrategyCrc32( );
}
|
Sollte wohl m_pStrategy heissen. Und wo wird das wieder gelöscht? Da
müsste ein Destruktor her! Falls das Objekt aus einer factory kommt,
wie in der erstenNachricht angedeutet, wird der Destuktor
wahrscheinlich auch eine Factory-Funktion fürs Aufräumen aufrufen
müssen.
Auch einen Konstruktor sowie operator= für das Kopieren und Zuweisung
wirst Du entweder auch implementieren müssen, oder diese deaktivieren,
indem Du sie als "private" deklarierst, aber nicht implementierst. Das
ist immer sehr wichtig, wenn man Objekte dynamisch verwaltet.
| Quote: | bool operator==(const Checksum& checksum) const
{
return *m_pStrategy == checksum.m_pStrategy;
}
private:
ChecksumStrategy* m_pStrategy;
}
|
Der Strichpunkt am Ende der Klassendefinition fehlt.
Ausserdem weiss Checksum noch nichts von ChecksumStrategy, daher muss
man die Klasse voraus deklarieren. Oberhalb der obigen
Klassendeklaration setzt man:
class ChecksumStrategy;
Dann kann man Members als Zeiger oder Referenz auf diese Klasse
einsetzen. Sollte man aber Objekte brauchen, muss der Compiler die
vollständige Deklaration von ChecksumStrategy vorher sehen können.
Dies geschieht i.d.R. durch #include-Directives.
| Quote: | class ChecksumStrategy
{
|
public: // protected geht hier auch
| Quote: | virtual bool operator==(const ChecksumStrategy& strat) const = 0;
}
|
Der Strichpunkt am Ende der Klassendefinition fehlt hier wieder.
Ausserdem sollte ChecksumStrategy einen virtuellen Destruktor haben,
da Du die Instanzen dynamisch anlegst und durch den Zeiger auf
ChecksumStrategy wieder wirst löschen müssen. Sonst gibt es
undefiniertes Verhalten!
| Quote: | class StrategyCrc32
: public ChecksumStrategy
{
|
public:
| Quote: | bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
|
StrategyCrc32 const *p =
dynamic_cast<StrategyCrc32 const*>( &strat );
Da das Argument als const-reference übergeben wird, muss der Zeiger
auf const StrategyCrc32 zeigen.
| Quote: | if( p != NULL )
{
...Vergleich....
return true;
}
return false;
}
}
|
Der Strichpunkt am Ende der Klassendefinition fehlt hier wieder.
--
Bob Hairgrove
NoSpamPlease (AT) Home (DOT) com
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Marcel Müller Guest
|
Posted: Tue Feb 28, 2006 3:02 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Hallo!
krk (AT) gmx (DOT) net schrieb:
| Quote: | class Checksum
{
public:
Checksum()
{
pStrategy=new StrategyCrc32( );
}
bool operator==(const Checksum& checksum) const
{
return *m_pStrategy == checksum.m_pStrategy;
}
private:
ChecksumStrategy* m_pStrategy;
}
class ChecksumStrategy
{
virtual bool operator==(const ChecksumStrategy& strat) const = 0;
|
Hier ist der Knackpunkt.
Ich darf bezweifeln, dass es einen Sinn ergibt, verschiedene
ChecksumStrategy Implementierungen miteinander zu vergleichen.
Ausßerdem sollten die Vergleichsoperatoren in der Regel global sein und
keine Klassenmethoden (wegen der impliziten Konvertierungen).
Wenn sie wiederum global sind, stellt sich die Frage so nicht mehr, denn
dann gibt es nur noch Überladungen, aber keine Vererbung.
Wenn ein globaler Operator
bool operator==(const ChecksumStrategy&, const ChecksumStrategy&)
gewünscht wird, weil bei der Beutzung tatsächlich ohne Kenntnis der
Implementation verglichen werden soll, handelt man sich ein
Multiple-Dispatch-Problem ein. Denn nun können ja beliebige
ChecksumStrategy-Implementierungen gegeneinander verglichen werden. Der
tatsächlich aufzurufende Operator kann erst durch die Auswertung der
Datentypen /beider/ Argumente zur Laufzeit bestimmt werden.
Multiple-Dispatch kann C++ nicht out of the box (so wie die meisten
gebräuchlichen OOP-Sprachen auch). Man muss es also per Hand
implementieren => dynamic_cast.
Alternativ könnte man operator==(const Checksum& , const Checksum&)
wirklich implementieren und zu diesem Zweck eine abstrakte Funktion
bereitstellen, die ein vergleichbares Objekt aus Typ und Wert der
jeweiligen Implementation erzeugt. Dann geht es ohne Downcasts.
| Quote: | class StrategyCrc32
: public ChecksumStrategy
{
bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
if( p != NULL )
{
...Vergleich....
return true;
}
|
Man könnte hier noch streiten, ob es nicht sinnvoller wäre eine
runtime_exception zu werfen, wenn Äpfel mit Birnen verglichen werden sollen.
Marcel
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Guest
|
Posted: Tue Feb 28, 2006 6:57 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Ich möchte auf Äquivalenz prüfen. Weiterhin kann es mehrere
Instanzen einer spezifischen Strategy geben.
Das Finden eines gemeinsamen Nenners wär sicherlich die eleganteste
Lösung, aber ich habe keinen Ansatz
wie ich das anstellen soll. Der Vorschlag mit dem Template sagt mir
jetzt nichts. Wie genau meinst du das ?
Was mich wie gesagt stört ist der cast. Man liest immer mal wieder,
"Cast = schlechtes Design....". Deswegen
frage ich mich ob sich das geschilderte Problem durch die Umstellung
des Designs umgehen lässt.
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Tue Feb 28, 2006 8:06 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Hallo,
krk (AT) gmx (DOT) net wrote:
^^^ hier könnte dein Name stehen.
| Quote: | class ChecksumStrategy
{
virtual bool operator==(const ChecksumStrategy& strat) const = 0;
}
class StrategyCrc32
: public ChecksumStrategy
{
bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
if( p != NULL )
{
...Vergleich....
|
Das ist IMHO eine Standardtechnik. Eigentlich bräuchtest du double-
dispatch (Methoden anhand mehrerer Klassen überladen), welches es in der
Sprache C++ nicht gibt. Als Alternative formuliert man es entweder aus:
struct ChecksumStrategy {
virtual bool operator==(const ChecksumStrategy&) const = 0;
virtual bool equals(const StrategyCrc32&) const { return false; }
virtual bool equals(const StrategyAdler32&) const { return false; }
virtual bool equals(const StrategyMD5&) const { return false; }
};
struct StrategyCrc32 : public ChecksumStrategy {
bool operator==(const ChecksumStrategy& x)
{ return x.equals(*this); }
bool equals(const StrategyCrc32& x) const
{ return .... };
};
struct StrategyAdler32 : public ChecksumStrategy {
bool operator==(const ChecksumStrategy& x)
{ return x.equals(*this); }
bool equals(const StrategyAdler32& x) const
{ return .... };
};
Oder man formuliert es wie du. Ich würde ohne zu zögern "deine" Methode
wählen. Ausformuliertes double-dispatch braucht halt pro Kindklasse eine
Methode in der Basisklasse. Das kann man machen, wenn das System recht
abgeschlossen ist und nach allen Regeln der Kunst "hübsch" aussehen
soll. Aber in der Praxis ist "deine" Methode erweiterbar und nur gering-
fügig -- wenn überhaupt -- langsamer.
Einziger Verbesserungsvorschlag wäre, den Cast und das if zusammenzu-
ziehen.
if (StrategyCrc32* p = dynamic_cast<StrategyCrc32*>(&strat)) {
// ...
}
Vorteil ist einfach, dass du die "ungültige" Variable p nicht mehr
rumgammeln hast, wenn das Objekt vom falschen Typ war.
Stefan
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Guest
|
Posted: Wed Mar 01, 2006 12:06 am Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Hi Stefan,
danke für dein Posting ! Allerdings muss ich zur Verwendung des
dynamic_cast
die RTTI Option im Compiler einschalten. Bisher habe ich das noch nicht
benötigt.
Handelt man sich da noch irgendwelche Seiteneffekte mit ein ?
Gruß
Markus
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Stephan Rupp Guest
|
Posted: Wed Mar 01, 2006 12:06 am Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Stefan Reuther wrote:
| Quote: | Hallo,
krk (AT) gmx (DOT) net wrote:
^^^ hier könnte dein Name stehen.
class ChecksumStrategy
{
virtual bool operator==(const ChecksumStrategy& strat) const = 0;
}
class StrategyCrc32
: public ChecksumStrategy
{
bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
if( p != NULL )
{
...Vergleich....
Das ist IMHO eine Standardtechnik. Eigentlich bräuchtest du double-
dispatch (Methoden anhand mehrerer Klassen überladen), welches es in der
Sprache C++ nicht gibt. Als Alternative formuliert man es entweder aus:
|
.... aber wohl emuliert werden kann. Du könntest beispielsweise
double-dispatching über das GoF Visitor-Pattern implementieren. Wie das
geht, kannst Du im Wikipedia nachschlagen. Hierzu einfach mal double
dispatch eingeben, dann solltest Du auf die entsprechende Stelle
verwiesen werden.
Viele Grüße,
Stephan
[snip]
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Bob Hairgrove Guest
|
Posted: Wed Mar 01, 2006 9:06 am Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
On 28 Feb 2006 05:57:07 -0800, krk (AT) gmx (DOT) net wrote:
| Quote: | Ich möchte auf Äquivalenz prüfen. Weiterhin kann es mehrere
Instanzen einer spezifischen Strategy geben.
Das Finden eines gemeinsamen Nenners wär sicherlich die eleganteste
Lösung, aber ich habe keinen Ansatz
wie ich das anstellen soll.
|
Zuerst muss man wissen, was es heisst, äquivalent zu sein. Ein
Beispiel von 2 oder 3 konkreten Strategy-Klassen wäre angebracht...
| Quote: | Der Vorschlag mit dem Template sagt mir
jetzt nichts. Wie genau meinst du das ?
|
Am Besten wäre es, einfach mal das Buch zu lesen, das ich genannt
habe, weil ich nicht gerne ganze Kapitel aus dem Zusammenhang heraus
hier zitieren möchte. Der Author zeigt, wie man viele verschiedene
Entwurfsmuster mit Template-Klassen realisieren kann. Strategy kann
man aber durchaus ohne Templates machen.
Der grosse Vorteil von Templates ist, dass die Bindung der Typen zum
Zeitpunkt des Kompilierens und nicht erst zur Laufzeit geschieht. Nach
dem Entwurfsmuster "Strategy" sollten diese Klassen vor allem ein
Interface für Algorithmen bereitstellen, die austauschbar sind. Wenn
alle Informationen bereits beim Kompilieren bekannt sind, ist es
meistens besser, ein Template zu benutzen. Wenn das nicht geht, macht
man es eben mit der klassischen Vererbung.
| Quote: | Was mich wie gesagt stört ist der cast. Man liest immer mal wieder,
"Cast = schlechtes Design....". Deswegen
frage ich mich ob sich das geschilderte Problem durch die Umstellung
des Designs umgehen lässt.
|
Ja, in der geschilderten Weise. Aber wir wissen ja immer noch nicht,
wie Du Dir Äquivalenz für die konkreten Typen genau vorstellst. Zuerst
die Beispiele, dann die Abstraktion.
--
Bob Hairgrove
NoSpamPlease (AT) Home (DOT) com
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Torsten Robitzki Guest
|
Posted: Wed Mar 01, 2006 7:06 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Stephan Rupp wrote:
| Quote: | Stefan Reuther wrote:
Hallo,
krk (AT) gmx (DOT) net wrote:
^^^ hier könnte dein Name stehen.
class ChecksumStrategy
{
virtual bool operator==(const ChecksumStrategy& strat) const = 0;
}
class StrategyCrc32
: public ChecksumStrategy
{
bool operator==(const ChecksumStrategy& strat) const
{
StrategyCrc32* p = dynamic_cast<StrategyCrc32*>( &strat )
if( p != NULL )
{
...Vergleich....
Das ist IMHO eine Standardtechnik. Eigentlich bräuchtest du double-
dispatch (Methoden anhand mehrerer Klassen überladen), welches es in der
Sprache C++ nicht gibt. Als Alternative formuliert man es entweder aus:
... aber wohl emuliert werden kann. Du könntest beispielsweise
double-dispatching über das GoF Visitor-Pattern implementieren. Wie das
geht, kannst Du im Wikipedia nachschlagen.
|
Oder man guckt noch mal in den Beitrag von Stefan auf den Du geantwortet
hast ;-)
mfg Torsten
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Stephan Rupp Guest
|
Posted: Wed Mar 01, 2006 8:06 pm Post subject: Re: Designfrage: Wie Downcast vermeiden ? |
|
|
Torsten Robitzki wrote:
| Quote: |
Oder man guckt noch mal in den Beitrag von Stefan auf den Du geantwortet
hast ;-)
mfg Torsten
|
Oder man schaut sich nochmal das GoF Pattern an und sieht, dass es eine
Entkopplung von Objektstruktur (Checksum Klassen) und dem eigentlichen
Visitor vorsieht Entsprechend wäre ein CompareEquality-Visitor zu
entwerfen, mit dem der Vergleichsoperator von ChecksumStrategy zu
parametrisieren wäre.... Dass dies für diesen Fall Overkill wäre und
Stefans Ansatz (sozusagen den Visitor in der Basisklasse der
Objektstruktur zu integrieren) sicherlich hier sinnvoller ist, ist
sicherlich richtig, allerdings demonstriert er nicht wirklich die
Anwendung des Visitor-Pattern. Soviel zu meiner kläglichen Verteidigung ;-)
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|