Automatic destruction of objects on scope exit

Share your advanced PureBasic knowledge/code with the community.
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Automatic destruction of objects on scope exit

Post by tinman »

This is just an example of a method to perform automatic destruction of objects on scope exit. In this case the scope is a procedure and the method I have used is macros to replace some of the PB keywords. The macros automatically create the supporting variables used and call the required functions (basically takes a copy of a function pointer and a parameter, all of which are called on exit from the procedure).

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)
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)