Skip to content

[Instantiable]: Add generated interface implementations for generic constraints purposes. #202

Description

@willnationsdev

I can use the InstantiableAttribute to define various static New() methods for types. However, if I want to write a generic method that takes advantage of these static New() methods, I'd have to provide my own means of doing so.

public interface IUseNewable
{
    void CreateMyWay<T>(T value); // I have no way of leveraging the generated `INewable` declarations
}

To achieve this, I have to first manually implement my own interfaces that I can use for generic constraints. For example:

public interface INewable;

public interface INewable<T> : INewable
{
    abstract static T New();
}

I then have to manually add these to every class I attach the attribute to. I would rather this be handled automatically by the source generator itself, if possible.

If people want to customize the names, (which could cause conflicts with other implementing partial declarations), we can just allow the user to override the generated interface name. For example:

// Sample declaration with custom names.
[Instantiable("Init", "CreateFromMilliseconds", GeneratedInterfaceType = "IGodotTemporal"]
public partial class Clock : Control
{
    public void Init(int milliseconds);
}

// Resulting generated interfaces (which `Namespace.Clock.g.cs` would now auto-implement for me).
public interface IGodotTemporal;

public interface IGodotTemporal<T> : IGodotTemporal
{
    void Init(int milliseconds);
    abstract static T CreateFromMilliseconds(int milliseconds);
}

// Usage
var clock = Clock.CreateFromMilliseconds(2000);

To implement this feature, we'd need to...

  1. Add the property to the InstantiableAttribute.
  2. Extract the value from each one by parsing the attribute text.
  3. De-duplicate the type names and consolidate which methods would need to be part of each interface.
  4. Ensure the interfaces are added to the RegisterPostInitialiationOutput for the source generator.
    • Or, alternatively, check if such a type has already been defined by a referenced assembly (e.g. if the user purposefully wants to isolate the abstractions from the main project for tests or use in other class libraries, etc.).
    • Or, alternatively, an assembly attribute could "remap" the would-be generated interface to instead use an existing type declaration from a referenced assembly. Something like, [assembly: RemapInstantiableInterface("IGodotTemporal", "MyClassLib.TimeStuff.IGodotTemporal")].
  5. The existing generated source would need to be updated to include the interface in its BaseListSyntax.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions