serenity icon indicating copy to clipboard operation
serenity copied to clipboard

GMLCompiler+Applications: Add common user-defined initializer for widgets

Open kleinesfilmroellchen opened this issue 2 years ago • 8 comments

Many widget classes need to run substantial initialization code after they have been setup from GML. With this change, an initialize_fallibles() function is called if available, allowing the initialization to be invoked from the GML setup automatically. This means that the GML-generated creation function can now be used directly for many more cases, and reduces code duplication.

Fixes #21401

kleinesfilmroellchen avatar Jan 19 '24 13:01 kleinesfilmroellchen

Haven't looked at it closely (and phone-posting right now), but couldn't we just add something like the following to the generated code?

if constexpr (requires { object.initialize_fallibles() })
    TRY(object.initialize_fallibles());

That would presumably allow us to sidestep the GUI::initialize_fallibles(T& object) roundtrip and offer a bit more flexibility when it comes to handling additional arguments in the future.

PS: And, notably, I think we could also make initialize_fallibles private then if it is called from try_create exclusively.

timschumi avatar Jan 19 '24 14:01 timschumi

Haven't looked at it closely (and phone-posting right now), but couldn't we just add something like the following to the generated code?

if constexpr (requires { object.initialize_fallibles() })
    TRY(object.initialize_fallibles());

Exactly what I tried. The code within if constexpr must still compile, therefore this does not work unfortunately. I could only get it to work with SFINAE.

PS: And, notably, I think we could also make initialize_fallibles private then if it is called from try_create exclusively.

That in turn makes the SFINAE solution impossible; the requires clause fails since the function is private, the function is never called and apps crash or break due to silently not invoking any initializer.

You may try some more template magic, but this is the most straightforward solution I managed to come up with. Besides, I think such a simple header function will be inlined so the performance should be equal.

kleinesfilmroellchen avatar Jan 19 '24 15:01 kleinesfilmroellchen

Additionally, private initialize_fallibles only works for the top-level widget. While that is the most common use case, it doesn't work for sub-widgets like those that are used in SystemMonitor.

kleinesfilmroellchen avatar Jan 19 '24 15:01 kleinesfilmroellchen

All you need to get the if constexpr requires to work is a template context. for example, a generic lamdba could work


auto initialize = [](auto& object) -> ErrorOr<void> {
    if constexpr (requires { object.initialize_fallibles() })
        return object.initialize_fallibles();
    else
        return {};
};

TRY(initialize(object));

ADKaster avatar Jan 20 '24 00:01 ADKaster

I also really dislike the name initialize_fallibles. what's wrong with just plain initialize ?

ADKaster avatar Jan 20 '24 00:01 ADKaster

I also really dislike the name initialize_fallibles. what's wrong with just plain initialize ?

I think that was suggested somewhere, but I didn't choose the name carefully.

kleinesfilmroellchen avatar Jan 21 '24 12:01 kleinesfilmroellchen

All you need to get the if constexpr requires to work is a template context. for example, a generic lamdba could work


auto initialize = [](auto& object) -> ErrorOr<void> {
    if constexpr (requires { object.initialize_fallibles() })
        return object.initialize_fallibles();
    else
        return {};
};

TRY(initialize(object));

Would you want that lambda generated into the code, or move it to some reusable place?

kleinesfilmroellchen avatar Jan 21 '24 12:01 kleinesfilmroellchen

CI seems to be a flake.

kleinesfilmroellchen avatar Jan 25 '24 19:01 kleinesfilmroellchen