-- someone
One of my favourite Vala things
So, I have been trying out different stuff in Vala lately, one of which is the GObject to JSON stuff (more on that in an other post). Today I thought that I would show you some dirty, dirty, dirty things to do with Vala.
Nested lambda functions
So the topic of today is -- as the heading suggests -- nested lambda functions (or closures, if you are so inclined).
using GLib; using Gtk; public class MyApp : Gtk.Application { static int main (string args[]) { MyApp app = new MyApp (); return app.run (); } Gtk.Label label; Gtk.Window window; MyApp () { Object(application_id: "testing.my.application", flags: ApplicationFlags.FLAGS_NONE); } protected override void activate () { /* Creating a window */ window = new Gtk.ApplicationWindow (this); this.build_menues (); window.set_default_size (400,400); this.label = new Gtk.Label ("Hello GTK!"); window.add (label); window.title = "Hello GTK!"; window.show_all (); } private void build_menues () { /* Creating the application menu */ GLib.Menu app_menu = new GLib.Menu (); app_menu.append ("Message", "app.message"); app_menu.append ("About", "app.about"); app_menu.append ("Quit", "app.quit"); this.set_app_menu (app_menu); GLib.SimpleAction quit_action = new GLib.SimpleAction ("quit", null); quit_action.activate.connect (() => { this.quit(); }); this.add_action (quit_action); GLib.SimpleAction about_action = new GLib.SimpleAction ("about", null); about_action.activate.connect (() => { Gtk.AboutDialog dialog = new Gtk.AboutDialog(); dialog.set_destroy_with_parent (true); dialog.set_transient_for(this.window); dialog.set_modal (true); dialog.response.connect((response_id) => { if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) { //dialog.hide_on_delete (); dialog.destroy (); } }); dialog.authors = {"Gustav Hartvigsson", "Some One Else"}; dialog.program_name = "Hello GTK!"; dialog.present(); }); this.add_action (about_action); GLib.SimpleAction message_action = new GLib.SimpleAction ("message", null); message_action.activate.connect (() => { label.set_text ("I have been clicked!"); }); this.add_action (message_action); } }
If you look at the highlighted code (line 49-66) you will see nested lambda functions. This is awesome, and something you would expect from a modern, high abstraction level, object oriented language.
Dissection the C code
The C code that this produces (using valac-0.20 main.vala --pkg gtk+-3.0 --pkg gio-2.0 -C
)
looks like this:
/* main.c generated by valac 0.20.1, the Vala compiler * generated from main.vala, do not modify */ #include <glib.h> #include <glib-object.h> #include <gtk/gtk.h> #include <stdlib.h> #include <string.h> #include <gio/gio.h> #define TYPE_MY_APP (my_app_get_type ()) #define MY_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MY_APP, MyApp)) #define MY_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_MY_APP, MyAppClass)) #define IS_MY_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MY_APP)) #define IS_MY_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_MY_APP)) #define MY_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_MY_APP, MyAppClass)) typedef struct _MyApp MyApp; typedef struct _MyAppClass MyAppClass; typedef struct _MyAppPrivate MyAppPrivate; #define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) typedef struct _Block1Data Block1Data; struct _MyApp { GtkApplication parent_instance; MyAppPrivate * priv; }; struct _MyAppClass { GtkApplicationClass parent_class; }; struct _MyAppPrivate { GtkLabel* label; GtkWindow* window; }; struct _Block1Data { int _ref_count_; MyApp * self; GtkAboutDialog* dialog; }; static gpointer my_app_parent_class = NULL; GType my_app_get_type (void) G_GNUC_CONST; #define MY_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_MY_APP, MyAppPrivate)) enum { MY_APP_DUMMY_PROPERTY }; static gint my_app_main (const gchar* args, int args_length1); static MyApp* my_app_new (void); static MyApp* my_app_construct (GType object_type); static void my_app_real_activate (GApplication* base); static void my_app_build_menues (MyApp* self); static void __lambda2_ (MyApp* self); static void ___lambda2__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self); static void __lambda3_ (MyApp* self); static Block1Data* block1_data_ref (Block1Data* _data1_); static void block1_data_unref (void * _userdata_); static void __lambda4_ (Block1Data* _data1_, gint response_id); static void ___lambda4__gtk_dialog_response (GtkDialog* _sender, gint response_id, gpointer self); static void ___lambda3__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self); static void __lambda5_ (MyApp* self); static void ___lambda5__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self); static void my_app_finalize (GObject* obj); static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func); static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func); static gint my_app_main (const gchar* args, int args_length1) { gint result = 0; MyApp* _tmp0_; MyApp* app; gint _tmp1_ = 0; _tmp0_ = my_app_new (); app = _tmp0_; _tmp1_ = g_application_run ((GApplication*) app, 0, NULL); result = _tmp1_; _g_object_unref0 (app); return result; } int main (int argc, char ** argv) { g_type_init (); return my_app_main (argv, argc); } static MyApp* my_app_construct (GType object_type) { MyApp * self = NULL; self = (MyApp*) g_object_new (object_type, "application-id", "testing.my.application", "flags", G_APPLICATION_FLAGS_NONE, NULL); return self; } static MyApp* my_app_new (void) { return my_app_construct (TYPE_MY_APP); } static void my_app_real_activate (GApplication* base) { MyApp * self; GtkApplicationWindow* _tmp0_; GtkWindow* _tmp1_; GtkLabel* _tmp2_; GtkWindow* _tmp3_; GtkLabel* _tmp4_; GtkWindow* _tmp5_; GtkWindow* _tmp6_; self = (MyApp*) base; _tmp0_ = (GtkApplicationWindow*) gtk_application_window_new ((GtkApplication*) self); g_object_ref_sink (_tmp0_); _g_object_unref0 (self->priv->window); self->priv->window = (GtkWindow*) _tmp0_; my_app_build_menues (self); _tmp1_ = self->priv->window; gtk_window_set_default_size (_tmp1_, 400, 400); _tmp2_ = (GtkLabel*) gtk_label_new ("Hello GTK!"); g_object_ref_sink (_tmp2_); _g_object_unref0 (self->priv->label); self->priv->label = _tmp2_; _tmp3_ = self->priv->window; _tmp4_ = self->priv->label; gtk_container_add ((GtkContainer*) _tmp3_, (GtkWidget*) _tmp4_); _tmp5_ = self->priv->window; gtk_window_set_title (_tmp5_, "Hello GTK!"); _tmp6_ = self->priv->window; gtk_widget_show_all ((GtkWidget*) _tmp6_); } static void __lambda2_ (MyApp* self) { g_application_quit ((GApplication*) self); } static void ___lambda2__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self) { __lambda2_ (self); } static Block1Data* block1_data_ref (Block1Data* _data1_) { g_atomic_int_inc (&_data1_->_ref_count_); return _data1_; } static void block1_data_unref (void * _userdata_) { Block1Data* _data1_; _data1_ = (Block1Data*) _userdata_; if (g_atomic_int_dec_and_test (&_data1_->_ref_count_)) { MyApp * self; self = _data1_->self; _g_object_unref0 (_data1_->dialog); _g_object_unref0 (self); g_slice_free (Block1Data, _data1_); } } static void __lambda4_ (Block1Data* _data1_, gint response_id) { MyApp * self; gboolean _tmp0_ = FALSE; gint _tmp1_; gboolean _tmp3_; self = _data1_->self; _tmp1_ = response_id; if (_tmp1_ == ((gint) GTK_RESPONSE_CANCEL)) { _tmp0_ = TRUE; } else { gint _tmp2_; _tmp2_ = response_id; _tmp0_ = _tmp2_ == ((gint) GTK_RESPONSE_DELETE_EVENT); } _tmp3_ = _tmp0_; if (_tmp3_) { gtk_widget_destroy ((GtkWidget*) _data1_->dialog); } } static void ___lambda4__gtk_dialog_response (GtkDialog* _sender, gint response_id, gpointer self) { __lambda4_ (self, response_id); } static void __lambda3_ (MyApp* self) { Block1Data* _data1_; GtkAboutDialog* _tmp0_; GtkWindow* _tmp1_; gchar* _tmp2_; gchar* _tmp3_; gchar** _tmp4_ = NULL; gchar** _tmp5_; gint _tmp5__length1; _data1_ = g_slice_new0 (Block1Data); _data1_->_ref_count_ = 1; _data1_->self = g_object_ref (self); _tmp0_ = (GtkAboutDialog*) gtk_about_dialog_new (); g_object_ref_sink (_tmp0_); _data1_->dialog = _tmp0_; gtk_window_set_destroy_with_parent ((GtkWindow*) _data1_->dialog, TRUE); _tmp1_ = self->priv->window; gtk_window_set_transient_for ((GtkWindow*) _data1_->dialog, _tmp1_); gtk_window_set_modal ((GtkWindow*) _data1_->dialog, TRUE); g_signal_connect_data ((GtkDialog*) _data1_->dialog, "response", (GCallback) ___lambda4__gtk_dialog_response, block1_data_ref (_data1_), (GClosureNotify) block1_data_unref, 0); _tmp2_ = g_strdup ("Gustav Hartvigsson"); _tmp3_ = g_strdup ("Some One Else"); _tmp4_ = g_new0 (gchar*, 2 + 1); _tmp4_[0] = _tmp2_; _tmp4_[1] = _tmp3_; _tmp5_ = _tmp4_; _tmp5__length1 = 2; gtk_about_dialog_set_authors (_data1_->dialog, _tmp5_); _tmp5_ = (_vala_array_free (_tmp5_, _tmp5__length1, (GDestroyNotify) g_free), NULL); gtk_about_dialog_set_program_name (_data1_->dialog, "Hello GTK!"); gtk_window_present ((GtkWindow*) _data1_->dialog); block1_data_unref (_data1_); _data1_ = NULL; } static void ___lambda3__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self) { __lambda3_ (self); } static void __lambda5_ (MyApp* self) { GtkLabel* _tmp0_; _tmp0_ = self->priv->label; gtk_label_set_text (_tmp0_, "I have been clicked!"); } static void ___lambda5__g_simple_action_activate (GSimpleAction* _sender, GVariant* parameter, gpointer self) { __lambda5_ (self); } static void my_app_build_menues (MyApp* self) { GMenu* _tmp0_; GMenu* app_menu; GSimpleAction* _tmp1_; GSimpleAction* quit_action; GSimpleAction* _tmp2_; GSimpleAction* about_action; GSimpleAction* _tmp3_; GSimpleAction* message_action; g_return_if_fail (self != NULL); _tmp0_ = g_menu_new (); app_menu = _tmp0_; g_menu_append (app_menu, "Message", "app.message"); g_menu_append (app_menu, "About", "app.about"); g_menu_append (app_menu, "Quit", "app.quit"); gtk_application_set_app_menu ((GtkApplication*) self, (GMenuModel*) app_menu); _tmp1_ = g_simple_action_new ("quit", NULL); quit_action = _tmp1_; g_signal_connect_object (quit_action, "activate", (GCallback) ___lambda2__g_simple_action_activate, self, 0); g_action_map_add_action ((GActionMap*) self, (GAction*) quit_action); _tmp2_ = g_simple_action_new ("about", NULL); about_action = _tmp2_; g_signal_connect_object (about_action, "activate", (GCallback) ___lambda3__g_simple_action_activate, self, 0); g_action_map_add_action ((GActionMap*) self, (GAction*) about_action); _tmp3_ = g_simple_action_new ("message", NULL); message_action = _tmp3_; g_signal_connect_object (message_action, "activate", (GCallback) ___lambda5__g_simple_action_activate, self, 0); g_action_map_add_action ((GActionMap*) self, (GAction*) message_action); _g_object_unref0 (message_action); _g_object_unref0 (about_action); _g_object_unref0 (quit_action); _g_object_unref0 (app_menu); } static void my_app_class_init (MyAppClass * klass) { my_app_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (MyAppPrivate)); G_APPLICATION_CLASS (klass)->activate = my_app_real_activate; G_OBJECT_CLASS (klass)->finalize = my_app_finalize; } static void my_app_instance_init (MyApp * self) { self->priv = MY_APP_GET_PRIVATE (self); } static void my_app_finalize (GObject* obj) { MyApp * self; self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_MY_APP, MyApp); _g_object_unref0 (self->priv->label); _g_object_unref0 (self->priv->window); G_OBJECT_CLASS (my_app_parent_class)->finalize (obj); } GType my_app_get_type (void) { static volatile gsize my_app_type_id__volatile = 0; if (g_once_init_enter (&my_app_type_id__volatile)) { static const GTypeInfo g_define_type_info = { sizeof (MyAppClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) my_app_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (MyApp), 0, (GInstanceInitFunc) my_app_instance_init, NULL }; GType my_app_type_id; my_app_type_id = g_type_register_static (GTK_TYPE_APPLICATION, "MyApp", &g_define_type_info, 0); g_once_init_leave (&my_app_type_id__volatile, my_app_type_id); } return my_app_type_id__volatile; } static void _vala_array_destroy (gpointer array, gint array_length, GDestroyNotify destroy_func) { if ((array != NULL) && (destroy_func != NULL)) { int i; for (i = 0; i < array_length; i = i + 1) { if (((gpointer*) array)[i] != NULL) { destroy_func (((gpointer*) array)[i]); } } } } static void _vala_array_free (gpointer array, gint array_length, GDestroyNotify destroy_func) { _vala_array_destroy (array, array_length, destroy_func); g_free (array); }
If we look at the function my_app_build_menues
(line 245) you will see that
on line 264 the action is added to the self
object (equivilant to this
in the vala code). The action adding just passes a function pointer to the function
___lambda3__g_simple_action_activate
, and if we follow the path we will see that that function
on line 228 calls an other function on line 193.
In the function prior mentioned ( __lambda3 ()
) we can see that on line 211 there is another
string-up to the function ___lambda4__gtk_dialog_response ()
on line 187 that runs the function
on line 166.
This is what nested lambdas in Vala look like. it is scary and beautiful at the same time. I have no idea if lambdas are slower than just creating a function and using them as parameters to the action/callback/delegates, I just wanted to show of some of the sexy and dirty internals of Vala.