g_object_{new,set,get} vs. class-specific function
Mark Doliner
mark at kingant.net
Sun May 20 13:04:22 EDT 2007
On Sun, 20 May 2007 08:16:25 -0500, Gabriel Schulhof wrote
> Hi, Richard!
>
> You asked me why I prefer using GObject properties rather than
> class-specific accessors and constructors. A bit of an essay follows,
> so please feel free to ignore it and instruct me to use the class-specific
> functions in Pidgin :o)
>
> I have found several reasons:
>
> - Using g_object_new instead of the class-specific constructors is more
> flexible, primarily because you're not restricted to the constructors
> available for the given type. For example:
>
> gtk_window_new (GTK_WINDOW_TOPLEVEL);
>
> Here, the argument will be GTK_WINDOW_TOPLEVEL 99.99% of the time. This
> also happens to be the default value of the "type" property, so it
> shouldn't need to be specified. OTOH, because of this lone
> constructor, you need to make additional calls to properly configure
> the window: gtk_window_set_title gtk_window_set_resizable
gtk_container_set_border_width
> gtk_window_set_default_size
> gtk_window_set_role
> With g_object_new, you could create and configure your window in one
> call:
> g_object_new(GTK_TYPE_WINDOW,
> "title", _("My Window"), "resizable", FALSE,
> "border-width", PIDGIN_HIG_BORDER, "role", "demo",
> "default-width", 320, "default-height", 240, NULL) ;
>
> And, even if that doesn't float your boat, you can at least replace the
> useless GTK_WINDOW_TOPLEVEL with, say, the more useful "title":
> -gtk_window_new(GTK_WINDOW_TOPLEVEL);
> +g_object_new(GTK_TYPE_WINDOW, "title", _("My Window"), NULL);
>
> Another example:
>
> label = gtk_label_new("Some Really Long Text");
> gtk_widget_show(label);
> gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
> gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
> gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
>
> vs.
>
> g_object_new(GTK_TYPE_LABEL,
> "visible", TRUE, "label", _("Some Really Long Text"),
> "wrap", TRUE, "justify", GTK_JUSTIFY_LEFT,
> "xalign", 0.0, "yalign", 0.0, NULL);
>
> This particular example illustrates a sequence that is present for
> almost all child widgets ever created:
>
> new_widget = gtk_some_widget_new();
> gtk_widget_show(new_widget);
>
> If you do nothing else, you could at least replace the above two
> lines with new_widget = g_object_new (GTK_TYPE_SOME_WIDGET,
> "visible", TRUE, NULL) and save a line of code for each child
> widget created. For constructors with more than 0 parameters, you
> can still specify those parameters on the one line of code that
> creates and now shows the widget.
>
> - Another benefit of g_object_new is that you can free yourself from
> having to track uninteresting widgets. For example:
>
> GtkWidget *label = NULL, *important_entry = NULL;
>
> label = gtk_label_new("Temperature:");
> gtk_widget_show(label);
> gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
> gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
> gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0) ;
>
> important_entry = gtk_entry_new() ;
> gtk_widget_show(important_entry);
> gtk_box_pack_start(GTK_BOX(hbox), important_entry, TRUE, TRUE, 0);
>
> vs.
>
> GtkWidget *important_entry = NULL;
>
> gtk_container_add_with_properties (GTK_CONTAINER (hbox),
> g_object_new (GTK_TYPE_LABEL, "visible", TRUE, "justify",
> GTK_JUSTIFY_RIGHT, "xalign", 1.0, "yalign", 0.5, NULL),
> "expand", FALSE, NULL);
>
> gtk_container_add (GTK_CONTAINER (hbox),
> important_entry = g_object_new (GTK_TYPE_ENTRY, "visible", TRUE,
> NULL));
>
> This sequence brings up another point:
>
> - With properties, you are not forced to specify values that should
> be kept default
>
> Your example:
> gtk_vbox_new (gboolean homogeneous, gint spacing);
>
> Why should I be forced to specify a spacing whenever I create a VBox?
> Keeping the default would probably eliminate a lot of hardcoded values.
> In fact (and with a little more blue-sky thinking) it would allow
> one to set a system-wide default value that all apps would all-of-a-sudden
> reflect. Kinda like style properties, actually, which (I speculate)
> would then become just like normal properties.
>
> Side note: "homogeneous" is also FALSE by default, and specified as
> "FALSE" 99.99% of the time when creating a vbox or and hbox. So,
> again, completely useless. g_object_new (GTK_TYPE_VBOX, "visible",
> TRUE, NULL) would be far more useful.
>
> In a similar vein, specifying the padding when calling
> gtk_box_pack_start is also a source of hardcoded values. So, why have
> it? Just use gtk_container_add_with_properties and specify only those
> child properties whose default values are not what you want.
>
> - Using g_object_{get,set} allows you to become a little more
> object-type-agnostic. Consider, for example, an object class that
> "sports" the "active" property. It could be a GtkToggleButton or a
> GtkToggleToolButton. If we were to always use g_object_get (G_OBJECT
> (widget), "active", &active, NULL); instead of
> gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)), we could
> change the type of the widget (and therefore our UI) without having
> to touch those parts of the code where we retrieve the "active"
> property, by virtue of the fact that different widget classes all
> implement this "active" property. I have experienced the
> GtkToggleButton/GtkToggleToolButton scenario when I introduced a
> GtkToolbar into one of my apps, and I had to hunt the code for calls
> to gtk_toggle_button_{get,set}_active (GTK_TOGGLE_BUTTON
> (the_toggle_button)) and change it to gtk_toggle_tool_button_{get,
> set}_active (GTK_TOGGLE_TOOL_BUTTON
> (the_toggle_button))
>
> Of course, if you use g_object_{get,set}, you can also connect a signal
> handler to multiple different object types, as long as they all have
> the property your signal handler is interested in.
>
> Gabriel
My biggest argument against using g_object_new() is that you lose a lot of
compile-time checking. What happens if you accidentally pass in "justlfy"
instead of "justify" when creating a label?
-Mark
More information about the Devel
mailing list