Can I get info about SUB inside SUB?
#1
Hi,

if code is running inside a SUB, can I somehow get the SUB name?
This would be great to have for debugging or logging.
Reply
#2
Must run your code inside the QB64 IDE, and enable debug mode. ("Debug" menu has "Call Stack" and "Watch List" options.) Press [F7] or [F8] instead of [F5] to start your program inside the IDE. Aside from that, I don't know what to tell you because I have never used the IDE like that before.
Reply
#3
If you can't figure out debug 

DIM Shared Debug as Long
DIM Shared SubName$ 
in all your subs set that name SubName$ = ... in  Sub (and maybe set to "main" or "" when exit sub?)

have a clause If Debug Then Print "Sub is ";SubName$

When Debug is set to -1 then it prints
If turn off, ie Debug = 0
then that clutter is removed.

Notice you can also comment out 'If Debug Then Print "Sub is ";SubName$
to pin down just the subs you are interested in.

Or Instead of print, try out MessageBox so dont mess up screen. Caveat - this does not work well in a fast loop!
b = b + ...
Reply
#4
(05-08-2023, 04:37 PM)thesolarcode Wrote: Hi,

if code is running inside a SUB, can I somehow get the SUB name?
This would be great to have for debugging or logging.

What mnrvovrfc said is probably right, but I'll share my caveman method of tracing my code and errors, when such features are not readily available, in case it helps...
I'm not saying this is the best method, but it has worked for me in the past. 

Here is the general pattern which should give you the gist. 
(You could use a global shared variable like bplus, I just opted to use local variables to preserve the state for nested calls, etc.)

I should probably look at the built-in debugging features, it can't hurt.

Code: (Select All)
' /////////////////////////////////////////////////////////////////////////////

Sub MySub(param1%, param2%, etc.)
    Dim RoutineName As String : RoutineName = "MySub"
    Dim sError As String : sError = ""
    Dim sOperation As String : sOperation = "(variable declarations)"
    Dim iLoop1%
    Dim MyValue1%
    Dim MyValue2%
    (rest of declarations)
    
    If len(sError)=0 then
        for iLoop1% = param1% to param2%
            sOperation = "MyValue1% = MyFunction$(iLoop1%, etc.)"
            sError = MyFunction$(iLoop1%, etc.)
            If len(sError) > 0 then exit for
        next iLoop1%
    End If
    
    If len(sError)=0 then
        (do next thing)
    End If
    
    Etc.
    
    If Len(sError) > 0 Then
        PrintDebugFile "Error in " & RoutineName & " at " & sOperation & ": " & sError
    End If
    
End Sub ' MySub

' /////////////////////////////////////////////////////////////////////////////

Function MyFunction$(param3%, etc.)
    Dim RoutineName As String : RoutineName = "MyFunction$"
    Dim sError As String : sError = "" ' If routine failed, we return empty string, else return error message.
    Dim sOperation As String : sOperation = "(variable declarations)"
    Dim MyValue3%
    (rest of declarations)
    
    If len(sError)=0 then
        sOperation = "if param3% = 0 then"
        if param3% = 0 then
            sError = "Error in " + RoutineName + ": param3% can't be 0"
        end if
    End If
    
    If len(sError)=0 then
        sOperation = "sError = MySubFunction$(MyValue3%, etc.)"
        sError = MySubFunction$(MyValue3%, etc.)
    End If
    
    If len(sError)=0 then
        (do something else)
        (if some condition fails then put error message in variable sError)
    End If
    
    etc.
    
    If Len(sError) > 0 Then
        PrintDebugFile "Error in " & RoutineName & " at " & sOperation & ": " & sError
    End If
    
    MyFunction$ = sError
End Function ' MyFunction$

' /////////////////////////////////////////////////////////////////////////////

Function MySubFunction$(param4%, etc.)
    Dim RoutineName As String : RoutineName = "MySubFunction$"
    Dim sError As String : sError = ""
    
    If len(sError)=0 then
        '(if some condition fails then put error message in variable sError)
    End If
    
    etc.
    
    MySubFunction$ = sError
End Function ' MySubFunction$
Reply
#5
Wouldn't the easiest way just be to assign a global variable for debug tracing?

DIM SHARED ProgFlow$

SUB Foo
   ProgFlow$ = "Foo Start"
   .... sub stuff
   ProgFlow$ = "Foo Clean Exit"
END SUB
Reply
#6
(05-08-2023, 07:18 PM)SMcNeill Wrote: Wouldn't the easiest way just be to assign a global variable for debug tracing?

Code: (Select All)
DIM SHARED ProgFlow$

SUB Foo
   ProgFlow$ = "Foo Start"
   .... sub stuff
   ProgFlow$ = "Foo Clean Exit"
END SUB

Go further and write a QB64 program that creates a copy of QB64 source code that does this. It wouldn't be very difficult, easier because QB64PE (yet) doesn't support nested subprogram definitions. Only detect "SUB" or "FUNCTION" header and place a line right after it. Then set a flag indicating the processor is inside a subprogram, so it doesn't get confused with "main module" level code. Then when "END SUB" or "END FUNCTION is encountered put a line before that saying the subprogram is about to exit. Also have to check "EXIT SUB" and "EXIT FUNCTION" for it.

With "FUNCTION" get even fancier and even report what type it returns. "Entered function ''Zeroes'' which returns STRING."

It takes more effort to use such an utility, that's why some people expected the IDE to be able to do it.
Reply
#7
(05-08-2023, 07:18 PM)SMcNeill Wrote: Wouldn't the easiest way just be to assign a global variable for debug tracing?

DIM SHARED ProgFlow$

SUB Foo
   ProgFlow$ = "Foo Start"
   .... sub stuff
   ProgFlow$ = "Foo Clean Exit"
END SUB

Pretty much what I do. Here is a snippet of code from a massive library I'm working on:

Code: (Select All)
    '+------------------+
    '| Check for errors |
    '+------------------+

    __CURRENT_ROUTINE = "__SET_AUTO_MOTION"
    IF IUO__IS_OBJECT_VALID(Handle) = 0 THEN __ERROR "The object specified does not exist."
    IF FPS <> __GLOBALFPS THEN
        IF FPS < 1 OR FPS > GL_FPS.FPS THEN __ERROR "FPS must be between 1 and global FPS setting of" + STR$(GL_FPS.FPS) + "."
    END IF
    __PREVIOUS_ROUTINE = __CURRENT_ROUTINE

I have two global shared variables, __CURRENT_ROUTINE and __PREVIOUS_ROUTINE. I set __CURRENT_ROUTINE, perform my error checks and report any errors. If no errors exist I then set __PREVIOUS_ROUTINE so I have a trail from one routine to the next. __ERROR is used to report any errors.

Code: (Select All)
SUB __ERROR (Message AS STRING) '                                                                                                       __ERROR |
    ' __________________________________________________________________________________________________________________________________________|____
    '/                                                                                                                                               \
    '| Set screen to text mode and display error information passed in.                                                                              |
    '|                                                                                                                                               |
    '| __ERROR "Message"                                                                                                                             |
    '|                                                                                                                                               |
    '| NOTE: Fatal error trap - halts program execution.                                                                                             |
    '\_______________________________________________________________________________________________________________________________________________/

    _FULLSCREEN _OFF '                                                    turn off full screen if active
    SCREEN 0, 0, 0, 0 '                                                   set screen to pure text mode
    CLS '                                                                 clear screen
    PLAY "l64o3ao2ao1ao3ao2ao1ao3ao2ao1a" '                               get developer's attention
    COLOR 12, 0
    PRINT '                                                               print error message
    PRINT " Game Library has encountered the following error condition:"
    COLOR 15, 0
    PRINT
    PRINT " Error in routine: ";
    COLOR 14, 0
    PRINT __CURRENT_ROUTINE
    COLOR 15, 0
    PRINT " Previous routine: ";
    COLOR 14, 0
    PRINT __PREVIOUS_ROUTINE
    COLOR 11, 0
    PRINT
    PRINT " "; Message
    COLOR 7, 0
    _KEYCLEAR '                                                           clear all key buffers
    END '                                                                 terminate with "Press any key to continue..."

END SUB
Software and cathedrals are much the same — first we build them, then we pray.
QB64 Tutorial
Reply
#8
Yeah so who needs Debug of IDE? ;-))

DIYD = Do It Yourself Debug

Way more fun because you programmed it yourself so you know how it's supposed to work, supposedly ;-))
b = b + ...
Reply
#9
Thanks for all the ideas. 

I wanted to do something like this:
SUB ApiOne
   logging "start"
   ...
   logging "end"
END SUB
But without adding the sub names into all the code.

Thinking about some simple preprocessor that could inject the sub names automatically.

For example if we have:
logging "$SUBNAME$: start"
it would be replaced with:
logging "ApiOne: start"

Soooo, how hard is it to implement a simple preprocessor?
Reply
#10
(05-10-2023, 03:16 PM)thesolarcode Wrote: Thanks for all the ideas. 

I wanted to do something like this:
SUB ApiOne
   logging "start"
   ...
   logging "end"
END SUB
But without adding the sub names into all the code.

Thinking about some simple preprocessor that could inject the sub names automatically.

For example if we have:
logging "$SUBNAME$: start"
it would be replaced with:
logging "ApiOne: start"

Soooo, how hard is it to implement a simple preprocessor?

How about you make a template to copy/paste into a new sub and after you have a name copy/paste that into the template.

You could also write some code to read your bas files and insert stuff right after Sub/Function definition and just before End Sub/Function but then you have to direct all Exit Subs... to the end so maybe a line label in template?

Wait would a line label in a sub need a unique name from the entire program of just in the sub? Don't know... sounds like a fun little experiment... stay tuned.
b = b + ...
Reply




Users browsing this thread: 5 Guest(s)