Seite 1 von 1

Makro-OOP

Verfasst: 24.12.2006 17:57
von Kekskiller
Pünktlich zu Weihnachten stelle ich mein 2-Nächte-Projekt vor. Es handelt sich um eine experimentelle Art und Weise der OOP in Purebasic. Die BEsonderheit dabei ist, dass es komplett auf Makrobasis ist und deshalb für den Benutzer sehr einfach zu verstehen. Ich habe mich über geärgert, dass Neuankömmlinge die Codes aus der Purebasic-Lounge nicht verstehen und das ohnehin eine Art Krampf ist, wie ich finde. Hiermit will ich das ändern. Probierst einfach mal aus.

Makro-OOP-Paket:
http://kekskiller.kohop.de/dateien/makrooopv1.zip
enthält: PDF-Hilfe (6 Seiten), Beispieldateien, OOP-PBI-Datei

Für alle, die keine Hilfe brauchen, können sich weiter unten den Code ansehen. Ich empfehle es aber dringend, sich für späteren Gebrauch die Hilfe zu ziehen, da man dort alles erfährt, was wichtig ist.

- diese Makro-OOP ist komplett zu Purebasic kompatibel, da sie auf Strukturen aufbaut.
- Sie benutzt keine Interface'

Ich weiß, viele Sachen sind noch so schön, aber auf andere widerum bin ich sehr stolz. Es ist eine sehr kleine "Engine", das Problem war für mich eher die Ansteuerung durch den Benutzer.

Die OOP-PBI-Datei:

Code: Alles auswählen

; Purebasic Makro-OOP v.1

;-currentobject
CompilerIf Defined(*_currentobject, #PB_Variable) = #False
  Global *_currentobject.l
CompilerEndIf

;- Macros

;/Klassendeklaration
Macro class_def(classname) ;beginnt eine klasse-definition
  Structure c#classname
    classname._c#classname ;<- konstruktor
EndMacro
Macro class_methode(procedurename) ;definiert eine methode mithilfe der vorgegebenen prozedur -> sollte nur innerhalb des klasse erfolgen
  procedurename.p#procedurename ;prototypen werden automatisch durch den konstruktor zugewiesen
EndMacro
Macro end_class_def ;beendet eine klassen-definition
  EndStructure
EndMacro

;/Methoden/Konstruktoren-Definition
Macro methode_assign(procedurename) ;weißt einer methode die passenden procedur zu -> sollte nur im konstruktor benutzt werden
  *me\procedurename = @m#procedurename#()
EndMacro
;bevor die prozedur ausgeführt wird, muss geprüft werden, ob überhaupt eine objekt gesetzt wurde, ansonsten gibts ne fehler-nachricht
Macro methode_def(procedurename, returntype=) beginnt eine methode
  Procedure#returntype m#procedurename
EndMacro
Macro constructor_def(procedurename, returntype=) ;beginnt einen konstruktor
  Procedure#returntype _c#procedurename
EndMacro
Macro elementclass(classname);gibt an, welchen typ *me hat
  :Protected *me.c#classname
  If *_currentobject > 0
    *me = *_currentobject
EndMacro
Macro end_def;
    EndIf
  EndProcedure
EndMacro

;/Objekt-Benutzung

Macro new_p(object, classname) ;erstellt ein neues objekt
  ;speicher allozieren und konstruktor aufrufen
  object = AllocateMemory(SizeOf(c#classname))
  *_currentobject = object ;aktuelles objekt setzen
  _c#classname
EndMacro
Macro new(object, classname) ;ruft den konstruktor eines bereits vorhandene objektes auf objekt
  *_currentobject = @object ;aktuelles objekt setzen
  _c#classname
EndMacro
Macro use_p(object)
  *_currentobject = object
EndMacro
Macro use(object)
  *_currentobject = @object
EndMacro
Macro oop_p(object) ;ändert automatisch das aktuelle element auf das in den klammern ausgewählte... benutzung: oop(object)\element
  *_currentobject = object: object
EndMacro
Macro oop(object)
  *_currentobject = @object: object
EndMacro
Macro delete_p(object) ;löscht ein zuvor alloziiertes Objekt
  FreeMemory(object)
EndMacro
Macro class_size(classname)
  SizeOf(c#classname)
EndMacro

;/Vorbereiten der Methoden/Konstruktoren
Macro make_methode_proto(proc, returntype=)
  Prototype#returntype p#proc
EndMacro
Macro make_methode_dec(proc, returntype=)
  Declare#returntype m#proc
EndMacro
Macro make_constructor_proto(proc, returntype=)
  Prototype#returntype _c#proc
EndMacro
Macro make_constructor_dec(proc, returntype=)
  Declare#returntype _c#proc
EndMacro

;nur nen kleiner faulheitsmacro, vereinfacht objektangabe
Macro PBobject(object, classname)
  object.c#classname
EndMacro
Macro PBclass(classname)
  c#classname
EndMacro
Die Hauptdatei von Beispiel:

Code: Alles auswählen

; Purebasic Makro-OOP-Sample: class_person.pb
; 
; Diese Datei ist unsere Hauptklasse. Diese Datei gilt als Beispieldatei,
; die aufgeführten Methoden, Klassen und Objekte haben keinen tieferen Sinn.
; 
; Diese Datei ist im Beispiel die Haupt-Klasse.

;{/einbinden der Includes
  ;Die oop.pb-Datei MUSS immer inkludiert werden, wenn oop benutzt werden soll.
  ;Ein XInclude ist empfehlenswert.
  XIncludeFile "OOPv1.pbi"
  IncludeFile "sample_class_hair.pb"
;}

;{/VORBEREITUNG
  ;Konstruktor deklarieren
  make_constructor_dec(Person)(age.b=-1, name.s="???")
  make_constructor_proto(Person)(age.b=-1, name.s="???")
  
  ;Methoden deklarieren
  make_methode_proto(Print)()
  make_methode_dec(Print)()
  
  make_methode_proto(Set)(age.l, name.s)
  make_methode_dec(Set)(age.l, name.s)
;}

;{/KLASSE
  class_def(Person)
    ;eigenschaften
      strName.s
      bAge.b
      PBobject(hHair, Hair);das "Hair"-Ibjekt wird hinzugefügt, muss im Konstruktor aber erst erstellt werden
    ;methoden (der konstruktor wird automatisch hinzugefügt)
      class_methode(Print)
      class_methode(Set)
  end_class_def
;}

;{/KONSTRUKTOR
  constructor_def(Person)(age.b=-1, name.s="???")elementclass(Person)
    ;Methoden-Adressen zuweisen
      methode_assign(Print)
      methode_assign(Set)
    ;"Hair"-Objekt erstellen (damit es für spätere Verwendung bereit ist
      new(*me\hHair, Hair)($FFFFFF, 2)
    ;andere Eigenschaften setzen
      *me\strName = name
      *me\bAge = age
  end_def
;}

;{/METHODEN
  ;methode gibt eigenschaften aus
  methode_def(Print)()elementclass(Person)
    Debug "Name: " + *me\strName + " Alter: " + Str(*me\bAge)
  end_def
  
  ;methode ändert eigenschaften
  methode_def(Set)(age.l, name.s)elementclass(Person)
    *me\bAge = age
    *me\strName = name
  end_def
;}

; --------------------------------------------------------------------------------

;{/Benutzen der Objekte
  
  ;objekte definieren
  Define.PBclass(Person) ich, er, sie, es
  Global Dim PBobject(du, Person)(5) ;<- objektfeld definieren (hier müssen die Feldgrößen getrennt vom PBobject()-Makro angegeben werden)
  
  ;objekte erstellen
  new(ich, Person)(17,"adalbert")
  new(du(0), Person)();nur das erste element im feld erstellen
  new(er, Person)()
  new(sie, Person)(26, "donald")
  new(es, Person)()
  
  ;Objekteigenschaften ändern, wenn Objekt noch nicht schon vom Konstruktor geändert wurde
  oop(du(0))\Set(20, "bernd")
  oop(er)\Set(23, "conrad")
  oop(es)\Set(29, "erich")
  
  ;Methode aus unserem internen Objekt aufrufen
  oop(du(0)\hHair)\Grow(10) ;sollte 12 sein
  
  ;Informationen über die Objekte ausgeben
  oop(ich)\Print()
  oop(du(0))\Print()
  oop(er)\Print()
  oop(sie)\Print()
  oop(es)\Print()
  ;internes Objekt abfragen
  Debug du(0)\hHair\lColor
  Debug du(0)\hHair\bLength
;}
Die Subklassen-Datei vom Beispiel:

Code: Alles auswählen

; Purebasic Makro-OOP-Sample: class_hair.pb
; 
; Diese Datei ist unsere Hauptklasse. Diese Datei gilt als Beispieldatei,
; die aufgeführten Methoden, Klassen und Objekte haben keinen tieferen Sinn.
; 
; Diese Datei ist im Beispiel der Sub-Klasse.

;{/einbinden
  XIncludeFile "OOPv1.pbi"
;}

;{/VORBEREITUNG
  make_constructor_dec(Hair)(color=0, length=0)
  make_constructor_proto(Hair)(color=0, length=0)
  
  make_methode_dec(Grow)(length=1)
  make_methode_proto(Grow)(length=1)
  
  make_methode_dec(Cut)()
  make_methode_proto(Cut)()
;}

;{/KLASSE
  class_def(Hair)
    lColor.l
    bLength.b
    class_methode(Grow)
    class_methode(Cut)
  end_class_def
;}

;{/KONSTRUKTOR
constructor_def(Hair)(color=0, length=0)elementclass(Hair)
    methode_assign(Grow)
    methode_assign(Cut)
    *me\lColor = color
    *me\bLength = length
  end_def
;}

;{/METHODEN
  methode_def(Grow)(length=1)elementclass(Hair)
    *me\bLength + length
  end_def
  
  methode_def(Cut)()elementclass(Hair)
    *me\bLength = 0
  end_def
;}
Viel Spaß damit und frohe Weihnachten euch allen :allright: ...

Verfasst: 26.12.2006 00:53
von Kekskiller
Ich habe ein paar kleine Änderungen vorgenommen:
- groben Fehler in der Hilfe behoben
- class_dec() in class_def() umbenannt
- end_dec in end_class_dec umbenannt

Gleichzeitig möchte ich euch -wenn ihr zwischen den Feiertagen noch ein wenig freie Zeit habt- dazu aufrufen, alles zu posten, was ich daran nicht gefällt, bzw. zu sagen, was ich gerne noch hättet. Mein Ziel ist eis sinnvolle Erweiterungen, bzw. Änderungen einzuführen und damit eine wirklich sinnvolle PB-Erweiterung zu erschaffen.

Verfasst: 04.01.2007 22:38
von Hellhound66
Coole Idee, aber ich weiss schon, warum ich die verworfen habe.
Das soll jetzt verständlicher und einfacher sein??

Verfasst: 06.01.2007 17:34
von Kekskiller
Ich persöhnliche finde es recht einfach. Man bedient das ganze nur mir einfachen Schlüsselwörtern und spart sich das lästige rumpointern. Ich setze es bereits in vollem Ausmaß in meinem Projekt ein und der Code ist sehr übersichtlich. Nunja, es mag Geschmackssache sein... Aber ich habe lieber ein unkryptischen Code beim Erstellen und Bedienen der Funktion.

Warum hast du sie verworfen :| ? Es ist mit Sicherheit ähnlich gewöhnungsbedürftig wie die Versionen in der Purebasic-Lounge, zu mal ich keine so größe Dokumentation darüber habe...

Verfasst: 06.01.2007 18:08
von Hellhound66
Ich kann mit Pointer gut umgehen und programmiere gerne mit Pointern. Für mich würde es nur Sinn machen, wenn ich Structures weglassen könnte oder nur einmal deklarieren zu müssen.

Code: Alles auswählen

constructor_def(Person)(age.b=-1, name.s="???")elementclass(Person) 
Sowas finde ich alles andere als einfach. Und letztendlich hab ich mehr Schreibarbeit als ohne die Macros.
Der größte Nachteil ist allerdings, dass sie keine Interfaces nutzt. So ist die Philosophie von OOP torpediert, da ich keine Trennung zwischen Methoden und Attributen habe. So sind die Attribute nicht geschützt. Bei klassischem OOP würde ich nur das Interface nutzen und gar nichts mitbekommen von den Attributen. So kann man auch nicht ungewollt irgendwelche Werte überladen.

Verfasst: 06.01.2007 18:44
von Kekskiller
Nun gut, da hast du Recht. Wahrscheinlich bin ich sowieso der einzige Mensch auf Erden, der das mit Strukturen macht, daher ists im Endeffekt auch wurscht...

Verfasst: 06.01.2007 19:07
von Hellhound66
Mit den Strukturen hab ich auch angefangen, weil ich nicht wusste, was Aufrufe von Interfaces wirklich machen (*This-Pointer übergeben). Da das stiefmütterlich beschrieben war, hab ich es durch Zufall viel später erfahren und auch kapiert. Aber seitdem will ich es nicht mehr missen.

Verfasst: 09.01.2007 22:39
von Andre
Hellhound66 hat geschrieben: Da das stiefmütterlich beschrieben war, hab ich es durch Zufall viel später erfahren und auch kapiert.
Wenn jemand für eine bessere Beschreibung der Interfaces sorgen kann, bin ich immer an Vorschlägen interessiert, um die Hilfe verbessern zu können. :wink: