I have a fortran subroutine that I call from C# and want to be able to terminate. This I can do using an integer variable that I alter in C# as the fortran routine runs. However changing this to a bool
/ logical(c_bool)
does not work. (In the code below is also an option where I change a value in a callback function.)
Working code (with int
parameters)
C#:
using System.Runtime.InteropServices;
Console.WriteLine("C# starting");
var cb = new Natives.ActionRefInt(OnUpdateProgress);
var terminate = 0;
var t = Task.Run(() => Natives.TestCallBackAndVariable(cb, ref terminate));
for (int i = 0; i < 4; i++)
{
await Task.Delay(900);
terminate = i * 100;
Console.WriteLine($"C# loop i = {i}, terminate = {terminate}");
}
terminate = -1;
await t;
static void OnUpdateProgress(ref int val2)
{
Console.WriteLine($"C# OnUpdateProgress received val2 = {val2}");
val2 += 10;
Console.WriteLine($"C# OnUpdateProgress setting val2 = {val2}");
}
public static class Natives
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ActionRefInt(ref int progress);
[DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestCallBackAndVariable(
[MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack,
ref int terminate);
}
Fortran:
subroutine TestCallBackAndVariable(callBack, terminate)
!DEC$ ATTRIBUTES DLLEXPORT::TestCallBackAndVariable
!DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBackAndVariable" :: TestCallBackAndVariable
use ISO_C_BINDING
implicit none
! Variables
external :: callBack
integer(C_INT),intent(inout) :: terminate
integer(C_INT) :: ii
integer(C_INT) :: val2
! Body of TestCallBackAndVariable
write(*,*) "Starting TestCallBackAndVariable in fortran lib"
do ii = 1,10
call sleep(1)
val2 = ii
call callBack(val2)
write(*,*) "Fortran loop ii = ",ii,", and terminate = ", terminate, ", and val from callback = ", val2
if (terminate<0) exit
end do
write(*,*) "Finished TestCallBackAndVariable in fortran lib"
end subroutine TestCallBackAndVariable
Not working as expected code (with bool
/ logical
)
C#:
using System.Runtime.InteropServices;
Console.WriteLine("C# starting");
var cb = new Natives.ActionRefBool(OnUpdateProgress);
var terminate = false;
var t = Task.Run(() => Natives.TestCallBackAndVariable(cb, ref terminate));
for (int i = 0; i < 4; i++)
{
await Task.Delay(900);
Console.WriteLine($"C# loop i = {i}, terminate = {terminate}");
}
terminate = true;
await t;
static void OnUpdateProgress(ref bool val2)
{
Console.WriteLine($"C# OnUpdateProgress received val2 = {val2}");
val2 = true;
Console.WriteLine($"C# OnUpdateProgress setting val2 = {val2}");
}
public static class Natives
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ActionRefBool(ref bool progress);
[DllImport(dllName: "FortranLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestCallBackAndVariable(
[MarshalAs(UnmanagedType.FunctionPtr)] ActionRefBool callBack,
ref bool terminate);
}
Fortran:
subroutine TestCallBackAndVariable(callBack, terminate)
!DEC$ ATTRIBUTES DLLEXPORT::TestCallBackAndVariable
!DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBackAndVariable" :: TestCallBackAndVariable
use ISO_C_BINDING
implicit none
! Variables
external :: callBack
logical(C_BOOL),intent(inout) :: terminate
integer(C_INT) :: ii
logical(C_BOOL) :: val2
! Body of TestCallBackAndVariable
write(*,*) "Starting TestCallBackAndVariable in fortran lib"
do ii = 1,10
call sleep(1)
val2 = .false.
call callBack(val2)
write(*,*) "Fortran loop ii = ",ii,", and terminate = ", terminate, ", and val from callback = ", val2
if (terminate) exit
end do
write(*,*) "Finished TestCallBackAndVariable in fortran lib"
end subroutine TestCallBackAndVariable
subroutine TestArray(i,j,arr)
In the bool/logical variant does not c# modify value of the logical terminate
fortran variable. However, changing the logical value in the OnUpdateProgress
does work. (propagates back to fortran)
Help on this is, as always, much appreciated.
(VS 2022 NET 6, IFORT 2021.6.0 64bit)
In Fortran, the logical(kind=c_bool)
type maps to the C _Bool
type of the companion C processor (=compiler).
In C, _Bool
is defined to be an unsigned integer type (size is implementation dependent but typically one byte) however with only two allowable values, 0 for false and 1 for true (0 and 1 here meaning the unsigned integer bit patterns that represent the integer values 0 and 1). Any other bit pattern is a trap representation and invokes undefined behavior.
However, in C# the internal representation of Bool variables is, per the ECMA CLI spec ( What is the binary representation of a boolean value in c# ), 0 for false, and all other bit patterns (that is, with any of the bits set to 1) are true.
Thus, AFAICT, the Fortran logical(c_bool)
and the C# bool
type are NOT interoperable, and you need some other mechanism to transfer boolean values between Fortran and C#. E.g. transferring them as integers.