[Feature Request-v6] Add defer for out of context actions.

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

[Feature Request-v6] Add defer for out of context actions.

Post by skywalk »

Please add a defer statement as proposed in several C replacement languages.
Defer statements are used to defer method calls or entire code blocks until a given scope is exited.
This would reduce memory errors as the user would immediately place the free very close to the allocate line.

Code: Select all

Procedure Doit()
  *b = AllocateMemory(2)
  defer 
    If *b
      FreeMemory(*b)
    EndIf
  enddefer
  ;..
  ;..lots of code here
  ;..
EndProcedure
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
tored
User
User
Posts: 60
Joined: Wed Feb 16, 2022 12:47 pm
Location: Sweden

Re: [Feature Request-v6] Add defer for out of context actions.

Post by tored »

Defer would be really sweet, reaching for it now, have complex procedure that does several different allocations, hard to separate, prodding against messy win32 api.

Here is macro implementation with all its drawbacks a user land macro always has.

Code: Select all

DeclareModule Defer
  EnableExplicit
  
  Macro Defer
    Global DeferLabelAddress#MacroExpandedCount = ?DeferLabel#MacroExpandedCount
    Runtime DeferLabelAddress#MacroExpandedCount
    
    CompilerIf Defined(DeferLabelStart, #PB_Variable)
      DeferLabelCount + 1
    CompilerElse
      Protected DeferLabelStart, DeferLabelCount, DeferLabelReturn, DeferLabelAddress
      DeferLabelStart = MacroExpandedCount
      DeferLabelCount = MacroExpandedCount
    CompilerEndIf
    
    Goto DeferLabelJump#MacroExpandedCount
    DeferLabel#MacroExpandedCount:
  EndMacro
  
  Macro EndDefer
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C
      !goto *v_deferlabelreturn;
    CompilerElse
      EnableASM
      jmp DeferLabelReturn
      DisableASM
    CompilerEndIf
    DeferLabelJump#MacroExpandedCount:
  EndMacro
  
  Macro DeferRun()
    DeferLabelReturn =  ?DeferLabelReturn#MacroExpandedCount
    DeferLabelReturn#MacroExpandedCount:
    If DeferLabelCount >= DeferLabelStart
      DeferLabelAddress = GetRuntimeInteger("DeferLabelAddress" + DeferLabelCount)
      DeferLabelCount - 1
      CompilerIf #PB_Compiler_Backend = #PB_Backend_C
        !goto *v_deferlabeladdress;
      CompilerElse
        EnableASM
        jmp DeferLabelAddress
        DisableASM
      CompilerEndIf
    EndIf
  EndMacro
  
  Macro DeferReturn(ret = #Null)
    DeferRun()
    ProcedureReturn ret
  EndMacro
  
EndDeclareModule

Module Defer
EndModule



UseModule Defer

Procedure Worker(value)
  Static count = 0
  count + 1
  
  Debug #PB_Compiler_Procedure + " Call " + count
  Debug #PB_Compiler_Procedure + " Call " + count + " Allocate memory"
  Defer
  Debug #PB_Compiler_Procedure + " Call " + count + " Defer 1 Free memory"
  EndDefer
  
  If value = #False
    Debug #PB_Compiler_Procedure + " Call " + count + " Return"
    DeferReturn(#False)
  EndIf
  
  Debug #PB_Compiler_Procedure + " Call " + count + " Allocate more memory"
  
  Defer
  Debug #PB_Compiler_Procedure + " Call " + count + " Defer 2 Free more memory"
  EndDefer
  
  Debug #PB_Compiler_Procedure + " Call " + count + " Return"
  DeferReturn(#True)
EndProcedure

Procedure Main()
  Debug #PB_Compiler_Procedure
  Debug #PB_Compiler_Procedure + " Allocate memory"
  Defer
  Debug #PB_Compiler_Procedure + " Defer Free memory"
  EndDefer
  
  Debug #PB_Compiler_Procedure + " Call Worker"
  Worker(#False)
  Debug #PB_Compiler_Procedure + " Do stuff"
  
  Debug #PB_Compiler_Procedure + " Call Worker again"
  Worker(#True)
  
  Debug #PB_Compiler_Procedure +  " Return"
  DeferReturn()
EndProcedure

Main()
Enjoy!

EDIT: Fix so that defers are called in Last In First Out order
EDIT: Added C backend support. Made variable names some what longer too minimize risk of collisions.
Post Reply