GMLCompiler+Applications: Add common user-defined initializer for widgets
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
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.
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_falliblesprivate then if it is called fromtry_createexclusively.
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.
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.
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));
I also really dislike the name initialize_fallibles. what's wrong with just plain initialize ?
I also really dislike the name
initialize_fallibles. what's wrong with just plaininitialize?
I think that was suggested somewhere, but I didn't choose the name carefully.
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?
CI seems to be a flake.