Silverfrost Forums

Welcome to our forums

Compiler problem dealing with arrays of .NET booleans

13 Apr 2015 5:51 (Edited: 13 Apr 2015 11:35) #16193

Hello,

This has had me stumped for a while but I am now fairly certain it is either a problem with the compiler itself or the way I have configured my FORTRAN project.

But basically I have an array of .NET bools, I then pass them into the FORTRAN code where it seems to simply interpret them incorrectly when reading them from the .NET array.

I believe this has something to do with the varying number of bytes a bool will use when in an array as opposed to where a bool represents a single member. This StackOverflow question delves into it a bit: Why in .NET System.Boolean takes 4 byte?

Anyway I have done up a sample application to demonstrate what I am talking about which you can download from here: [LINK REMOVED - SEE POST FURTHER DOWN FOR UPDATED VERSION]

Here is the FORTRAN code:

SUBROUTINE MapData(inputData, outputData)
    IMPLICIT NONE
    ASSEMBLY_INTERFACE(NAME='MapData')

    ! Declare params
    INTEGER :: i
    LOGICAL, DIMENSION(1:12) :: logicalData
    OBJECT('DataLibrary.FortranData'), INTENT(IN) :: inputData
    OBJECT('DataLibrary.FortranData'), INTENT(OUT) :: outputData

    ! Perform input mapping
    logicalData = .FALSE.
    DO i = 1, 12
        logicalData = inputData%MonthlyValues(i-1)
    END DO

    ! Perform output mapping
    outputData = NEW@('DataLibrary.FortranData')
    DO i = 1, 12
        outputData%MonthlyValues(i-1) = logicalData(i)
    END DO
END SUBROUTINE MapData

So basically the input array MonthlyValues should end up being exactly the same as the output array MonthlyValues. However this is not the case as logicalData will be filled with an odd mutation of values which can actually change between application runs and even adding break points seem to affect this behaviour...

However here is a screenshot of some sample output: http://i.imgur.com/WZ4RCVX.png

This is kind of causing me headaches with a bit of a schedule to keep so if anyone could suggest a work around that would be awesome, are there some settings I should try changing in my project configuration.

Thanks, Alex.

13 Apr 2015 6:57 #16194

I don't have a quick answer to your query but you may need to make sure that your .NET implementation is 'locked down' in memory. .NET problems can arise because of garbage collection and the resulting movement of objects in memory. DBK_LINKx.exe is designed to handle this for the Fortran but sometimes the programmer must handle this on the .NET side. You might be able to track memory position changes to test this (e.g. using LOC in Fortran, off hand I don't remember how to do this in C# etc.).

13 Apr 2015 8:17 #16195

Hmmm I will have a look into this but I don't think it is the problem as other value type and object arrays seem to work just fine. In the actual application I am working on its maps a few hundred arrays and the only ones that do not work are boolean arrays.

Could this possibly be due to the nature of how booleans are stored in .NET as described in the StackOverflow question I linked to?

A bool is actually only 1 byte, but alignment may cause 4 bytes to be used on a 32-bit platform, or even 8 bytes on a 64-bit platform. For example, the Nullable<bool> (aka bool?) type uses a full 32 or 64 bits—depending on platform—even though it's comprised of just two bools. EDIT: As pointed out by Jon Skeet, padding for alignment isn't always present. As an example, an array of Nullable<bool>s takes only 2 bytes per object instead of 4 or 8.

13 Apr 2015 10:50 #16197

On the Fortran side you can have logical*1, *2 or *4 (the default). The kinds are 1,2 and 3 respectively.

13 Apr 2015 11:04 #16198

Quoted from alex21 Here is the FORTRAN code:

SUBROUTINE MapData(inputData, outputData)
    IMPLICIT NONE
    ASSEMBLY_INTERFACE(NAME='MapData')

    ! Declare params
    INTEGER :: i
    LOGICAL, DIMENSION(1:12) :: logicalData
    OBJECT('DataLibrary.FortranData'), INTENT(IN) :: inputData
    OBJECT('DataLibrary.FortranData'), INTENT(OUT) :: outputData

    ! Perform input mapping
    logicalData = .FALSE.
    DO i = 1, 12
        logicalData = inputData%MonthlyValues(i-1)
    END DO

    ! Perform output mapping
    outputData = NEW@('DataLibrary.FortranData')
    DO i = 1, 12
        outputData%MonthlyValues(i-1) = logicalData(i)
    END DO
END SUBROUTINE MapData

So basically the input array MonthlyValues should end up being exactly the same as the output array MonthlyValues. However this is not the case as logicalData will be filled with an odd mutation of values which can actually change between application runs and even adding break points seem to affect this behaviour...

There is probably an error in input mapping part of your code. Your statement seems to be missing array index.

It should probably be written as :

    ! Perform input mapping 
    DO i = 1, 12 
        logicalData(i) = inputData%MonthlyValues(i-1) 
    END DO
13 Apr 2015 6:39 #16203

Hi, i just tested this...

FTN95 compiler can properly convert logical data type into .NET framework System.Boolean data type. However, when you try to assign array of logical into array of System.Boolean you get a 'System.IndexOutOfRangeException' exception.

Easy solution is to always use .NET System.Boolean data type instead of the Fortran logical data type.

So, change:

logical, dimension(0:4) :: logicalData

to:

object('System.Boolean[]') logicalData
logicalData = new@('System.Boolean[]',5)

Hope this helps...

13 Apr 2015 10:48 #16205

Quoted from jalih

Quoted from alex21 Here is the FORTRAN code:

SUBROUTINE MapData(inputData, outputData)
    IMPLICIT NONE
    ASSEMBLY_INTERFACE(NAME='MapData')

    ! Declare params
    INTEGER :: i
    LOGICAL, DIMENSION(1:12) :: logicalData
    OBJECT('DataLibrary.FortranData'), INTENT(IN) :: inputData
    OBJECT('DataLibrary.FortranData'), INTENT(OUT) :: outputData

    ! Perform input mapping
    logicalData = .FALSE.
    DO i = 1, 12
        logicalData = inputData%MonthlyValues(i-1)
    END DO

    ! Perform output mapping
    outputData = NEW@('DataLibrary.FortranData')
    DO i = 1, 12
        outputData%MonthlyValues(i-1) = logicalData(i)
    END DO
END SUBROUTINE MapData

So basically the input array MonthlyValues should end up being exactly the same as the output array MonthlyValues. However this is not the case as logicalData will be filled with an odd mutation of values which can actually change between application runs and even adding break points seem to affect this behaviour...

There is probably an error in input mapping part of your code. Your statement seems to be missing array index.

It should probably be written as :

    ! Perform input mapping 
    DO i = 1, 12 
        logicalData(i) = inputData%MonthlyValues(i-1) 
    END DO

Indeed I completely missed that in the sample! The sample actually works fine with that mistake fixed... I am now going through my application code looking for the same mistake because the sample was producing the same kind of weirdness.

EDIT: Actually the output produced is now correct however the values in FORTRAN are still incorrect..... I will make another post to show this soon.

13 Apr 2015 11:10 #16206

Quoted from PaulLaidler On the Fortran side you can have logical*1, *2 or *4 (the default). The kinds are 1,2 and 3 respectively.

Yeah I tried using different LOGICAL kinds however it does not seem to affect the output.

13 Apr 2015 11:13 #16207

Quoted from jalih Hi, i just tested this...

FTN95 compiler can properly convert logical data type into .NET framework System.Boolean data type. However, when you try to assign array of logical into array of System.Boolean you get a 'System.IndexOutOfRangeException' exception.

Easy solution is to always use .NET System.Boolean data type instead of the Fortran logical data type.

So, change:

logical, dimension(0:4) :: logicalData

to:

object('System.Boolean[]') logicalData
logicalData = new@('System.Boolean[]',5)

Hope this helps...

Due to the restrictions on me for the FORTRAN project I am working with, I am unable to use .NET types in the actual simulation code this is why it needs to be mapped to the variables that the simulation code is already using.

However I do not get a System.IndexOutOfRangeException because I am mapping one index at a time? and taking 1 from the .NET array index to account for zero-based vs counting number-based indexes.

13 Apr 2015 11:45 #16208

After a bit more messing around I am again starting to think this is a Silverfrost problem, as pointed out by jalih there was a mistake in my original sample application, after fixing this it actually started to behave a bit stranger.

Basically the values mapped into the FORTRAN are interpreted incorrectly in the FORTRAN, however if these values are not manipulated in the FORTRAN mapping them back to .NET objects actually yields the same values that were mapped into the FORTRAN.

So if you try to use the values in the FORTRAN code they seem to be interpreted like this: http://i.imgur.com/VapOxaS.png

Also here is the updated sample application and a snippet of the updated FORTRAN code for those who do not want to download it:

SUBROUTINE MapData(inputData, outputData)
    IMPLICIT NONE
    ASSEMBLY_INTERFACE(NAME='MapData')

    ! Declare params
    INTEGER :: i
    LOGICAL, DIMENSION(1:12) :: logicalData
    OBJECT('DataLibrary.FortranData'), INTENT(IN) :: inputData
    OBJECT('DataLibrary.FortranData'), INTENT(OUT) :: outputData

    ! Map .NET methods
    ASSEMBLY_EXTERNAL('DataLibrary.FortranData.LogMessage') :: LogMessage
    ASSEMBLY_EXTERNAL('DataLibrary.FortranData.LogBool') :: LogBool

    ! Perform input mapping
    logicalData = .FALSE.
    DO i = 1, 12
        logicalData(i) = inputData%MonthlyValues(i-1)
    END DO

    ! Display FORTRAN interpretation
    CALL LogMessage(inputData, 'FORTRAN Values')
    DO i = 1, 12
        CALL LogBool(inputData, logicalData(i))
    END DO
    CALL LogMessage(inputData, '')

    ! Perform output mapping
    outputData = NEW@('DataLibrary.FortranData')
    DO i = 1, 12
        outputData%MonthlyValues(i-1) = logicalData(i)
    END DO
END SUBROUTINE MapData

Currently I am thinking I might simply wrap my boolean arrays in another array which converts them to integers of 0 or 1, as the FORTRAN seems to interpret these fine.

Thanks, Alex.

21 Jun 2019 4:50 #23823

Also this is still an issue for me, the sample application I provided is no longer available but I am willing to build another to demonstrate this issue again for further investigation. I am 90% sure it is because booleans use a different number of bytes in a .NET array if certain conditions are met, whatever is converting byte representations between LOGICAL and System.Boolean needs to account for this.

24 Jun 2019 12:09 #23840

Quoted from John-Silver

, the sample application I provided is no longer available

??????

It was available at the time of posting, but I did not maintain the dropbox account so it was deleted years ago. Robert has the latest example of some other issues in a sample I put on github more recently.

Please login to reply.