my best effort

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: my best effort

Post by paul doe »

owen wrote:OK I admit it: I am new to oop. In prep for Paul's contribution I am trying to explain some stuff and ran into a question about passing UDT's through subs and functions.
Ironic, since I'm actually coding the little game I mentioned using purely procedural programming. There's no OOP in sight, so don't worry too much about it for now =D
owen wrote:Why does draw_ball_2(ByRef b) not work?
You don't need to specify the 'byref' clause when you pass the parameter, only on the function prototype:

Code: Select all

ScreenRes 600,400
Window(0,0)-(600,400)

Type ball
   x As Integer
   y As Integer
   r As Integer
   c As Integer
End Type

Declare Sub draw_ball_1(ByVal aball As ball)
Declare Sub draw_ball_2(ByRef aball As ball)
Declare Sub draw_ball_3(aball As ball)

Dim b As ball
b.x=300
b.y=200
b.r=100
b.c=14

Circle(b.x,b.y),b.r,b.c
Print "press any key"
Sleep
Cls
b.c=13
draw_ball_1(b)
Circle(b.x,b.y),b.r,b.c
Print "press any key"

Sleep
draw_ball_2(b)
Circle(b.x,b.y),b.r,b.c
Print "press any key"

Sleep
draw_ball_3(b)
Circle(b.x,b.y),b.r,b.c
Print "press any key"

Sleep


Sub draw_ball_1(ByVal aball As ball)
   Circle(aball.x,aball.y),aball.r,aball.c
   aball.r=50
End Sub

Sub draw_ball_2(ByRef aball As ball)
   Circle(aball.x,aball.y),aball.r,aball.c
   aball.r=200
End Sub

Sub draw_ball_3(aball As ball)
   Circle(aball.x,aball.y),aball.r,aball.c
   aball.r=300
End Sub
See? Your code works flawlessly without them =D
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

When 'ByVal' and 'ByRef' are used as declaration specifiers, they must be specified at the procedure declaration level only (not at the call level).

Only 'ByVal' can also be used to specify a subtlety when passing an argument to a procedure or returning a parameter from a function:
- 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 BYVAL.
If used outside of this context when passing an argument to a procedure or returning a parameter from a function, 'ByVal' is allowed but useless ('ByRef' can never be used at this level of passing an argument to a procedure or returning a parameter from a function).
owen
Posts: 555
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: my best effort

Post by owen »

Thanks all.

You know I never paid any attention to ByRef and ByVal until I started using GTK for fbcad. Even then I didn't really understand it. Basically I just went with the flow thinking to myself well if they're using it it must be needed.

Now that I'm trying to explain it to these kids it's obvious I have no clue. Well maybe that's an over exaggeration. I understand what fxm is saying for the most part.

My natural coding-style is to never specify either.
In fact I'm still kind of confused why anybody would want the passed parameter values to be affected. A thing like this is something that I'm trying to warn them about and perhaps I shouldn't be doing that at all maybe there is a good reason why they can be affected or changed within the function or subroutine.
My concept of functions and subroutines we're always to make use of the passed parameters to produce a result.

Never did I consider the idea the result could be also to change the initial variables value via changing the parameter value hence my abuse of dim shared.

I just might be free from dim shared.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

Although I am familiar with the default rules for passing parameters (depending on the language used), I always explicitly specify ByVal or ByRef for clarity and also to avoid warnings because I always compile with the option -w pedantic among others.

Default rules for passing parameters:
- In the -lang fb dialect, ByVal is the default parameter passing convention for all built-in types except String and user-defined Type which are passed ByRef by default. The ZString and WString built-in types are also passed ByRef by default, but passing ByVal is forbidden. Arrays are always passed ByRef and the use of the specifier ByRef or ByVal is forbidden.
- In -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention.
owen
Posts: 555
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: my best effort

Post by owen »

Code: Select all

Dim As Integer x

Sub test(ByRef x As Integer)
	Dim As Integer a,b
	'make use of the passed parameter's value
	a=x*2
	b=x*4
	Print x,a,b
	'contiue making use of the passed parameter's value
	x+=1'simultaneously change x's value eliminating the need of dim shared
	a=x*2
	b=x*4
	Print x,a,b
End Sub

x=1
test(x)
Print "new x";x
test(x)
Print "new x";x

'in order to do the same i might have done this:
'Dim Shared As Integer x
'
'Sub test(c As Integer)
'	Dim As Integer a,b
'	'make use of the passed parameter's value
'	a=c*2
'	b=c*4
'	Print x,a,b
'	'contiue making use of the passed parameter's value
'	c+=1
'	x=c
'	a=c*2
'	b=c*4
'	Print c,a,b
'End Sub
'
'x=1
'test(x)
'Print "new x";x
'test(x)
'Print "new x";x

Sleep


owen
Posts: 555
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: my best effort

Post by owen »

fxm wrote:Although I am familiar with the default rules for passing parameters (depending on the language used), I always explicitly specify ByVal or ByRef for clarity and also to avoid warnings because I always compile with the option -w pedantic among others.

Default rules for passing parameters:
- In the -lang fb dialect, ByVal is the default parameter passing convention for all built-in types except String and user-defined Type which are passed ByRef by default. The ZString and WString built-in types are also passed ByRef by default, but passing ByVal is forbidden. Arrays are always passed ByRef and the use of the specifier ByRef or ByVal is forbidden.
- In -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention.
Do you normally declare your subs and functions also?
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

When returning a variable from a function, the default returning is ByValue (a copy of the variable is returned):
Declare Function name ( parameter-list ) As datatype
(specifying explicitly ByVal in the return type declaration is forbidden)

To return from a function a variable by reference, ByRef must be specified in the return type declaration:
Declare Function name ( parameter-list ) ByRef As datatype
(but the variable to return must be compatible with a by-reference return)
owen
Posts: 555
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: my best effort

Post by owen »

Can you post an example of returned ByRef value
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

owen wrote:Do you normally declare your subs and functions also?
For an archived code, it is a good practice to declare the procedures used at the code top.
In the declaration syntax, the name of each parameter passed is not mandatory, but I think it is better to also specify it because it clarifies the procedure use (obviously if the name of the parameter is chosen wisely).
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

owen wrote:Can you post an example of returned ByRef value
See documentation at BYREF (function results).
owen
Posts: 555
Joined: Apr 19, 2006 10:55
Location: Kissimmee, FL
Contact:

Re: my best effort

Post by owen »

yup right there. looked at it several times. just now seeing it. thanks

Code: Select all

Function min( ByRef I As Integer , ByRef J As Integer ) ByRef As Integer
    '' The smallest integer will be returned by reference, no copy will be created.
    If I < J Then
        Return I
    Else
        Return J
    End If
End Function

Dim As Integer A = 13, B = 7
Print A, B
Print min( A , B )
min( A , B ) = 0
Print A, B
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

fxm wrote:For an archived code, it is a good practice to declare the procedures used at the code top.
In the declaration syntax, the name of each parameter passed is not mandatory, but I think it is better to also specify it because it clarifies the procedure use (obviously if the name of the parameter is chosen wisely).
Similarly, when a procedure is first declared, the initializer of an optional parameter is not mandatory in the prototype procedure (only in the declaration of procedure), but it is a good habit (IMHO) to also specify it (with obligatorily the same value) at the prototype level to enlighten the comprehension of the code in the procedure body.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: my best effort

Post by MrSwiss »

fxm wrote:In the declaration syntax, the name of each parameter passed is not mandatory, but I think it is better to also specify it because it clarifies the procedure use (obviously if the name of the parameter is chosen wisely).
I disagree on the ground of: code maintainability. The declaration is solely for the use of the compiler.
(A forward reference, of used data-types.)

It leaves the programmer the abiliity, to choose any "name" at the implementation level, at which,
it is to be commented apropriately. (A "name" change, doesn't change the declaration, ever!)

Example: (research into: LCase() / UCase() purely numerical String processing)

Code: Select all

' Upper-Lower-Case_funcs.bas -- 2018-10-19, MrSwiss
'
' compile: -s console
'

' declaration(s)
Declare Function MK_LCase( ByRef As Const String ) As String
Declare Function MK_UCase( ByRef As Const String ) As String

' ===== DEMO =====
Dim As String   suc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", _
                slc = "abcdefghijklmnopqrstuvwxyz", _
                smc = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"

Print "all upper case string"; Tab(29); "all lower case string"
Print
Print "original"
Print suc, slc
Print "MK_LCase()"
Print MK_LCase(suc), MK_LCase(slc)
Print "MK_UCase()"
Print MK_UCase(suc), MK_UCase(slc)

Print : Print

Print "mixed case string"
Print
Print "original"
Print smc
Print "MK_LCase()"
Print MK_LCase(smc)
Print "MK_UCase()"
Print MK_UCase(smc)

Print : Print

Print " ... press a key, to EXIT ... ";

Sleep
' ===== End-DEMO =====
' implementation(s) -- ASCII version only! (mode: NOT implemented)
Function MK_LCase( _                    ' similar to: LCase() 'keyword'
    ByRef iStr  As Const String _       ' a read only string's reference
    ) As String                         ' returns a modified string copy!
    Dim As UByte    t                   ' temporary string char's ASCII value
    Dim As String   stmp = iStr         ' internal string copy (mandatory!)

    For i As UInteger = 0 To Len(stmp) - 1  ' process whole string
        t = stmp[i]                     ' string[index] = ASCII value of char
        If t > 64 AndAlso t < 91 Then   ' if in range: Asc("A") to Asc("Z")
            stmp[i] += 32               ' convert to lower case
        End If                          ' (otherwise: don't touch!)
    Next

    Return stmp
End Function

Function MK_UCase( _                    ' similar to: UCase() 'keyword'
    ByRef iStr  As Const String _       ' a read only string's reference
    ) As String                         ' returns a modified string copy!
    Dim As UByte    t                   ' temporary string char's ASCII value 
    Dim As String   stmp = iStr         ' internal string copy (mandatory!)

    For i As UInteger = 0 To Len(stmp) - 1  ' process whole string
        t = stmp[i]                     ' string[index] = ASCII value of char
        If t > 96 AndAlso t < 123 Then  ' if in range: Asc("a") to Asc("z")
            stmp[i] -= 32               ' convert to upper case
        End If                          ' (otherwise: don't touch!)
    Next

    Return stmp
End Function
' ----- EOF -----
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Re: my best effort

Post by anonymous1337 »

You can say the declarations are purely for use by the compiler all you want, but in practice header files are incredibly useful since *they contain the declarations*.

Otherwise, you have to hunt for the implementation and look up its parameters (which might not even be available, especially when linking to binaries), or have some kind of metadata describing the function (ex: annotations, documentation, etc.).

I have used header files that were automatically generated and contained only the types, and no parameter names or annotations, and it was a pain in the ass. As if I'm somehow going to know what the three nameless parameters are supposed to be by type alone...

At least in Java, C#, and even NodeJS there are standard conventions for making comments with annotations / metadata. These usually are applied to the declarations (where applicable) which IMO defeats the philosophical argument that declarations are for the compiler only.

Also, you're not obligated to update the parameter names in declarations (afaik) just because you change a name in the implementation. I would argue that any significant changes to implementation of function would mandate a change in the declaration or parameter documentation, IMO negating the maintainability argument for most cases.

If you have some other way of generating or maintaining documentation of functions and parameters, this obviously is not the case. Also not the case if you're not documenting any of this stuff at all. I would do so before a prod release, or at least provide examples.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: my best effort

Post by fxm »

I completely approve. :-)
Post Reply