Posted: 8th Dec 2021 23:10
I'll start off with my question and then explain the background to the question.

Question: How can I assign a reference to an 'object' instead of making a copy?

Background

I have a nice button type and I also add a set of these buttons to another type I call a RadioButtonSet
I have sets of functions for setting text, visibility, etc. that allow me to modify the items in the types of the button. For [a simplified] example I have:

+ Code Snippet
Type tButton
	text as string // button caption
	AllowSounds as integer 
Endtype

myButton as tButton
tButton = CreateButton("FRED")

Function CreateButton(Text as string)
     gui as tButton
     gui.text = text
EndFunction gui // return a new tButton object

Function SetButtonSounds(gui ref as tButton, Allowed as integer)
	gui.AllowSounds = Allowed
EndFunction


That all works very well. Now I add some of these buttons into my RadioButton set like this:

+ Code Snippet
type tRadioButtonSet
	Value as integer // the button that is currently on   -1 means none
	Buts as tButton[0] // array of tButtons
	Count as integer // number of buttons added
endtype

Function CreateRadioButtonSet()
	gui as tRadioButtonSet
	gui.Value = -1 
	gui.Count = 0
EndFunction gui

function AddRadioButton(gui ref as tRadioButtonSet, NewBut ref as tButton)
	gui.Buts.length = gui.count // zero based
	inc gui.count
	gui.buts[gui.Buts.length] = newBut // seems to make a COPY of NewBut
EndFunction

ButtonSet as tRadioButtonSet
ButtonSet = CreateRadioButtonSet()

// now we add a button to the set

AddRadioButton(ButtonSet,MyButton)
AddRadioButton(ButtonSet,SomeOtherButton) // not defined in this sample code



This also works well, I can call Functions that handle the button set

Now, lets say I want to change one of the properties of the buttons, like a color or if it is allowed to make click sounds...

I can call the function for updating the content of the structure:

+ Code Snippet
SetButtonSounds(MyButton,1)


What I find is that the AllowSounds value is updated correctly in MyButton, however the first button added to ButtonSet is unchanged.

This leads me to believe that when I assign the added buttons to the array of buttons in AddRadioButton with the line:

+ Code Snippet
gui.buts[gui.Buts.length] = newBut // seems to make a COPY of NewBut


It is in fact creating a copy of the tButton object that was passed in by reference. This is further supported by the fact that if I change properties of a button before adding it to the ButtonSet it maintains its value.

So my question is: how can I assign a reference to an 'object' instead of making a copy? I am hoping that there is some function/modifier/syntax to do this.
Something like:

Set gui.buts[gui.Buts.length] = newBut


I tried this:

+ Code Snippet
Function ByRef(b ref as tButton)
endfunction b

gui.buts[gui.Buts.length] = byref(newBut)


But sadly the assignment still seems to make a copy.

I suspect I can do this:

+ Code Snippet
ButtonSet.buts[0].AllowSound  = 1


But that is only OK for Functions that don't do any side-effect work in addition to just setting a property.
Posted: 8th Dec 2021 23:25
I suspect I can do this:


indeed that works as does this:

+ Code Snippet
SetButtonSounds( ButtonSet.buts[0]  , 1)


This is better as it can also handle side-effect work, but it would be more elegant to not have a copy of the data in the buttonset array and be able to refer to the items by name rather than an index number [0] in the example above...
Posted: 9th Dec 2021 2:11
gui.buts[gui.Buts.length] = newBut // seems to make a COPY of NewBut


Hmm, I see your point, it should store the reference, maybe try with .insert

+ Code Snippet
gui.buts.insert(newBut)


I am not sure if AppGameKit honours the ref pointer in the case of copying or storing the var sent in, I have never run into this, I usually confine this stuff inside a function so its all self contained with no scope issues

personally I like to use a single global to manage object sets, it saves on memory and gives cleaner code

an arbitrary example
+ Code Snippet
Type tButton
	name as String
	id
	// bla bla bla
	
EndType
Global gButton as tButton[]

Function CreateButton(name as string)

	btn as tButton
	btn.id = // create button, 
	// do other stuff
	
	gButton.insertSorted(btn)
	
EndFunction


Function SetButtonColor(name as string, color as integer)

	index = gButton.find(name)
	if index <> -1
		SetButtonColor(gButton[index].id, color)
	endif

EndFunction


CreateButton("MyButton")
SetButtonColor("MyButton", newcolor)
Posted: 9th Dec 2021 2:12
[Double Post] again.... dam phone!
Posted: 9th Dec 2021 2:57
Using insert is an interesting idea... thanks.

Changing that line in my code results in a run-time error about things not existing so something is not working in the same manner I need to construct a simpler test version to see what is failing

Certainly I don't see any definition on how InsertSorted and find could work on user defined types, which fields does it use to sort on and find... the first one maybe?

Your approach is creating an array of the objects, and I have some that are in arrays also. When you update 'properties', your code is changing the one in the array, and I can see that working because you are searching for for them at every reference.

I could have my AddButton function do the creation of the button so that there is not an additional copy being made and I may do that if '.Insert' also stores a copy and nobody else know how to assign a pointer. (Which may only be possible in Tier2.)
Posted: 9th Dec 2021 3:17
yes as I guessed:

main.agc:52: error: To search an array of types the search expression must match the first variable of the type (Integer in this case)
Posted: 9th Dec 2021 3:26
for completeness, here is a working code using your approach:

+ Code Snippet
Type tButton
    
    name as String  // index field
    color as integer
         
EndType
Global gButton as tButton[]
 
Function CreateButton(name as string)
 
    btn as tButton
    btn.name = name
    btn.color = 3
    // do other stuff
     
    gButton.insertSorted(btn)
     
EndFunction
 
 
Function SetButtonColor(name as string, color as integer)
 
    index = gButton.find(name)
    if index <> -1
        gButton[index].color = color
    endif
 
EndFunction
 
Function GetButtonColor(name as string)
	result as integer
    index = gButton.find(name)
    if index <> -1
        result = gButton[index].color
    endif
 
EndFunction result 
 
 
CreateButton("MyButton")
SetButtonColor("MyButton", 6)

CreateButton("another")


do
    print( getButtoncolor("MyButton"))
    print( getButtoncolor("BadButton"))
    print( getButtoncolor("another"))
    print (gbutton[0].name)
    print (gbutton[1].name)
    
    
    Sync()
loop


it produces:

6
0
3
MyButton
another

as expected.
Posted: 9th Dec 2021 3:55
OK I managed to do the test, sadly, InsertSorted inserts a copy also.

The reason my code did not run without the simple substitution is because the insert approach creates a 1-based array (Edit: because I declared the array [0] ) and my code was designed to be zero-based.
Posted: 9th Dec 2021 4:40
I don't think it does, arrays are zero based no matter how they are managed, when you create the array is it defined as empty, ie [] or [-1], if you define it [0] then there is already 1 item in the array so any insert ontop of that would be at position 1
Posted: 9th Dec 2021 13:52
yes, after i went to bed I realized why!
Posted: 9th Dec 2021 20:07
I have now moved the button creation into my 'addbutton' function so that the new button goes out of scope and no second copy of the data is kept.

+ Code Snippet
function AddRadioButton(gui ref as tRadioButtonSet, Index as integer, x as float, y as float, width as float, height as float, text as string, style as integer)
	// buttonset plus same params for creating a button	
	// create button
	Newbut as tButton 
	Newbut = CreateButton(Index, x , y , width , height , text , style )
	inc gui.count	
	gui.buts.insert (newBut) // puts a copy of the tButton into the array	
EndFunction


Just to be clear to anyone coming to this thread. The original question still stands.
The discussion has mostly revolved around getting rid of the duplicated data.
Posted: 9th Dec 2021 20:50
that function makes a lot more sense to me, you have to remember that AppGameKit is not derived from Basic standards, its a in house hand built scripting language made by a company with a reputation for not conforming to industry standard's, Tier1 is fantastic but it has quirks and I guess the lack of a copy constructor for byref variables is one of them.

you could mention it on the Github page, maybe it will get looked into.
Posted: 9th Dec 2021 21:06
Question: How can I assign a reference to an 'object' instead of making a copy?

I'd be very happy to be wrong about this, but as far as I know, you can't. Reference variables in Tier 1 are very limited and you can't use them at all outside of functions. All you can do is pass an existing UDT variable (or array of any type) into a function by reference in order to access or modify its data directly within that function. That's it. You can't return a reference from a function and you can't assign a reference to another variable at all.