Frage zur OOP (Klassenaufruf aus anderer Klasse?)
-
Hallo Zusammen,
Ich habe mal eine Frage, die sich wahrscheinlich mit den Grundsätzen der Projektorientierung befasst und die mich schon dazu bringt an mir zu ZWeifeln.
Vieleicht sehe aber auch nach 15 Jahren fast ausschließlicher Assemblerprogrammierung mit ein klein wenig C den Wald vor lauter Bäumen nicht mehr. Leider muss ich etwas weiter ausholen:Die Rahmenbedingungen:
Ich bin dabei eine Anwendung zu erstellen die über die Ser. Schnittstelle eine von mir entwickelte µP Steuerung kontrolliert.
Zu diesem Zweck müssen dreißig verschiedene Steuerkomandos bestehend aus jeweils drei Byte ausgegeben werden. Ausserdem müssen aus einem empfangenem Datenstring mit variabler Länge verschiedene Steuerzeichen extrahiert werden und anhand dieser verschiedene Maßnahmen im Programm aufgerufen werden.Der Einfachheit halber und um schnell Resultate zu haben verwende ich ersteinmal für die Bildschirmdarstellung Windows-Forms und für die Serielle Ausgabe die System.io.ports. .
Evtl. soll aber später die (physikalische) Ausgabeschnittstelle geändert werden, ausserdem möchte das Herzstück des Codes auch auf anderen Plattformen lauffähig machen.Mein erster, nach der Spagetti-Methode in "Quick & Dirty" Manier hingeschmierter Code läuft auch zufriedenstellend!
Nur ist dies nicht das was ich möchte. Um später einfach die Ausgabe zu ändern oder die .Net spezifischen Elemente (Serielle Kommunikation und Bildschirmdarstellung) durch ISO C++ konforme Elemente ersetzen zu können möchte ich das ganze jetzt in der "sauberen" Endversion modularer Aufbauen.Mein Problem:
Und hier komme ich nicht weiter.
(Die folgenden Zeilen beschreiben mein Problem anhand einer .NET Klasse. Allerdings bezieht sich meine Frage ja nicht speziell auf diese Klasse, sondern auf das vorgehen allgemein, auch und gerade unter ISO C++.
Später soll es ja auch reines ISO C++ sein...)Bisher habe ich im komplett Hauptprogramm die Klasse SYSTEM.IO.PORTS eingebunden, eine Instanz "port" erzeugt und mittels des Elementzugriffsoperators "." dann direkt auf die einzelnen Methoden der Klasse zugegriffen: (port.Open(), port.write(), port.BaudRate usw...)
Die auszugebenden Daten habe ich vorher in einem Byte-Array abgelegt und geben diese mittels port.write aus, wobei der Arrayname und die auszugebenden Bytes (3) Konstant sind.
Der Offset ist variabel durch die auslösende Aktion bestimmt, was mir dann die 30 verschiedenen Kombinationen erzeugt.Da dieses aber sehr speziell auf die Serielle Ausgabe mittels System.io.Ports ausgelegt ist und ich somit sowohl bei änderung der Schnittstellenart als auch bei Versicht auf die .NET Klassen massive Änderungen am HAuptprogramm vornehmen müsste, bin ich auf die Idee gekommen eine eigene Klasse "Schnittstelle" zu erzeugen.
Im Hauptprogramm soll es lediglich nur noch die Methoden "SOpen" zum Öffnen der Vervindung, "SClose" zum Schließen der Verbindung, "Ausgabe(Nummer)" zum Ausgeben der drei Steuerbytes, wobei die festlegung welche Bytes ausgegeben werden durch die Nummer 1-30 geschieht, und als letztes "Einlesen()" die den gefilterten String an das Hauptprogramm zurückgibt bestehen.
Halt damit ich einfach durch Austausch der Klasse bei ansonsten gleichem Code die eigenschaften ändern kann, was nach meinem Verständnis ja einer der Grundsätze der OOP ist.Die gesamte weitere Bearbeitung soll dann innerhalb der Klasse geschehen:
Und genau hier beginnt dann mein Problem. Ich komme bei der Erstellung der Methoden nicht weiter.
Mein erster Gedanke war einfach den bisherigen Programmcode für die Schnittstelle in diese Klasse zu verlegen, innerhalb dieser Klasse dann die System.io.ports einzubinden und dann ähnlich wie im Hauptprogramm dann darauf zuzugreifen:
Also etwas so:...Klassendefinition... SerialPort port; void SCHNITTSTELLE::Sopen() { port.PortName = "COM6"; port.BaudRate = 1200; port.DataBits = 8; port.StopBits = System::IO::Ports::StopBits::One; port.Handshake = System::IO::Ports::Handshake::None; port.Parity = System::IO::Ports::Parity::None; port.Open(); } ;... (Noch in der Q&D Ausführun)
Für die anderen Methoden in vergleichbarer Manier.Dies ist aber so wohl nicht zulässig:
"error C2601: 'SCHNITTSTELLE::s_open': Lokale Funktionsdefinitionen sind unzulässig"Soweit also mein Problem und hier meine Fragen:
1. Wo liegt mein Denkfehler? Was mache ich falsch?
Mein Bauchgefühl sagt mir das ich irgendwie MÄCHTIG daneben liege, aber Warum?2. Wie würde man ein solches Problem geschickt lösen???
Ich hoffe Ihr könnt mir weiterhelfen und meine Gedanken endlich wieder auf den richtigen Weg führen.
Gruß
Carsten
-
Rein auf dein Codefragment bezogen:
Lass SCHNITTSTELLE:: weg, wenn Du die Definition im Header File hast.Zur ganzen Frage managed / unmanaged ist mein Tip einfach:
Mische es nicht, ausser Du musst (und das musst Du meiner Meinung nicht, denn es erzeugt mehr Probleme als dass es löst).Hier noch eine sehr gute Klasse für die Serielle Kommunikation (unmanaged C++ für die WinAPI): http://www.codeproject.com/KB/system/serial.aspx
Simon
-
Was sehr beliebt ist, sind State-Tabellen. Keine Ahnung ob das bei dir machbar ist - aber bei vielen protokollen ist das moeglich.
Die Idee ist: Das Geraet hat N zustaende in denen es sich befinden kann und kann von einem Zustand in M andere Zustaende uebergehen.
Man kodiert das etwa so:
Du hast eine map mit Zustaenden drinnen, zB "Data Available", "Busy", "Idle",... und dann gibt es natuerlich eine Status Aenderung die das System sendet. zB von "Idle" springt er auf "User Input Available" oder so. Und dem ist eine Aktion zugeordnet:state_table states = { //zustand: event: aktion: { S_IDLE, E_WAKEUP, WakeUp } { S_IDLE, E_POWEROFF, PowerOff } ... };Wobei WakeUp bzw. PowerOff dann zB Functors oder funktionszeiger sind. (uU gibt es dann in der Tabelle auch ein NextState dass den naechsten State nach diesem Event definiert, oder aber die Aktions-Funktion setzt den status...)
So kann man Kommunikation ziemlich gut abstrahieren - da man nur die state-Tabelle bearbeiten muss. Geht aber natuerlich nicht bei allen Protokollen - haengt ja stark davon ab wie zustandsbehaftet das protokoll ist

Eine andere Moeglichkeit wenn man keine Zustaende hat, ist ein Plugin System zu verwenden. Man bekommt eine Nachricht vom Geraet und hat ja irgendwo den Typ der Nachricht stehen - meistens eine Event-ID oder aehnliches.
Das ganze kann man nun wieder in eine map packen und dort funktionszeiger/functors abspeichern und je nach Event-ID den passenden functor ausfuehren.
jedenfalls brauchst du so eine art plugin konzept. Die generelle Kommunikation laeuft immer gleich ab - was sich aendert sind die Zustaende und/oder Events die auftreten.
Dann musst du natuerlich auch das Senden und empfangen abstrahieren, aber das sollte ja klar sein. ist ja auch relativ trivial loesbar - die kommunikation ansich ist dagegen mit den plugins natuerlich erstmal aufwendig - aber wenn es dann aenderungen im system gibt, ist das system dann natuerlich absolut genial.
Wichtig ist: die Protokoll-Plugins sollen nichts mit der Kommunikation ansich zu tun haben. Sie sollen lediglich daten senden koennen und zustaende veraendern koennen. Die baudrate und aehnliches muss komplett weggekapselt sein - genauso das aufbauen der nachricht die verschickt wird.
Du machst in der Protokoll Klasse dann nur ein:
send(E_EVENTID, data);
Und send baut dann message header etc auf.durch so eine abstraktion kannst du es dann nachtraeglich zB parallelisieren.
-
Hallo Zusammen,
Eine kurze Rückmeldung:
@Simon
Die Klassendefinition im Listing ist extern, Mein Listing ist da tatsächlich etwas Missverständlich... Ansonsten hast du natürlich Recht. Managed & unmanaged C++ zu kombinieren ist nicht der Weisheit letzter Schluss. Und das dies auch neue Probleme schafft merke ich gerade wieder...(Da dies aber ein .NET spezifisches Problem ist stelle ich die Frage im passenden Unterforum).
Wenn ich das jetzt nicht verwechsle habe ich die von dir empfohlene Klasse schon getestet, die lief aber nicht, da ich im Moment noch VC2005 Express verwende wo ich auch die MFC nicht zugreifen kann. Sobald ich wieder Fit bin(hoffentlich nächste Woche), kann ich mir aber endlich die aktuellen Zugangsdaten für unser Hochschulnetz abholen (habe meine Verbummelt
und man muss persöhnlich erscheinen) und auf die Proff. Version umsteigen. Mein Ziel ist es bis dahin die "Kombinierte" Version am Laufen zu haben und dann sukzessive alle .NET Komponennten durch ISO C++ zu ersetzen, einfach um das PROG auch auf einem Standart-Linux lauffähig zu machen...@Shade Of Mine
Wenn ich dich jetzt nicht völlig Missverstehe bist du schon in deinen Überlegungen einen Schritt weiter und beschreibst Ansätze eines Lösungsweges unabhängig von .NET.
Oder bin ich jetzt richtig auf dem Holzweg?Falls ich deine Ausführungen jetzt richtig verstanden habe, so klingt es doch interessant und ich werde da sicher drauf zurückkommen wenn ich in den nächsten Tagen anfange das ganze zu bereinigen. Vom Übertragungsprotokoll bin ich übrigends ganz flexibel. Schließlich ist das Steuerprogramm des µP ja von mir und in Assembler bin ich absolut Sattelfest
.
Im Moment läuft es ganz ohne jegliche Flussteuerung. Daher werden auch drei Bytes übertragen. Das ersteByte beinhaltet das Komando. Das zweite Byte eine Prüfziffer mit einer gewissen Wertdifferenz und das dritte Byte entspricht genauder Wertdifferenz. Somit kann im µP genau errechnet werden ob die Übertragung gültig war.
(Hauptziel des Projektes bei mir ist ja das "Learning by Doing" um endlich mal in die Materie Objektorientiert einzusteigen
)@all
Jedenfalls habe ich den Fehler gestern noch gefunden. Manchmal braucht man halt etwas abstand und die ausreichende Menge geistreicher Getränke
Ich traue mich gar nicht es zuzugeben aber es war einfach eine in der Deklaration vergessene schließende geschweifte Klammer
!
(Die Sache mit dem Wald und den Bäumen halt)
Nach schließen der Deklaration hatte ich dann nur noch die Fehlermeldung das PORT ein unbekannter bezeichner ist. Die erzeugung des Objektes "port" habe ich bereits in die .h gepackt. Erst nachdem ich dann die gesamten Methoden inline definiert habe lief es.
Wenn mir vieleicht noch jemand erklären könnte, warum bei einer Inline Definition der Methoden das Objekt bekannt ist, bei derselben definiton extern aber nicht, das währe nettAnsonsten ist zumindest dieses Problem erst einmal gelöst...
Gruß
Carsten
-
Hallo,
CarstenS schrieb:
Wenn mir vieleicht noch jemand erklären könnte, warum bei einer Inline Definition der Methoden das Objekt bekannt ist, bei derselben definiton extern aber nicht, das währe nett
Hat nichts mit inline zu tun. Wenn du den Klassennamen voranstellst:
void SCHNITTSTELLE::Sopendann hast du Zugriff auf port, auch wenn die Methode nicht innerhalb der Klasse definiert wurde.
MfG,
Probe-Nutzer