Possible bug in basic class setter implementation

It looks to me like the argument list aspect of the property setter is being ignored yielding unexpected results.
Why does this not work as expected?
I have this class (CTest) with an array member that I want to set using an indexed property let statement

option Compatible 
option ClassModule 
option Explicit 
	
	private _n() as integer
	
	private sub class_initialize() 
		redim _n(1 to 10) 
	end sub 
	public property let item(byval index as integer, byval value as integer)
		_n(index) = value 
	end property 

This is the code I am using to set the value:

sub m()
	dim n as ctest
	set n = new ctest
	n.item(1) = 12
end sub 

I get this error:

BASIC runtime error.
Object variable not set.

Am I missing something here?
Is this not a bug?

LibreOffice 7.1.5.2 on Windows 8

As far as I know, LO BASIC does not support indexed properties in classes.
Use class methods like

Public Sub put(ByVal index, ByVal value)

As sokol92 says, “un-currying” the index into the first parameter of the function signature is syntactic sugar provided by some languages, but not LO BASIC, as far as I know. It definitely is not a bug; it’s simply not a part of LO BASIC syntax. If a person wants to pass multiple values via the = assignment then they’ll probably want to assign the property to a containing class that holds all the values.

Here’s the “functioning” class:

Option Compatible
Option ClassModule
Option Explicit

'Module MyClass

Dim mValue As Variant

Private Sub class_initialize()
End Sub

Public Property Let Property1(Value As Variant)
	mValue = Value
End Property

Public Property Get Property1()
	Property1 = mValue
End Property

Here’s the “container” class:

Option Compatible
Option ClassModule
Option Explicit

'Module MyContainer

Dim mThing1 As Variant
Dim mThing2 As Variant

Private Sub class_initialize()
End Sub

Public Property Let Thing1(Value As Variant)
	mThing1 = Value
End Property
Public Property Get Thing1()
	Thing1 = mThing1
End Property

Public Property Let Thing2(Value As Variant)
	mThing2 = Value
End Property
Public Property Get Thing2()
	Thing2 = mThing2
End Property

Here are a couple “consumers”:

Option Explicit

'Module MyConsumers

Public Sub TestThings()
	Dim MyInstance As New MyClass
	Dim Things As New MyContainer
	
	Things.Thing1 = 1
	Things.Thing2 = 2
	
	MyInstance.Property1 = Things
	
	MsgBox MyInstance.Property1.Thing1

End Sub

Public Sub TestNested()
	Dim MyInstance As New MyClass
	
	Dim MyOtherInstance As New MyClass
	
	MyOtherInstance.Property1 = 99
	MyInstance.Property1 = myOtherInstance
	
	MsgBox MyInstance.Property1.Property1
	
End Sub

See the next comment about a bug…

There seems to be a problem with the IDE environment in which old property setters and getters stick around as ghosts in the works.

Demonstration:

Create a class with a simple Property Let/Set and Property Get. Now, create a simple test consumer that instantiates the class then sets then gets the property.

Now, remark out the property setter in the class and run the consumer again. It will run without errors, but return Null from the getter.

Now, remark out the property getter in the class (that is, both setter and getter) and run the consumer again. It will work as if neither the setter nor the getter were remarked out. It will, in fact, work “perfectly.”

Now save the document, exit, and reload it. Now, of course, BASIC will complain that it cannot set a property that doesn’t exist.

This behavior could certainly create considerable mystery when trying to finesse class usage in a project.

LO BASIC does not support indexed properties in classes.

That statement seems to be contrary to this:

Property setters often use a single argument. Multiple arguments are equally accepted.

From:
https://help.libreoffice.org/latest/gu/text/sbasic/shared/property.html

The reason I believe it is a bug is because there is no error message. The second argument is just ignored. That can’t be intended behaviour.

  1. My statement was “…as far as I know…” which is not contrary to the docs, because I don’t know all the docs! :slight_smile: That said, while the highlighted pullout says that properties can have multiple arguments, the BNF signature diagrams do not show that, and…
  2. Even if the docs are correct, LO BASIC doesn’t offer a tuples syntax. How would one pass multiple parameters to a multi-parametric setter using LO BASIC = assignment? I know that OP sees passing index syntax as the first parameter as being “natural,” but it isn’t…it’s just sugar used in some languages.
  3. I’ll add to my use of a container class the simpler idea of assigning to an explicit array, so take an array as the Property argument then use MyObject.Lots = Array(44, 66) in the consumer and back in the class parse the array in the property setter. This is closer to tuples syntax.
  4. There is something fundamentally bizarre about LO BASIC = assignment. See the next post, which also has some examples to play with for the above comments 1.-3.

Here is some LO BASIC code that shows what is either a bizarre behavior of LO BASIC (7.2.2.2), or I’m going crazy. I seem to have seen even more bizarre related behaviors, but I have not been able to solidify them into a deterministic demonstration.

What I am seeing is that when I assign to a string using a reference to an array element that is stored in a class variable by accessing the reference through a property getter, instead of the current value of the element of that array being included in the string, nothing is included in the string and the value of the element becomes empty.

Option Compatible
Option ClassModule
Option Base 0
Option Explicit

'Module SimpleClass

Dim mPrivateArray As Variant

Public Property Set TheArray(ValuesArray() As Variant)
	mPrivateArray = ValuesArray

	'Debug.Print "Now Showing mPrivateArray within the TheArray setter..."
	'XRay mPrivateArray
	
End Property

Public Property Get TheArray As Variant
	TheArray = mPrivateArray
End Property

And

Option Base 0
Option Explicit

'Module Consumers

Sub Test()
	Dim MyInstance As New SimpleClass
	Dim MyArray(0 To 2)  As Variant
	Dim Result As Variant
	Dim Index As Integer
	Dim Execution As Integer
	
	'Vary Execution from 1 to 3 and observe the results
	'	Execution = 0 will work as expected
	'	Execution = 1 will result in suppression of first MyArray element values
	'	Execution = 2 will result in suppression of first two MyArray element values
	'	Execution = 3 will result in suppression of all three MyArray element values
	'	Execution = 4 will work as expected
	'	This is not just a question of SimpleClass's mPrivateArray being a reference to MyArray
	'		mPrivateArray is never assigned to after the TheArray property setter
	Execution = 2
	
	For Index = LBound(MyArray) To UBound(MyArray)
		MyArray(Index) = 100 + Index '100+ to distinguish from the indices themselves
	Next Index
	
	On Error Resume Next
		If (Not GlobalScope.BasicLibraries.isLibraryLoaded("XrayTool")) Then
			GlobalScope.BasicLibraries.LoadLibrary("XrayTool")
		End If
		MsgBox "Now Showing MyArray before MyInstance.TheArray = MyArray (if XRay is installed)"
		XRay MyArray
	On Error Goto 0

	MyInstance.TheArray = MyArray

	On Error Resume Next
		MsgBox "Now Showing MyArray after MyInstance.TheArray = MyArray (if XRay is installed)"
		XRay MyArray
	On Error Goto 0

	Select Case Execution
		Case 0:
			Result = "The no-class-involvement result is: (" & MyArray(0) & ", " & MyArray(1) & ", " & MyArray(2) & ")"
		Case 1:
			Result = "The one-index class-involvement result is: (" & MyInstance.TheArray(0) & ")"
		Case 2:
			Result = "The two-index class-involvement result is: (" & MyInstance.TheArray(0) & ", " & MyInstance.TheArray(1) & ")"
		Case 3:
			Result = "The three-index class-involvement result is: (" & MyInstance.TheArray(0) & ", " & MyInstance.TheArray(1) & ", " & MyInstance.TheArray(2) & ")"
		Case 4:
			Result = MyInstance.TheArray
	End Select
	
	On Error Resume Next
		MsgBox "Now Showing Result after Result = ... (if XRay is installed)"
		XRay Result
		MsgBox "Now Showing MyArray after Result = ... (if XRay is installed)"
		XRay MyArray
	On Error Goto 0
	
	If Execution <> 4 Then MsgBox Result
	If Execution = 4 Then	MsgBox Result(0) & ", ..."
	
End Sub

In this case you can add the ‘set’ here

'MyInstance.TheArray = MyArray  
set MyInstance.TheArray = MyArray 

and it works as expected.
Note that I also changed the setter so that it is not expecting an array of variants but a single variant that may be an array:

Public Property Set TheArray(ValuesArray As Variant)
	mPrivateArray = ValuesArray

	'Debug.Print "Now Showing mPrivateArray within the TheArray setter..."
	'XRay mPrivateArray
	
End Property
1 Like

Yep! Or really what I wanted was

Public Property Let TheArray(...)

with ValuesArray() or ValuesArray, since a Variant holding an array isn’t an Object (or at least, I don’t recall it ever being one in VBA).

Thanks.

Rem CTest Module
Option Compatible
Option ClassModule
Option Explicit
	
Private colItems As New Collection


'Private sub Class_Initialize()
'	'Set colItems = New Collection
'End Sub

Private Sub Class_Terminate()
	Set colItems = Nothing
End Sub

Sub Add(nIndex%, nValue%)
	colItems.Add nValue, CStr(nIndex)
End Sub

Function Item%(nIndex%)
	'Item = colItems.Item(CStr(nIndex))
	Item = colItems.Item(nIndex)
End Function

Rem Module2
Sub m()
	Dim n As New CTest

	n.Add(1, 12)
	Print n.Item(1)  'returns 12
End sub

May I kindly ask what this is for?

some_list = [12, 4, 3, 42, 55]
print(some_list[1]) #→ # 4
print(some_list[-2] ) #→ # 42
print(some_list.index(42)) #→ # 3
some_list.append("hello World")
print(some_list)

2021-12-04_502x203_scrot

It is necessary to ask the OP.
It makes more sense if a class is added as an element of the collection.