Re: Bikeshedding the gnome-class mini-language



On Wed, 2017-10-25 at 13:20 -0500, Federico Mena Quintero wrote:
On Wed, 2017-10-25 at 10:41 +0200, Sebastian Dröge wrote:

How about (public) fields in classes though? Those allow to do some
degree of meta-programming with GObject and we use that in
GStreamer
(and GObject also uses that, e.g. properties).

Could you point me to a place in GStreamer that uses this?

With the "impl GObject for MyClass" thing, I was thinking of putting
the GTypeInfo's base_init, base_finalize, class_init, class_finalize
functions there.  I feel like this is related to public fields in
classes, but I'm not completely sure.

You also need at least class_init in GStreamer to configure the base
class (add pad templates, metadata for the element class like its name,
etc). Like I said about class fields, this actually allows for some
nice meta-programming :)

So check all the gst_*_class_* functions, most important ones being
  gst_element_class_add_pad_template()
  gst_element_class_set_metadata()

We also have some base classes where you can configure the behaviour by
setting a boolean or enum in the class struct, e.g. GstBaseTransform
has two booleans for that.


Also base_init() can be useful in various cases. We mostly use it in
GStreamer for plugins that dynamically register types. E.g. with ffmpeg
or various plugin APIs (ladspa, lv2, frei0r, VST3, ...) you implement a
generic element subclass and then register a different type for each
(e.g.) ladspa plugin that the user has installed on the system and
configure it slightly different based on the ladspa plugin metadata
(for example install different properties).

class_finalize and base_finalize are not used in GStreamer because we
don't do unloadable GTypeModules.

(... aren't public class fields just as problematic as instance
fields
ABI-wise?  Or is this so that type plugins can provide "global
variables" in a sane way?)

IMHO that's the users problem really. The user needs to be aware of
what they can do and not while keeping API/ABI compatibility.
Note that for class fields you can also use a class private struct and
class methods for setting/getting them.

That would also be my opinion on instance struct fields, but those are
less useful.

[reserve_slots(N) for ABI]
Much nicer, yes. In GStreamer we just use a
  gpointer _padding[GST_PADDING-X];

Nice, so there is a clear need for this.  I'll put it in.

Related, how do you define the *order* in which signals and vfuncs go
into the class struct? You wouldn't want that adding a new vfunc breaks
ABI because it gets inserted between two older ones for example.

Everything in instance_init() is already initialized (to all-
zeroes).
But I was more thinking of calling functions of the subclass. E.g.
in
GStreamer you usually set up static pads in there.

I've just read a bit of gobject.c to really see the initialization
sequence.

- gathers construct properties (*)

- calls ::constructor() with the construct properties, which chains
up...

- ... until it reaches the topmost g_object_constructor(), which...

- allocates memory for the instance...

- calls ::init() on each superclass...

Note that the GType at that point in each instance struct is the one of
the superclass that is currently handled. You can only know that your
final instance will be of a specific GType by taking the secret second
parameter to instance_init (the final type's class struct).

- calls ::set_property() just for the construct properties...

- ... and then the rest of your ::constructor() runs.  But apparently
if you don't chain up, you can use this to implement singletons?  Do
you know of a place where this may be used?

- calls ::constructed()

- calls ::set_property() for the non-construct properties.

(*) Until now I didn't realize that a GParamSpec's default value only
gets used if the property in question is a construct property.  Is
this correct?

Yes

Also, I used to think that ::constructor() was responsible for
directly
using the construct_params that it gets passed, but they are just to
call the parent class constructor.  (I wouldn't be surprised if some
silly object actually frobs those values before passing them on, but
jeez.)

I have written some GObjects that override constructor and sneak in new
construct properties into that array, change values, or remove some.
But not sure if you want to support that :)

From my reading of this, everything until ::constructed() is called
means that the instance is only halfway-initialized.  At ::init() it
is
"basically uninitialized memory, actually zeros"; after chaining up
in
::constructor() it is "the bare minimum properties have been set";
and
only in ::constructed() is it "minimum initialization is complete;
all
extra properties are sugar on top".

Ack

Therefore, in general I don't think code should call methods during
::init() - whether it actually works will of course depend on the
code
in question :)  I do remember having bugs with this in the past ("no
wonder the method fails; the object is not fully initialized yet").

As a thought exercise, could GStreamer set up those static pads in
::constructor() or ::constructed() or something?

constructed() should also work, yes

  #[attributes...]
  property some_property: u32 {
    get(&self) -> u32 {
      self.something
    }
    set(&self, value: u32) {
      self.something = value;
    }    
  }
}

Maybe? Not sure about providing the type 3 times, but this way
seems most consistent.

Makes sense.  I'll look more closely at Vala.  From it and from C# I
don't like the magic "value" that it uses inside setters; it appears
to come from nowhere.

Yeah, that's what I like about modeling them as something like a
function with an actual return type or value parameter. That part in C#
always seemed a bit inconsistent to me :)

Would "_" make sense just avoid repeating the type?

Same opinion here as Adrian, I don't like it :) A where clause seems
nicer.

Also I like his other suggestion to auto-derive the setter/getter for
the trivial cases (but that needs some thought for thread-safe GObjects
then, as for those you'd like to take a mutex somewhere when accessing
the values)

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]