[Gambas-user] How can I instantiate a virtual class?

Tobias Boege taboege at gmail.com
Thu Sep 13 19:48:01 CEST 2018


On Thu, 13 Sep 2018, Me wrote:
> Dumb question, I know but...
> 
> Specifically I want to get a copy of MyMenu.Children into an array of Menu[] so I can re-order it. However, 
> Menu.Children is virtual (.Menu.Children).
> (I have a request to make the order of (a particular menu) user configureable.)
> I figured that if I can get a copy of the array, I could re-order it, clear the actual menu and re-add the menus in 
> the required order.
> 
> tia
> b
> p.s. I just noticed that Menu.Children is a collection not an array, but that doen't change my problem.
> 

TL;DR: You can't do that *in-place* with a virtual object. You have to
make a completely new menu with the desired order and "plug that in",
though I'm not sure how to do that, see last paragraph. (Alternatively:
bug Benoit to implement Move* methods on .Menu.Children like we have
in TreeView, a Sort event like GridView or at least a Reparent method.)

There are two types of virtual classes: native and Gambas virtual classes.
You can distinguish them roughly by the component they are coming from,
or by the first character in their name:

  * a leading dot means native, e.g. .Menu.Children from the native gb.qt4
    component or .Watch.Events in gb.inotify, vs.
  * a leading underscore which means it's written in Gambas, e.g. _MapTile
    in gb.map.

This naming is not a requirement but a convention. [*]

A virtual class is meant to provide an alternate interface to some part of
an object. In native components, a virtual property is oftentimes implemented
by returning the exact same object but telling the interpreter to "cast" it
to some other class, so that you have a more specialised set of methods
available. The property accessor usually sets some sort of flag or records
some access parameter like the settings slot in _Settings_Keys._get() so
that the more specialised methods know where you wanted them to operate. [#]
You can see that with the way native virtual classes are named: .Menu.Children
is a virtual class for managing the .Children property of a Menu object and
.Watch.Events does the same for the .Events of a Watch. [~]

You may see the problem now: virtual objects don't actually exist. They are
just a volatile view on a specific part of an object. So you can't put them
in an array. As soon as the line where you accessed MyMenu.Children finished
executing, the virtual object is destroyed; the hidden state used by the
Menu object to tell that you are accessing .Children now might have been
overwritten already. Virtual classes can be declared in native components
but they aren't creatable and always have size zero <-- also answering your
Subject line.

You can of course re-use the menu items that the virtual Menu.Children shows
you, like so:

  Dim hMenu As Menu, aSubMenus As New Menu[]

  For Each hMenu In MyMenu.Children
    aSubMenus.Push(hMenu)
  Next

But even then, I'm stuck. You can reorder the submenus, put them into a new
parent menu but how do you get the new parent menu into your existing menu
hierarchy? I'm missing a Menu.Reparent() method (why does Menu not inherit
Control?) to plug just the changed menu into what you already have.
The only way I see right now is to rebuild your entire menu -- even the
remote parts that didn't change -- every time you have to reorder some
submenu. I've never been a menu user in Gambas. Maybe others found a recipe?

Regards,
Tobi

PS: I moved into a new apartment and was absent from the internet for two
weeks. In my case, it went quite a way to show how much of my social life
happens over the cable. Anyway, this was a nice question to find on my
first day back. I may have written too much again, but that's because
I missed it :-)

[*] An outlier is _GridView_Cell in gb.qt4 which seems like it's both or
neither. The truth is that by now GridView is implemented in Gambas, in a
hidden gb.gui.base component and only *documented* in gb.qt4. So the leading
underscore tells the truth and the docs, as usual, lie.

[#] BTW, this is one of the obstacles of making Gambas thread-safe.
Lots of (all?) components rely on the assumption that they can just
put some data into a field in the object's underlying data structure
and return themselves to implement a virtual property and that the
data won't be touched until the virtual object is done. This is a
very cheap way of decluttering the interface of a class but also
very wrong in a multi-threaded environment.

[~] Actually, Gambas virtual classes (the ones which are non-native) are
just normal classes. The "casting part of an object to a different class"
is a strictly native feature. They probably shouldn't even be called
"virtual" but they are often used for the same purpose. The leading
underscore is generally used to make classes hidden, e.g. to the IDE
autocompletion popup. You could theoretically store these objects into
an array (and I remember doing that once) but that isn't safe either,
depending on what assumptions the class makes on its lifetime.

-- 
"There's an old saying: Don't change anything... ever!" -- Mr. Monk


More information about the User mailing list