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