Dim Byref syntax

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

BTW does a null reference have to be deleted explicitly?
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Dim Byref syntax

Post by fxm »

No, as for a null pointer, but deleting it is still an allowed syntax (inducing no action).
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

fxm wrote:No, as for a null pointer, but deleting it is still an allowed syntax (inducing no action).
So I noticed, thanks.
St_W
Posts: 1626
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Dim Byref syntax

Post by St_W »

In general you only need to delete objects you allocated on the Heap, with "new", "allocate", malloc(), etc.
Objects stored on the stack or in a data segment of the application do not need to be deleted.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

While it is possible via a Define construct to create an object with null reference within an object, it appears FB insists on creating the object before the (default) constructor is invoked. The following code compiles, but execution raises a segmentation fault. When commenting out object 'a' in 'tt' it runs fine.

Code: Select all

#define nilref(o) *cptr(o ptr, 0)
#define nilobj(o) o = nilref(o)
#define obj(o) byref o
#define ref(o) @(o)

type ta extends object
	s as string
end type

type tt extends object
	s as string
	a as nilobj(ta)
end type

dim obj(t) as nilobj(tt)

print "new reference:"
ref(t) = ref(tt())
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Dim Byref syntax

Post by fxm »

More simply, this obviously crashes:

Code: Select all

Dim As Integer I = *Cptr(Integer Ptr, 0)
Print "NOK"
Sleep
while it normally works:

Code: Select all

Dim Byref As Integer RI = *Cptr(Integer Ptr, 0)
Print "OK"
Sleep
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

Yes it is the BYREF cake that must be eaten. ;-) Ideally this would work, but it doesn't:

Code: Select all

#define unref(o) delete @(o)
#define newobj *new
#define nil(o) @(o) = 0
#define nilref(o) *cptr(o ptr, 0)
#define nilobj(o) o = nilref(o)
#define obj(o) byref o
#define ref(o) @(o)

type ta extends object
	s as string
end type

type tt extends object
	s as string
	a as nilobj(ta)
	declare constructor()
	declare destructor()
end type

constructor tt()
	a = newobj ta
end constructor

destructor tt()
	a = nilref(ta)
end destructor

function InitTT(s as string) byref as tt
	if len(s) then
		dim obj(res) as tt = newobj tt()
		res.s = s
		return res
	end if
	return nilref(tt)
end function

dim obj(t) as nilobj(tt)

ref(t) = ref(InitTT("Hello world!"))
if not nil(t) then
	print t.s
	unref(t)
else
	print "no ref"
end if
This code compiles but it doesn't run. object 'a' doesn't seem to be recognized as such because any reference (@) fails.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

Interestingly, nested objects that obviously are not created BYREF, do accept a new instance from a byref function result, which makes these kind of function constructs definitely valuable:

Code: Select all

#define unref(o) delete @(o)
#define newobj *new
#define nil(o) @(o) = 0
#define nilref(o) *cptr(o ptr, 0)
#define nilobj(o) o = nilref(o)
#define obj(o) byref o
#define ref(o) @(o)

type ta extends object
	s as string
end type

type tt extends object
	s as string
	a as ta
	declare constructor()
end type

declare function InitTA(s as string) byref as ta
declare function InitTT(s as string) byref as tt

dim obj(t) as nilobj(tt)

ref(t) = ref(InitTT("Hello world!"))
if not nil(t) then
	print t.s
	print t.a.s
	'unref(t)
else
	print "no ref"
end if

end

constructor tt()
	'a = newobj ta
	a = InitTA("ok")
end constructor

function InitTA(s as string) byref as ta
	if len(s) then
		dim obj(res) as ta = newobj ta()
		res.s = s
		return res
	end if
	return nilref(ta)
end function

function InitTT(s as string) byref as tt
	if len(s) then
		dim obj(res) as tt = newobj tt()
		res.s = s
		return res
	end if
	return nilref(tt)
end function
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

But in the constructor, if 'a' gets assigned a null reference because the string passed is empty, a segmentation fault is raised upon the assignment. This can be solved by first creating a new null reference for 'a'. Comment out the dim statement in the constructor to see the difference.

Code: Select all

#define unref(o) delete @(o)
#define newobj *new
#define nil(o) @(o) = 0
#define nilref(o) *cptr(o ptr, 0)
#define nilobj(o) o = nilref(o)
#define obj(o) byref o
#define ref(o) @(o)

type ta extends object
	s as string
end type

type tt extends object
	s as string
	a as ta
	declare constructor()
end type

declare function InitTA(s as string) byref as ta
declare function InitTT(s as string) byref as tt

dim obj(t) as nilobj(tt)

ref(t) = ref(InitTT("Hello world!"))
if not nil(t) then
	print t.s
	'print t.a.s
	'unref(t)
else
	print "no ref"
end if

end

constructor tt()
	' make a new nil object
	dim obj(a) as nilobj(ta)
	ref(a) = ref(InitTA(""))
end constructor

function InitTA(s as string) byref as ta
	if len(s) then
		dim obj(res) as ta = newobj ta()
		res.s = s
		return res
	end if
	return nilref(ta)
end function

function InitTT(s as string) byref as tt
	if len(s) then
		dim obj(res) as tt = newobj tt()
		res.s = s
		return res
	end if
	return nilref(tt)
end function
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

OK, nice fooling myself. The dimmed object 'a' inside the constructor is seen as a new local object, which is destroyed at the end of the constructor.

Bottomline, avoid null references of nested objects, NOT just inside constructors! What can be done is a new reference of a nested object with the NEW keyword, but a subsequent DELETE is also not allowed.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Dim Byref syntax

Post by coderJeff »

I think the main idea of references is that they always actually refer to something valid. I think references can be reassigned; it's just the assignment syntax that needs to clear; either assigning the value or rebinding the reference.

Here's a different approach:

Code: Select all

type NothingObject extends object
end type

dim shared Nothing as NothingObject

function IsNothing( byref obj as object ) as boolean
	function = ( obj is NothingObject )
end function

'' maybe a 'set variable = object' syntax? like from VB?
#macro set_obj( x, expr )
	'' careful, this macro is case sensitive
	#if #expr = "Nothing"
		@x = @Nothing
	#else
		'' never assign zero address
		if( @expr ) then
			@x = @expr
		else
			@x = @Nothing
		end if
	#endif
#endmacro

#define identify( x ) _
	iif( x is NothingObject, "Nothing", "Something" )

type T extends object
end type

dim obj as T

dim byref ref as object = Nothing
print "ref is " & identify( ref )

set_obj( ref, obj )
print "ref is " & identify( ref )

set_obj( ref, Nothing )
print "ref is " & identify( ref )

set_obj( ref, obj )
print "ref is " & identify( ref )

dim byref NullRef as object = *cptr(object ptr, 0)
set_obj( ref, NullRef )
print "ref is " & identify( ref )
EDIT:
The extra check for "Nothing" is due to a bug in 1.05. For the example, in 1.06, can use:

Code: Select all

'' maybe a 'set variable = object' syntax?
#macro set_obj( x, expr )
	'' Never set a zero value
	if( @expr ) then
		@x = @expr
	else
		@x = @Nothing
	end if
#endmacro
If a reference always points to something valid, there's no need for null pointer checks on every use.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Dim Byref syntax

Post by fxm »

coderJeff wrote: EDIT:
The extra check for "Nothing" is due to a bug in 1.05. For the example, in 1.06, can use: ...
Can you recall more precisely the fix and the corresponding sentence in changelog.txt?
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

coderJeff wrote:I think the main idea of references is that they always actually refer to something valid.
There is a price for that philosophy: inefficiency. Objects are created before the programmer has a choice what to assign (usually different types of constructors with specific parameters). So way too often a new copy is made always leaving something in memory even if there is no need to. From my RealBASIC experience I know how valuable null objects are. Granted, early on I had a lot of Nil Object exceptions because with QuickBASIC as background I thought of classes/objects as simple Type structures that were automatically assigned. Here is a RealBASIC example that demonstrates the usefulness of null objects:

Code: Select all

' create instance immediately (but is it necessary?)
dim f as FolderItem = new FolderItem()

' declare object but no instance yet (null object)
dim f as FolderItem

' call file open dialog
f = GetOpenFolderItem()
if f <> nil then
	' user selected a file
	' ...
end if
' If the user did not select a file, we have no use for an instance...
coderJeff wrote:If a reference always points to something valid, there's no need for null pointer checks on every use.
In my years long experience with RealBASIC I found only two logical places where objects could be null; that's 1. in the constructor where nested objects HAD to be instantiated explicitly, and 2. in procedures where local instances were created. In either case the programmer had full control over references. When programmed logically one never had to check several times if a reference was actually there. Only when calling specific functions that could fail, a nil object could be returned. Usually a try..except..finally block was part of such code parts.

The idea of the NEW keyword in general is to create an instance of an object. Currently I have to declare and assign an object in one go in order to avoid two constructor calls:

Code: Select all

dim s as SomeObject = SomeObject()
Furthermore it is currently much less clear to the programmer what the difference is between objects declared with and without the NEW keyword. If I understand correctly, all objects declared without NEW are created at startup of the program whereas objected created with NEW are dynamically allocated at runtime. I do not see much use for having these two approaches.

I believe the main issue with FB is that it initially is based on the procedural programming legacy of the 80s and 90s, most notably QuickBASIC. A compiler designed for OOP from scratch usually has a very different and more efficient approach with object handling. In the case of FB the introduction of the keyword CLASS could have separated such implementation from Types, making things less complicated.

However I do believe that FB has come a long way and that its attempt to support full OOP is admirable. But it could be implemented with more efficiency and programmer choosing in mind.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Dim Byref syntax

Post by fxm »

Munair wrote:Currently I have to declare and assign an object in one go in order to avoid two constructor calls:

Code: Select all

dim s as SomeObject = SomeObject()
I don't understand:
dim s as SomeObject
calls implicitly the default constructor (which exists because the below syntax rightly compiles),
while:
dim s as SomeObject = SomeObject()
calls explicitly the default constructor,
but the two generated codes are identical.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: Dim Byref syntax

Post by Munair »

fxm wrote:I don't understand:
dim s as SomeObject
calls implicitly the default constructor (which exists because the below syntax rightly compiles),
while:
dim s as SomeObject = SomeObject()
calls explicitly the default constructor,
but the two generated codes are identical.
In this case, yes, but if I need to call a different constructor later on with a parameter which value is not known yet, or assign the object to the result of a function, I am still left with a full instance for which I may have no use. This is why in RealBASIC every (nested) object had to be instantiated explicitly, because there was no telling when and how the object should be instantiated.
Last edited by Munair on Nov 27, 2018 8:36, edited 1 time in total.
Post Reply