guidod


Documents
-(personal info)
-pTA study
-largefile *
-AutoFS howto
-Geschichte der Informatik
 
(* other webserver)

Bigger Projects
-PFE*
-AC-Archive*
-ZZIPlib *
-XML/G
-C.L.F.R. *
-XM Tool *
 
Smaller Projects
-errno(1)
-glib-man
-gstdint *
... (patches)
-wine-vol-a
 

Older Projects
-MPEG split *
-XFCE *
-htm1-pp
-cc-headers
-runso
-substruct-c
-submorph-c
... (patches)
-xwpe
-xfce 3
 

Download Area *
Sourceforge Project
freespace.sf.net Home
 

sourceforge.net

generated
(C) Guido Draheim
guidod@gmx.de

 

submorph C

What is submorph C

First, it has been some training for me to code for gcc - in an attempt to create a "C with Classes" implementation later. ("CC" is also known as "C++ 1.x", and that is where the c++ file-extension '*.cc' comes from).

Really, it just adds the inheritance syntax known from C++ to our plain old C. And it does not break thereby any older program, since it does not need any additional keywords, ...
the ':'-colon syntax format fits in nicely.

news: this patch is superseded by the substruct C patch.

The inclusion-syntax of submorph C

Just like in C++, but without any access-scope modifier before, and only one struct-identifier - this is single-inheritance only.

so you can write in order to get a struct-definition
that looks internally just like

   struct B {
      int a;
      int b;
   };

   struct C : B {
      int c;
   }
   struct C {
      int a;
      int b;
      int c;
   }

Hence you can access the member a without needing to know in which instance it hade been introduced. Just write c->a when you need it.

note: C++ will read the above code as expected: all member-fields included from the base-struct are treated as public members, so the result is the same as with the C-language based inclusion syntax - remember, C has no access-scope anywhere

The submorph-pointers aliasing

Well, in order to benefit even more from this inclusion-syntax, we need to be able to cast pointers from C* to B* without warning. Writing such an inclusion-cast routine is quite easy.

   struct B* p; struct C c;
   p = &c;

But it would not have been of many help in today's programmer's life. The main objective of such an inclusion-inheritance and inclusion-pointer-aliasing would be object-oriented programming in the C language family. Well, the current C-based object-oriented libraries such as GTK do not use the inclusion syntax, and hence, the simple-wise pointer-aliasing would not help.

Instead it would be nice, if we could also cast pointers of structures that have inherited the base-struct's fields by putting an instance of it as (the first) member of the derived struct. This is how GTK is doing it, and probably anyone else in C.

So instead of making a shallow compare for equivalence, we allow the fields to be nested inside a member struct whose actual type we take no interest in. And that is why I coined it casting submorph-pointers. Here is what it does:
Any two structures which have the same non-struct fields (with the same type, the same offset and the same size) can match. If a struct contains a sub-struct field, the matching goes with fields contained in that sub-struct.

Structures where all fields have a match in each others's struct, are called isomorph. They are considered to be fully equivalent. If one struct has a match for all its fields in the other struct, but the other struct has additional fields, then they are in a real-submorph-relation. Otherwise they are simply non-morph.

For pointer-aliasing, we allow upcasting, ie. the left-hand side of an assignment must be a submorph of the right-hand side, (submorph means either being isomorph or real-submorph).

Example: these two structs are actually isomorph
   struct B1 {
      int b;
   };
   struct B2 {
      int b;
   };
just like these are all isomorph to each other
   struct C1 : B1 
   {
      int c;
   }
   struct C2 : B2 
   {
      int c;
   }

   struct C3 {
      int b;
      int c;
   }

   struct C4 {
      struct B1 base;
      int c;
   }

GTK-example

  • this code stems from testgtk.c
    static void /* create modal window helper */
    cmw_file (GtkWidget *widget, GtkWidget *parent)
    {
        GtkWidget *fs;
    
        fs = gtk_file_selection_new("This is a modal file selection dialog");
    
        /* Set as modal */
        gtk_window_set_modal (GTK_WINDOW(fs),TRUE);
    
        /* And mark it as a transient dialog */
        gtk_window_set_transient_for (GTK_WINDOW (fs), GTK_WINDOW (parent));
    
        gtk_signal_connect (GTK_OBJECT(fs), "destroy",
                            GTK_SIGNAL_FUNC(cmw_destroy_cb),NULL);
    
        gtk_signal_connect_object (GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
                                   "clicked",GTK_SIGNAL_FUNC(gtk_widget_destroy),
                                   GTK_OBJECT (fs));
        gtk_signal_connect_object (GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
                                   "clicked",GTK_SIGNAL_FUNC(gtk_widget_destroy),
                                   GTK_OBJECT (fs));
    
        /* wait until destroy calls gtk_main_quit */
        gtk_widget_show (fs);
        gtk_main();
    }
    

  • and the same taking advantage of submorph-relations
    static void /* create modal window helper */
    cmw_file (GtkWindow *widget, GtkWindow *parent)
    {
        GtkFileSelection *fs;
    
        /* GTK-people are severly misguided returning a `GtkWidget*' upon _new */
        fs = (GtkFileSelection*)
             gtk_file_selection_new("This is a modal file selection dialog");
    
        /* Set as modal */
        gtk_window_set_modal (fs,TRUE);
    
        /* And mark it as a transient dialog */
        gtk_window_set_transient_for (fs, parent);
    
        gtk_signal_connect (fs, "destroy", GTK_SIGNAL_FUNC(cmw_destroy_cb), NULL);
        gtk_signal_connect_object (fs->ok_button, "clicked", gtk_widget_destroy, fs);
        gtk_signal_connect_object (fs->cancel_button, "clicked", gtk_widget_destroy, fs);
    
        /* wait until destroy calls gtk_main_quit */
        gtk_widget_show (fs);
        gtk_main();
    }
    
note: all _new-functions in GTK return 'GtkWidget*' - well this is obviously far away from object-oriented programming as far as even the least novice can tell...

the current function that does all the submorph-matching, well, it can not match functions all too well. Anybody wants to do the extension?

Installation

The submorph-feature is distributed as a patch against gcc-2.95. You can get the current submorph.patch right here. Then go to the directory where you unpacked the gcc-2.95 tree. Then do `cat submorph.patch | patch` and `make` a new compiler. The new compiler does not work differently unless you provide special options.

Invokation

The patch installs two new compilation-standards into the gcc:

  • The `gcc -std=gtk2`
    enables the submorph-features described above. The name is chosen for non-obvious reasons.

  • The `gcc -std=gtk2+`
    enables all the above, plus it does not warn if some fields do not match in their naming. Use with caution.

You can say `-Wcast-qual` to switch on additional warnings, that will show you when the submorph-match was invoked, and what it yielded. You can also read from the output, what manual cast you would have to apply to your code to get rid of warnings of elder C versions.

The patch carries additional messages that are enabled with `-W`, ie. "extra warnings". That's mainly for debugging.

Note that the inclusion-syntax for the struct-definition will be enabled as soon as `-+` (c++-features) are enabled. This is also the case for most -std=gnu** standards, - it does also enable the '//'-comment. Of course, you will see warnings about the use of this unusual construct, so you don't accidently stumble about it.

On the choice of no multiple inheritance

Multiple inheritance in this context would be a no-goer. Although we could include all the members of secondary base types as primary names in the new structure ... but what for? Any function you have been writing for any of the secondary base types is useless since you can not get the address where the secondary base's members start of. You would even not be able to write wrapper functions, it is just the same problem.

Instead it is wiser by far to put secondary types as an instances (has-a), and write wrapper functions.

So, any upcast possible in this single-inheritance type-tree happens to be a `void*`-cast, ie. the actual address to the struct does not change. This is very different in C++, where pointers may as well be magically moved to point to inherited secondary base types. Hence
    struct C : A, B { int c };
    void* vp; B* bp; C x;
    vp = &x; bp = &x; printf ("%p\n", bp); bp = vp; printf ("%p\n", bp);
will actually yield different numbers, ... imagine that.

Discussion

I would be pleased to open a discussion on putting the inclusion-syntax for struct-definitions into the official C language standard. If people could be sure about the availability of inclusions, and their simple upcast-relations - well, we could even dump the submorph-specialty presented here, which is just there to work around the current implementation style of object-oriented libraries in C.

I do like this patch foremost since it does not pollute my gtk-sourcecode with those macro-casts, and at the same time it provides me with static type checking at compile-time - as opposed to dynamic type checking at run-time in GTK (enabled by those very macro-casts).

Future

The submorph-specialty has no real future. May be I get the hooks, so I may make my mind up and add that function-type matching. Would be nice too.

Otherwise, I'll go to create a new front-end subdir in gcc to hold a "CwC"-frontend. Just look into your old books on "C++ 1.1", and you will know what it is heading for. Especially ... no name mangling! - it is almost impossible to be used from plain C. Even more, contemporary C++ is so different, that it is just a different language being able to read plain C header files, so why are they calling their headers `*.h' ??

Things different in CwC from contempory C++:
- copy operations are always bitwise, not memberwise.
- must use `overload`-keyword to overload. In my CwC implementation those will implicitly be inline-functions, so they do not generate (name-mangled) procedures.

news: look at the (latest) substruct C pages


(c) Sep'99-Oct'99 Guido Draheim submorph C (last change 04.Oct'99) guidod@gmx.de