Home > Blockchain >  How can I type an int index?
How can I type an int index?

Time:04-02

I'm working in a C# environment that does not allow use of reference types to reference something. Yes, you're reading that right. I can use structs, and I can use special arrays that are not objects.

To link data, I'm using an index into another, predetermined array. Something like this:

static class Data {
    public static float[] Length = new ...;
    public static float[] Weight = new ...;
    // In reality these arrays are NativeArray<T>, provided by the Unity framework
}

int indexLength = 3; // index refers to an entry in the Length array
Data.Length[indexLength]  = 1.23;

int indexWeight = 3; // index refers to an entry in the Weight array
Data.Weight[indexWeight]  = 1.23;

This is, of course, opposed to an object oriented approach, and is a more complicated to keep properly aligned.

To help this effort, I would like to amend indexes such that they 'know' which array they belong to. I guess this is similar to what a Measure does in F#, though I have never used that myself yet. It could maybe look like this:

WeightIndex indexWeight = (WeightIndex) 3; // The index knows which array it belongs to
Data.Weight[indexWeight] = 42; // OK (maybe implicit cast of indexWeight to int)
Data.Length[indexWeight] = 42; // Compiler error: Wrong index type

Is there any way to achieve this goal?

Additionally, an optimizing compiler should completely remove it to avoid any runtime overhead. I'm using Burst (an LLVM-based compiler), so this is probably not a problem unless the approach is too complicated for it to understand.

CodePudding user response:

Initially I was thinking about Generics and potential operator overriding, but then I remembered extension methods.

Here's some code using a static data class with two static arrays (I obviously don't have access to a NativeArray type), but it will type check on the value passed to the array.

static class DataExtension
{
    public static float Entry(this float[] array, int index)
    {
        return array[index];
    }
    public static void Entry(this float[] array, int index, float value)
    {
        array[index]=value;
    }

    public static int Entry(this int[] array, int index)
    {
        return array[index];
    }
    public static void Entry(this int[] array, int index, int value)
    {
        array[index]=value;
    }
}

And its use:

static class Data 
{
    public static float[] Length = new float[]{1.0f, 1.1f, 1.2f};
    public static int[] Weight = new int[]{4,5,6};
}
static void Main()
{

    for(var i=0; i< Data.Length.Length; i  )
    {
        Console.WriteLine($"Data.Length[{i}]={Data.Length[i]}");
        
        Console.WriteLine($"Data.Length.Entry(i) = {Data.Length.Entry(i)}");
        
        Data.Length.Entry(i, Data.Length.Entry(i) 0.5f);
        Console.WriteLine($"Data.Length.Entry(i) 0.5f = {Data.Length.Entry(i)}");
        
    }

    for(var i=0; i< Data.Weight.Length; i  )
    {
        Console.WriteLine($"Data.Weight[{i}]={Data.Weight[i]}");
        
        Console.WriteLine($"Data.Weight.Entry(i) = {Data.Weight.Entry(i)}");
        
        Data.Weight.Entry(i, Data.Weight.Entry(i) 2);
        Console.WriteLine($"Data.Weight.Entry(i) 2 = {Data.Weight.Entry(i)}");
        
    }

}

This gives the following output:

Data.Length[0]=1
Data.Length.Entry(i) = 1
Data.Length.Entry(i) 0.5f = 1.5
Data.Length[1]=1.1
Data.Length.Entry(i) = 1.1
Data.Length.Entry(i) 0.5f = 1.6
Data.Length[2]=1.2
Data.Length.Entry(i) = 1.2
Data.Length.Entry(i) 0.5f = 1.7
Data.Weight[0]=4
Data.Weight.Entry(i) = 4
Data.Weight.Entry(i) 2 = 6
Data.Weight[1]=5
Data.Weight.Entry(i) = 5
Data.Weight.Entry(i) 2 = 7
Data.Weight[2]=6
Data.Weight.Entry(i) = 6
Data.Weight.Entry(i) 2 = 8

CodePudding user response:

You can create a MyTypedArray<T, TIndex> struct that encapsulates an array (or NativeArray<T>) and only allows access through TIndex indexes.

For example, a MyTypedArray<float, LengthIndex> could only be indexed via a LengthIndex. LengthIndex would be a struct with a single int IndexValue { get; } property. To ensure that MyTypedArray can access the IndexValue property, TIndex needs be constrained to structs implementing the IIndex interface defining that property. The indexer of MyTypedArray then takes a TIndex instead of an int.

Here's an example implementation (fiddle):

interface IIndex
{
    int IndexValue { get; }
}

readonly struct MyTypedArray<T, TIndex> where TIndex : struct, IIndex
{
    readonly T[] contents;
    
    public T this[TIndex index]
    {
        get => contents[index.IndexValue];
        set => contents[index.IndexValue] = value;
    }
    
    public MyTypedArray(int length) => contents = new T[length];
}

readonly struct LengthIndex : IIndex
{
    public int IndexValue { get; }
    public LengthIndex(int i) => IndexValue = i;
    public static implicit operator LengthIndex(int i) => new LengthIndex(i);
}

readonly struct WidthIndex : IIndex
{
    public int IndexValue { get; }
    public WidthIndex(int i) => IndexValue = i;
    public static implicit operator WidthIndex(int i) => new WidthIndex(i);
}

public class Program
{
    public static void Main()
    {
        var lengths = new MyTypedArray<float, LengthIndex>(4);
        var widths = new MyTypedArray<float, WidthIndex>(4);
        
        LengthIndex indexLength = 3;    // implicit conversion

        lengths[indexLength]  = 1.0f;
        // widths[indexLength]  = 1.0f; // won't compile: cannot convert from 'LengthIndex' to 'WidthIndex'

    }
}
  • Related