- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)
..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
FF's suggestion is more elegant (C# is creating the wrapper functions in the declarations).
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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/ConvertingplusFORTRANpluscodeplustoplusCRegards
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page