Page 1 of 1

Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to do.

Posted: Sat Oct 09, 2021 3:42 am
by Axeman
Something I always find myself scratching my head over is whether or not it is valid to use ProcedureReturn from inside a loop. It's extremely useful to be able to do this as loops are often needed when detecting abort conditions for a procedure, and using flag variables to trigger a ProcedureReturn adds extra code and complexity.

My concern is that using ProcedureReturn in that case will leave cruft behind due to loop initialization data, etc, not being cleaned up. So, ideally the documentation should be perfectly clear on whether or not using ProcedureReturn from within a loop is valid.

The current docs say:-
A call of ProcedureReturn exits immediately the procedure, even when its called inside a loop.
Which tells me nothing about whether or not it is a valid thing to do. Assuming calling it within a loop is valid then I'd rather it say:-
A call to ProcedureReturn immediately exits the procedure, even when it's called inside a loop. Note that calling ProcedureReturn from within a loop is valid, as all resources are cleaned up and no issues can arise from this approach.
Assuming calling it within a loop is not valid then I'd rather it say:-
A call to ProcedureReturn immediately exits the procedure, even when it's called inside a loop. Note that calling ProcedureReturn from within a loop is NOT valid, as some resources are not properly cleaned up and issues can arise from this approach.
Because I'm unsure which approach is valid I find myself using code such as that below.

Code: Select all

; Ensure that the nodes do not have an existing straight connection between them.
abort = 0
ForEach StraightPathList()
	If ( *node1 = StraightPathList()\node1 And *node2 = StraightPathList()\node2 ) Or ( *node2 = StraightPathList()\node1 And *node1 = StraightPathList()\node2 ) ; If the nodes are connected in either order.
		abort = 1 ; Flag that the function should abort and return a zero to indicate that the nodes can't connect.
		Break
	EndIf
Next
If abort : ProcedureReturn 0 : EndIf

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sat Oct 09, 2021 4:27 am
by jacdelad
You can leave every loop (for, while, repeat...) and if/select structure safely. You only have to be careful when using gosub/goto within these (I, for one, have never used goto/gosub).

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sat Oct 09, 2021 6:38 pm
by juergenkulow

Code: Select all

; ProcedureReturn with CBackend Compilers\pbcompilerc.exe /Commented
Global *p
Procedure Test()          ; static integer f_test() { integer r=0; integer v_i=0;
  Shared *p               ; 
  Protected i=1           ;   v_i=1;
  While i<100             ;   while (1) { If (!(v_i<100)) { Break; }
    *p=AllocateMemory(42) ;     integer rr0=PB_AllocateMemory(42); p_p=(void*)rr0;
    Debug i               ;
    If i=13               ;     if (!(v_i==13)) { goto no3; }
      ProcedureReturn     ;       r=0; Goto end; //------------------------------------------------
    EndIf                 ;     no3:;                                                          // |
    i+1                   ;     v_i=(v_i+1);                                                   // |
    FreeMemory(*p)        ;     integer p0=(integer)p_p; integer rr1=PB_FreeMemory(p0);        // |
    *p=0                  ;     p_p=0;                                                         // |
  Wend                    ;     } il_wend1:;                                                   // |
EndProcedure              ;   end: Return r; }   //<-- ProcedureReturn jumps to the label end. ----

Test()                    ; integer rr0=f_test();
Debug Hex(*p)             ; 44012F0 // Memory leak 

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sat Oct 09, 2021 8:24 pm
by mk-soft
The resources of a procedure are deleted. Also with a ProcedureReturn, because it jumps to the "end".
// Procedure foo()
static integer f_foo() {
integer r=0;
integer v_i=0;
void* v_s1=0;
pb_list t_s2={0};
// Protected s1.s = "Hello", i
SYS_FastAllocateStringFree4(&v_s1,_S1);;
// Protected NewList s2.s()
PB_NewList(8,&t_s2,ms_s,8);;
//
// AddElement(s2())
void* p0=(void*)(t_s2.a);
integer rr0=PB_AddElement(p0);
// s2() = s1
SYS_PushStringBasePosition();
SYS_CopyString(v_s1);
SYS_AllocateString4(&(*(void**)&t_s2.b->c),SYS_PopStringBasePosition());
//
// For i = 1 To 10
v_i=1;
while(1) {
if (!((integer)10>=v_i)) { break; }
// If i = 3
if (!(v_i==3)) { goto no4; }
// ProcedureReturn
r=0;
goto end;
// EndIf
no4:;
// Next
next1:
v_i+=1;
}
il_next2:;
//
// Debug s2()
//
// EndProcedure
end:
SYS_FreeString(v_s1);
PB_FreeList(t_s2.a);
return r;
}

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sun Oct 10, 2021 1:39 am
by Axeman
It's good to know that it is a valid thing to do that won't lead to complications, so thanks for the info.

That fact needs to be made clear in the docs, though. That way, new Purebasic users who don't have experience with programming best practices aren't left in any doubt on the matter.

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sun Oct 10, 2021 10:09 am
by jacdelad
The help tells it for loops, but I agree for the rest. A clear sentence would surely help.

Re: Calling ProcedureReturn from within a loop. More information needed in docs to clarify if this is a valid thing to d

Posted: Sun Oct 10, 2021 12:10 pm
by mk-soft
But remember that resources you have created yourself must also be released again.
This applies to AllocateMemory, AllocateStructure, dynamic objects with PB_Any (CreateImage, etc).