Question on ByVal parameters

General FreeBASIC programming questions.
Post Reply
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Question on ByVal parameters

Post by wallyg »

I have a routine with several parameters that are passed by value. I gather that a temporary variable is created in the calling routine and the address of this passed after setting it to the value to pass.

Given that this is true, can I then change the value of this byval parameter in the routine without causing any problems later? I am reminded of an early optimizing Fortan 77 compiler that decided to optimize all references to the constant 1 to a single location and passed that to any routine that had a 1 in the call list. I suspect that this specific behavior has not been done for a long time, but was wondering about any other gothca's that I should be aware of if I change the value of a byval parameter.

Wally
bcohio2001
Posts: 556
Joined: Mar 10, 2007 15:44
Location: Ohio, USA
Contact:

Re: Question on ByVal parameters

Post by bcohio2001 »

According to documentation:
ByVal in a parameter list of a declare statement causes a copy of the variable to be passed to the procedure (for example, a sub or function) by its value.

This means that if the value of the variable x is passed, then the original variable x will not be modified in any way; however, if the variable were passed ByRef, the value of the original variable x could be modified by the called function.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Question on ByVal parameters

Post by MrSwiss »

wallyg wrote:... can I then change the value of this byval parameter in the routine without causing any problems later?
That is correct: changes to a 'ByVal' passed variable (inside Sub/Function), don't affect the 'outside' variable in any way.
Another way a 'ByVal' can be explained is: a temporary COPY of the original variable (for the procedures 'run time' only).
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

When passing a variable by value, it is possible to locally modify its value in the procedure body without impacting on the origin variable.

Before, this practice was useful for example in QuickBasic in order to limit the number of code lines in module (by avoiding new local variable declarations), because the memory space for compilation was very limited. But doing this leaded to maintenance headaches.

Now (no memory space limitation for compilation), the safest practice is to explicitly declare as many local variables that we need.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

By throwing a glance on a code posted on the forum, I want to emphasize a second usage of Byval that the one well known (second usage different from that got with VB compiler) and in addition a bizarre behavior in FB.

Some know that the Byval keyword (placed before a passed variable) may be also used in the context of Byref parameters and Byref function results, where it can be used to explicitly override the by-reference semantics in order to pass or assign a pointer as-is to a Byref parameter or function result (see documentation at BYVAL with the two associated links).

But the same syntax used with Byval parameters and Byval function results is also allowed (no error neither warning from compiler).
These two syntaxes seem to work well also, but for the one only used in front of a Byval parameter ('Declare procedure_name(Byval As data_type)'), the behavior is slightly different:
- 'procedure_name(parameter_name)' : the copy-constructor is called, the explicit if exists,
- 'procedure_name(Byval parameter_name)' : the implicit copy-constructor is always called, even if an explicit exists.
That last behavior may be generator of bug if a simple memory allocation plus a shallow copy is not sufficient.

I think the better should be to forbid the Byval keyword (placed before a passed variable) in the context of Byval parameters and Byval function results.

See the following code highlighting this behavior:

Code: Select all

Type UDT
  Dim As Integer I
  Declare Constructor ()
  Declare Constructor (Byref u As UDT)
  Declare Operator Let (Byref u As UDT)
  Declare Destructor ()
End Type

Constructor UDT ()
  Print "UDT.Constructor()", @This
End Constructor

Constructor UDT (Byref u As UDT)
  Print "UDT.Constructor(Byref As UDT)", @This, @u
  This.I = u.I
End Constructor

Operator UDT.Let (Byref u As UDT)
  Print "UDT.Let(Byref As UDT)", @This, @u
  This.I = u.I
End Operator

Destructor UDT ()
  Print "UDT.Destructor()", @This
End Destructor



Sub PassByVal (Byval u As UDT)
  Print u.I
End Sub

Function ReturnByval (Byref u As UDT) As UDT
  Return u
End Function

Function ReturnByvalByval (Byref u As UDT) As UDT
  Return Byval u
End Function




Scope
  Dim As UDT u
  u.I = 123456789
  Print
  PassByVal(u)
  Print
  PassByval(Byval u)
  Print
  Print ReturnByval(u).I
  Print
  Print ReturnByvalByval(u).I
  Print
End Scope

Sleep
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

fxm wrote: .....
That last behavior may be generator of bug if a simple memory allocation plus a shallow copy is not sufficient.
.....
Indeed, for example in case of virtuality use, the vptr of such an instance ('procedure_name(Byval parameter_name)') is baddly initialized.
From the previous example, and by defining the UDT extending Object and the Destructor as Virtual, the program aborts at the instance destruction step:

Code: Select all

Type UDT Extends Object
  Dim As Integer I
  Declare Constructor ()
  Declare Constructor (Byref u As UDT)
  Declare Operator Let (Byref u As UDT)
  Declare Virtual Destructor ()
End Type

Constructor UDT ()
  Print "UDT.Constructor()", @This
End Constructor

Constructor UDT (Byref u As UDT)
  Print "UDT.Constructor(Byref As UDT)", @This, @u
  This.I = u.I
End Constructor

Operator UDT.Let (Byref u As UDT)
  Print "UDT.Let(Byref As UDT)", @This, @u
  This.I = u.I
End Operator

Destructor UDT ()
  Print "UDT.Destructor()", @This
End Destructor



Sub PassByVal (Byval u As UDT)
  Print u.I
End Sub

Function ReturnByval (Byref u As UDT) As UDT
  Return u
End Function

Function ReturnByvalByval (Byref u As UDT) As UDT
  Return Byval u
End Function




Scope
  Dim As UDT u
  u.I = 123456789
  Print
  PassByVal(u)
  Print
  PassByval(Byval u)
  Print
  Print ReturnByval(u).I
  Print
  Print ReturnByvalByval(u).I
  Print
End Scope

Sleep

Code: Select all

UDT.Constructor()           1638068

UDT.Constructor(Byref As UDT)             1638060       1638068
 123456789
UDT.Destructor()            1638060

 123456789

Aborting due to runtime error 12 ("segmentation violation" signal) in D:\Users\T
0003830\Documents\Mes Outils Personnels\FBIde0.4.6r4_fbc1.06.0\FBIDETEMP.bas::()
wallyg
Posts: 270
Joined: May 08, 2009 7:08
Location: Tucson Arizona

Re: Question on ByVal parameters

Post by wallyg »

Thank you everyone who checked this out and replied. I have taken the advice given here and will not try to change the ByRef vars passed to a routine.

As far as I am concerned this item is closed. However it does seem to have pointed out an error in FB 1.05. I hope it has been reported through the proper channels to get fixed ASAP.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

Usually, I wait for a reaction of an administrator during one or two days before filling a bug report if I judge it justified like my above case.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Question on ByVal parameters

Post by D.J.Peters »

@fxm, really good find :-)

Joshy
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Question on ByVal parameters

Post by fxm »

coderJeff wrote: Dec 29, 2022 22:23 With our recent efforts,
#830 Byval placed before a passed variable for a Byval parameter induces an incomplete copy construction

now throws a compile error. So I will probably take a look at that for a bit.
Otherwise, unexpected !
This is what I proposed (compile error) as a solution to this bug report.

The Byval keyword (placed before a passed variable) may be used in the context of Byref parameters and Byref function results, where it can be used to explicitly override the by-reference semantics in order to pass or assign a pointer as-is to a Byref parameter or function result.
And the proper use of such a kind of BYVAL still works after the last fbc change:

Code: Select all

Function fillPassedZstring (Byref z As Zstring, Byval pz As Zstring Ptr, Byval count As Integer = 1) Byref As Zstring
    For I As Integer = 1 To count
        z &= *pz
    Next I
    Return z
End Function

Dim As String s1 = "FreeBASIC"
'Dim Byref As Zstring rz1 = fillPassedZstring(*Cast(Zstring Ptr, Callocate(Len(s1) + 1)), s1)
Dim Byref As Zstring rz1 = fillPassedZstring(Byval Callocate(Len(s1) + 1), s1)
Print "'" & rz1 & "'"
Deallocate(@rz1)


Function getNewZstring (Byval size As Integer) Byref As Zstring
'    Return *Cptr(Zstring Ptr, Callocate(size + 1))
    Return Byval Callocate(size + 1)
End Function

Dim As String s2 = "Version 1.10.0"
Dim Byref As Zstring rz2 = getNewZstring(Len(s2))
rz2 = s2
Print "'" & rz2 & "'"
Deallocate(@rz2)

Sleep
'FreeBASIC'
'Version 1.10.0'
coderJeff
Site Admin
Posts: 4323
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Question on ByVal parameters

Post by coderJeff »

coderJeff wrote: Dec 29, 2022 22:23 now throws a compile error. So I will probably take a look at that for a bit.
fxm wrote: Dec 29, 2022 22:48 This is what I proposed (compile error) as a solution to this bug report.
The Byval keyword (placed before a passed variable) may be used in the context of Byref parameters and Byref function results, where it can be used to explicitly override the by-reference semantics in order to pass or assign a pointer as-is to a Byref parameter or function result.

In all my time using fb, I have never used this BYVAL semantic.

In the case of bug #830 - just the highlights of the demonstration code:

Code: Select all

type T
  declare constructor( byref arg as T )  '' also `byval arg T` won't be allowed here
end type
declare sub PassByval( byval arg as T )
dim udt as T
PassByval( byval udt )
The reason fbc now throws an error:
1. The parameter to the procedure is BYVAL, and since now (as of fbc.1.10.0) the priority for parameters of 'byval as UDT' is to firstly check for a constructor.
2. As a consequence of find the constructor first, the handling for BYVAL argument semantics is never used / skipped.
3. Then, when building the call to pass byval udt to constructor byref arg as T, we get a type mismatch because of the BYVAL/BYREF difference.

The error only shows if a constructor is found. If udt has no constructor, then no error will be given.
Post Reply