Branch crossing local variable definition

General FreeBASIC programming questions.
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

Mr Swiss wrote:Just thought, it might have something to do, with your apparent "seeing links" issue,
you've once mentioned. :-)
Image
The top screenshot is how most, if not all of you, see links. The bottom screenshot is how I see them. I have an extension to my web browser which allows me to change some aspects of a web page of a specific site.

It maybe that what you see in the above are not that different but to me the top one is hardly different to 'normal' text whereas the bottom one is royal blue;as I see it.
Last edited by deltarho[1859] on Mar 02, 2018 22:59, edited 1 time in total.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Branch crossing local variable definition

Post by fxm »

Error (when crossing complex variable):

Code: Select all

scope
  dim as integer I
  goto label
  dim as string s
  label:
end scope
  • error 105: Branch crossing local array, var-len string or object definition, to label: LABEL, local string: S
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Branch crossing local variable definition

Post by fxm »

More information in the thread: Impossible to redefine END + branch-crossing bug
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

It seems to me that the only time that we can jump over a definition is when that definition is within a scope or Scope and we are jumping over the scope or Scope.

So, instead of having a long list of what we cannot do we simply have a short list, of one, of what we can do. <smile>
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Branch crossing local variable definition

Post by fxm »

Most simply:
Do not use GOTO.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Branch crossing local variable definition

Post by dodicat »

As in the link i could again suggest asm call.
But it seems to be broken in 64 bits.

Code: Select all

 


scope
  dim as integer I
  asm call label
  dim as string s
  asm label:



#ifdef s
print "yes"
#else
print "no"
#endif


end scope
sleep


  
But not a great loss IMHO
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

@fxm Had a look at Tourist Trap's thread. What a nightmare.
Most simply:
Do not use GOTO.
Out of the question. I use a lot of error trapping when using the Windows crypto' APIs. Getting a successful compilation is not difficult but with some procedures having double figure parameter lists getting NTSTATUS values other than zero is a piece of cake. Very often we don't get a run-time error but just garbage results. It is essential that the return values are checked. Pulling the ripcord with a GoTo is the best way, by far, to land safely at the error trapping code. We could be nested up to our arm pits but GoTo couldn't care less - it comes flying in just like Superman. I send GoTo a bottle of Scotch every Christmas. <smile>
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

@dodicat You keep doing things which I have never seen before. I did not think that Asm could be used without it's sibling 'End Asm'.

We could use jmp instead of Call.

I don't use ASCII labels in assembly any more - gcc's -03 sometimes gets it's knickers in a twist so I would use:

Code: Select all

Scope
  Dim As Integer I
  Asm jmp 0f
  Dim As String s
Asm 0:
	#Ifdef s
	Print "yes"
	#Else
	Print "no"
	#Endif
End Scope
Sleep
I was expecting 'no' to be printed.

Nice solution to what I now regard as a GoTo bug. <smile>
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: Branch crossing local variable definition

Post by caseih »

Will using ASM jumps actually work without crashing, though? Every time you start a new scope, FB is moving the stack pointer along to allocate space for the scope-local variables. Might want to check the assembly output to make sure this isn't doing something bad. If FB is giving you an error when trying to goto, I suspect it's for good reason.
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

I have been using error trapping for years and always placed my error trapping code at the bottom of Subs/Functions. I cannot remember using GoTo for anything other than error trapping. I need to go back to Flowcharting days and pre-procedural coding.

Hows about putting the error trapping code at the top of Subs/Functions.

This returns -1 and it does not matter where 'Dim As Integer nbloaded' is placed.

Code: Select all

Function foo() As Long
Dim As Long lError
 
  Goto SkipErrorTrap
ErrorTrap:
  Return lError
SkipErrorTrap:
 
  'lError = -1
  'Goto ErrorTrap
 
  Dim As Integer nbloaded
 
  lError = -1
  Goto ErrorTrap
 
End Function
 
Print foo
 
Sleep

Provided we GoTo the north and not to the south then we are in the clear.

This will not solve 'Branch crossing local variable definition' in general but I am not interested in a general solution - just using GoTo for error trapping. Well, not just error trapping. I may want to exit a procedure prematurely but do some tidying up before I leave. I often have handles to destroy. To avoid 'Branch crossing ...' in this case we could do this.

Code: Select all

Function foo() As Long
Dim As Long lError
 
  Goto SkipHeader
TidyUp:
  ' Do some tidying up
  Return lError
SkipHeader:
 
  Dim As Integer nbloaded
 
  Goto TidyUp
 
  'lError = -1
  'Goto TidyUp

  GoTo TidyUp ' Nothing untoward happened but we still need to tidy up, perhaps. 
End Function
 
Print foo
 
Sleep
If all is well we just GoTo TidyUp and the function returns 0. If not all well we can set lError to some value and still GoTo TidyUp.

Can anyone see a fault in this approach?
coderJeff
Site Admin
Posts: 4323
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Branch crossing local variable definition

Post by coderJeff »

deltarho, I think I can see what you are trying to do: you have a subroutine with a hundred things that could go wrong and may need to bail-out, but want one clean-up routine only. yea, I would typically put the clean-up code at the end as well.

General solution, without considering too many specifics, I think is, if the tidy-up routine needs to know about a variable, then it needs to be available from the start of main routine. And any nested local scope variables need to be nested local scope only.

Hopefully this response gives you a better understanding.

These are warnings/errors that occur when you tell the compiler to do things in an order it does not expect and can't handle.

Branch crossing local variable definition
Branching to scope block containing local variables
Branching to other functions or to module-level
Branch crossing local array, var-len string or object definition

In fb dialect, the compiler processes the DIM statements in the order they appear and where they appear. You can only reference & access the variable after it has been declared & defined, and only while it is in scope.

The DIM statement is all of these things:
declaration: tells the compiler the name of the variable and it's type
definition: allocates memory for the variable's data
initialisation: loads the the variable's data with a default value, or results of an expression, or calls a TYPE constructor method

The SCOPE statement and compound statements list DO:LOOP, IF:END IF, etc, introduce a new local scope.
So,
DO
EXIT DO
LOOP
is equivalent to:
DO: SCOPE
EXIT DO
END SCOPE: LOOP

EXIT DO (and similar statements) are GOTOs in disguise. They will jump to the end of the compound block. This is why you can GOTO (south) out of a scope. (Hopefully not to fan the flames of a GOTO debate, but I think it's a matter of personal preference to use GOTO. It's a valid statement and up to the programmer to make appropriate use of it).

When a variable goes out of scope:
de-initialisation: the variable's object destructor is called, if one exists.
un-definition: memory is deallocated: an objects the DELETE method is called; for STRING types, memory is freed; stack memory is released; etc
un-declaration: the compiler forgets about the variable

The reason for the warnings & errors are: if using GOTO to jump to a point in the code before the variable has been declared (north), you are going to a place where the variable does not exist, yet. Or if you using GOTO to skip ahead (south) you might be skipping over important steps of defining & initializing a variable.

As already mentioned, there are differences in dialect, with fb dialect being the most strict. The fblite and qb dialect are more relaxed in that the the variable's declaration & definition are moved to the beginning of the SCOPE, but more restricted in other ways, can only use some kinds of initializer's at certatin times.
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

Thanks coderJeff. Blimey, that is a good read for quite a few folk here I should imagine.

I am not interested in a variable but what line did the error occur in and what the value of the error is.

In some recent work I put an API return value in dwStatus and did this:

Code: Select all

If dwStatus <> STATUS_SUCCESS Then lError = 30 : Goto ErrorTrap
and at ErrorTrap I have

Code: Select all

TDWrapper Hwnd, "Error", "Error at Position " + Str(lError) + " " + Hex(dwStatus), TD_ERROR_ICON
TDWrapper is simply a TaskDialog wrapper. lError is the offending API, each one gets a unique id, and dwStatus is the NTSTATUS error code.

So, I know which API 'went down' and why.

Code: Select all

0xC000000D STATUS_INVALID_PARAMETER
is a PITA when you are looking at 10 parameters and are not told which one is the culprit. Maybe that is easier said than done.
coderJeff
Site Admin
Posts: 4323
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Branch crossing local variable definition

Post by coderJeff »

heh, it seems with windows you need to call 100's of API functions just to get to the thing you actually want to do. Here's a generic non-windows example that might give you an idea. Two main things about it is that is uses a macro #define to wrap some common code, and the __LINE__ built-in define to capture the line number. The fake API randomly passes or fails, so the output is different each time you run it.

Code: Select all

function SomeAPIFunc( byref arg as string ) as boolean
	print "Calling API " & arg
	'' 50% chance of success, 50% chance of failure
	return cbool( rnd > 0.5 )
end function

function DoStuff() as boolean 
	'' multiple API calls just to do one thing successfully

	'' variable to store the line number
	dim lerror as integer
	
	'' wrapper to set current line number and jump to ErrorTrap
	#undef BAILOUT
	#define BAILOUT( ) lerror = __LINE__: goto ErrorTrap
	
	'' some object we might be progressively initializing 
	dim progress as string = "begin"
	 
	'' return status for our API calls
	dim ret as boolean
	
	'' begin the initializing sequence
	scope
		dim x as string = "one: x"
		ret = SomeAPIFunc( x )
		if( ret = false ) then BAILOUT()
		progress = x
	end scope
		
	scope
		dim x as string = "two: x"
		ret = SomeAPIFunc( x )
		if( ret = false ) then BAILOUT()
		progress = x
		
		dim y as string = "two: y"
		ret = SomeAPIFunc( y )
		if( ret = false ) then BAILOUT()
		progress = y
		
	end scope
	
	scope	
		dim x as string = "three: x"
		ret = SomeAPIFunc( x )
		if( ret = false ) then BAILOUT()
		progress = x
	end scope
	
	'' made it through the gauntlet - success
	progress = "Success"
	return true

ErrorTrap:
	'' bailed out
	print "ErrorTrap at line " & lerror
	print "Our progress got as far as  '" & progress & "'' successfully"
	print "...need to clean-up accordingly"
	return false
	
end function

randomize timer

dim ret as boolean
ret = DoStuff()
print "End result of DoStuff() = " & ret
Example of some output:

Code: Select all

Calling API one: x
Calling API two: x
Calling API two: y
ErrorTrap at line 39
Our progress got as far as  'two: x'' successfully
...need to clean-up accordingly
End result of DoStuff() = false
Now, if you want to show some information like a dialog box that captures line number but return to the same place as where it occurred then, you can still use __LINE__ built-in but call a subroutine instead. fb dialect won't allow GOSUB, what would have been common in other basics.
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

coderJeff wrote:might give you an idea.
It has.

My first thought was an API failure could have a cascading effect. Some crypto' APIs need the result of one just before it, and it needs the result of one just before it and so on. Sometimes I will have a BASIC error followed by half a dozen others and then see them all disappear on correcting the first one.

Anyway, thanks for that - there is definitely some food for thought there.
deltarho[1859]
Posts: 4305
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Branch crossing local variable definition

Post by deltarho[1859] »

@coderJeff

I had an opportunity this evening to go through your error trapping code. When I first glanced at it, it looked like it reported a failure and then kept ploughing on. I can see now that it doesn't do that: It reports on successful calls and bails out on a failure; as my code does.

So, your code is pretty much the same as mine but with a much better approach with regard where failures, if any, take place. I give each API a unique id, 10, 20, 30 and so on, whereas you advise the actual line number via __LINE__. I had seen that in the manual but never used it. Once I inadvertently used an id twice, as in 10, 20, 30, 30 ,40, which had me scratching my head for a while. That cannot happen with __LINE__,

My next project will be using __LINE__.

Thanks.
Post Reply