Property returns incorrect object

General FreeBASIC programming questions.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Property returns incorrect object

Post by sancho2 »

I have the class testa which contains a member x which is type statestring (another class).
In testa I have a property which returns that member, this.x.
That property returns a statestring object whose address does not match this.x. Consequently changes made to the object returned by the property do not affect the member x.
Seemingly the property returns a new object of type statestring.
In the following code you can see that the address of the member x._old_value does not match the address of the object m._old_value.
What is happening?

Code: Select all

Type StateString
	Public:
		Declare Sub InitState(ByRef txt As String)
		Declare Property IsChanged() As boolean
		Declare Operator Cast() As String
		Declare Operator Let(ByRef txt As String)
		
		Declare Constructor()
		Declare Constructor(ByRef txt As String)
	'Private:
		As String _value
		As String _old_value
End Type
Constructor StateString()
	'
End Constructor
Constructor StateString(ByRef txt As String)
	'
	this._value = txt
	this._old_value = txt
End Constructor

operator StateString.Cast() As String
	'
	Return This._value
End Operator

operator StateString.Let(ByRef value As String)
'
	Static As boolean b = TRUE
	
	This._value = value
	If b Then
		b = FALSE
		this._old_value = value
	EndIf
End Operator
Sub StateString.InitState(ByRef txt As String)
	'
	this._value = txt
	this._old_value = txt
End Sub
Property StateString.IsChanged() As boolean
	'
	Dim As boolean b
	? "ssss ";@this._old_value
	'Sleep 
	'end
	If this._value <> this._old_value Then
		b = TRUE
	EndIf
	
	If b = TRUE Then
		this._old_value = this._value
	EndIf
	Return b
End Property

Type testa
	x As statestring
	Declare Property m() As StateString
	Declare Constructor
End Type

Constructor testa
	this.x = "a"
End Constructor
Property testa.m() As statestring
	'
	Return this.x
End Property

Dim t As testa

t.x.ischanged
? "aaaa ";@t.m._old_value
sleep
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Property returns incorrect object

Post by dodicat »

If you make your property byref it doesn't jump addresses.
I have tested using the let and using the constructor -- type("a")
I also tested a function
Declare function m2() byref As StateString

So long as it is byref, the same address holds.

YOUR CODE:

Code: Select all

Type StateString
   Public:
      Declare Sub InitState(ByRef txt As String)
      Declare Property IsChanged() As boolean
      Declare Operator Cast() As String
      Declare Operator Let(ByRef txt As String)
      
      Declare Constructor()
      Declare Constructor(ByRef txt As String)
   'Private:
      As String _value
      As String _old_value
End Type
Constructor StateString()
   '
End Constructor
Constructor StateString(ByRef txt As String)
   '
   this._value = txt
   this._old_value = txt
End Constructor

operator StateString.Cast() As String
   '
   Return This._value
End Operator

operator StateString.Let(ByRef value As String)
'
   Static As boolean b = TRUE
   
   This._value = value
   If b Then
      b = FALSE
      this._old_value = value
   EndIf
End Operator
Sub StateString.InitState(ByRef txt As String)
   '
   this._value = txt
   this._old_value = txt
End Sub
Property StateString.IsChanged() As boolean
   '
   Dim As boolean b
   ? "ssss ";@this._old_value
   'Sleep 
   'end
   If this._value <> this._old_value Then
      b = TRUE
   EndIf
   
   If b = TRUE Then
      this._old_value = this._value
   EndIf
   Return b
End Property

Type testa
   x As statestring
   Declare Property m() byref As StateString
   Declare Constructor
End Type

Constructor testa
   this.x = "a"
End Constructor
Property testa.m() byref As statestring
   '
   Return this.x
End Property

Dim t As testa

t.x.ischanged

? "aaaa ";@t.m._old_value
sleep 
But I don't understand class testa / class statestring!
What does this mean?
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

KeyPgProperty → fxm [Added in description that a get-Property can also return a reference (with associated syntax)]
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Property returns incorrect object

Post by Tourist Trap »

fxm wrote:KeyPgProperty → fxm [Added in description that a get-Property can also return a reference (with associated syntax)]
Hi fxm,
is there a reason why it is in the text and not in the syntax description? Just missing a [byref] before the " as return type".
A get-Property can also return a reference by specifying Byref as return_type
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

OK, done (only in the main pages concerned).
Last edited by fxm on Aug 25, 2017 11:08, edited 2 times in total.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Property returns incorrect object

Post by sancho2 »

I expected the object instance to be returned but now I see that doens't happen by design. A function/property/operator will return a new copy of the object, unless you specify byref. The same is true for simple data types like integer.
I also see now were return by reference can be useful.

@dodicat:
Statestring is part of a larger project. It is a string variable that knows if its been changed since the last time it was checked. I am not sure if it is entirely useful or not.
I stripped it out and added a test class for easy forum access and running, since I thought it was an FB bug. Its not a bug.

Thanks everyone.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

With a static Boolean "b" to memorize a change, this change monitoring process can work only if only one single "Statestring" instance exists at the same time.
Why not use a classic non-static Boolean member (one data for each instance)?
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Property returns incorrect object

Post by sancho2 »

I didn't see that fxm, but yes you are right.
I had already changed it to a member variable and not a boolean. This was to accomodate integers as well as strings.
Here is the code, still not complete and not fully tested:

Code: Select all

Type StateBase 
	Public:

		Enum _type
			_none
			_int
			_string
		End Enum
		As _type init_flag = _none

		'Declare Sub InitState(ByRef txt As String)
		Declare Property IsChanged() As boolean	
		Declare Operator Cast() As String
		Declare Operator Cast() As Integer
	
		Declare Operator Let(ByRef txt As String)
		Declare Operator Let(ByRef value As Integer)
		
	Private:
			
		As String _string_value
		As Integer _int_value
		
		As String _string_old_value
		As Integer _int_old_value
		
End Type
operator StateBase.Cast() As String
	'
	If this.init_flag = _string Then
		Return This._string_value
	ElseIf this.init_flag = _int Then
		Return Str(this._int_value)
	EndIf
End Operator

operator StateBase.Cast() As Integer
	'
	Return This._int_value
End Operator

operator StateBase.Let(ByRef value As String)
'
	This._string_value = value
	If this.init_flag = _none OrElse this.init_flag = _int Then
		this.init_flag = _string
		this._string_old_value = value
	EndIf
End Operator
Operator StateBase.Let(ByRef value As Integer)
'
	This._int_value = value
	If this.init_flag = _none OrElse this.init_flag = _string Then
		this.init_flag = _int
		this._int_old_value = value
	EndIf
End Operator
Property StateBase.IsChanged() As boolean
	'
	Dim As boolean b
	
	If this.init_flag = _int Then
		If this._int_value <> this._int_old_value Then
			b = TRUE
		EndIf
		
		If b = TRUE Then
			this._int_old_value = this._int_value
		EndIf
	
	ElseIf this.init_flag = _string Then
		If this._string_value <> this._string_old_value Then
			b = TRUE
		EndIf
		
		If b = TRUE Then
			this._string_old_value = this._string_value
		EndIf
		
	EndIf
	Return b
End Property


Dim s As StateBase

s = "test"
? s, s.ischanged
s = 12
? s, s.ischanged
s= 13
? s, s.ischanged
s= "moo"
? s, s.ischanged
s= "moo"
? s, s.ischanged

's = "1"
'? s.ischanged
 
sleep 
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Property returns incorrect object

Post by dodicat »

sancho2
I cannot understand your changed logic, what constitutes a change?

Very simplified, my idea of a change:

Code: Select all


Type StateBase 
   Public:

      Declare Operator Cast() As String            'custom printout
      Declare Operator Let(ByRef txt As String)    ' = a string
      Declare Operator Let(ByRef value As Integer) ' = an integer
      
      as boolean ischanged ,isint,isstring
   Private:
      As String _string_value
      As Integer _int_value
End Type

operator StateBase.cast() as string
if isint    then return str(_int_value)
if isstring then return (_string_value)
return "not initialized"
end operator

operator StateBase.Let(ByRef value As String)
isstring=1:isint=0
if value<>_string_value then ischanged=1 else ischanged=0
   This._string_value = value
End Operator

Operator StateBase.Let(ByRef value As Integer)
isint=1:isstring=0
if value<>_int_value then ischanged=1 else ischanged=0
   This._int_value = value
End Operator

dim as StateBase s

print s,s.ischanged

s = "test"
? s, s.ischanged
s = 12
? s, s.ischanged
s= 13
? s, s.ischanged
s= "moo"
? s, s.ischanged
s= "moo"
? s, s.ischanged
s=13
? s, s.ischanged
s="test"
?s,s.ischanged
sleep



sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Property returns incorrect object

Post by sancho2 »

Your code is essentially the same as mine.
The differences I can find:
Your class behaves as though it were a container for two variables, a string and an integer.
When my object is assigned a string and then an integer, it forgets it was a string and considers itself re-initialized. If it changes types it is re-initialized and not considered a change.

I wanted a generic object that could hold any type. FB does not handle generics as of yet. That is the reason I want my object to behave as though it was a single self aware variable and not a container type. It was never intended to hold both integer and string at the same time.
I even delved into your macro world a bit to see if I could improvise generics. But I couldn't come up with anything that wasn't really clunky, bizarre or that impressed me as useable.

The only other glaring thing to my eyes is that you shortened the code a little by using a boolean member var instead of a property. I may borrow that.

I was fooling around with trying to establish a base class that could be extended by whatever type you wanted rather than contain it all in one. In that aspect I ran into trouble with the lack of generics, so my base class ended up being only two abstract operator overloads. At that point it just didn't make sense to continue that direction. Thats where I was at when I rebuilt the object into what I posted. (That is the reason for the strange name "StateBase")

Also, you didn't provide an integer Cast. I still want to be able to do ? s * 12, etc.

But here is the latest (still not completely happy with it):

Code: Select all

Type StateVar 
	Private:
			
		Enum _type
			_none
			_int
			_string
		End Enum

		As String _string_value
		As Integer _int_value
		
		As String _string_old_value
		As Integer _int_old_value

		As _type init_flag = _none

	Public:

		Declare Property IsChanged() As boolean	
		Declare Operator Cast() As String
		Declare Operator Cast() As Integer
	
		Declare Operator Let(ByRef txt As String)
		Declare Operator Let(ByRef value As Integer)
		
		
End Type
operator StateVar.Cast() As String
	'
	If this.init_flag = _string Then
		Return This._string_value
	ElseIf this.init_flag = _int Then
		Return Str(this._int_value)
	EndIf
End Operator

operator StateVar.Cast() As Integer
	'
	Return This._int_value
End Operator

operator StateVar.Let(ByRef value As String)
'
	This._string_value = value
	If this.init_flag = _none OrElse this.init_flag = _int Then
		this.init_flag = _string
		this._string_old_value = value
	EndIf
End Operator
Operator StateVar.Let(ByRef value As Integer)
'
	This._int_value = value
	If this.init_flag = _none OrElse this.init_flag = _string Then
		this.init_flag = _int
		this._int_old_value = value
	EndIf
End Operator
Property StateVar.IsChanged() As boolean
	'
	Dim As boolean b
	
	If this.init_flag = _int Then
		If this._int_value <> this._int_old_value Then
			b = TRUE
		EndIf
		
		If b = TRUE Then
			this._int_old_value = this._int_value
		EndIf
	
	ElseIf this.init_flag = _string Then
		If this._string_value <> this._string_old_value Then
			b = TRUE
		EndIf
		
		If b = TRUE Then
			this._string_old_value = this._string_value
		EndIf
		
	EndIf
	Return b
End Property


Dim s As StateVar
'
s = "test"
? "test", s, s.ischanged
s = 12
? s * 12, s, s.ischanged
s= 13
? 13, s, s.ischanged
s= "test"
? "test",s, s.ischanged
s= "moo"
? "moo", s, s.ischanged

 
sleep 
I will also explain why I want a self aware variable.
In the main loop of a program there is a printing of information to the screen, such as cursor location. This gets printed thousands of times because of the loop, even if the info doesn't change. With a self aware variable this re-print can be avoided.
As I type this out now I just got the idea that I can move that logic into the the cast overload and turf the 'if myvar.ischanged then". I should be able to just "print myvar" and let the cast do the check.

After posting a version using your system of a boolean variable 'is_changed' I realized it wasn't going to work. I deleted it.
Neither will your version work. The reason is that the variable must be able to tell the code whether its been changed since its been checked last. In your code the member var is_changed is true and remains true until you change the variable value.

Therefore is_changed will work if it is a property that can then set the member var _is_changed to false after its been checked. This also means that a member print function is not workable either.
So I am reverting back to the version I posted here.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

Suppressed 2 useless 'if':

Code: Select all

Property StateVar.IsChanged() As boolean
   '
   Dim As boolean b
   
   If this.init_flag = _int Then
      If this._int_value <> this._int_old_value Then
         b = TRUE
         this._int_old_value = this._int_value
      EndIf
      
   ElseIf this.init_flag = _string Then
      If this._string_value <> this._string_old_value Then
         b = TRUE
         this._string_old_value = this._string_value
      EndIf
      
   EndIf
   Return b
End Property
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Property returns incorrect object

Post by Tourist Trap »

sancho2 wrote: After posting a version using your system of a boolean variable 'is_changed' I realized it wasn't going to work. I deleted it.
Neither will your version work. The reason is that the variable must be able to tell the code whether its been changed since its been checked last. In your code the member var is_changed is true and remains true until you change the variable value.
I think I tried to think about what you're trying here, weeks ago. What I can say is that I found that it could be done at a cycle level. I mean, you define a cycle (some main or secondary loop) in your program, and you refresh the states of this kind of variable altogether (in the good order) at each end of cycle. I started something very complete that shapes a program with a main loop to manage that in full generality but failed to go very far for lack of time as usual. Maybe I posted a first try at tip&tricks section, some searching to do so, maybe today or tomorrow I hope.

Found it:
viewtopic.php?f=7&t=25264&p=227159&hili ... 2A#p227159

A first try so.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

General remark on your code (in seeing operator 'StateVar.Cast() As String'):
- It is a good habit to always define the return value (from function, property, operator, ...) whatever the output branch used, otherwise unexpected behavior may sometime occur (only safe for simple predefined datatype for which the default value is returned).
- Indeed, without return value initialization, memory for return value is well allocated normally, but the implicit constructor (for object) is not called after.
- That can cause (when using an object) some unexpected behavior or even crashing (see examples at viewtopic.php?p=227137#p227137)
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Property returns incorrect object

Post by sancho2 »

I fixed the sub.
In theory the code should never reach the end of the cast without hitting one of the returns. The only reason I had the elseif was in case I added another data type (maybe double) down the road. But safer is better so I added the fix.
I tried out the code you linked and yes that is a violent crash.
Good info, thanks fxm.

Code: Select all

operator StateVar.Cast() As String
   '
   If this.init_flag = _string Then
      Return This._string_value
   ElseIf this.init_flag = _int Then
      Return Str(this._int_value)
   EndIf
   Return ""	'<---------added
End Operator
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Property returns incorrect object

Post by fxm »

sancho2 wrote:I fixed the sub.
In theory the code should never reach the end of the cast without hitting one of the returns.
Are you sure?

Code: Select all

Type StateVar
   Private:
         
      Enum _type
         _none
         _int
         _string
      End Enum

      As String _string_value
      As Integer _int_value
      
      As String _string_old_value
      As Integer _int_old_value

      As _type init_flag = _none

   Public:

      Declare Property IsChanged() As boolean   
      Declare Operator Cast() As String
      Declare Operator Cast() As Integer
   
      Declare Operator Let(ByRef txt As String)
      Declare Operator Let(ByRef value As Integer)
      
      
End Type
operator StateVar.Cast() As String
   '
   If this.init_flag = _string Then
      Return This._string_value
   ElseIf this.init_flag = _int Then
      Return Str(this._int_value)
   EndIf
   Return "Should never happen!"   '<---------added
End Operator

operator StateVar.Cast() As Integer
   '
   Return This._int_value
End Operator

operator StateVar.Let(ByRef value As String)
'
   This._string_value = value
   If this.init_flag = _none OrElse this.init_flag = _int Then
      this.init_flag = _string
      this._string_old_value = value
   EndIf
End Operator
Operator StateVar.Let(ByRef value As Integer)
'
   This._int_value = value
   If this.init_flag = _none OrElse this.init_flag = _string Then
      this.init_flag = _int
      this._int_old_value = value
   EndIf
End Operator
Property StateVar.IsChanged() As boolean
   '
   Dim As boolean b
   
   If this.init_flag = _int Then
      If this._int_value <> this._int_old_value Then
         b = TRUE
         this._int_old_value = this._int_value
      EndIf
      
   ElseIf this.init_flag = _string Then
      If this._string_value <> this._string_old_value Then
         b = TRUE
         this._string_old_value = this._string_value
      EndIf
      
   EndIf
   Return b
End Property


Dim s As StateVar
? s
 
Sleep
Post Reply