PDA

View Full Version : Wow, GNOME development should really *not* be done in C...



kripkenstein
December 22nd, 2007, 08:58 AM
This is a followup to the "Wow, development should really be done in Python..." (http://ubuntuforums.org/showthread.php?t=644946) thread. I'll try to show you why programming in C can be sometimes cumbersome, and why something like Python or Vala (http://live.gnome.org/Vala) can be nicer.

Here is the source code for a simple hello world app (taken from the Vala website). It creates a window with a button. Click the button, and the window title changes. That is all. Here is the source code in Vala:


using GLib;
using Gtk;

public class Sample : Window {
construct {
title = "Sample Window";
create_widgets ();
}

public void create_widgets () {
destroy += Gtk.main_quit;

var button = new Button.with_label ("Hello World");
button.clicked += btn => {
title = btn.label;
};

add (button);
}

static int main (string[] args) {
Gtk.init (out args);

var sample = new Sample ();
sample.show_all ();

Gtk.main ();
return 0;
}
}

Nice, right? Short and to the point. In fact it looks like Python except with a few curly braces and types ;)

Vala is a language that compiles itself into C. Here is the C code generated by this short hello world app. First, the .h file:



#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__

#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>

G_BEGIN_DECLS


#define TYPE_SAMPLE (sample_get_type ())
#define SAMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SAMPLE, Sample))
#define SAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SAMPLE, SampleClass))
#define IS_SAMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SAMPLE))
#define IS_SAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_SAMPLE))
#define SAMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SAMPLE, SampleClass))

typedef struct _Sample Sample;
typedef struct _SampleClass SampleClass;
typedef struct _SamplePrivate SamplePrivate;

struct _Sample {
GtkWindow parent;
SamplePrivate * priv;
};
struct _SampleClass {
GtkWindowClass parent;
};

void sample_create_widgets (Sample* self);
Sample* sample_new (void);
GType sample_get_type (void);

G_END_DECLS

#endif

Now the actual C code:



#include "helloworld.h"
#include <stdlib.h>
#include <string.h>

enum {
SAMPLE_DUMMY_PROPERTY
};
static void __lambda0 (GtkButton* btn, Sample* self);
static gint sample_main (int args_length1, char** args);
static GObject * sample_constructor (GType type, guint n_construct_properties, GObjectConstructParam * construct_properties);
static gpointer sample_parent_class = NULL;


static void __lambda0 (GtkButton* btn, Sample* self) {
g_return_if_fail (btn == NULL || GTK_IS_BUTTON (btn));
gtk_window_set_title (GTK_WINDOW (self), gtk_button_get_label (btn));
}


void sample_create_widgets (Sample* self) {
GtkButton* button;
g_return_if_fail (IS_SAMPLE (self));
g_signal_connect (self, "destroy", ((GCallback) gtk_main_quit), NULL);
button = g_object_ref_sink (gtk_button_new_with_label ("Hello World"));
g_signal_connect_object (button, "clicked", ((GCallback) __lambda0), self, 0);
gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (button));
(button == NULL ? NULL : (button = (g_object_unref (button), NULL)));
}


static gint sample_main (int args_length1, char** args) {
Sample* sample;
gint _tmp0;
gtk_init (&args_length1, &args);
sample = g_object_ref_sink (sample_new ());
gtk_widget_show_all (GTK_WIDGET (sample));
gtk_main ();
return (_tmp0 = 0, (sample == NULL ? NULL : (sample = (g_object_unref (sample), NULL))), _tmp0);
(sample == NULL ? NULL : (sample = (g_object_unref (sample), NULL)));
}


int main (int argc, char ** argv) {
g_type_init ();
return sample_main (argc, argv);
}


Sample* sample_new (void) {
Sample * self;
self = g_object_newv (TYPE_SAMPLE, 0, NULL);
return self;
}


static GObject * sample_constructor (GType type, guint n_construct_properties, GObjectConstructParam * construct_properties) {
GObject * obj;
SampleClass * klass;
GObjectClass * parent_class;
Sample * self;
klass = SAMPLE_CLASS (g_type_class_peek (TYPE_SAMPLE));
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
obj = parent_class->constructor (type, n_construct_properties, construct_properties);
self = SAMPLE (obj);
{
gtk_window_set_title (GTK_WINDOW (self), "Sample Window");
sample_create_widgets (self);
}
return obj;
}


static void sample_class_init (SampleClass * klass) {
sample_parent_class = g_type_class_peek_parent (klass);
G_OBJECT_CLASS (klass)->constructor = sample_constructor;
}


static void sample_init (Sample * self) {
}


GType sample_get_type (void) {
static GType sample_type_id = 0;
if (G_UNLIKELY (sample_type_id == 0)) {
static const GTypeInfo g_define_type_info = { sizeof (SampleClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) sample_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Sample), 0, (GInstanceInitFunc) sample_init };
sample_type_id = g_type_register_static (GTK_TYPE_WINDOW, "Sample", &g_define_type_info, 0);
}
return sample_type_id;
}


The C code is over 5 times longer, and I would say 10 times if not more harder to read for someone not extremely experienced with GObject.

This C code is how most GNOME apps are written! Imagine how many more people could contribute to the GNOME project with code if some of its apps were written in something like Vala. And how much faster any programmer hacking on that code would be. GNOME development would speed up in an extremely significant way.

To avoid misunderstandings, note that Vala compiles into C. So it is not slower than C, except perhaps by little (see benchmarks (http://code.google.com/p/vala-benchmarks/wiki/BenchResults) linked to on the Vala site). Unlike Python which is say 5-10 times slower. So you do not need to trade off speed for code clarity. You can have both if you use a modern development language like Vala.

Edit: I edited this post a little to make it less anti-C. I want to clarify, I am not anti-C - I use it and like it. But I am trying to make the point that GTK can perhaps be nicer to code in in a few other languages. Clearly all languages have their advantages - C is fast, and well-known - and even if it has a disadvantage here, that isn't to say it isn't an excellent language overall.

Jucato
December 22nd, 2007, 09:52 AM
I don't think it's fair to use a Vala-generated C code as an example. For sure, it has probably generated more than what's required for a basic Hello, World in GTK+/GNOME. See these examples:

http://www.gtk.org/tutorial/c39.html#SEC-HELLOWORLD
http://developer.gnome.org/doc/GGAD/cha-gtk.html
http://www.gnomebangalore.org/?q=node/14

(Although I'm still having problems believing how GTK was able to pull of GUI programming on a non-object oriented language).

LaRoza
December 22nd, 2007, 10:03 AM
This thread doesn't make much sense.

They length of code has no bearing on the actual size of an executable, and although C may take more typing for higher level functions, like GUI's, that is unimportant, as it has very low level capabilities.

C has libraries for such things, so developers don't sit there reinventing the wheel each time they do something.

Although Python make be faster to develop with, it is often rewritten in C or another language for efficiency.

I asked this thread to be moved because it has no technical merit, and will only attract pointless discussions, that were already addressed.

bapoumba
December 22nd, 2007, 03:04 PM
Moved to "Recurring Discussions" and reopened.
Second chance. If it triggers reports again, we will not hesitate to close it down for good.
Have fun ;)

Jessehk
December 22nd, 2007, 03:42 PM
#include <QApplication>
#include <QWidget>
#include <QPushButton>

class Sample : public QWidget {
Q_OBJECT
private:
QPushButton *button_;
private slots:
void buttonPressed();
signals:
void titleChanged( const QString &newTitle );
public:
Sample();
};

Sample::Sample() :
QWidget( 0 ),
button_( new QPushButton( "Hello World", this ) ) {


connect( button_, SIGNAL( clicked() ),
this, SLOT( buttonPressed() ) );

connect( this, SIGNAL( titleChanged( const QString & ) ),
this, SLOT( setWindowTitle( const QString & ) ) );
}

void Sample::buttonPressed() {
emit titleChanged( button_->text() );
}

int main( int argc, char *argv[] ) {
QApplication app( argc, argv );

Sample window;
window.resize( 200, 50 );
window.show();

return app.exec();
}

#include "main.moc"


:)

kripkenstein
December 22nd, 2007, 03:55 PM
I don't think it's fair to use a Vala-generated C code as an example. For sure, it has probably generated more than what's required for a basic Hello, World in GTK+/GNOME. See these examples:

http://www.gtk.org/tutorial/c39.html#SEC-HELLOWORLD
http://developer.gnome.org/doc/GGAD/cha-gtk.html
http://www.gnomebangalore.org/?q=node/14

(Although I'm still having problems believing how GTK was able to pull of GUI programming on a non-object oriented language).
You make a very valid point. Vala might not generate the nicest C code.

However, the C example you give here doesn't use the class functionality of GObject. It just calls a few GTK routines. If you rewrote the C example to use classes, then it would look much more like the Vala output.

However, it seems that gtkmm (C++ bindings for GTK) might solve that issue.

Edit: Oops, I thought you linked to something but I found it somewhere else. Sorry.

kripkenstein
December 22nd, 2007, 04:02 PM
#include <QApplication>
#include <QWidget>
#include <QPushButton>

class Sample : public QWidget {
Q_OBJECT
private:
QPushButton *button_;
private slots:
void buttonPressed();
signals:
void titleChanged( const QString &newTitle );
public:
Sample();
};

Sample::Sample() :
QWidget( 0 ),
button_( new QPushButton( "Hello World", this ) ) {


connect( button_, SIGNAL( clicked() ),
this, SLOT( buttonPressed() ) );

connect( this, SIGNAL( titleChanged( const QString & ) ),
this, SLOT( setWindowTitle( const QString & ) ) );
}

void Sample::buttonPressed() {
emit titleChanged( button_->text() );
}

int main( int argc, char *argv[] ) {
QApplication app( argc, argv );

Sample window;
window.resize( 200, 50 );
window.show();

return app.exec();
}

#include "main.moc"


:)

I had no idea Qt could be this elegant, thanks. :)

saulgoode
December 22nd, 2007, 05:59 PM
You make a very valid point. Vala might not generate the nicest C code.

However, the C example you give here doesn't use the class functionality of GObject. It just calls a few GTK routines.

How is this a criticism? GTK is an object/class system. Though it does not depend upon the GObject system (largely because it was developed before GObject existed), the only difference between g_objects and gtk_objects is the fact that gtk_objects can have "unassigned" ownership of the reference count.


If you rewrote the C example to use classes, then it would look much more like the Vala output.

Classes ARE being used, it is just not necessary for the programmer to deal with them directly (not necessary, but the capability is there). The C language implementation allows a programmer to not be concerned with such cumbersome details.

kripkenstein
December 22nd, 2007, 06:04 PM
Classes ARE being used, it is just not necessary for the programmer to deal with them directly (not necessary, but the capability is there). The C language implementation allows a programmer to not be concerned with such cumbersome details.

Sorry, I wasn't being clear. I meant that these Hello World examples didn't define new classes. Most every GNOME application does in fact define new classes, so a Hello World that did that would be more comparable IMO. And the Vala code does that.

Wybiral
December 22nd, 2007, 07:54 PM
There's nothing wrong with C or C++, but when you have a choice between them or a higher level language that's safer and easier to prototype in, I don't see why you would waste your precious time picking at bits and bytes for no reason (unless you just love to do that kind of thing, which I understand, but it's just not practical in the real world).

Don't get me wrong, I use both C and C++ (and occasionally assembly). For the most part, I enjoy them. But in a serious project, I only use them when I need to, which isn't very frequently. The fastest development strategy I've found is to use a higher level language (like Python) and cut out the rough prototype of your solution (and with a clean HLL, this is usually a pretty quick task). Then I test and profile to determine where I need to optimize (or if I need to optimize, sometimes HLLs will surprise you).

In software such as 3d graphics or statistical/mathematical models, I will usually find one or two small chunks of code that end up bottlenecking. This is where you step back and see if there's a HL optimization (an algorithm change or some kind of refactoring). If that doesn't work THEN you can write those small chunks in C. But it makes much more sense to look for a high level optimization first.

A good example I have is a 3d terrain renderer I was working on. At first, I took the brute force approach and rendered it as a heightmap (bad idea). I kept wasting all of my time trying to optimize it using C and assembly (shaving this operation, trying to optimize that loop). It got me nowhere, this thing was slow as could be. Then I stepped back and looked for a HL solution. The first obvious one was to use the camera frustum to cull the unneeded points, this increased the speed quite a bit! Even better than this was when I started researching binary triangle trees and ROAM LOD implementations. This ended up being a PAIN to code in my C/Assembly combo, so I had to move up to C++ (which even then was a hassle to handle some of the memory). Long story short, it ended up running WAY faster the higher-level I got, because it was easier to optimize the solution algorithmically (which cuts out way more operations than trying to hand-tune little bits of assembly).

kripkenstein
December 23rd, 2007, 02:47 PM
Ok, I decided to put my code where my mouth is and write something in Vala.

It was pretty rough going because Vala is not yet stable-quality, and has very sparse documentation, but still the language is very neat. And when something goes wrong, you can read the intermediate C code and debug that, which turned out helpful once or twice.

So, I decided to write a simple PDF viewer in Vala, here it is if anyone wants to take a look: Valence (https://launchpad.net/valence/) (you can browse/download the code here (https://code.launchpad.net/~kripkenstein/valence/devel)).

The program is far from finished, but I'm already using it instead of xpdf for light PDF viewing. It takes 20-25MB to show the sample PDF in the code, about the same as xpdf, which is nice. Presumably in Python/C#/etc. this would be higher. Evince takes 35MB on the same PDF, but it has lots of features so this isn't a fair comparison.