[Gtk-sharp-list] Handling errors when generating binding code for asynchronous APIs

Philip Van Hoof spam at pvanhoof.be
Sun Feb 10 09:27:51 EST 2008


Hi there,

I'm trying to make GtkSharp's GAPI generate the typical asynchronous
GObject style C API to something that looks a bit like the Asynchronous
Pattern often used in .NET.

My plan is not to achieve an exact version of the Asynchronous pattern,
but nonetheless something workable.

Some pointers:
http://msdn2.microsoft.com/en-us/library/wewwczdw.aspx
http://msdn2.microsoft.com/en-us/library/aa719595(VS.71).aspx

Versus, for example, this one:
http://tinymail.org/API/libtinymail-1.0/libtinymail-tny-folder.html#tny-folder-get-msg-async
http://tinymail.org/API/libtinymail-1.0/libtinymail-tny-shared.html#TnyGetMsgCallback

It's actually awesome about GtkSharp's GAPI, but not surprisingly did
GAPI do a splendid job already. There's however one big problem:

The GError based error being passed to the callback (which in C is a
function pointer typedeffed as TnyGetMsgCallback) is not being converted
to an Exception type.

My goal is to wrap the "IntPtr err" (which is the GError pointer in C)
with a factory's Create method that will create me the right managed
exception:

public class Tny.ExceptionFactory
{
	static Tny.TException Create (IntPtr gerror) {
		// Pseudo, to get the type I'll indeed need to make
		// a small piece of glue code in C ...
		if (gerror->type == TNY_ERROR_THIS)
			return new Tny.ThisException (gerror);
		if (gerror->type == TNY_ERROR_THAT)
			return new Tny.ThatException (gerror);
		...
	}
}

That way can my application developer use the error being reported in
the callback using his normal exception handling methods.

For example:

- private void GetMsgCallBack (Tny.Folder folder, bool cancel, Tny.Msg msg, IntPtr err)
+ private void GetMsgCallBack (Tny.Folder folder, bool cancel, Tny.Msg msg, Tny.TException err)
{
-	if (err != IntPtr.Zero) {
-		Exception ex = new Tny.TException (err);
-		Console.WriteLine (ex.Message);
+	if (err != null)
+		throw err; // Or do whatever with Exceptions
	} else {
		if (msg != null && !cancel)
			this.msg_view.Msg = msg;
	}
}

Note that throwing it is not necessarily what the application developer
will want to do with the exception. In that callback he'll typically
want to show an error dialog to the user with the err.Message displayed.

To achieve this, after doing some analysis on Gapi's generator, this is
my conclusion:

Parameter needs a new property Wrapper that looks a lot like its Scope parameter (gets it from the XML file)
Signature::ToString() needs to recognise parameters that are to be wrapped (change the type)
CallbackGen::Generate(GenerationInfo) needs to be verified that sig.ToString() does the right thing
CallbackGen::GenWrapper(GenerationInfo) at line 227 needs to wrap the parameter with p.Wrapper


## This

<callback name="FolderCallback" cname="TnyFolderCallback">
  <return-type type="void" />
  <parameters>
    <parameter type="TnyFolder*" name="self" />
    <parameter type="gboolean" name="cancelled" />
    <parameter type="GError*" name="err" />
    <parameter type="gpointer" name="user_data" />
  </parameters>
</callback>


## Would become

<callback name="FolderCallback" cname="TnyFolderCallback">
  <return-type type="void" />
  <parameters>
    <parameter type="TnyFolder*" name="self" />
    <parameter type="gboolean" name="cancelled" />
    <parameter type="GError*" name="err" wrapper_type="Tny.TException" wrapper="Tny.ExceptionFactory.Create ({0})" />
    <parameter type="gpointer" name="user_data" />
  </parameters>
</callback>


## This

namespace Tny {

	using System;

	public delegate void FolderCallback(Tny.Folder self, bool cancelled, IntPtr err);

}


## Would become (err's type comes from the XML above)

namespace Tny {

	using System;

	public delegate void FolderCallback(Tny.Folder self, bool cancelled, Tny.TException err);

}

## This

internal class FolderCallbackWrapper {
	public void NativeCallback (IntPtr self, bool cancelled, IntPtr err, IntPtr user_data)
	{
		try {
			Tny.Folder _arg0 = Tny.FolderAdapter.GetObject (self, false);
			bool _arg1 = cancelled;
			IntPtr _arg2 = err;
			managed ( _arg0,  _arg1,  _arg2);
			if (release_on_call)
				gch.Free ();
		} catch (Exception e) {
			GLib.ExceptionManager.RaiseUnhandledException (e, false);
		}
	}
	...
}


## Would become (arg2 wrap comes from the XML above)

internal class FolderCallbackWrapper {
	public void NativeCallback (IntPtr self, bool cancelled, IntPtr err, IntPtr user_data)
	{
		try {
			Tny.Folder _arg0 = Tny.FolderAdapter.GetObject (self, false);
			bool _arg1 = cancelled;
			IntPtr _arg2 = err;
			managed ( _arg0,  _arg1,  Tny.ExceptionFactory.Create (_arg2));
			if (release_on_call)
				gch.Free ();
		} catch (Exception e) {
			GLib.ExceptionManager.RaiseUnhandledException (e, false);
		}
	}
	...
}


-- 
Philip Van Hoof, freelance software developer
home: me at pvanhoof dot be 
gnome: pvanhoof at gnome dot org 
http://pvanhoof.be/blog
http://codeminded.be



More information about the Gtk-sharp-list mailing list