toolness / pathfinder-unity-fun

Attempting to build a Unity native plugin for Pathfinder's Canvas API.
5 stars 1 forks source link

Don't use finalizers in the C# binding code #9

Open DaZombieKiller opened 5 years ago

DaZombieKiller commented 5 years ago

Instead you should implement IDisposable and use a class inheriting from SafeHandle to represent the native handle. The reasoning for why can be found here.

The autogenerated bindings can then take/return an instance of that SafeHandle derivative instead of an IntPtr (although PFPathDestroy should still take IntPtr).

The PFPath class would look something like this after incorporating the changes (it could also optionally be transformed into a struct instead of a class now that it doesn't rely on a finalizer):

using System;
using System.Runtime.InteropServices;
using UnityEngine;

sealed class PFPathHandle : SafeHandle
{
    public override bool IsInvalid => handle == IntPtr.Zero;

    public PFPathHandle() : base(IntPtr.Zero, ownsHandle: true) {}

    protected override bool ReleaseHandle()
    {
        PF.PFPathDestroy(handle);
        return true;
    }
}

public class PFPath : IDisposable
{
    readonly PFPathHandle handle;

    public PFPath() => handle = PF.PFPathCreate();

    public PFPath(PFPath targetToClone) => handle = PF.PFPathClone(targetToClone.handle);

    public void MoveTo(Vector2 to)
    {
        var pfTo = PFUnityConv.PFVector2F(to);
        PF.PFPathMoveTo(handle, ref pfTo);
    }

    public void LineTo(Vector2 to)
    {
        var pfTo = PFUnityConv.PFVector2F(to);
        PF.PFPathLineTo(handle, ref pfTo);
    }

    public void Ellipse(Vector2 center, Vector2 axes, float rotation, float startAngle, float endAngle)
    {
        var pfCenter = PFUnityConv.PFVector2F(center);
        var pfAxes   = PFUnityConv.PFVector2F(axes);
        PF.PFPathEllipse(handle, ref pfCenter, ref pfAxes, rotation, startAngle, endAngle);
    }

    public void QuadraticCurveTo(Vector2 ctrl, Vector2 to)
    {
        var pfCtrl = PFUnityConv.PFVector2F(ctrl);
        var pfTo   = PFUnityConv.PFVector2F(to);
        PF.PFPathQuadraticCurveTo(handle, ref pfCtrl, ref pfTo);
    }

    public void BezierCurveTo(Vector2 ctrl0, Vector2 ctrl1, Vector2 to)
    {
        var pfCtrl0 = PFUnityConv.PFVector2F(ctrl0);
        var pfCtrl1 = PFUnityConv.PFVector2F(ctrl1);
        var pfTo    = PFUnityConv.PFVector2F(to);
        PF.PFPathBezierCurveTo(handle, ref pfCtrl0, ref pfCtrl1, ref pfTo);
    }

    public PFPath Clone() => new PFPath(this);

    public void ClosePath() => PF.PFPathClosePath(handle);

    public void Dispose() => handle.Dispose();
}
toolness commented 5 years ago

Whoa cool! I am a complete C# noob so while I had read about something called IDisposable I didn't fully understand why I should have used it instead of the finalizer. I also didn't know about a bunch of the coding conventions you used in your code snippet, so this is really helpful. Thanks!

DaZombieKiller commented 5 years ago

I'm glad to have helped!

dgerding commented 4 years ago

Hi, I've been hoping to use this Unity plugin for an NLP-related application that, curiously, uses 3D visualization. So, I'll be using Unity and hoped to leverage Pathfinder via this plugin.

Since I'll need to render LOTS of words, like whole documents, my thought had been to start with a "word-as-mesh" library and serialize a dictionary of strings, plush PF Mesh data (?), to disk and load in the needed meshes depending on text content.

With all that said, if I wanted to "save" the mesh that PF uses to render to canvas, what property or method would I use to get a mesh from a string at runtime? I can handle the serialization.

Any ideas/feedback appreciated.

Thanks, Dave Gerding