"It is so wrong in all the right ways"
-- 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.