Question

I am trying to pass a float array from C# to fortran, and have fortran change its reference to an internal (in the fortran code) array. I am just getting garbage when I do that, although it runs fine. The following is what I do:

float[] test = new float[50];
testpointer_( test);

[DllImport("ArrayPointerTest.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void testpointer_([Out] float[] array);//Out keyword makes no difference



!DEC$ ATTRIBUTES DLLEXPORT::testpointer
subroutine testpointer(arrayout)
  implicit none
real, dimension(1:50), target :: arrayin
real, dimension(:), pointer :: arrayout
integer :: i


DO i=1,50
    arrayin(i)=i
end do
arrayout => arrayin
end subroutine

Why? Because I am making a legacy code into a dll, and don't want to change any more than necessary. Any ideas?

UPDATE USING ACCEPTED ANSWER PLUS SOME CHANGES
This code successfulys make C#:"test" target the values of fortran:"arrayin".

[DllImport("ArrayPointerTest.dll", CallingConvention = CallingConvention.Cdecl)]
        static unsafe extern void testpointer(float* arrayPtr);

  private unsafe static void PointerTest()
        {
            float[] teste = new float[50];
            teste[49] = 100;

            fixed (float* testePtr = teste)
            {
                testpointer(testePtr);
            }
            for (int i = 0; i < 50; i++)
            {
                Console.WriteLine(teste[i]);
            }
            Console.Read();
        }


!DEC$ ATTRIBUTES DLLEXPORT::testpointer
subroutine testpointer(arrayout_) bind(c)
  use iso_c_binding
  implicit none



real(c_float), dimension(1:50), target :: arrayin
type(c_ptr), value ::arrayout_
real(c_float), dimension(:), pointer :: arrayout

integer :: i

call c_f_pointer(arrayout_, arrayout, [50])

do i=1,50
        arrayin(i) = i*2!you can also change arrayout here, it will be reflected
end do

arrayout = arrayin ! todo: is this causing a copy to be made, or is it changing the pointer's references?


end subroutine
Was it helpful?

Solution

Use iso_c_binding.

!DEC$ ATTRIBUTES DLLEXPORT::testpointer
subroutine testpointer(arrayout) bind(c)
  use iso_c_binding
  implicit none

real(c_float), dimension(1:50), target :: arrayin
real(c_float), dimension(:), pointer :: arrayout
integer :: i


DO i=1,50
    arrayin(i)=i
end do
arrayout => arrayin
end subroutine

With bind(c) you also don't need to mangle the procedure name either:

static extern void testpointer([Out] float[] array);//Out keyword makes no difference

You will also have to be careful with passing the array. I'd use a c_ptr:

!DEC$ ATTRIBUTES DLLEXPORT::testpointer
subroutine testpointer(arrayout_) bind(c)
  use iso_c_binding
  implicit none

type(c_ptr),value::arrayout_
real(c_float), dimension(:), pointer :: arrayout

call c_f_pointer(arrayout_, arrayout, [50])
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top