Android Question Can I get the row of a code in B4A?

Hi, in my project i print in a file the name of the current module with the corresponding problem. Now my question is:"Can i get the row of a code in B4A?"
for example the final result of the print must be:"[current module _nameofthemodule_ at LineCode _getnumberoftherowcode_ _problemthatiprint_]"
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Depending on your project, you can add a line tracer.

B4X:
'    As Global, Private L_N As String
Private Sub test
    Dim x As Float = 0.0 :L_N = "test_1"
    Dim y As Float = 100 :L_N = "test_2"
    Dim z As Float = 3.14159 :L_N = "test_3"
    Try
        Dim a As Int = ""
    Catch
        Log("Last valid line " & L_N & TAB & LastException)
    End Try :L_N = "test_8"
End Sub

You can automate this by writing a helper sub that takes what's on the clipboard and appends the key elements.
You have to skip Sub and End Sub lines, For multiline structures, you have to skip all but the last line.

Obviously not elegant, but possible :)
 
Upvote 0
Depending on your project, you can add a line tracer.

B4X:
'    As Global, Private L_N As String
Private Sub test
    Dim x As Float = 0.0 :L_N = "test_1"
    Dim y As Float = 100 :L_N = "test_2"
    Dim z As Float = 3.14159 :L_N = "test_3"
    Try
        Dim a As Int = ""
    Catch
        Log("Last valid line " & L_N & TAB & LastException)
    End Try :L_N = "test_8"
End Sub

You can automate this by writing a helper sub that takes what's on the clipboard and appends the key elements.
You have to skip Sub and End Sub lines, For multiline structures, you have to skip all but the last line.

Obviously not elegant, but possible :)
I think an another idea, i dont know if it is possible but do you think could make a cpp script where i wrote only a cout that print "__LINE__" and then used him how a library on my b4a app? and then re-call it?
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
This is VERY BAD coding practice, so do not try this at home. At least not before carefully saving your project, and not before you understand fully what it does.
But just in case you want to experiment at your own risk, here are the helper Subs that add and remove code line tags.
You have been warned, I must decline responsibility for what might happen.

1. copy one or more subs in your code to clipboard (THIS REPLACES WHAT WAS ON THE CLIPBOARD)
2. run AddLineTags (I put the call early in the code, and comment and uncomment as needed)
3. paste clipboard contents

I recommend pasting it in Notepad++, so you can check validity before transferring to the project.
It will not be always valid, so modify the helper subs to suit your needs.

Same for RemoveLineTags.

Don't forget to add a global "Private L_N As String"
B4X:
Private Sub AddLineTags            'ignore
    Dim codeLines As String = fx.Clipboard.GetString
    Dim sb As StringBuilder: sb.Initialize
    Dim v() As String = Regex.Split(Chr(13) & CRLF, codeLines)
    Dim currentSub As String = "UNKNOWN"
    Dim lineNumber As Int = -1
    Dim skipTags As List = Array As String("end ", "for ", "do ", "try ", "catch ", "select ", "case ", "if ", "else ", "loop ", "next ")
    For i = 0 To v.Length - 1
        lineNumber = lineNumber + 1
        Dim s As String = v(i)
        sb.Append(s)
        Dim t As String = s.trim.ToLowerCase
        If t.Contains(" sub ") Then
            Dim k2 As Int = s.IndexOf("(")
            If k2 = -1 Then k2 = s.length
            Dim prefix As String = s.SubString2(0, k2).trim
            Dim k1 As Int = prefix.LastIndexOf(" ")
            currentSub = prefix.SubString(k1 + 1)
            lineNumber = 0
        Else
            Dim found As Boolean
            For Each g As String In skipTags
                If t.StartsWith(g) Then
                    found = True
                    Exit
                End If
            Next
            If found = False Then sb.Append($" :L_N = "${currentSub}_${lineNumber}""$)
        End If
        If i < v.Length - 1 Then sb.append(CRLF)
    Next
    fx.Clipboard.SetString(sb.ToString)
End Sub

Private Sub RemoveLineTags            'ignore
    Dim codeLines As String = fx.Clipboard.GetString
    Dim sb As StringBuilder: sb.Initialize
    Dim v() As String = Regex.Split(Chr(13) & CRLF, codeLines)
    For i = 0 To v.Length - 1
        Dim s As String = v(i)
        Dim k As Int = s.IndexOf(" :L_N =")
        If k > - 1 Then s = s.SubString2(0, k)
        sb.Append(s)
        If i < v.Length - 1 Then sb.append(CRLF)
    Next
    fx.Clipboard.SetString(sb.ToString)
End Sub
 
Last edited:
Upvote 0
Uhm
This is VERY BAD coding practice, so do not try this at home. At least not before carefully saving your project, and not before you understand fully what it does.
But just in case you want to experiment at your own risk, here are the helper Subs that add and remove code line tags.
You have been warned, I must decline responsibility for what might happen.

1. copy one or more subs in your code to clipboard (THIS REPLACES WHAT WAS ON THE CLIPBOARD)
2. run AddLineTags (I put the call early in the code, and comment and uncomment as needed)
3. paste clipboard contents

I recommend pasting it in Notepad++, so you can check validity before transferring to the project.
It will not be always valid, so modify the helper subs to suit your needs.

Same for RemoveLineTags.

Don't forget to add a global "Private L_N As String"
B4X:
Private Sub AddLineTags            'ignore
    Dim codeLines As String = fx.Clipboard.GetString
    Dim sb As StringBuilder: sb.Initialize
    Dim v() As String = Regex.Split(Chr(13) & CRLF, codeLines)
    Dim currentSub As String = "UNKNOWN"
    Dim lineNumber As Int = -1
    Dim skipTags As List = Array As String("end ", "for ", "do ", "try ", "catch ", "select ", "case ", "if ", "else ", "loop ", "next ")
    For i = 0 To v.Length - 1
        lineNumber = lineNumber + 1
        Dim s As String = v(i)
        sb.Append(s)
        Dim t As String = s.trim.ToLowerCase
        If t.Contains(" sub ") Then
            Dim k2 As Int = s.IndexOf("(")
            If k2 = -1 Then k2 = s.length
            Dim prefix As String = s.SubString2(0, k2).trim
            Dim k1 As Int = prefix.LastIndexOf(" ")
            currentSub = prefix.SubString(k1 + 1)
            lineNumber = 0
        Else
            Dim found As Boolean
            For Each g As String In skipTags
                If t.StartsWith(g) Then
                    found = True
                    Exit
                End If
            Next
            If found = False Then sb.Append($" :L_N = "${currentSub}_${lineNumber}""$)
        End If
        If i < v.Length - 1 Then sb.append(CRLF)
    Next
    fx.Clipboard.SetString(sb.ToString)
End Sub

Private Sub RemoveLineTags            'ignore
    Dim codeLines As String = fx.Clipboard.GetString
    Dim sb As StringBuilder: sb.Initialize
    Dim v() As String = Regex.Split(Chr(13) & CRLF, codeLines)
    For i = 0 To v.Length - 1
        Dim s As String = v(i)
        Dim k As Int = s.IndexOf(" :L_N =")
        If k > - 1 Then s = s.SubString2(0, k)
        sb.Append(s)
        If i < v.Length - 1 Then sb.append(CRLF)
    Next
    fx.Clipboard.SetString(sb.ToString)
End Sub
Uhm.....no no very very bad in this way, another solution that i think was a "inline java" with a easy function just like:

'#If JAVA
' Public int print()
' {
' Return Thread.currentThread().getStackTrace()[1].getLineNumber();
' }
'#End If

So in sub i call it in this way:
Native.InitializeContext
Dim linecodestr As String = Native.RunMethod("print",Null)
Log(linecodestr)
actually print a int value but is always '580' and i dont know why, my log was at 62 row and it print me 580, why? xD
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
The line number in release mode is the line in the compiled code and not useful for debugging.
When an error occurs in release mode, the logs already show this number, you don't need the inline java.
Almost always, I have to change from release to debug mode to trace my errors.
 
Last edited:
Upvote 0

MicroDrie

Well-Known Member
Licensed User
Longtime User
It's actually not that hard at all to make an error routine. You actually already answered the question yourself in the first post.
the print must be:"[current module _nameofthemodule_ at LineCode _getnumberoftherowcode_ _problemthatiprint_]"
Breaking this requirement down into three variables in the following error loggigng routine:
B4X error routine:
Public Sub dLog(nameOfTheModule As String, getNumberOfTheRowCode As String, problemThatiPrint As String)
    Log($"[Current module: ${nameOfTheModule} at LineCode ${getNumberOfTheRowCode} ${problemThatiPrint}]"$)
End Sub

Then the hardest part to create a fault test routine to make some mistakes to generate logging:

Generate errors to test:
'        --- prevent error detection by IDE, use variable
    Dim FileName As String = "NoExist.txt"
    If File.Exists(File.DirAssets, FileName) = False Then
        Dim getNumberOfTheRowCode As String = "unique Err_0200"   
        dLog(theModuleName, getNumberOfTheRowCode, problemThatiPrint)
    End If
   
'    --- Generate  file error
    Dim FileName As String = ""
    Try
'        --- Generate a error
        Dim LineNumber As Int
        LineNumber = File.ReadString(File.DirAssets, FileName)
        Log(File.ReadString(File.DirAssets, FileName))
'        --- prevent error detection by IDE, useless program line
        Log(LineNumber)
    Catch
        If FileName = "" Then
'            --- Log the specific error
            Dim getNumberOfTheRowCode As String = "unique Err_0210"
            Dim problemThatiPrint As String = "No linenumber exist."
            dLog(theModuleName, getNumberOfTheRowCode, problemThatiPrint)
        End If
    End Try
   
    Try
        Dim x As Int = 12
        Dim y As Int = Null
        Dim z As Int = x / y
        Log(z)
    Catch
        Dim getNumberOfTheRowCode As String = "unique Err_0220"
        Dim problemThatiPrint As String = "division by 0 error."
        dLog(theModuleName, getNumberOfTheRowCode, problemThatiPrint)
'        Log(LastException)
    End Try

Follow the following steps:
  1. Put the B4XPages title name / form name in a global variable so that you can see it in the error routine.
  2. Provide a unique error code and accompanying brief explanation other than "An unknown error has occurred!" which you can search back with the Ctrl-F search
  3. Think about where something can go wrong and use an IF THEN or TRY line calling the error routin
1664225625328.png

Attached is a B4XPages example of a B4X test implementation.
 

Attachments

  • DetailError.zip
    7.1 KB · Views: 71
Upvote 0
Top