Open hugopl opened 2 months ago
Weird... the tests don't fail on me locally.
Tests now pass... but I noticed something that may be a compiler bug, if I change the class on c_born_crystal_objects_spec.cr
to
private class UserObj < GObject::Object
@[GObject::Property]
property crystal_prop = ""
getter crystal_attr : Int32 = 42
def initialize(@hey = "")
super()
end
end
It doesn't initialize crystal_attr to 42 and the test fail.
@BlobCodes , I remember you wanted this feature years ago, and it was only possible due to your work on toggle refs. If you have time, at your own pace, could you take a look on this and give me some feedback? Thanks!
This MR fails to compile with abstract classes, mainly because GICrystal doesn't set any flags in the generated C Type if the Crystal class is abstract.
Hmmm, CI was failing with old GObject libs, after updating the CI things start passing.
With the current patch things works as expected, but there's the breaking change that requires Crystal GObjects to define initialize()
, after few minutes porting Tijolo to use this patch I saw how annoying is this, so I'll try to avoid this breaking change, i.e. If the type doesn't have a default constructor it cannot be created from C.
The challenge will be able to provide a good compiler error message in these cases.
I believe now it's ready to be merged. And it's no more a breaking change, since it doesn't require the Crystal object to have a default constructor. However I'll only merge this after I start using Tijolo with this for few days.
@BlobCodes , I remember you wanted this feature years ago, and it was only possible due to your work on toggle refs. If you have time, at your own pace, could you take a look on this and give me some feedback? Thanks!
It seems like you are initializing the entire object inside the _instance_init
hook, which has a few disadvantages.
There are multiple steps to the initialization of a GObject:
_instance_init
hook is called bottom-up (GObject.init() -> Gtk::Widget.init() -> MyObject.init())
This hook is supposed to initialize any memory needed during the construction processCONSTRUCT
or CONSTRUCT_ONLY
flags are applied one-by-oneconstructed
is called, chained (MyObject.constructed() is supposed to call Gtk::Widget.constructed(), which is ...)
Objects are supposed to do more advanced logic here.For example, if our GObject is a templated Gtk::Widget
, the template is initialized during Gtk::Widget
s constructed
vfunc.
This means that there is currently a big behavioral difference between objects created from C or from Crystal:
Objects created from Crystal already have their template initialized, while Objects created from C aren't allowed to query for template objects yet.
I think we should do the following to better match GObject
s behaviour:
protected def initialize
in GObject
, which calls the previous constructed
vfunc
This would allow a super()
call to initialize the inherited GObject, matching normal crystal behaviourinitialize
params to CONSTRUCT_ONLY
GObject parameters_instance_init
, call initialize
inside vfunc constructed
When I started with the idea of always initialize the Crystal instance in the instance_init, but I guess I was not able to make custom constructors work, now I see that I can just use the method_added
macro we already have and add a self.new
everytime I see a def initialize
.
I felt something was wrong and I was unsure of how many time my code was setting the qdata depending on the 3 use cases of object construction.
Because I forgot that I would write a new method, I had only the self.new()
available, so the use case Foo.new(prop: value)
always have the Crystal instance first... anyway, writing a new
method for each initialize
found on that macro will reduce this to the base 1 use case.
Thanks for the feed back! I'll re-work on this patch and ping you when there's something worth to double check.
I think we should do the following to better match
GObject
s behaviour:
- Create a
protected def initialize
inGObject
, which calls the previousconstructed
vfunc This would allow asuper()
call to initialize the inherited GObject, matching normal crystal behaviour- Automatically map any
initialize
params toCONSTRUCT_ONLY
GObject parameters
I don't like this, IMO it's ok to have constructor params that has nothing to do with GObject properties. I also don't think is useful to support construct only Crystal gobject properties, but I may be wrong.
- Only initialize memory of crystal object in
_instance_init
, callinitialize
inside vfuncconstructed
This is the problem, I want to allow the user to write any possible constructor, so on constructed
vfunc I would need to known what Crystal constructor was called, so I could do the correct initialize
call. IIRC this is why the Crystal instance is created in more than one place, something I agree is bad... maybe I can fix that creating closures that call the correct initialize func, but it would involve the use of hacky thread local variables again.
- Always create crystal GObjects through C, instead of having two separate flows
Maybe my requirements for the desired API are too high.
This is a breaking change, since it now requires all GObject subclasses to have a constructor without arguments.
What changed:
There are 2 thread local variables to inform if the object is being created in C land or Crystal land, we store the objects instance pointers there.
When the object is created in C land, the Crystal instance is created on GObject instance_init method.
When the object is created in Crystal land, the GObject instance_init method creates no Crystal instance.
This also fixes the use case of tryign to use a Crystal defined GObject property before the Wrapper gets fully initialized.
Fixes https://github.com/hugopl/gtk4.cr/issues/69