2010-03-10 20 views
5

tengo esa claseConvertir de matriz doble a puntero

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); 
    } 
} 

Como se puede adivinar, necesito convertir de double matriz al puntero. ¿Cómo hacerlo?

Nota: la función de interoperabilidad Interop.CallFortranCode(double* dPtr) es algo que no puedo cambiar.

Nota 2: Ambos constructores son necesarios porque algunos de mis usuarios API quieren pasar punteros, y algunos quisieran pasarlos a la matriz. No puedo obligarlos a elegir.

Respuesta

8

Utilice la fixed declaración:

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

Mientras que en el contexto de fixed, se fija la memoria para la variable de modo que el recolector de basura no va a tratar de moverlo.

me gustaría tener este enfoque en su lugar:

 
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); 
     } 
    } 
    } 
} 

esto supone de CallFortranCode supuesto no trate de usar el puntero fuera de la llamada. Una vez que la declaración fijo se sale del ámbito del puntero ya no es válida ...

ACTUALIZACIÓN:

No hay ninguna manera se puede tomar la dirección de su argumento double[] aa y lo almacena en su campo de instancia. Incluso si el compilador le permitiera, el GC está obligado a mover esa memoria dejando su puntero inútil.

Probablemente pueda hacer esto: use Marshal.AllocHGlobal para asignar suficiente memoria para almacenar todos los elementos de la matriz (aa.Length * sizeof(double))). A continuación, utilice Marshal.Copy para copiar el contenido de la matriz a la memoria recién asignada:

 
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)); 
    } 
} 

Esperamos que esto funciona ...

Andrew

+0

Bien, ¿entonces debería hacer algo en el método 'Solve'? – Graviton

+0

Tendré que mantener ambas sobrecargas del constructor para fines de interfaz. Así que no puedo hacer que la propiedad cambie como lo hace – Graviton

+0

Quizás debería expresarme más claramente. Lo que quiero decir es que no puedo cambiar el tipo '_aa' de puntero a matriz. Entonces la solución anterior no funcionará. – Graviton

1

Mientras fixed es de hecho la respuesta que está buscando, no puedes usarlo de la manera que estás tratando de hacerlo. Una vez que termina el bloque fixed (en el corsé de cierre) el puntero se invalida. Por lo tanto, no puede usarlo en un constructor para almacenar el puntero. Tampoco debería querer, porque fijar un objeto administrado en la memoria por largos períodos de tiempo conduce a una degradación drástica del rendimiento.

Lo que haría en su lugar es pasar la matriz (como objeto administrado) a su función Solve. De esta forma puedes fijarlo solo para hacer tu llamada de interoperabilidad fortán y luego dejar que el gc se ocupe de ello.

+0

En realidad, el código es una versión simplificada de mi código. Esto significa que no importa qué, ** no puedo ** pasar el puntero a mi método 'solve' como sugieres sin hacer * grandes * cambios en mi diseño. – Graviton

+0

En ese caso, consulte 'Marshal.AllocHGlobal' para asignar memoria sin fijar un objeto administrado. Eso debería cubrir su constructor de puntero. Para el constructor de matriz, debe buscar en 'GCHandle.ToIntPtr' (después de' Alloc' el manejador primero) para obtener el puntero. Llamar 'Gratis' en el mango en el destructor. – Blindy

+0

gracias, ¿tiene ejemplos de código para esto? – Graviton