I need errors to be logged in the same way across a large number of function calls. Here I want errors from foo.create(...)
and File.new_tmp(...)
to be logged by handle_error(...)
.
// compile with `valac --pkg gio-2.0 main.vala`
void log_error(Error e) {
// error logging here
}
void main() {
var foo = File.new_for_path("foo");
try {
foo.create(FileCreateFlags.NONE);
} catch (Error e) {
log_error(e);
}
FileIOStream tmp_stream;
try {
File.new_tmp(null, out tmp_stream);
} catch (Error e) {
log_error(e);
}
}
(Yes, main
should continue with the FileIOStream
stuff if foo.create
fails, which is why they're in separate try/catch
blocks.)
I want to factor out the use of try {...} catch (Error e) {log_error(e);}
into a function like so:
delegate void Action();
void log_error(global::Action action) {
try {
action();
} catch (Error e) {
// error logging here
}
}
void main() {
var foo = File.new_for_path("foo");
log_error(() => foo.create(FileCreateFlags.NONE));
FileIOStream tmp_stream;
log_error(() => File.new_tmp(null, out tmp_stream));
}
But valac gives the warning unhandled error 'GLib.IOError'
because you can't seem to catch errors thrown within a closure, nor can I just rewrite log_error(...)
as a #define
macro as vala doesn't support them. So what can I do?
You can catch exceptions thrown in closures, you just need to have the delegate throw the exception. What you want is probably something like this:
public delegate T? Action<T> () throws GLib.Error;
T? log_error<T> (global::Action<T> func) {
try {
return func ();
} catch (GLib.Error e) {
// error logging here
return null;
}
}
void main () {
var foo = File.new_for_path("foo");
log_error<GLib.FileOutputStream> (() => foo.create (FileCreateFlags.NONE));
FileIOStream? tmp_stream = null;
GLib.File? f = log_error<GLib.File> (() => File.new_tmp (null, out tmp_stream));
}
Note that I've made it a generic so you can actually use a return value. If you want it should be trivial to remove the generic type argument and just return void, though you'll lose some flexivility.