title: substruct-C
keywords: gcc patch c++ inheritance upcast gtk
html-tag2: c "code"
html-tag2: blue "font color=blue"
html-def: C-with-classes "{i:{nbsp:C with Classes}}"
html-def: GTK "{font:(color=#100060){i:GTK}}"
html-def: C "{b:C}"
html-def: C++ "{b:C}{small:++}"
html-def: submorph-c "{serif:($(.)) {nobr:{i:submorph}} {b:C}}"
html-def: substruct-c "{serif:($(.)) {nobr:{i:substruct}} {b:C}}"
h2: {substruct-c:(color=blue)}
{content-table:
h3-: What is {substruct-c:}
The {substruct-c:} adds the base type inclusion syntax known from {C++:}
to our plain old {C:}. This does not harm the syntax of elder {C:}
- the ':'-colon syntax fits in nicely. Field members from
the (single!) base type are accessible as if written
directly in the declared struct itself.
The real benefit stems from another fact: the {C:} compiler
knows about the inheritance chain and allows upcasts,
ie. without an explicit cast, without any warning.
This provides us with static type checking for those
object-oriented libraries such as {GTK:}.
Well, the current implementation scheme of {GTK:} does not
use the base type inclusion syntax - such
libraries have to work around the problem that
the base type inclusion has not been available in {C:} so far.
Instead they put an instance of the base type as
the first member. And the {substruct-c:} patch knows about it,
and accepts this too.
So in the end, you will benefit from static type checking
in plain old {C:} with big big libraries. Your first {GTK:}
will be less writing and it will have less error;
and even more, the
declarations are much more compatible with {C++:} declarations,
so we'll get a good chance that some basic type structs
will get to be shared between {C:} and {C++:} (as long as {C++:}
does not put virtual members down there - or even
rtti{nbsp:}({small:{i:horror}}))
{small:
This is also another step that shall support me later in an
attempt to create a {C-with-classes:} implementation.
-: (The c++{nbsp:}file-extension '*.cc' comes from the name
{C-with-classes:}, as it was called in its early stages). }
h3-: The inclusion-syntax of {substruct-c:}
The inclusion follows the same basic syntax just like in {C++:},
but without any access-scope modifier before,
and only {i:one} struct-identifier can be included,
{i: so this is single-inheritance only.}
===:(bottom center)
---:
so you can write
---:
that will provide a struct-definition
-: as if you had been writing this...
===:(top)
---:(50%)
{c-code:
struct B {
int a;
int b;
};
struct C : B {
int c;
}
}
---:(left 50%)
{c-code:
struct C {
int a;
int b;
int c;
}
}
Hence you can access the member {c:`a`} without needing to know in which
base type it hade been introduced. Just write {c:`c->a`} when you need it.
{i: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 base type
inclusion syntax here - and remember, {C:} has no access-scope anywhere.
h3-: The substruct pointer aliasing
In order to benefit form the inclusion syntax, the compiler must
be able to cast pointers from {c:`C*`} to {c:`B*`} without
warning. This simply means an inheritance type upcast where
the actual pointer value stays untouches, as it would be with
an explicit upcast.
{c-code:
struct B* p; struct C c;
p = &c;
}
Now you can use all functions written for {c:`struct B`} on
instances of type {c:`struct C`} since it is a base type.
There will be again no warnings if you provide a {c:`struct C*`}
in the place of a {c:`struct B*`}-argument to a function.
This works also with the current {C:}-based object-oriented libraries
such as {GTK:}, that had not been using the base type inclusion
syntax. The compiler will do the type matching against the first
field (if that is a struct-instance).
The distributed {substruct-c:} patch includes the
href: ../submorph-c {submorph-c:} checking
as an extensions.
Using some additional compiler-switches will then tell if
it may be safe to cast two structs by comparing their field
memory layout. This is quite often very helpful in running
programs against huge object-libraries such as {GTK:}.
h3-: GTK-example
{ul:
*: {b:this code stems from {c:testgtk.c}}
-:
{c-code:
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();
}
}
*: {b:and the same taking advantage of {i:substruct}-relations}
-:
{c-code:
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();
}
} {-#-: c-code}
} {-#-: ul}
-: note: all _new-functions in {GTK:} return {c:'GtkWidget*'} - well
this is obviously far away from object-oriented programming as far
as even the least novice can tell... and that is what the explicit
cast at the _new-function is needed for.
{small:
the current patch provides a matching function
that does all the substruct-matching for struct-pointers, but
it can not match functions all too well. Anybody wants to do
the extension? }
h3-: Installation
The substruct-feature is distributed as a patch against gcc-2.95.
You can get {href: substruct.patch the current substruct.patch}
right here. Then go to the directory where you unpacked the gcc-2.95
tree. Then do {c:`cat substruct.patch | patch`} and {c:`make`} a new
compiler. The new compiler does not work differently unless you
enable {C++:}-extensions.
{small:
news: gcc-2.95.1 should be fine for this patch -
the files used in my patch aren't
touched by the update-diff from gcc-2.95 to gcc-2.95.1 }
h3-: Invokation
Just add `-+` (ie. enable-cplusplus-extensions)
or any later gcc standard (like `-std=gnu9x`) that has
the {C++:}-extensions as a default. This will
enable the substruct-syntax and pointer-aliasing.
(and it will also enable '//'-comments...)
You can say {c:`-Wcast-qual`} to switch on additional warnings,
that will show you when the substruct-match was invoked, and what
it yielded. The output will give you fine hints 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 {c:`-W`},
(ie. extra-warnings), which are mainly for debugging.
Of course, you will see warnings about the use of
this unusual construct, so that you don't accidently stumble about it.
You can disable them with -fno-long-long (originally to suppress
warnings on the use of {c:`long long`} type).
The patch installs also two new compilation-standards into the gcc:
{ul:
*: The `gcc -std=submorph`
-: enables the {i:submorph}-features described in the
href: ../submorph-c {submorph-c:} section
, ie. it will tell you if the fields layout match.
*: The `gcc -std=sumorph+`
-: enables all the above, plus it does not warn if some fields
do not match in their naming. Use with caution.
}
latest news: an explicit `-fsubmorph` option does now exist too.
h3-: On the choice of {i: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 off. 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 named instances
(has-a), and write wrapper functions.
So, any upcast possible in this single-inheritance type-tree
happens to be a {c:`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
-:
{c-code:
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 {i:different} numbers, ... imagine that.
h3-: 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, I suspect
it would be widely used in big projects that define
very many struct-types. Including {GTK:} of course.
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). The static type checking is very needed in
such huge projects.
h3-: Future
May be I get the hooks, so I may make my mind up and add that
function-type matching. That would be quite nice.
Otherwise, I'll go to create a new front-end subdir in gcc to hold
a "{C:}/C"-frontend. Just look into your old books on "{C++:} 1.x", and
you will know what it is heading for. Especially ...
{em:{nbsp:no name mangling!}} - it is almost impossible to be used
from plain {C:}. IMHO 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 still `*.h' ??)
Things noteworthy different in {C-with-classes:} from contempory {C++:}:
-: - copy operations are always bitwise, not memberwise.
-: - must use {c:`overload`}-keyword to overload. In my {C:}/C implementation
those will be implicitly inline-functions, so they do not generate
(name-mangled) procedures.
h3-: Reference
{ul:
*href: ../submorph-c the {submorph-c:} Patch
*href: http://gcc.gnu.org the gcc-2.95 compiler
can be found near the
{href: http://egcs.cygnus.com egcs developpers} at cygnus.com
*href: www.gtk.org the {GTK:} library
is a huge windowing library
written in {C:} - the {href: www.gimp.org GIMP} and
{href: www.gnome.org GNOME} are based on {GTK:}
*: written with {href: ../htm1-pp the htm1-perl-processor}
{small: (just change the extension from
{href: index.htm1 {c:.html} to {c:.htm1}}
to see the source code)}
} {-#-:ul}
h3-: Comments
Any comments wellcome. (sorry, no guestbook here)
{ul:
*: ...
}
} {-#-: contents }
hr:
{box-3: (C) Oct'99
{href: guidod@gmx.de?subject=substruct-C Guido Draheim} @gmx.de
| {put:h2} | last change {file-ctime:} }