7

I have such class

public unsafe class EigenSolver
{
   public double* aPtr
   {get; private set;}
   public EigenSolver(double* ap)
   {
      aPtr = ap;
   }
   public EigenSolver(double[] aa)
   {
     // how to convert from aa double array to pointer?
   }

   public void Solve()
   {
     Interop.CallFortranCode(aPtr);
   }
}

As you can guess, I need to convert from double array to pointer. How to do it?

Note: the interop function Interop.CallFortranCode(double* dPtr) is something I can't change.

Note 2: Both the constructors are needed because some of my API users want to pass in pointers, and some would like to pass in array. I can't force them to choose.

2 Answers 2

9

Use the fixed statement:

fixed (double* aaPtr = aa) {
   // You can use the pointer in here.
}

While in context of fixed, the memory for your variable is pinned so the garbage collector will not try to move it around.

I would take this approach instead:

public class EigenSolver
{
   public double[] _aa;
   /*
   There really is no reason to allow callers to pass a pointer here, 
   just make them pass the array.
   public EigenSolver(double* ap)
   {
      aPtr = ap;
   }
   */
   public EigenSolver(double[] aa)
   {
     _aa = aa;
   }

   public void Solve()
   {
     unsafe {
        fixed (double* ptr = _aa) {
           Interop.CallFortranCode(ptr);
        }
     }
   }
}

This assumes of course CallFortranCode does not try to use the pointer outside of the call. Once the fixed statement goes out of scope the pointer is no longer valid...

UPDATE:

There is no way you can take the address of your argument double[] aa and store it in your instance field. Even if the compiler would let you, the GC is bound to move that memory around leaving your pointer useless.

You could probably do this: Use Marshal.AllocHGlobal to allocate enough memory to store all the elements of the array (aa.Length * sizeof(double))). Then, use Marshal.Copy to copy the contents of the array to your newly allocated memory:

bool _ownsPointer; 
public EigenSolver(double[] aa) {
   IntPtr arrayStore = (double*)Marshal.AllocHGlobal(aa.Length * sizeof(double));
   Marshal.Copy(aa, 0, arrayStore, aa.Length);
   this.aPtr = (double*)arrayStore.ToPointer();
   _ownsPointer = true;
}

~EigenSolver {
   if (_ownsPointer) {
      Marshal.FreeHGlobal(new IntPtr(this.aPtr));
   }
}

Hopefully this works...

Andrew

Sign up to request clarification or add additional context in comments.

3 Comments

OK, then should I do anything in Solve method?
I will have to keep both constructor overloads for interface purpose. So I can't make the property changes as you do
Maybe I should express myself more clearly. What I want to say is that I can't change the _aa type from pointer to array. So the above solution won't work.
1

While fixed is indeed the answer you're looking for, you can't use it in the way you're trying to. Once the fixed block ends (at the closing brace) the pointer is invalidated. So you can't use it in a constructor to store the pointer. Nor should you want to, because pinning a managed object in memory for extended periods of time leads to drastic performance degradation.

What I'd do instead is pass the array (as a managed object) to your Solve function. This way you can pin it down just to make your fortran interop call then let the gc take care of it.

3 Comments

Actually the code is a simplified version of my code. This means that no matter what, I cannot pass the pointer into my solve method as you suggest without making huge changes to my design.
In that case, look at Marshal.AllocHGlobal to allocate memory without pinning a managed object. That should cover your pointer constructor. For the array constructor you should look into GCHandle.ToIntPtr (after you Alloc the handle itself first) to get the pointer. Call Free on the handle in the destructor.
The msdn GCHandle page has a pretty detailed example, check it out at msdn.microsoft.com/en-us/library/…. You basically call Alloc in the constructor and hold the handle in your class, call Free in the destructor and when you want the pointer you call ToIntPtr (ie in your Solve method).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.