Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Calling Fortran DLL from C# for Multi-dimension array

R__K
Beginner
649 Views

Hi, 

I am using VS 2013 and Intel Composer XE2017 for Fortran. I needed help in the process to pass a 2-d array from C# code to a Fortran DLL. The declarations are as given below:

In C# code: (the intent is that the array is 4x4)

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public double[,] yVar2Dims;
before calling the fortran DLL, I have the initialization of the 2-D in C# code as: myVar.yVar2Dims = new double[4, 4];
 
In the Fortran code:
        do 20 i=1,4
            do 21 j = 1,4
                yVar2Dims(i,j) = i + j
21          continue
20      continue  
 
When I put a break in the Fortran code, the 2-D array is populated correctly. However, the return values are junk numbers. 
 
Thanks.
 
KR
0 Kudos
6 Replies
Steve_Lionel
Honored Contributor III
649 Views

Maybe C# is different, but I always understood that in C a "2-D" array was an array of arrays of pointers.

What I would do is stop in the C# code just before the call and examine memory at the start of the array and make sure that the four elements are what I expect, also noting the address. Then step into the Fortran code and look at the same thing.

You have not shown us the Fortran procedure declaration nor the declarations of its arguments. These matter. I don't know how C# is passing the values. I will note that a rank-2 array is not interoperable with C, as far as the standard and BIND(C) are concerned. If you were using BIND(C), you'd have to declare the Fortran array as DIMENSION(4). Also keep in mind row-major vs. column-major.

0 Kudos
R__K
Beginner
649 Views

Hi Steve, Thanks for the quick reply. Here are the C# and Fortran code for the above problem that I am having.

The C# Code:
 
        [StructLayout(LayoutKind.Sequential)]
        public struct TestFixedLengthVar
        {
            public double xVar;
            public short i;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public double[] yVar;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public double[,] yVar2Dims;
        }
 
void CallFortranTest
{
    TestFixedLengthVar myVar = new TestFixedLengthVar(); 
            //Initialize struct variables before calling Fortran DLL;
            myVar.i = 0;
            myVar.xVar = 0.0;
            myVar.yVar = new double[10];
            myVar.yVar2Dims = new double[4, 4];
 
            FortranCall.SetFixedLengthVariables(ref myVar);
}
        public class FortranCall
        {
            [DllImport("C:/Lapdata/myDevelopment/SPEEDSelect/Rotor4/DynamicLib/Debug/DynamicLib.dll", CallingConvention = CallingConvention.Cdecl)]
            public static extern void SetFixedLengthVariables(ref TestFixedLengthVar myVar);
        }
 
The Fortran Code:
 
module modTestDLL
      !dec$ pack:8
       
        TYPE typeTestFixedLengthVariables
         real*8 :: xVar
         INTEGER*2 :: i
         real*8 :: yVar(10)
         real*8 :: yVar2Dims(4,4)
       END TYPE typeTestFixedLengthVariables
    end module
 
    !DEC$ ATTRIBUTES DLLEXPORT::SetFixedLengthVariables
    !DEC$ ATTRIBUTES ALIAS : "SetFixedLengthVariables" :: SetFixedLengthVariables
 
    SUBROUTINE SetFixedLengthVariables(givenFixedLengthType)
        use modTestDLL
        type(typeTestFixedLengthVariables)::givenFixedLengthType
       
        givenFixedLengthType.xVar = 4.5
        givenFixedLengthType.i = 4
        do  10 i=1,10
           givenFixedLengthType.yVar(i) = i
10      continue 
        do 20 i=1,4
            do 21 j = 1,4
                givenFixedLengthType.yVar2Dims(i,j) = i + j
21          continue
20      continue        
 
    END SUBROUTINE SetFixedLengthVariables
 
0 Kudos
Steve_Lionel
Honored Contributor III
649 Views

Well - that's different! See what I keep harping about snippets and paraphrases not being useful?

In your C# code, yVar and yVarDims are pointers to arrays, but your Fortran code has them as being arrays in the structure. You want the Fortran components to be of type C_PTR from ISO_C_BINDING. You can then use C_F_POINTER to map them onto a Fortran pointer of the desired shape. I would also STRONGLY recommend that you use the kind constants in ISO_C_BINDING rather than REAL*8 and INTEGER*2. (You would instead have REAL(C_DOUBLE) and INTEGER(C_SHORT).)

At this point I would recommend declaring the Fortran routine with BIND(C), and the derived type definition as well.

0 Kudos
R__K
Beginner
649 Views

Hi Steve, Thanks again. I have a favor to ask. Would you  have an example of a Fortran code that shows the different pointers and Bindings that you mention in your email. I admit that I am not an "in-depth" Fortran programmer. Thanks.

0 Kudos
gib
New Contributor II
649 Views

Google "Fortran example with bind(C)" and go to the link at the top of the list.  There is a wealth of info online.

0 Kudos
FortranFan
Honored Contributor II
649 Views

R, K wrote:

Hi Steve, Thanks again. I have a favor to ask. Would you  have an example of a Fortran code that shows the different pointers and Bindings that you mention in your email. I admit that I am not an "in-depth" Fortran programmer. Thanks.

@R. K.,

See Quote #25 in this thread for a fully-functional example: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/740561

And also Quote #4 in this thread for some additional info: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/697972

 

 

0 Kudos
Reply