GTK Core: Remove the need for manual casting

Hello,

the use of many gtk functions enforce manual casting, as in the example below.
gtk_window_set_title (GTK_WINDOW(window), "Window title");

Why is the gtk interface not implement to be used as:
gtk_window_set_title (window, "Window title");
whereas the casting is done inside the function.

At the moment I’m writing macros for each function, that looks like this:
#define ggtk_window_set_title (window, title) gtk_window_set_title (GTK_WINDOW(window), title)
to mask the casting.

I understand, that it’s cleaner to not cast inside the function, as in some cases a cast might not be necessary. So I would suggest to provide a second interface to a function, that does the casting, so the user can choose to cast or not to cast.

Reason:
Manual castings make code lines very long and harder to read in many cases. Especially, because information redundancy is high:

  • Function name tells: “this is a function for a window”
  • Cast tells: we’re going to use a window
  • Name of first parameter tells: “This is a window”

More examples:
g_signal_connect(G_OBJECT(quitMi), "activate", G_CALLBACK(gtk_main_quit), NULL);
VS:
gg_signal_connect(quitMenuItem, "activate", gtk_main_quit, NULL);

gtk_container_add(GTK_CONTAINER(window), eventBox);
VS.
ggtk_container_add(window, eventBox);

Regards

When you do not like the verbosity of GTK C, why not use a different language? The big advantage of GTK is that there are so many language bindings. You dont have to use Nim, there are many more like D, Rust, Python, Java-Script, Java. For Crystal, Ruby, Go, Julia I am not sure how good the language bindings are currently.

For C: I think one reason why people use it for application development is, that some are paid for number of lines of code or byte size of source code. Or they feel great when they have generated many thousand lines of code. Well, not really serious :slight_smile:

For the GTK C style, I can remember similar discussions 10 years ago. I think basically it was just a design decision, original authors just like that most data types are plain GtkWidgets.

“Type safety” is the reason. Those macroes call subroutines that check the integrity of the structures behind the pointers. A program doesn’t need them – they’re a debugging tool. The source code can use simpler casts:

gtk_window_set_title((GtkWindow *)window, "Window title");

GTK_WIDGET does not only cast, it also does type checking, something C is lacking at a language level.

If you can’t live with such verbosity, just use a gpointer instead of using a specific type: you will gain for free what you are looking for (automatic pointer casting) without having to wrap every single API.

gpointer stuff = gtk_dialog_new(...);
gtk_widget_show(stuff);
gtk_dialog_run(stuff);

IMO the first time you’ll meet a memory corruption you will sorely regret this decision.

@StefanSalewski suggestion is surely a better alternative, although for personal preferences I would choose Lua instead of Nim.

2 Likes

Let me offer you my beginner explanation on this kind of situation and let us see where exactly come this one first.

Sit back and relax because it is going to be messy.

C language

A beginner naive decision we find it all the time and in all possible situations as well.
Let us take a look at the following C example:

    char *ptr = NULL;
    ptr = malloc( 256 * sizeof( *ptr ) );

The new comers see this two lines like the following:

  1. declaring ptr as a pointer to char and set it to NULL.
  2. allocating memory for the Pointer.

Now you see, here is a big problem of understanding the things what exactly happens.
You never allocate memory for the pointer, what exactly really happens is:

  1. you are making a request to the Operating System.
  2. this request takes place with the help ( by calling) of the malloc() function.
  3. again, this request takes place only and I repeat ONLY, if your Compiler is involved as well.
  4. you can (probably) get this memory (if there is some free memory of course) only if it could be reserved some of it for your application.

Now this is an example of a long chain of things what really happens when you are trying to get some memory for your application.

Lets us move on:

    char *ptr = NULL;
    ptr = "A string";

Another beginner naive understanding would be:

  1. declaring ptr as a pointer to char and set it to NULL.
  2. create a String called “A string”.

You see here are also a lot of misunderstanding things:

  1. declaring ptr as a pointer to char and set it to NULL.
  2. You never create a String, in fact in C, there are NO strings at all.
  3. “A string” it is a Literal String, which means that it is created in a spacial place ( Read Only Memory) inside memory and it is very hard coded as well.
  4. you cannot (and you should never try) ever modify a Literal string.
  5. in other words this: “A string”, it is a null terminated sequence of characters.
  6. that being said, here “ptr = “A string”;” you are pointing your pointer to that location where only Read can take place.

Now lets us see the next example:

    char arr[256] = "A string";

Here a naive beginner would say:

  1. we declare an array and we create a string.

Well this is not a bad explanation, because at some point you are doing that, but what’s the story with it?

Wel, let us split them to see what exactly happens:

  1. char arr[256] means that you are requesting a memory enough (up to) 256 bytes for your char.
  2. = “A string”; means that you are initializing a part of that requested memory with that information.

Now the thing are a little bit different, why?

  1. you make a request of 256 bytes.

  2. you are creating a Literal String, which again is hard coded in Read-Only-Memory

  3. at a moment when you are initializing your char, what really happens is that you are making a copy of that Literal String ( “A string” ) and you put that value (the copy) inside your char.

  4. again, what you are later doing with your array, has nothing to do with the Literal String “A string”.

  5. if fact the value of your array, which is “A string” has nothing to do with the Literal String “A string” which you set it up at the moment when you initialized your Array.

  6. if you think that there is exactly the same value, then a naive beginner would thing that this should be correct:

     char arr[256];
     arr = "A string";
    

Now because we understand the basics lets move on to the real problem.

When you are declaring a pointer which gets initialized with a value, more over when you pointer points to a valid memory location at the moment of its declaration ( where becomes a definition as well), like this:

    char *arr = "A string";

You got your self to problems, but why?
Again, go up some lines an read about what a Literal String is.

  1. “A string” is only a valid memory location for you application with respect to its read-only-memory location.
  2. it is not something what you as User or through your application to play with.

This bring us to this:

const char *ptr = "A string";

This is the only valid declaration and initialization (which is also a definition) in C.
Why this is this the Right way?

  1. by putting that const qualifier in front of char* we are informing our Compiler that we are going to work with some read-only-memory location
  2. this mean you do a promise to your compiler that you are not going to “play” with that memory at all, and you are only going to use that value at some point in your application.

Now knowing that give us now to opportunities to work with that Literal string:
1)

    char *arr = "A string";
    arr[0] = 'B';

Here you get your self with a nice Segmentation fault.

    const char *arr = "A string";
    arr[0] = 'B';

Here you get your self with a warning that you are trying to mess your Read-Only-Memory location, of course only if you are using a proper compiler (like GCC for example).

Why a warning instead of a Segmentation fault? Well because that const qualifier it is like a “cast” for your compiler, but depends if which level of understanding you are using the word “cast” in situations like this one here were the const qualifier is involved.

Now it is the GTK’s time.

There is a difference between using plain C cast: (type*) or doing a TYPE_CASTING() in GCC.

  1. the first one is only a plain cast in C.
  2. the second one is not a cast at all, it is a MACRO used to check for compatibility of two types.
  3. this MACRO does a Run-Time-Check four you.

Now let us see this example:

#include <gtk/gtk.h>

int main ( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init ( &argc, &argv );
    /// *** Create a Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 200, 200 );
    /// *** create a button
    button = gtk_button_new_with_label ( "Click me" );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    /// *** example 1
    gtk_window_set_title (  GTK_WINDOW ( window ), "my Window" );
    /// *** example 2
    gtk_window_set_title ( ( GtkWindow* ) window, "my Window" );
    /// *** Show them all and loop
    gtk_widget_show_all ( window );
    gtk_main();
}

There is no fundamental difference for you to choose example 1 or example 2, because you are passing the right pointer.

How about this?:

#include <gtk/gtk.h>

int main ( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init ( &argc, &argv );
    /// *** Create a Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 200, 200 );
    /// *** create a button
    button = gtk_button_new_with_label ( "Click me" );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    /// *** example 1
    gtk_window_set_title (  GTK_WINDOW ( button ), "my Window" );
    /// *** example 2
    gtk_window_set_title ( ( GtkWindow* ) button, "my Window" );
    /// *** Show them all and loop
    gtk_widget_show_all ( window );
    gtk_main();
}

in the example 1 case where the MACRO is used:

 gtk_window_set_title (  GTK_WINDOW ( button ), "my Window" );

You get a nice warning:

(Scale:4359): GLib-GObject-WARNING **: 11:11:09.907: invalid cast from ‘GtkButton’ to ‘GtkWindow’

(Scale:4359): Gtk-CRITICAL **: 11:11:09.907: gtk_window_set_title: assertion ‘GTK_IS_WINDOW (window)’ failed

In case of the example 2 where a plain C cast is used:

`gtk_window_set_title ( ( GtkWindow* ) button, "my Window" );`

You get a complicate short warning (happy debugging:

(Scale:4411): Gtk-CRITICAL **: 11:14:05.426: gtk_window_set_title: assertion ‘GTK_IS_WINDOW (window)’ failed

Now why would you think that the use of cast is not (or should not be) needed and some one should reinvent a system which already exists just to conform to your need?

Let me show you 95% of mistakes in simple applications:

The wrong GTK Code:

#include <gtk/gtk.h>

void clicked_callback ( GtkWidget *button, gpointer data );

int main ( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init ( &argc, &argv );
    /// *** Create a Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 200, 200 );
    /// *** create a button
    button = gtk_button_new_with_label ( "Click me" );
    /// g_signal_connect_swapped() is the right call here
    g_signal_connect( button, "clicked", G_CALLBACK( clicked_callback ), NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();
}

void clicked_callback ( GtkWidget *button, gpointer data )
{
    /// 1) What is the Story with GtkWidget instead of GtkButton?
    /// 2) What is the Story with gpointer data?
    g_print ( "The button was pressed\n" );
}

The right GTK Code:

#include <gtk/gtk.h>

void clicked_callback ( GtkButton *button );

int main ( int argc, char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init ( &argc, &argv );
    /// *** Create a Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 200, 200 );
    /// *** create a button
    button = gtk_button_new_with_label ( "Click me" );
    g_signal_connect_swapped( button, "clicked", G_CALLBACK( clicked_callback ), button );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();
}

void clicked_callback ( GtkButton *button )
{
    if ( GTK_IS_BUTTON ( button ) )
    {
        g_print ( "The button was pressed\n" );
    }
}

Answer: in the case of the first code, should never be used.
I hope you understand the differences now.

Please join my YouTube channel for more information about working with GTK3, CSS and C language ==>> https://www.youtube.com/channel/UCeZXCNsTodx0EPcAfc6s0Ew/playlists

Have a nice Day.

1 Like

Thank you for your consideration on my question!

@StefanSalewski:

For the GTK C style, I can remember similar discussions 10 years ago. I think basically it was just a design decision, original authors just like that most data types are plain GtkWidgets.

You don’t happen to have a link or anything about that discussion? I would be really interested in the reasons for the decision (If it’s not for the topic, it’s for design education).

@greg-king,ntd,MichiB:
I should’ve been more specific on this code:
gtk_window_set_title (window, "Window title");

I didn’t (necessarily) mean to leave the cast out. I’m asking for hiding it to the user. Like I did with the define.

@StefanSalewski

When you do not like the verbosity of GTK C, why not use a different language?
I’m planning on working no ubuntu applications, that are written in C. So I don’t have much of a choice.

@ MichiB:
Thank you for your effort and the link to your channel!

It’s a coding style decision, and it mostly lives in the old days of GTK.

The coding style for GTK typically returns the top-most abstract type from constructors; or, at least, we return the type that would be most typically used with the largest API surface. On the other hand, the functions of each class take a pointer to that class for type safety.

Since this is a coding style decision, you’re entirely free to adopt your own convention for your own code.

Sorry, that won’t ever happen.

Side note: don’t use a cast to GObject as the first argument to g_signal_connect(). The C API takes a void* for a reason: signals are tied to GTypeInstance, not GObject. You don’t need to cast anything, as all pointers are implicitly cast when using void * for arguments and return values (in C).

1 Like

It’s unfortunate about the decision-discussion. I wish there would be more whys explained in general. This would prevent wrong decisions in the future.

ide note: don’t use a cast to GObject as the first argument to g_signal_connect()

Thank you the information. If I find where I learned it, I will forward it to the tutorial-creator. I feel a read it a couple of times on the web…

As I said: this is a coding style decision, so there’s no “right” or “wrong”, just what the people that started the project decided to use as convention.

Hm, I think I’m being misunderstood.
I’m not searching for right or wrong. I’m simply searching for the reason. Even “simply a design decision” had their reasons. May it be “this looks better” or “We don’t want to think of an alternative”. :slight_smile:

At the moment you are going to start writing your own Widgets you will understand better the importance of the Gtk Type Checking/Casting.

I said coding style, not design.

Coding style is built on taste. Don’t try to divine reasons for it: it’s pointless.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.