gir.core icon indicating copy to clipboard operation
gir.core copied to clipboard

Improve Exception integration

Open badcel opened this issue 2 years ago • 3 comments

Currently GLib has GException which is thrown if an error occurs in C:

https://github.com/gircore/gir.core/blob/712ab1740ca138a9a6dcd36975e2302b0a58f8d0/src/Libs/GLib-2.0/Public/Exception.cs#L6

GLib errors contain a domain and error code in addition to its message.

Derive from GException and create GException<T> with a property Code of type T.

The libraries define their error domains as enums which map to error domains. If an error is received we can get the domain code, map it to the corresponding enum and convert the error code into an enum value.

A user catching GException<ConvertError> can get the specific error from the Code property.

Compare the ConvertError domain: https://docs.gtk.org/glib/error.ConvertError.html

See generated ConvertError enum: https://gircore.github.io/api/GLib.ConvertError.html

Using generics could potentially be hard if the domain is unknown during compile time.

badcel avatar May 22 '23 05:05 badcel

I am contributing to a project that uses gir.core and would benefit if exceptions exposed error codes:

https://github.com/PintaProject/Pinta/pull/1136

@cameronwhite might also appreciate getting updates on this

Lehonti avatar Nov 17 '24 12:11 Lehonti

Currently this is planned for version 0.10.0 (see milestones).

Contributions are welcome to speed things up.

badcel avatar Nov 29 '24 21:11 badcel

Improved Concept: Use Static methods and generics to Throw an exception. This avoids list allocations and looping at all. Similar things are done here.

The following code must be generated:

{
   TryThrow<RegexErrorDomain>(errorHandle);
    TryThrow<ConvertErrorDomain>(errorHandle);

...
}

Initial concept:

In the GirModel is the "glib:error-domain" missing for enumerations. Which is needed for this to work. There musst be a special renderer that renders the ExceptionHelper when given all Enumerations which carry all the information of the available ErrorDomains.

 <enumeration name="RegexError"
                 version="2.14"
                 c:type="GRegexError"
                 glib:error-domain="g-regex-error-quark" />
[TestMethod]
    public void TestException()
    {
        try
        {
            var a = GLib.Regex.New("[ab-.?!c", RegexCompileFlags.Default, RegexMatchFlags.Anchored);
        }
        catch(GException<RegexError> ex)
        {
            Console.WriteLine(ex);
        }
        
    }

public interface ErrorDomain
{
    void TryThrow(Internal.ErrorHandle errorHandle);
}

//Class must be generated with all domains
public class ExceptionHelper
{
    //Register all domains
    private static readonly IEnumerable<ErrorDomain> Domains = [new RegexErrorDomain()];
    
    [StackTraceHidden]
    public static void Throw(Internal.ErrorHandle errorHandle)
    {
        foreach (var domain in Domains)
            domain.TryThrow(errorHandle);
        
        throw new GException(errorHandle);
    }
}

//Class must be generated
public class RegexErrorDomain : ErrorDomain
{
    private uint DomainQuark { get; } = Functions.QuarkFromString("g-regex-error-quark");

    [StackTraceHidden]
    public void TryThrow(Internal.ErrorHandle errorHandle)
    {
        if (errorHandle.GetDomain() != DomainQuark)
            return;
        
        throw new GException<RegexError>(errorHandle, (RegexError) errorHandle.GetCode());
    }
}

public sealed class GException<T> : GException where T : Enum
{
    public T Code { get; }
    
    internal GException(Internal.ErrorHandle errorHandle, T code)
        : base(Marshal.PtrToStringUTF8(errorHandle.GetMessage()))
    {
        Code = code;
        _errorHandle = errorHandle;
    }
}

badcel avatar Sep 18 '25 20:09 badcel