[Gtk-sharp-list] Easy version of the boilerplate gtype code
Ben Maurer
bmaurer@users.sourceforge.net
Mon, 29 Mar 2004 17:34:59 -0500
--=-7Mz8xxWyMKJ+0GsJPa/1
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Hello,
After the issue of the annoyance of the 8 line boilerplate code that is
needed to do overriding, I decided to take a look at the issue.
The attached patch takes care of it, I think.
Here is how it works:
You do your class as you would normally do it. That is, you can type:
public class MyButton : Gtk.Button {
public MyButton ()
{
Label = "I'm a subclassed button";
}
protected override void OnClicked ()
{
Console.WriteLine ("Button::Clicked default handler fired.");
}
}
And it will work as you expect it to.
On the inside, what happens is that the default ctor for an object now
looks like:
> protected Window () : base (true) {
> Raw = g_object_new (GetGType (GetType ()).Val, IntPtr.Zero);
> }
The base (true) construct is to lead to a constructor that will not do
stuff like registering gtypes. I realize this is messy, and welcome
suggestions.
GetGType will get the gtype for a given Type, or create it if it does not exist.
If the class has a default ctor, like Button, it generates code like:
> public Button() : base (true)
> {
> if (GetType () != typeof (Button)) {
> Raw = g_object_new (GetGType (GetType ()).Val, IntPtr.Zero);
> return;
> }
> Raw = gtk_button_new();
> }
So, what this means is that if the object is a subclass of Button that
it will do the gtype creation rather than the default ctor. I think that
inside this if statement is where the bug that Mike was telling me about
(that you cant both register a gtype and call a ctor that sets values)
could be fixed. As I mention in the comments, someone smarter than I am
might know how to do this.
Mike brought up a valid concern about this patch, which I would like to
address here. He stated that the overhead of reflection operations
inside such a common operation would hurt performance. There are a few
mitigating factors here, however:
1. This construct is only activated when a user creates one of his
own types. This is a pretty rare operation in the scheme of
things.
2. A user who found that this construct was still too expensive
could use the 8 line boiler plate code, the new sugar is not
required.
3. Object creation in gtk# already involves a hashtable lookup (in
the GLib.Object.Raw property).
I would really appreciate feedback on this patch. I realize it is a bit
messy right now, and I admit it needs some cleaning. Mike, you always
have ideas on how to make code cleaner, so I would value your input.
The patch is attached, as well as the `new' version of Subclass.cs using
the new sugar.
-- Ben
--=-7Mz8xxWyMKJ+0GsJPa/1
Content-Disposition: attachment; filename=gtksharp-easy-override.patch
Content-Type: text/x-patch; name=gtksharp-easy-override.patch; charset=UTF-8
Content-Transfer-Encoding: 7bit
? sample/BenSubclass.cs
Index: generator/ClassBase.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/ClassBase.cs,v
retrieving revision 1.22
diff -u -r1.22 ClassBase.cs
--- generator/ClassBase.cs 2 Feb 2004 20:19:43 -0000 1.22
+++ generator/ClassBase.cs 29 Mar 2004 22:01:46 -0000
@@ -21,7 +21,7 @@
protected bool hasDefaultConstructor = true;
private bool ctors_initted = false;
- private Hashtable clash_map;
+ protected Hashtable clash_map;
public Hashtable Methods {
get {
Index: generator/Ctor.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/Ctor.cs,v
retrieving revision 1.17
diff -u -r1.17 Ctor.cs
--- generator/Ctor.cs 2 Feb 2004 20:19:43 -0000 1.17
+++ generator/Ctor.cs 29 Mar 2004 22:01:46 -0000
@@ -121,7 +121,8 @@
parent = (ObjectGen) parent.Parent;
}
- sw.WriteLine("\t\tpublic static " + safety + modifiers + name + " " + clashName);
+ sw.WriteLine("\t\tpublic static " + safety + modifiers + name + " " + clashName);
+
sw.WriteLine("\t\t{");
body.Initialize(gen_info, false, false, "");
@@ -133,9 +134,30 @@
sw.Write ("new {0} (", name);
sw.WriteLine (cname + "(" + body.GetCallString (false) + "));");
} else {
- sw.WriteLine("\t\tpublic " + safety + name + "(" + sig.ToString() + ")");
- sw.WriteLine("\t\t{");
-
+ sw.Write ("\t\tpublic " + safety + name + "(" + sig.ToString() + ")");
+ //
+ // We need to make sure it uses the empty constructor here.
+ //
+ if (container_type is ObjectGen)
+ sw.Write (" : base (true) ");
+
+ sw.WriteLine ();
+
+ sw.WriteLine("\t\t{");
+
+ if (container_type is ObjectGen && (Params == null || Params.Count == 0)) {
+ //
+ // if this class is being subclassed, we need to register the GType.
+ // Sadly, this does not call the ctor for the base class in native
+ // code, there is a bug filed on this in bugzilla. Someone smart
+ // would know how to solve this ;-).
+ //
+ sw.WriteLine ("\t\t\tif (GetType () != typeof (" + name + ")) {");
+ sw.WriteLine ("\t\t\t\tRaw = g_object_new (GetGType (GetType ()).Val, IntPtr.Zero);");
+ sw.WriteLine ("\t\t\t\treturn;");
+ sw.WriteLine ("\t\t\t}");
+ }
+
body.Initialize(gen_info, false, false, "");
sw.WriteLine("\t\t\t{0} = {1}({2});", container_type.AssignToName, cname, body.GetCallString (false));
body.HandleException (sw, "");
Index: generator/ObjectGen.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/ObjectGen.cs,v
retrieving revision 1.55
diff -u -r1.55 ObjectGen.cs
--- generator/ObjectGen.cs 18 Mar 2004 20:56:32 -0000 1.55
+++ generator/ObjectGen.cs 29 Mar 2004 22:01:46 -0000
@@ -193,10 +193,23 @@
gen_info.Writer.WriteLine("\t\t}");
gen_info.Writer.WriteLine();
gen_info.Writer.WriteLine("\t\tprotected " + Name + "(GLib.GType gtype) : base(gtype) {}");
- gen_info.Writer.WriteLine("\t\tpublic " + Name + "(IntPtr raw) : base(raw) {}");
- gen_info.Writer.WriteLine();
+ gen_info.Writer.WriteLine("\t\tpublic " + Name + "(IntPtr raw) : base(raw) {}");
+ gen_info.Writer.WriteLine("\t\tpublic " + Name + "(bool ignore_me) : base(ignore_me) {}");
- base.GenCtors (gen_info);
+ gen_info.Writer.WriteLine();
+
+ bool default_ctor = hasDefaultConstructor;
+ hasDefaultConstructor = false;
+
+ base.GenCtors (gen_info);
+
+ if (!clash_map.ContainsKey("") && default_ctor) {
+ hasDefaultConstructor = false;
+ gen_info.Writer.WriteLine("\t\tprotected " + Name + " () : base (true) {");
+ gen_info.Writer.WriteLine("\t\t\tRaw = g_object_new (GetGType (GetType ()).Val, IntPtr.Zero);");
+ gen_info.Writer.WriteLine("\t\t}");
+ gen_info.Writer.WriteLine();
+ }
}
private void GenVMGlue (GenerationInfo gen_info, XmlElement elem)
Index: glib/Object.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/glib/Object.cs,v
retrieving revision 1.58
diff -u -r1.58 Object.cs
--- glib/Object.cs 16 Mar 2004 19:43:04 -0000 1.58
+++ glib/Object.cs 29 Mar 2004 22:01:46 -0000
@@ -138,8 +138,23 @@
GtkSharp.ObjectManager.RegisterType (name, t.Namespace + t.Name, t.Assembly.GetName().Name);
GType gtype = new GType (gtksharp_register_type (name, parent_gtype.Val));
ConnectDefaultHandlers (gtype, t);
+ g_types [t] = gtype;
return gtype;
}
+
+ static Hashtable g_types = new Hashtable ();
+ public static GType GetGType (System.Type t)
+ {
+ object o = g_types [t];
+ if (o != null)
+ return (GType) o;
+
+ PropertyInfo pi = t.GetProperty ("GType", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public);
+ if (pi != null)
+ return (GType) pi.GetValue (null, null);
+
+ return RegisterGType (t);
+ }
protected Object () {}
@@ -149,11 +164,15 @@
}
[DllImport("libgobject-2.0-0.dll")]
- static extern IntPtr g_object_new (IntPtr gtype, IntPtr dummy);
+ protected static extern IntPtr g_object_new (IntPtr gtype, IntPtr dummy);
protected Object (GType gtype)
{
Raw = g_object_new (gtype.Val, IntPtr.Zero);
+ }
+
+ protected Object (bool ignore_me)
+ {
}
protected virtual IntPtr Raw {
--=-7Mz8xxWyMKJ+0GsJPa/1
Content-Disposition: attachment; filename=BenSubclass.cs
Content-Type: text/x-csharp; name=BenSubclass.cs; charset=UTF-8
Content-Transfer-Encoding: 7bit
// Subclass.cs - Widget subclass Test
//
// Author: Mike Kestner <mkestner@ximian.com>
//
// (c) 2001-2003 Mike Kestner, Novell, Inc.
namespace GtkSamples {
using Gtk;
using System;
public class ButtonApp {
public static int Main (string[] args)
{
Application.Init ();
Window win = new Window ("Button Tester");
win.DeleteEvent += new DeleteEventHandler (Quit);
Button btn = new MyButton ();
win.Add (btn);
win.ShowAll ();
Application.Run ();
return 0;
}
static void Quit (object sender, DeleteEventArgs args)
{
Application.Quit();
}
}
public class MyButton : Gtk.Button {
public MyButton ()
{
Label = "I'm a subclassed button";
}
protected override void OnClicked ()
{
Console.WriteLine ("Button::Clicked default handler fired.");
}
}
}
--=-7Mz8xxWyMKJ+0GsJPa/1--