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

C# call fortran dll with option argument

yan__san-chi
Beginner
824 Views

I want to use Fortran dll in C#, in  function "wrapper_integrate",  forth argument "intsteps" is an optional argument.  

My fortran code:

module integration
  implicit none
contains
  function Integrate(func, a,b, intsteps) result(integral) 
    interface
      real function func(x) 
        real, intent(in) :: x
      end function func
    end interface
    real :: integral, a, b
    integer :: intsteps
    intent(in) :: a, b, intsteps
    optional :: intsteps
    real :: x, dx
    integer :: i,n
    integer, parameter :: rk = kind(x)
    n = 1000
    if (present(intsteps)) n = intsteps
    dx = (b-a)/n
    integral = 0.0_rk
    do i = 1,n
      x = a + (1.0_rk * i - 0.5_rk) * dx
      integral = integral + func(x)
    end do
    integral = integral * dx
  end function
end module integration
   function wrapper_integrate(func, a, b, intsteps) result(integral) bind(C, name='wrapper_integrate')
!DEC$ ATTRIBUTES DLLEXPORT :: wrapper_integrate
  use iso_c_binding
  use Integration
  implicit none
  interface
    function func(x) bind(C)
      use, intrinsic :: iso_c_binding
      implicit none
      real(c_float), intent(in) :: x
      real(c_float) :: func
    end function func
  end interface
  real(c_float), intent(in),value :: a,b
  integer(c_int), intent(in), optional :: intsteps
  real(c_float) :: integral
  real :: local_a, local_b
  integer :: local_intsteps
  local_a = a
  local_b = b
  if (present(intsteps)) then
    local_intsteps = intsteps
    integral = Integrate(local_func, a, b, local_intsteps)
  else
    integral = Integrate(local_func, a, b)
  end if
contains
  function local_func(x)
    real, intent(in) :: x
    real :: local_func
    real(c_float) :: local_x
    local_x = x
    local_func = func(local_x)
  end function local_func
end function wrapper_integrate

My C# code in VS2013:

using System;

using System.Runtime.InteropServices;
namespace Console_use_integrate{
    class Program
    {


        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate float ActionRefInt(ref float n);

        public ActionRefInt callbackHandler;

        public Program()
        {
            callbackHandler = new ActionRefInt(square);
        }

        public float square(ref float n)
        {
            return n*n;
        }


        static void Main(string[] args)
        {
            Program myProg2 = new Program();

            float a = 1f;
            float b = 3f;
            int step = null;
            float result;
            Console.WriteLine("Interval  from {0} to {1} ", a, b);
       
            result = integrate_Dll(myProg2.callbackHandler, a,  b,ref step);
            Console.WriteLine("integration result is {0}",result);
            Console.ReadKey();
        }


        [DllImport(@"integrate_Dll.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "wrapper_integrate")]
        public static extern float integrate_Dll([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack, float a, float b,ref int c);
    }

    }

Can I pass "null" if I don't want to use argument "step"  which is corresponding to optional argument "intsteps" in  Fortran dll? 

 

Thanks!

0 Kudos
6 Replies
Steve_Lionel
Honored Contributor III
824 Views

You would have to pass null by value. I am not a C# expert but I think what you have is wrong in a couple of ways. You initialize step as an int with null, which simply is zero. You then pass step by reference, which passes the address of an int with zero. Fortran doesn't consider this an omitted argument.

I think the simplest approach is to pass null instead of step - I believe that will work.

0 Kudos
FortranFan
Honored Contributor II
824 Views

C# treats int as a value type which is not nullable, so the C# code in the original post at line 31 of " int step = null" should not compile:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/value-types

My recommended approach is to simply be consistent with Microsoft's own approach in .NET and overload the external unmanaged procedure with additional signatures as necessary to handle the optional parameters with overloads making use of IntPtr from the System namespace; invocations then pass in the constant System.IntPtr.Zero to denote the absent argument:

        [DllImport(@"integrate_Dll.dll", CallingConvention = CallingConvention.Cdecl, 
        EntryPoint = "wrapper_integrate")]
        public static extern float integrate_Dll([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
        float a, float b, ref int c);
        [DllImport(@"integrate_Dll.dll", CallingConvention = CallingConvention.Cdecl, 
        EntryPoint = "wrapper_integrate")]
        public static extern float integrate_Dll([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
        float a, float b, System.IntPtr c);

        ..
        result = integrate_Dll(myProg2.callbackHandler, a,  b,  System.IntPtr.Zero)
        ..

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
824 Views

Steve,

>>You initialize step as an int with null, which simply is zero.

In C# every variable is a reference (reference to reference type as well as reference to value type). Initializing an int as null will generate an error if an attempt to use the variable other than to test for null reference.

The item referenced (for int) for all intents and purposes is a struct. It is unclear as to if the data object of the struct for int is identical as int, or if it contains a type code as well as the int value. (implementation issue). Some searching on msdn might provide this information.

I suggest writing a C wrapper function, that you call from C#, that in turn calls Fortran with the address of the optional argument.

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
824 Views

FF's suggestion is more elegant (C# is creating the wrapper functions in the declarations).

Jim Dempsey

0 Kudos
John_C_
Beginner
824 Views
Dear @yan, san-chi, @FortranFan Would you please upload your c# project (fortran and c# code) or tell me important points that make changes in vb throw c# in intel mixedlanguages sample projects as a sample code that works.I want only know how can call fortran via c#. according to : https://software.intel.com/en-us/comment/1910475 I know how to manage vb.net with fortran in visual studio 2012.but the c#.net version is needed. Any help will be appreciated. Best regards
0 Kudos
John_C_
Beginner
824 Views
@FortranFan,

I want to migrate from VB.Net to C#.These are my codes:

Module1.vb file:

 

Module Module1
    REM Use ByVal to pass strings unless the called routine expects BSTR structures
    
    Public Declare Auto Sub electrostatica3D Lib "FCALL.DLL" _
  (ByVal DBL_IN() As Double, ByVal STR_IN As String, ByVal DBL_OUT() As Double)

End Module

fcall.f90 fortran file:

subroutine electrostatica3D(DBL_IN, STRING_IN, DBL_OUT)
    
  use malla_3DP1
  use electros3D
  use external_electros3D
  use derivados3D
  use resolucion_sistema_lineal
     
  implicit none
  
  ! Specify that DLL_ROUT is exported to a DLL
! and that the external name is 'DLL_ROUT'
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: electrostatica3D
!DEC$ ATTRIBUTES ALIAS:'electrostatica3D' :: electrostatica3D

REAL(8), INTENT(IN) :: DBL_IN(0:3)
CHARACTER(10), INTENT(IN) :: STRING_IN
!DEC$ ATTRIBUTES REFERENCE :: STRING_IN
! When VB passes in a "ByVal String", it passes the address
! of a NUL-terminated string, similar to what C would do,
! and no separate length.  The REFERENCE attribute tells
! Fortran not to expect a length.  In order to use a function
! such as INDEX, we need to supply some maximum length to Fortran
! which should be at least as long as the longest expected string.
REAL(8), INTENT(OUT) :: DBL_OUT(4)

REAL(8) STRVAL
INTEGER IOS, STRLEN
  
  integer          :: info
  double precision :: suma
  integer          :: i

  
  STRLEN = INDEX(STRING_IN, CHAR(0)) - 1

! Convert STRING_IN to a double.  If we get an error, we'll
! supply 1.0 as a default

READ (STRING_IN(1:STRLEN), *, IOSTAT=IOS) STRVAL
IF (IOS /= 0) STRVAL = 1.0
DBL_OUT = STRVAL * DBL_IN

call arint3D ()

 return
end

Form1.vb file:

Public Class Form1
    Inherits System.Windows.Forms.Form

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim DBL_IN(4) As Double
        Dim DBL_OUT(4) As Double
        Dim MULTIPLIER As String
        ' Note that in VB, arrays are zero-based.
        DBL_IN(0) = 1.0
        DBL_IN(1) = 2.0
        DBL_IN(2) = 3.0
        DBL_IN(3) = 4.0
        MULTIPLIER = "2.0"
       
        Call electrostatica3D(DBL_IN, MULTIPLIER, DBL_OUT)

        TextBox1.Text = DBL_OUT(0)
        TextBox2.Text = DBL_OUT(1)
        TextBox3.Text = DBL_OUT(2)
        TextBox4.Text = DBL_OUT(3)
    End Sub

 

use C# code as :

using System;

using System.Runtime.InteropServices;
namespace Console_use_integrate{
    class Program
    {


        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate float ActionRefInt(ref float n);

        public ActionRefInt callbackHandler;

        public Program()
        {
            callbackHandler = new ActionRefInt(square);
        }

        public float square(ref float n)
        {
            return n*n;
        }


        static void Main(string[] args)
        {
            Program myProg2 = new Program();

            float a = 1f;
            float b = 3f;
            int step = null;
            float result;
            Console.WriteLine("Interval  from {0} to {1} ", a, b);
       
result = integrate_Dll(myProg2.callbackHandler, a,  b,  System.IntPtr.Zero)
            Console.WriteLine("integration result is {0}",result);
            Console.ReadKey();
        }


       [DllImport(@"FCALL.DLL", CallingConvention = CallingConvention.Cdecl, 
EntryPoint = "electrostatica3D")]
public static extern float electrostatica3D([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
float a, float b, ref int c);
[DllImport(@"FCALL.DLL", CallingConvention = CallingConvention.Cdecl, 
EntryPoint = "electrostatica3D")]
public static extern float electrostatica3D([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
float a, float b, System.IntPtr c);

    }

    }

 

and changing 
 

float a, float b, System.IntPtr c


to something like below but in C# format
 

ByVal DBL_IN() As Double, ByVal STR_IN As String, ByVal DBL_OUT() As Double

and a little modification maybe needed.

so what should I do?

I found a link maybe it's useful Converting FORTRAN code to C# https://www.codeproject.com/Questions/491373/ConvertingplusFORTRANpluscodeplustoplusC

Regards

0 Kudos
Reply