Arch icon indicating copy to clipboard operation
Arch copied to clipboard

Event handling via generics instead of conditional compilation

Open stanoddly opened this issue 1 year ago • 3 comments

I think that there is a better approach to optionally handle events than conditional compilation. .NET does some smart optimizations based on generics and struct generic argument, for example:

public interface IEventsHandler
{
    // ...
}

// a struct for compiler optimization
public struct NullEventsHandler: IEventsHandler
{
    // empty implementations with aggressively inlined methods
}

// World with generic TEventsHandler, which would have nicely optimized calls for NullEventsHandler
public class World<TEventsHandler> where TA: IEventsHandler
{
    // ...
}

public static class World
{
    public World<NullEventsHandler> Create() { /* ... */}
    public World<TEventsHandler> Create(TEventsHandler handler) where TEventsHandler: IEventsHandler  { /* ... */}
}

The only issue is that a generic World is returned. If that's a problem, an interface of IWorld could be created and returned instead. If an only one class exists for such interface, that would be optimized too (devirtualization).

The approach with generics has been successfully utilized in pebuphysics2: https://github.com/bepu/bepuphysics2/blob/a763813/Demos/Demos/SimpleSelfContainedDemo.cs

Specifically it's described here: https://github.com/bepu/bepuphysics2/blob/a763813/Demos/Demos/SimpleSelfContainedDemo.cs#L20

stanoddly avatar Feb 08 '24 21:02 stanoddly

I've just realized that World can just inherit from World<NullEventsHandler>, and the Arch API would stay compatible, e.g.:

public class World: World<NullEventsHandler>
{
    // shadows World<NullEventsHandler>.Create
    public static World Create(/*...*/)
    {
        // ...
    }
}

So, if anyone wants to use events, they may simply use World<MyEventsHandler>.Create instead of World.Create.

stanoddly avatar Feb 10 '24 12:02 stanoddly

This is definitely worth considering. This could simplify some things and it has the advantage that the performance remains stable. Unfortunately I'm currently working on some other features, but that would also be worth considering ^^

genaray avatar Mar 06 '24 11:03 genaray

How would you then listen for individual component changes? That doesn't really make sense to me right now.

E.g. world.OnAdded<Velocity>(() => {});

So how would this generic world variant implement this?

genaray avatar Mar 06 '24 12:03 genaray