Tacodiva
Tacodiva

Reputation: 518

How to reinterpret a `ref T*` as a `ref nint`?

I have a method which takes in a ref T* and I need to reinterpret it as a ref nint. Normally, I would use ref Unsafe.As<T*, nint>(ref value), but in this case that will not compile because "The type 'T*' may not be used as a type argument".

How can I reinterpret my ref T* as a ref nint?

Upvotes: 1

Views: 136

Answers (2)

Corey
Corey

Reputation: 16574

This is a fun one.

First, I'm assuming you're already using unsafe in your code since, well, pointers.

Try this:

static unsafe nint Exch<T>(ref T* ptr, nint val)
{
    fixed (void* p = &ptr)
    {
        return Interlocked.Exchange(ref Unsafe.AsRef<nint>(p), val);
    }
}

It's not as direct or efficient as @shingo's answer, but it seems to handle the exchange correctly.

This is what I tested with:

class Foo
{
    public int Value { get; set; }
}

unsafe void Main()
{
    Foo v1 = new() { Value = 12345 };
    
    Foo* ptr = null;
    Console.WriteLine($"Initial: {(nint)((void*)ptr):X}");
    
    Console.WriteLine();
    nint res1 = Exch(ref ptr, (nint)(&v1));
    Console.WriteLine($"First:   {res1:X} -> {(nint)((void*)ptr):X}");
    
    Console.WriteLine();
    nint res2 = Exch(ref ptr, 0);
    Console.WriteLine($"Second:  {res2:X} -> {(nint)((void*)ptr):X}");
}

Output:

Initial: 0

First:   0 -> 4920AFCE78

Second:  4920AFCE78 -> 0

Upvotes: 5

shingo
shingo

Reputation: 27164

I'm not sure how to achieve this using C#, but you can achieve this using IL: sharplab

.assembly UnsafeHelper {}
.module UnsafeHelper.dll
.class public auto ansi beforefieldinit UnsafeHelper
       extends [System.Runtime]System.Object
{
  .method public hidebysig static native int& 
          As<valuetype .ctor (class [System.Runtime]System.ValueType modreq([System.Runtime]System.Runtime.InteropServices.UnmanagedType)) T>(!!T*& p) cil managed
  {
    .param type T 
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) 
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0002:  ret
  }
}

Using ilasm to compile this as a library, then you can use it:

double d = 2.75;
double* pd = &d;
ref var p2 = ref UnsafeHelper.As(ref pd);

Upvotes: 3

Related Questions