When used with objects you can implement the destructor part of the "resource acquisition is initialization" idiom.
Even if you don't use it with objects, it's useful for always performing some task on exit from a procedure.
It's no different from calling all the functions on exit manually, but could you imagine if you had 20 variables to be destructed and (please no) multiple ProcedureReturns.
Code: Select all
; Uses srod's stack class
; http://www.purebasic.fr/english/viewtopic.php?t=29367
IncludePath "stack"
XIncludeFile "stackClass.pbi"
Structure void
EndStructure
Prototype Destructor(*variable.void)
Structure DestructorElement
*variable.void
*dtor.Destructor
EndStructure
Structure ScopeDestructorList
destruct_stack.StructuredStackObject
EndStructure
Procedure CreateDestructorList(*scope.ScopeDestructorList)
*scope\destruct_stack = NewStructuredStack(10, SizeOf(DestructorElement), 0)
EndProcedure
Procedure AddDestructor(*scope.ScopeDestructorList, *variable.void, *dtor.Destructor)
Protected element.DestructorElement
element\variable = *variable
element\dtor = *dtor
*scope\destruct_stack\Push(element)
EndProcedure
; Automatically cleans up the stack at the end
Procedure CallDestructors(*scope.ScopeDestructorList)
Protected element.DestructorElement
While *scope\destruct_stack\NumberOfElementsPushed()>0
*scope\destruct_stack\Pop(element)
Debug Str(*scope\destruct_stack\NumberOfElementsPushed())+" destructor 0x"+Hex(element\dtor)+" object=0x"+Hex(element\variable)
element\dtor(element\variable)
Wend
*scope\destruct_stack\Destroy()
EndProcedure
Macro ScopedProcedure(type = .l, name = , params = ())
Procedure#type name#params
Protected scope_return_value#type
Protected scope_destructor.ScopeDestructorList
CreateDestructorList(@scope_destructor)
EndMacro
Macro EndScopedProcedure
CallDestructors(@scope_destructor)
EndProcedure
EndMacro
; There needs to be something to mimic the functionality of "ProcedureReturn" with no parameter
;Macro ScopedProcedureReturn
; Push eax or copy to scope_return_value
; CallDestructors(@scope_destructor)
; Pop eax
; ProcedureReturn
;EndMacro
Macro ScopedProcedureReturn(value)
scope_return_value = value
CallDestructors(@scope_destructor)
ProcedureReturn scope_return_value
EndMacro
Macro ScopeProtected(name, type)
Protected name.type
CompilerIf Defined(type#_dtor, #PB_Procedure)
AddDestructor(@scope_destructor, @name, @type#_dtor())
CompilerEndIf
EndMacro
Structure MyClass
memory.l
EndStructure
Procedure MyClass_ctor1(*this.MyClass, size.l)
*this\memory = AllocateMemory(size)
Debug "Object at 0x"+Hex(*this)+" Allocated "+Str(size)+" bytes at 0x"+Hex(*this\memory)
EndProcedure
Procedure MyClass_dtor(*this.MyClass)
Debug "Object at 0x"+Hex(*this)+" Freeing 0x"+Hex(*this\memory)
FreeMemory(*this\memory)
*this\memory = 0
EndProcedure
ScopedProcedure(.l, foo, (a.l, b.l))
; Declare local variable
ScopeProtected(obj, MyClass)
ScopeProtected(obj2, MyClass)
ScopeProtected(obj3, MyClass)
ScopeProtected(obj4, MyClass)
; Call it's constructor (would be nice to do both of the above together)
MyClass_ctor1(@obj, 512)
MyClass_ctor1(@obj2, 512)
MyClass_ctor1(@obj3, 512)
MyClass_ctor1(@obj4, 512)
; Exit procedure
ScopedProcedureReturn(a+b)
EndScopedProcedure
Debug foo(1, 5)