Seite 1 von 1

Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 12:03
von mk-soft
Hatte mal wieder zu viel Zeit 8)

Code: Alles auswählen

;-TOP

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

CompilerIf #PB_Compiler_Version > 561
  CompilerError "Warning: Check ASM-Compiler for valid RBP-Register!"
CompilerEndIf

Macro GetCaller(self) ; Get frame pointer from caller
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    EnableASM
    mov self, ebp
    DisableASM
  CompilerElse
    EnableASM
    mov self, rbp
    DisableASM
  CompilerEndIf  
EndMacro

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

Prototype protoAdd(Value.i)
Prototype protoSub(Value.i)
Prototype protoResult()

Structure sObject
  Add.protoAdd
  Sub.protoSub
  Result.protoResult
  Value.i
EndStructure

Procedure Add(Value.i)
  Protected *self.sObject
  GetCaller(*self)
  ;Debug *self
  *self\Value + Value
EndProcedure

Procedure Sub(Value.i)
  Protected *self.sObject
  GetCaller(*self)
  ;Debug *self
  *self\Value - Value
EndProcedure

Procedure Result()
  Protected *self.sObject
  GetCaller(*self)
  ;Debug *self
  ProcedureReturn *self\Value
EndProcedure

Procedure Init(*self.sObject)
  ; set procedure address
  *self\Add = @Add()
  *self\Sub = @Sub()
  *self\Result = @Result()
  ; set default values
  *self\Value = 0
EndProcedure

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

;-Test

Define a1.sObject
Debug "Init Object"
Init(a1)
;Debug @a1
Debug "Add and Sub"
a1\Add(100)
a1\Sub(20)
Debug "Result = " + a1\Result()

;-Test 2

Debug "Array of object"
Dim args.sObject(1000)
For i = 1 To 1000
  Init(args(i))
Next

For i = 1 To 1000
  args(i)\Add(Random(100))
  ;args(i)\Add(1); Random(100))
Next

For i = 1 To 1000
  r1 + args(i)\Result()
Next

Debug "Result = " + r1
Information
Ich habe mich mal wieder mit ASM beschäftigt und den Aufruf-Konvention.
Weil 'Fred' unser großer Purebasic Entwickler ist, hält er sich als Profi an den Aufruf-Konvention und verwendet das rbp-register als Zeiger auf den aktuellen Rekord (Frame-Pointer).
Somit können wird auf den Zeiger von der aktuellen Struktur (record) vom Aufrufer zugreifen.

Diese funktioniert natürlich nur so lange, wenn der Compiler kein anderes Verfahren verwendet. Zum beispiel auf ein Prozessor unabhängigen Compiler (LLVM-Compiler)

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 12:09
von GPI
Das kommt mir bekannt vor. Und soweit ich weis, gabs damals das Problem, das sehr schnell Beispiele aufgetaucht sind, wo das ganze nicht mehr ging.

Interessante Möglichkeit, aber auch irgendwie aufwendiger. Vor allen weil man immer ein Prototype schreiben muss.

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 12:35
von mk-soft
Prototypes gibt es seit PB-Version 4.0 und habe die version noch einmal installiert.
Zur Sicherheit habe ich ein CompilerError eingebaut um eine Warnung auszugeben falls jemand diese verwendet.

Ist aber eine interessante Möglichkeit um schnelle Methoden zu schreiben, aber nicht Zukunftssicher.
Der sichere Weg ist weiterhin mit Interfaces zu arbeiten.

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 12:46
von uweb
@mk-soft:
Ich habe den Trick noch nicht gekannt. Deswegen auch hier im deutschen Forum: Vielen Dank!

Interessante Möglichkeit, aber auch irgendwie aufwendiger. Vor allen weil man immer ein Prototype schreiben muss.

Code: Alles auswählen

;-TOP

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

CompilerIf #PB_Compiler_Version > 561
  CompilerError "Warning: Check ASM-Compiler for valid RBP-Register!"
CompilerEndIf

Macro GetCaller(self) ; Get frame pointer from caller
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    EnableASM
    mov self, ebp
    DisableASM
  CompilerElse
    EnableASM
    mov self, rbp
    DisableASM
  CompilerEndIf 
EndMacro

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

; Simpler Procedures:

Procedure Add(ObjectValue.i, OperationValue.i) : ProcedureReturn ObjectValue + OperationValue : EndProcedure : Add = @Add()

Procedure Sub(ObjectValue.i, OperationValue.i) : ProcedureReturn ObjectValue - OperationValue : EndProcedure : Sub = @Sub()

Procedure Result(ObjectValue.i, OperationValue.i) : ProcedureReturn ObjectValue : EndProcedure : Result = @Result()


; For additional operators there is no need to change anything elsewhere. - e.g:

Procedure Mult(ObjectValue.i, OperationValue.i) : ProcedureReturn ObjectValue * OperationValue : EndProcedure : Mult = @Mult()

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

Prototype protoExecute(*Operation, OperationValue.i=0)

; Needs a little less memory:

Structure sObject
  Execute.protoExecute
  Value.i
EndStructure

Procedure Execute(*Operation, OperationValue.i=0)
  Protected *self.sObject
  GetCaller(*self)
  ;Debug *self
  *self\Value = CallFunctionFast(*Operation, *self\Value, OperationValue.i)
  ProcedureReturn *self\Value
EndProcedure

Procedure Init(*self.sObject)
  ; set procedure address
  *self\Execute = @Execute()
  ; set default values
  *self\Value = 0
EndProcedure

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

;-Test

Define a1.sObject
Debug "Init Object"
Init(a1)
;Debug @a1
Debug "Add and Sub"
a1\Execute(Add, 100)
a1\Execute(Sub, 20)
Debug "Result = " + a1\Execute(Result)

a1\Execute(Mult, 42)
Debug "Result * 42 = " + a1\Execute(Result)
;-Test 2

Debug "Array of object"
Dim args.sObject(1000)
For i = 1 To 1000
  Init(args(i))
Next

For i = 1 To 1000
  args(i)\Execute(Add, Random(100))
  ;args(i)\Execute(Add, 1); Random(100))
Next

For i = 1 To 1000
  r1 + args(i)\Execute(Result)
Next

Debug "Result = " + r1

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 13:38
von GPI
Dann hast du aber auch nur ein Parameter :) und irgendwie auch kompliziert. Auch der große Vorteil, das IDE die Methoden vorschlägt, fällt auch flach.

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 13:43
von mk-soft
Neues Beispiel :wink:

Code: Alles auswählen

;-TOP

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

CompilerIf #PB_Compiler_Version > 561
  CompilerError "Warning: Check ASM-Compiler for valid RBP-Register!"
CompilerEndIf

Macro GetCaller(self) ; Get frame pointer from caller
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    EnableASM
    mov self, ebp
    DisableASM
  CompilerElse
    EnableASM
    mov self, rbp
    DisableASM
  CompilerEndIf  
EndMacro

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

Prototype protoInvoke()

Structure sBox
  width.i
  height.i
  depth.i
  volume.protoInvoke
  sureface.protoInvoke
EndStructure

Procedure Volume()
  Protected *self.sBox, result.i
  GetCaller(*self)
  With *self
    result = \width * \height * \depth
  EndWith
  ProcedureReturn result
EndProcedure

Procedure Sureface()
  Protected *self.sBox, result.i
  GetCaller(*self)
  With *self
    result = \width * \height * 2 + \width * \depth * 2 + \height * \depth * 2 
  EndWith
  ProcedureReturn result
EndProcedure

Procedure New()
  Protected *self.sbox
  *self = AllocateStructure(sBox)
  If *self
    *self\volume = @Volume()
    *self\sureface = @Sureface()
  EndIf
  ProcedureReturn *self
EndProcedure

*box1.sbox = New()
*box1\width = 20
*box1\height = 40
*box1\depth = 100
Debug "Volume = " + *box1\volume()
Debug "Sureface = " + *box1\sureface()

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 15:48
von uweb
@GPI

... nur ein Parameter?
Was spricht gegen ...

Code: Alles auswählen

Prototype protoExecute(*Operation, Parameter_1.i=0, Parameter_2.i=0, Parameter_3.i=0)
... auch kompliziert?
Wenn Du das Grundgerüst erst einmal übernommen hast reduziert sich der Mehraufwand auf 'Procedure = @Procedure()'.

Code: Alles auswählen

; For additional operators there is no need to change anything elsewhere. - e.g:

Procedure Mult(ObjectValue.i, OperationValue.i) : ProcedureReturn ObjectValue * OperationValue : EndProcedure : Mult = @Mult()
Dafür reduziert sich der Aufwand an anderer Stelle.


Der große Vorteil, dass die IDE die Methoden vorschlägt, fällt tatsächlich weg. Da hast Du ganz klar Recht. Aber das ist wohl auch eine Frage der Vorlieben und Gewohnheiten.

Ein Stück weit kann ich Dich verstehen. Ich tue mich auch noch mit OO schwer. Das liegt aber u.a. auch daran, dass ich mich gerne von Fundamentalisten fern halte - egal welche Religion, welches Programmierparadigma oder was auch immer. Mit dem Trick von mk-soft kann man nach meinem Empfinden aber etwas OO-Vereinfachung nutzen ohne sich komplett auf das OO-Spiel einlassen zu müssen.

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 19:30
von GPI
Ach ich bin jetzt auch kein OOP-Fanatiker. Ich hätte gerne es in PB, aber Fred ist - aus welchen unsinnigen Gründen auch immer - dagegen.

Mit den Execute hat immer den Nachteil, das du mit den Parameter begrenzt bist, egal wie du das drehst und wendest.

Ich weis noch, die Methode wurde schon mal in Englischen Forum besprochen und da kamen sehr schnell Beispiele, wo es auf einmal nicht mehr funktioniert hat. Ich kann mich aber beim besten Willen nicht mehr erinnern. Irgendwie hat diese Lösung nie einer benutzt...

Das größte Problem ist, dass das ganze eine nicht dokumentierte Funktion von PB nutzt. Und diesmal gehts halt wirklich ins Detail runter. Wenns Blöd läuft, funktioniert das zu 99% und genau diese 1% suchst du dann ewig in Fehlerfall.

Problematisch ist auch, das in Zukunft das nicht mehr zwingend Funktioniert. Wenn das Passiert, darfst du deinen Code entweder kompliziert anpassen oder wegschmeißen.

Re: Objekt ohne Interface oder Struktur mit Funktion

Verfasst: 03.09.2017 22:20
von mk-soft
Ich weis noch, die Methode wurde schon mal in Englischen Forum besprochen und da kamen sehr schnell Beispiele, wo es auf einmal nicht mehr funktioniert hat. Ich kann mich aber beim besten Willen nicht mehr erinnern. Irgendwie hat diese Lösung nie einer benutzt...
Also diese Methode funktioniert schon seit der Version 4.0 von 8. Mai 2006 mit der Einführung von Prototypes :wink:
Muss also was anderes gewesen sein...

Habe aber selber geschrieben das es nicht unbedingt Zukunftssicher ist, aber interessant und schnell