On Tue, 2017-10-24 at 19:03 -0500, Federico Mena Quintero wrote:
On Wed, 2017-10-25 at 00:27 +0200, Sebastian Dröge wrote:Do you plan to also add support for public fields in the instance struct?Nope. This has made it all to easy to break the ABI in the past, and all of GTK+ has moved to private structs, anyway. I realize that public fields may be convenient for "internal" GObjects that are never exported, though. Maybe we have different use cases in mind? (I'm all about libraries with GObject Introspection.)
I don't need support for public fields, I was just curious what your plans there are :) 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).
maybe we should have something like gobject_gen! { class Foo { ... reserve_slots(N); // to be decremented when needed } }
Much nicer, yes. In GStreamer we just use a gpointer _padding[GST_PADDING-X]; field, which is basically the same as your reserve_slots() API functionality-wise.
private_init() is a mandatory function that gets called duringThis should probably take the instance as parameter, or a different function for hooking into instance_init would be useful. To set-up any things that might be necessary for the base-class to properly initialize everything.The idea is that private_init() returns a FooPrivate struct, without ever having the chance to look at uninitialized memory.
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.
To set up extra things, we can very well have an init(&self) {} block or something. I suppose this ties with the discussion on constructors below.
Sounds good
Then it should also be possible to impl derive for the struct manually, the default derived impl is not always possible/correct :)Something like gnome_class! { class Foo { struct FooPrivate { ... } impl Default for FooPrivate { ... } } } ? You know, that may be more Rust-like than the whole private_init() thing. Do you like it better?
Yes :)
I'm torn between putting the "impl Blah for Bleh" inside or outside the class block. If I were trying to make this look more like an extension to the language, maybe something like gnome_class! { class Foo: Superclass { private = FooPrivate; // similar to associated types, "type Foo = Bar;" // should we put the init/constructor/etc here? fn init(&self) { ... } fn constructor(&self) { // how do we pass in the construct properties comfortably? } } struct FooPrivate { ... } // or #[derive(Default)] above if it works for you impl Default for FooPrivate { fn default() -> FooPrivate { ... } } // this defines the class ABI, basically impl Foo { pub fn static_method(&self, ...) { ... } virtual fn virtual_method(&self, ...) { ... } fn this_private_method_is_an_implementation_detail(&self) { // and is not exported or put in the class slots } signal some_signal(&self, ...); signal with_default_handler(&self, ...) -> Bar { // default handler code goes here } } // or just "impl Superclass for Foo"? // I kind of like making it obvious that these are overriden things
"impl SuperClass for Foo" would be nicer IMHO. It's the same thing with implementing traits that have default implementations. Everything you write in an impl block that is not for your type itself will be something that is overridden in one way or another.
Note the init()/constructor() inside the "class" block.
+1 How about class_init, base_init and their finalize parts? Or maybe these should be part of some kind of "impl GObject for Foo"? They are not really part of Foo but conceptually something that is overridden/extended.
I haven't gotten to the set_prop/get_prop stuff yet. If you figure out a nice syntax for them, feel free to add it here :)
impl Foo { #[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.
Also, what about dispose/finalize? In the old code generator, ::finalize() would just Drop the private struct; we don't provide a good place to put a custom Drop implementation, but with the scheme above, it is easy to put an "impl Drop for FooPrivate" along with "impl Default for FooPrivate".
impl Drop for finalize sounds good
Should we have a fake "impl Dispose for Foo" that must have a "fn dispose(&self)", or add a fake function dispose() along with the init()/constructor() above?
"impl Dispose" seems more consistent
Could also use a construct {} block for that, it seems like a reasonable choice. Similarly for class and base construct blocks.Hmmm, how would you notate those in the scheme above? Or some other scheme?
I prefer the functions you use in your example above.
public MyConstructor (int a, int b, int c) { Object (some_construct_only_prop: a, some_other_prop: b); // construct superclass this.my_own_prop = c; }I assume this would only take CONSTRUCT properties, and not all of them, and there can only ever be a single constructor like this?I don't know if Vala lets you have multiple constructors; it doesn't sound terribly hard to support them. Right now I'm a bit more uncertain on how to get the construct-time properties to a constructor function :)
You mean how to get them there code-wise? Or how to do the mapping between parameters and property names?
[signal accumulators]We use them quite often in GStreamer. You basically need them whenever you have a non-trivial return value, or you want to use your return value to decide if further signal handlers have to be called. I can give examples if needed.This would be very helpful - in your gobject-example-rs?
I meant examples where they're used in GStreamer :) Code-wise it's all simple really. For example (let's take decodebin): 1) "autoplug-continue" bool accumulator: First TRUE stops signal emission. This is used for e.g. deciding whether to place a decoder for a stream or just provide the compressed stream. First TRUE wins. I think GTK has something similar for various signals, called "inhibit" iirc? 2) "autoplug-factories" array accumulator: Only the first signal handler will ever be called and the returned array will be used 3) "autoplug-sort" array accumulator: Stop after the first signal handler that returned a non-empty array 4) "autoplug-select" enum accumulator: Stop after the first signal handler that returned a specific enum value 5) "autoplug-query" bool accumulator: Return the boolean || over all return values I can find more for you in other places if you're interested.
You could provide them by name in the signal attribute.signal my_signal(&self, ...) where accumulator(i_dont_even_know_the_params) Syntax ideas appreciated :)
That looks more like Haskell :) In Rust "where" is only used for putting further bounds on generic type parameters. I think something like #[accumulator(my_accumulator_function)] signal my_signal(&self, ...) -> ... { ... } would be nicer.
What about the class/default handler, how would you handle that?Just by writing signal my_signal_with_default_handler(&self, x: i32) { println! ("default handler {}": x); } That would generate a function and assign its function pointer to the signal's slot in the class.
Makes sense :)
Sorry to put so much pressure on the syntax right now - it's just that writing syn parsers that mix a made-up language with syn's own view of Rust constructs... is not the most pleasant thing, but I'm getting better at it :)
Don't worry, syntax is the most discussion-friendly part of a new mini- language :) See also Wadler's law https://wiki.haskell.org/Wadler%27s_Law
Attachment:
signature.asc
Description: This is a digitally signed message part