Add FortranCalculator.md

This commit is contained in:
autism 2024-05-13 13:46:24 +00:00
commit 4e1daa237d

157
FortranCalculator.md Normal file
View file

@ -0,0 +1,157 @@
## Introduction
Default Location:
C# - <Documents Folder>\FTN95 Examples \NET\ FortranCalculator\CS
VB .NET - <Documents Folder>\FTN95 Examples \NET\ FortranCalculator\VB
Note: FortranCalculator has both a Visual Basic .NET (VB) and a C# implementation.
This example program is a basic calculator designed to demonstrate the multi language capabilities of FTN95 and Microsoft .NET. The user interface is completely designed with a Microsoft .NET language using Microsoft Visual Studio. The backend of the program that performs calculations is written in Fortran (and compiled with FTN95). While not very complex, this serves the purpose of showing how to create a multi language program.
To build this program for yourself you will require the Microsoft .NET Framework to be installed.
We recommend you use Microsoft Visual Studio .NET to examine and compile this example program, although we have provided a batch file for easy compiling from the command line.
To compile under Microsoft Visual Studio .NET you should load the solution file (VB_Calculator.sln or CS_Calculator.sln) in the project directory. To load the solution select File -> Open Solution, navigate to the solution file and open it. To build the solution, navigate to the Build menu and select Build Solution.
Once you have built the program you can run it in debug mode using 'F5' or the executable can be found in the Debug\NET subdirectory of the project folder and can be run directly.
To compile from the command line you can run the supplied builddbg.bat or buildrel.bat (Debug or Release) in the project directory. The executables will be placed in bin\Debug and bin\Release respectively. They can be run by simply double clicking the executable.
## Example Creation
To create a multi language program such as this from scratch using Microsoft Visual Studio .NET you need to create two projects, one for each language. Since we are making a C#/VB windows application and calling functions from Fortran we should make the Fortran project output a DLL. To create this project, go to the File menu and select New Project. Select the FTN95 Projects branch, Choose FTN95 Application Extension from the available projects and type an appropriate name for the project then press OK.
One source file for the project is created automatically, if you needed more than one you could create them by selecting Add New Item from the Project menu. Enter the Fortran code as shown below, using the new ASSEMBLY_INTERFACE directive as shown in the example project to give a name to each subroutine/function. This becomes the externally (case sensitive) callable name of the routine containing the directive.
A new C#/VB project should now be added to the solution using Add -> New Project. Select Windows Application from the Visual C# Projects branch or the Visual Basic Projects branch and enter an appropriate name. After pressing OK the C# project is created and an empty dialog is shown. You can add new window controls to this dialog and add relevant C#/VB code for each. See the C#/VB documentation for more details on this.
Before you can call the Fortran code from your other project you must add a reference to the Fortran project to the C#/VB project.
The default namespace has been changed to FTN95Calculator to illustrate the use of the linker switch /CONTAINING_CLASS. Specification of this switch will change the default classname to that specified. Any routines contained within the assembly should then be qualified with the new classname. By default the classname is the project name. To change the default class name the following option should be set within Visual Studio .NET.
To call a subroutine or function from the Visual Basic .NET code you need to add the imported classname to the Project Imports list under Imports in the Visual Basic .NET project properties. Then make the call with that name as a class name, and the name you gave to the subroutine/function with the ASSEMBLY_INTERFACE directive as the method name, i.e. ClassName.methodName (arguments).
The arguments should be equivalent data types, e.g. real*8 is equivalent to double.
Note that in the code below, all function/subroutine names have different names than those provided by the ASSEMBLY_INTERFACE directive. This is to avoid any ambiguities when using the generated assembly from a case insensitive language such as Visual Basic .NET. Another approach to this problem is to use the /INTERFACE_ONLY switch on the linker command line. If using FTN95 within Visual Studio .NET then as of version 4.5 this option is on by default.
## Code Features
This example uses a new keyword for FTN95 .NET - 'THROW'. When given a character string parameter this keyword will throw a UserException in the normal Microsoft .NET manner, which can then be caught by the calling code. Fortran is also capable of catching a thrown exception itself, using a similar syntax to C#.
## Fortran Source
```
real*8 function add_ (x, y)
assembly_interface(name="Add")
real*8 x, y
add_ = x + y
end
real*8 function subtract_ (x, y)
assembly_interface(name="Subtract")
real*8 x, y
subtract_ = x - y
end
real*8 function multiply_ (x, y)
assembly_interface(name="Multiply")
real*8 x, y
multiply_ = x * y
end
real*8 function divide_ (x, y)
assembly_interface(name="Divide")
real*8 x, y
divide_ = x / y
end
real*8 function sqroot_ (x)
assembly_interface(name="SqRoot")
real*8 x
object ("System.Exception") calculatorException
calculatorException = new@ ("System.Exception")
if (x < 0) throw calculatorException
sqroot_ = sqrt(x)
end
real*8 function log_e_ (x)
assembly_interface (name="Log")
real*8 x
object ("System.Exception") calculatorException
calculatorException = new@ ("System.Exception")
if (x < 0) throw calculatorException
log_e_ = log(x)
end
real*8 function piValue_ ()
assembly_interface (name="PiValue")
piValue_ = 3.1415926535897932384626433832795
end
```
The code above illustrates the use of the ASSEMBLY_INTERFACE directive to provide case sensitive externally callable names for the functions/subroutines contained within the file.
Basic use of Fortran exception handling is also demonstrated in the above file. Within functions sqroot_ and log_e_ exception objects are created which are then conditionally thrown. The thrown exception should then be caught by any routine on the call stack. If no such catch exists then the exception will be presented as a program error. In this example the calling routine catches the thrown exception as can be seen in the following Visual Basic .NET code.
## Visual Basic .NET source
Note that some of the non-interop code has been removed for brevity. Full source can be found in the actual example. C# source can also be found within the example directories.
```
Private Sub sqrtButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles sqrtButton.Click
Try
checkFlagState()
DB1 = CDbl(textBox.Text)
result = FTN95Calculator.SqRoot(DB1)
res = (CStr(result))
textBox.Text = res
Catch
ex As Exception When DB1 < 0
Beep()
End Try
End Sub
Private Sub piButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles piButton.Click
checkFlagState()
result = FTN95Calculator.PiVal()
res = (CStr(result))
textBox.Text = res
End Sub
Private Sub logButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles logButton.Click
Try
checkFlagState()
DB1 = CDbl(textBox.Text)
result = FTN95Calculator.Log(DB1)
res = (CStr(result))
textBox.Text = res
Catch ex As Exception When DB1 < 0
Beep()
End Try
End Sub
Private Sub equalsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles equalsButton.Click
DB2 = CDbl(textBox.Text)
Select Case opcode
Case "add"
result = FTN95Calculator.Add(DB1, DB2)
eqpr = True
Case "subtract"
result = FTN95Calculator.Subtract(DB1, DB2)
eqpr = True
Case "multiply"
result = FTN95Calculator.Multiply(DB1, DB2)
eqpr = True
Case "divide"
result = FTN95Calculator.Divide(DB1, DB2)
eqpr = True
Case Else
result = DB2
End Select
res = (CStr(result))
textBox.Text = res
needini = True
End Sub
```