Android Question Passing values by Reference or by Value

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I want convert some code from my Arduino libraries I wrote, to B4X enviroment, and I've encountered some problems.

This is a small part of Arduino code I want to convert:
B4X:
bool WaveFile::ReadSample(uint8_t& sample) {
  if (GetBitsPerChannel() != 8) {
    error = "Read sample size mismatch (need uint8_t)";
    return false;
  }

  return ReadRaw((uint8_t*) &sample, 1);
};

bool WaveFile::WriteSample(uint8_t sample) {
  if (GetBitsPerChannel() != 8) {
    error = "Write sample size mismatch (need uint8_t)";
    return false;
  }

  return WriteRaw((uint8_t*) &sample, 1);
};

bool WaveFile::ReadSample(short & sample) {
  if (GetBitsPerChannel() != 16) {
    error = "Read sample size mismatch (need short)";
    return false;
  }

  return ReadRaw((uint8_t*) &sample, 2);
};

bool WaveFile::WriteSample(short sample) {
  if (GetBitsPerChannel() != 16) {
    error = "Write sample size mismatch (need short)";
    return false;
  }

  return WriteRaw((uint8_t*) &sample, 2);
};

As you can see, I pass to functions a pointer of variables I want to modify, then the functions modify it and variables changes it's values in my main code.
As you can see any function return a Boolean value to confirm values I passed are really writed or readed.

To read/write values in my main code, this can be do in a simple way, eg:
B4X:
uint8_t mySample
wave.ReadSample(mySample);  ' here mySample contain a new read value of type uint8_t (unsigned byte)
'or
short mySample;
wave.ReadSample(mySample);  ' here mySample contain a new read value of type short
'or
short sampleL, sampleR;
wave.ReadSample(sampleL);  ' here sampleL contain a new read value of type short
wave.ReadSample(sampleR);  ' here sampleR contain a new read value of type short
'or
uint8_t mySample
bool success = wave.ReadSample(mySample);  ' here mySample contain a new read value of type uint8_t (unsigned byte)
if(success)
   ' process mySample
else
   ' show error

I think on B4X can be used global variables, but as you see "mySample" can be an unsigned byte, a short, float, double etc...

What is the right way to do this?
Many thanks
 
Last edited:

keirS

Well-Known Member
Licensed User
Longtime User
B4X passes everything by value and does not support method overloading.

Taking the B4x passes everything by value first. Here is a bit of sample code.

Main Code
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show
    Dim catName As String
    Dim catName = "Smut"
    ChangeCatName(catName,"Tigger")
    Log(catName)
    Dim CatObject As Cat
    CatObject.Initialize("Smut")
    ChangeNameOfCat(CatObject,"Tigger")
    Log(CatObject.GetCatName)
   
   
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub ChangeCatName(Cat As String, NewName As String)
 Cat.Replace(Cat,NewName)
   
End Sub


Sub ChangeNameOfCat(CatToChange As Cat,NewName As String)
    CatToChange.ChangeCatName(NewName)
   
End Sub

Cat Class

B4X:
Sub Class Globals
  Private Catname As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(NameOfCat As String)
    Catname = NameOfCat
   

End Sub

Public Sub ChangeCatName(NameOfCat As String)
    Catname = NameOfCat
End Sub

Public Sub GetCatName() As String
    Return Catname
End Sub

So the first bit of code:

B4X:
Dim catName As String
 Dim catName = "Smut"
ChangeCatName(catName,"Tigger")
Log(catName)

CatName will still be Smut because the variable is passed by value.

The second bit of code:

B4X:
  Dim CatObject As Cat
  CatObject.Initialize("Smut")
  ChangeNameOfCat(CatObject,"Tigger")
  Log(CatObject.GetCatName)

This creates a new instance (object) of the Cat class. the ChangeNameOfCat sub works because when you pass an object to a sub you are actually passing a pointer to the object and not the object itself. The pointer is still passed by value however so the following wont work.

B4X:
Dim CatObject As Cat
CatObject.Initialize("Smut")
Dim TiggerCat As Cat
TiggerCat.Initialize("Tigger")
AssignNewCat(CatObject,TiggerCat)
Log(CatObject.GetCatName)
  
Sub AssignNewCat(OldCat As Cat, NewCat As Cat)
     OldCat= NewCat
 End Sub

Log(CatObject.GetCatName) will return "Smut"






As regards to method overloading.

B4X:
Dim Wave As WaveFile
        Wave.Initialize
        Dim I As Int = 12
        Dim B As Byte = 28
        Dim S As Short = 45
       Wave.WriteSample(I)
       Wave.WriteSample(B)
       Wave.WriteSample(S)

[code]

WavFile Class

[code]
    
Sub Class_Globals
    Private fx As JFX
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

End Sub

Sub WriteSample(Sample As Object)
    Dim JavaType As String = GetType(Sample)
   
    Select True
        Case JavaType.Contains("java.lang.Integer")
            Log("Int")
        Case JavaType.Contains("java.lang.Byte")
            Log("Byte")
        Case JavaType.Contains("java.lang.Short")
            Log("Short")
    End Select
       
       
           
End Sub

Instead of having many WriteSample methods you declare the passed sample parameter as a generic object and then find it's actual type using the GetType method.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
B4X passes everything by value
Not quite true. Complex data types are passed by reference. But yes, no overloading, that is why you often see method names with a 2 or 3 appended in B4X.

Main module:
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region

Sub Process_Globals
   
End Sub

Sub AppStart (Args() As String)
    Dim w As Wave
    w.Initialize
    Dim intArray(1) As Int
    intArray(0) = 5
    Log(intArray(0))
    w.WriteSampleInt(intArray)
    Log(intArray(0))
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Su

Wave class:
B4X:
'Class module
Sub Class_Globals
   
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   
End Sub

Sub WriteSampleByte(data() As Byte)
    data(0) = 55
End Sub

Sub WriteSampleInt(data() As Int)
    data(0) = 16000
End Sub

Sub WriteSampleShort(data() As Short)
    data(0) = 800
End Sub

Output:
Waiting for debugger to connect...
Program started.
5
16000
Program terminated (StartMessageLoop was not called).

So I cheat here by putting a "simple" variable into a "complex" variable and now I'm simulating pass by reference for simple values. Not elegant, but may be a solution (or not).
 
Upvote 0

Misterbates

Active Member
Licensed User
Types are also considered to be complex variables, so instead of a one-cell array you could use for example
B4X:
Type myInt(value as int)
Dim x as myInt
myInt.value = 3
 
Upvote 0

keirS

Well-Known Member
Licensed User
Longtime User
Not quite true. Complex data types are passed by reference. But yes, no overloading, that is why you often see method names with a 2 or 3 appended..

That is incorrect. An array is actually a container object. So what is being passed is a pointer to the object. That pointer is passed by value. B4i maybe different but Java does not support passing by reference.
 
Upvote 0

keirS

Well-Known Member
Licensed User
Longtime User
And here's me thinking that passing by reference meant passing a pointer to a variable. If it's not that, what is it?

From my original post:

B4X:
Dim CatObject As Cat
CatObject.Initialize("Smut")
Dim TiggerCat As Cat
TiggerCat.Initialize("Tigger")
AssignNewCat(CatObject,TiggerCat)
Log(CatObject.GetCatName)

 Sub AssignNewCat(OldCat As Cat, NewCat As Cat)
 OldCat= NewCat

End Sub

In this example if it was passing by reference then the Log output would show "Tigger" because I would have changed the value of the pointer itself. It doesn't though because the pointer is passed by value.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Many thanks to everyone for very fast replies, here in Italy is night now :D tomorrow I try your suggestions.

I have already developed many classes and I know B4X does not support overloading, so I've used methods like SendByte, SendInt, or Send1, Send2 etc ...

I find interesting the keirS method to get the data type, many times I've passed objects to functions, but this method never used. This way can reduce drastically the library complexity, so finally the end user call same function passing one, or more objects that can contain any variable type, and is like real overloading.

I can pass a custom type as object? and a class itself?

Very interesting also the OliverA method to pass arrays, so pass arguments as a pointer reference.

Misterbates, I have used custom types many times and are very comfortable, I use them when I have to pass more than 3 arguments and can not do with CallSub (), but unfortunately they have the defect that is viewed globally, this happens even if they are declared in a class. Since you can not decide to declare custom Types within a class so that they are not seen from the outside of class, I have always avoided using them in the libraries I have developed.

As mentioned before, tomorrow I will study and look for a solution to this problem.
 
Last edited:
Upvote 0

OliverA

Expert
Licensed User
Longtime User
That is incorrect. An array is actually a container object. So what is being passed is a pointer to the object. That pointer is passed by value. B4i maybe different but Java does not support passing by reference.
Ugh. Java has primitives and references to objects. I guess a more technical accurate wording would have been "when you pass a non-primitive to a sub or when you assign it to a different variable, a copy of the reference is passed". These are the words from a post by @Erel about Variables & Objects in Basic4android. As to pointers vs references, this (https://softwareengineering.stackexchange.com/a/141838) seems to sum it up pretty well. Also, as an excuse for some imprecision in wording, I was thinking more in VB's term of reference passing via ByRef then Java's type system.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I've tested a bit of code, with B4J, because is best to test on desktop, and because users posted B4J code too, but this is working on B4A too, just a little of changes.

As a mix of code in this discussion, this code show a class that call itself passed as an Object, first time with constructor parameters, next with new parameters.
The GetType() method gets the type and recognize custom types and classes passed as reference (Object)

This test project contains 3 code modules:

Main
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type CustomUserType(Name As String, Ages As Byte, PhoneNumber As String)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    MainForm.Show

    Dim Wave As WaveFile
    Wave.Initialize
    Dim I As Int = 12
    Dim B As Byte = 28
    Dim S As Short = 45
    Wave.WriteSample(I)
    Wave.WriteSample(B)
    Wave.WriteSample(S)
    Log(" ")

    Dim UserType As CustomUserType
    '     T.Initialize
    UserType.Name = "Name1"
    UserType.Ages = 40
    UserType.PhoneNumber = "78987897987"
    Wave.WriteContact(UserType)
    Log(" ")

    Dim UserClass As CustomUserClass
    UserClass.Initialize("Name2", 50, "4565456544")
    UserClass.WriteContact(UserClass)  ' A Class pass itself as Object argument with same arguments of costructor
    Log(" ")

    UserClass.Ages = 60
    UserClass.Name = "Name3"
    UserClass.PhoneNumber = "5555555555555"
    UserClass.WriteContact(UserClass)  ' A Class pass itself as Object argument with custom arguments
    Log(" ")

    Dim Obj1, Obj2 As Object

    Dim intArray(1) As Int
    intArray(0) = 5
    Obj1 = intArray

    Dim shortArray(10) As Short
    For i = 0 To shortArray.Length-1
        shortArray(i) = i * 1000
    Next
    Obj2 = shortArray

    UserClass.MyFunction(Obj1, Obj2, shortArray) ' First and second do not work, third works because we  pass an array, no Object
    'NOTE: I even tried to pass array not converted to Object, but do not work, so tis way (UserClass.MyFunction(shortArray, shortArray, shortArray)
    'so I think Object cannot cast an Array? Right?
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

WaveFile (only as test)
B4X:
Sub Class_Globals
    Private fx As JFX
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

End Sub

Sub WriteSample(Sample As Object)
    Dim JavaType As String = GetType(Sample)

    Select True
        Case JavaType.Contains("java.lang.Integer")
            Log("Object Type: Int" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Byte")
            Log("Object Type: Byte" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Short")
            Log("Object Type: Short" & "  -  " & JavaType)
    End Select
    
End Sub

Sub WriteContact(User As Object)
    Dim JavaType As String = GetType(User)

    Select True
        Case JavaType.Contains("java.lang.Integer")
            Log("Object Type: Int" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Byte")
            Log("Object Type: Byte" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Short")
            Log("Object Type: Short" & "  -  " & JavaType)
        Case JavaType.Contains("customuser")
            Log("Object Type: Custom User Type" & "  -  " & JavaType)

            Dim usr As CustomUserType = User
            Log("Name: " & usr.Name)
            Log("Ages: " & usr.Ages)
            Log("Phone: " & usr.PhoneNumber)
    End Select
    
End Sub

CustomUserClass
B4X:
Private Sub Class_Globals
    Private fx As JFX
    Private uName As String, uAges As Byte, uPhoneNumber As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Name As String, Ages As Byte, PhoneNumber As String)
    uName = Name
    uAges = Ages
    uPhoneNumber = PhoneNumber
End Sub

Sub WriteContact(User As Object)
    Dim JavaType As String = GetType(User)

    Select True
        Case JavaType.Contains("java.lang.Integer")
            Log("Object Type: Int" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Byte")
            Log("Object Type: Byte" & "  -  " & JavaType)
        Case JavaType.Contains("java.lang.Short")
            Log("Object Type: Short" & "  -  " & JavaType)
        Case JavaType.Contains("customuserclass")
            Log("Object Type: Custom User Class" & "  -  " & JavaType)

            Dim usr As CustomUserClass = User
            Log("Name: " & usr.Name)
            Log("Ages: " & usr.Ages)
            Log("Phone: " & usr.PhoneNumber)
    End Select

End Sub

Sub setName(Name As String)
    uName = Name
End Sub
Sub getName() As String
    Return uName
End Sub

Sub setAges(Ages As Byte)
    uAges = Ages
End Sub
Sub getAges() As Byte
    Return uAges
End Sub

Sub setPhoneNumber(PhoneNumber As String)
    uPhoneNumber = PhoneNumber
End Sub
Sub getPhoneNumber() As String
    Return uPhoneNumber
End Sub

Sub MyFunction(Arg1 As Object, Arg2 As Object, data() As Short)

    Dim JavaTypeArg1 As String = GetType(Arg1)
    Dim JavaTypeArg2 As String = GetType(Arg2)

    Log("Arg1: " & JavaTypeArg1)
    Log("Arg2: " & JavaTypeArg2)

    Select True
        Case JavaTypeArg1.Contains("java.lang.Integer")
            Log("Arg1 Object Type: Int" & "  -  " & JavaTypeArg1)
        Case JavaTypeArg1.Contains("java.lang.Byte")
            Log("Arg1 Object Type: Byte" & "  -  " & JavaTypeArg1)
        Case JavaTypeArg1.Contains("java.lang.Short")
            Log("Arg1 Object Type: Short" & "  -  " & JavaTypeArg1)

        Case JavaTypeArg2.Contains("java.lang.Integer")
            Log("Arg2 Object Type: Int" & "  -  " & JavaTypeArg2)
        Case JavaTypeArg2.Contains("java.lang.Byte")
            Log("Arg2 Object Type: Byte" & "  -  " & JavaTypeArg2)
        Case JavaTypeArg2.Contains("java.lang.Short")
            Log("Arg2 Object Type: Short" & "  -  " & JavaTypeArg2)
   
    End Select

    Log(" ")
    For i = 0 To data.Length-1
        Log("data(" & i & ") -> " & data(i))
    Next 
End Sub

Note that passing array as Objects returns strange results as you can see in the log. This is the log:
Object Type: Int - java.lang.Integer
Object Type: Byte - java.lang.Byte
Object Type: Short - java.lang.Short

Object Type: Custom User Type - b4j.example.main$_customusertype
Name: Name1
Ages: 40
Phone: 78987897987

Object Type: Custom User Class - b4j.example.customuserclass
Name: Name2
Ages: 50
Phone: 4565456544

Object Type: Custom User Class - b4j.example.customuserclass
Name: Name3
Ages: 60
Phone: 5555555555555

Arg1: [I
Arg2: [S

data(0) -> 0
data(1) -> 1000
data(2) -> 2000
data(3) -> 3000
data(4) -> 4000
data(5) -> 5000
data(6) -> 6000
data(7) -> 7000
data(8) -> 8000
data(9) -> 9000
Now I'm pretty confused on how implement in my library, just pass objects or array of bytes? Or maybe pass a List as object instead of array containing multiple samples?
I've functions to send simple sample or an array of samples that can be a simple data type like Int, Short, Float etc...

And how to read a sample, returning the sample itself and a Boolean that ensure the data is successfully writed? Maybe use a Flag on the library and read it eg. with this method: wave.GetLastError() that return a Boolean? Mmmmm I am confused.
 
Last edited:
Upvote 0
Top