Let’s call Fortran DLL from F# language.

This text is 14th article in F# Advent Calendar 2014 in Japanese.

The original article’s the Japanese, but since there was a request, I am rewriting in English. So this year is also in English version, and users to access the F# Advent of English version and traced from the following.

F# Advent Calendar 2014 – connpass
http://connpass.com/event/9758/
F# Advent Calendar in English 2014 | Sergey Tihon’s Blog
http://sergeytihon.wordpress.com/2014/11/24/f-advent-calendar-in-english-2014/

Well, last year it wrote of that “before the F# and Fortran story … of” What it is sadly the setback was me…, this time agein !!! We succeeded in calling a Fortran from F#, who deals and technical information for you, but the time being, because the head is harf-joke article capital F connection :P

Make DLL by Fortarn

This DLL is made by Intel Visual Fortran 2013.

Intel Fortan can make DLL on Windows. Export Function is defined “!DEC$ ATTRIBUTES DLLEXPORT::<EXPORT-FUNCTION>”. We have a fortran fo GNU, but without that it was made only static libraries more of over there, I do not know well. However, it is possible to to create a *.so file. If you are run on linux use as it is from the C language, you’ll probably make in a manner similar from F#.

! This function has two number parameters.
integer function add1( a, b )
    !DEC$ ATTRIBUTES DLLEXPORT::ADD1
    integer, intent(in) :: a, b
    integer :: total

    total = a + b
    add1  = total
end function add1

! This function has a One-dimensional array parameter.
integer function sum1( v, count )
    !DEC$ ATTRIBUTES DLLEXPORT::SUM1
    integer, intent(in) :: v(10)
    integer, intent(in) :: count
    integer :: i, total

    total = 0
    do i=1,count
        print *, 'in sum1: ', i, v(i)
        total = total + v(i)
    end do
    sum1 = total
    end function sum1

! This function has a Two-dimensional array parameter.
integer function sum2( v, cnti, cntj )
    !DEC$ ATTRIBUTES DLLEXPORT::SUM2
    integer :: v(3,4)
    integer :: cnti, cntj
    integer :: i,j
    integer :: total

    total = 0
    do j=1,cntj
        do i=1,cnti
            print *,i,j,v(i,j)
            total = total + v(i,j)
        end do
    end do
    sum2 = total
end function sum2

Fortran of contents is too cheap, but I have tried an array numeric (int type). To after practice, We should try double, the per float.

Try to call from C++ Language

Let’s call from C++ in order to confirm the operation. The DLL function that is published from Fortran, and I call using the dllimport, but I just want to note is where we pass the value is in the pointer. because via global variables rather than via this Fortran function of passing stack. In the case of C language and pass laden with value on the stack (C# same when passing from a .NET language, etc.), when it passed to Fortran, that do not attention to this, it’s a strange feeling. Well, when you’re doing just Fortran, and so convenient to be very well done to be taken into account (like pass as it is a huge array).

#include "stdafx.h"
#include <iostream>

#define DllImport __declspec( dllimport )

extern "C" {
	DllImport int ADD1(const int *, const int *);
	DllImport int SUM1( int(*)[] , const int *);
	DllImport int SUM2(int(*)[4][3], const int *, const int *);
}

int _tmain(int argc, _TCHAR* argv [])
{
	int x = 10;
	int y = 20;
	int ans = ADD1(&x, &y);
	std::cout << "ans " << ans << std::endl;

	int  v[] = { 1, 2, 3, 4, 5 };
	int cnt = 5;
	int ans2 = SUM1((int(*)[])&v, &cnt);
	std::cout << "ans2 " << ans2 << std::endl;

	int v2[][3] = {
		{1,2,3},
		{11,22,33},
		{10,20,30},
		{100,200,300}
	};
	int cnti = 3;
	int cntj = 4;
	int ans3 = SUM2( &v2, &cnti, &cntj);
	std::cout << "ans3 " << ans3 << std::endl;

	return 0;
}

If we run this code, you will like that.

Try to Call from C# Language

Let’s rewrite in C#. And to use the DllImport is we ware found, but it does not do not pass a pointer int type, we can use the ‘ref’ keyword. Call form of the function, we need to be defined explicitly “CallingConvention = CallingConvention.Cdecl”.

class Program
{
	[DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)]
	extern static int SUM1([In] int[] v, [In] ref int count);
	[DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)]
	extern static int SUM2([In] int[,] v, [In] ref int cnti, [In] ref int cntj);
	[DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)]
	extern static int ADD1([In] ref int a, [In] ref int b);

	static void Main(string[] args)
	{
		int a = 10;
		int b = 20;
		int ans = ADD1(ref a, ref b);
		Console.WriteLine("ans {0}", ans);

		int[] v = { 1, 2, 3, 4, 5 };
		int cnt = v.Length;
		int ans2 = SUM1(v, ref cnt);
		Console.WriteLine("ans2 {0}", ans2);
		/*
		int[][] v2 = {
							new int[]{1,2,3,4},
							new int[]{11,22,33,44},
							new int[]{10,20,30,40},
						};
		*/
		int[,] v2 = {
						{1,2,3},
						{11,22,33},
						{10,20,30},
						{100,200,300},
					};
		int cnti = 3;
		int cntj = 4;
		int ans3 = SUM2(v2, ref cnti, ref cntj);
		Console.WriteLine("ans3 {0}", ans3);
	}

When you pass a two-dimensional array, int[][] in rather than int[,] you can use the C++ and because the int value the same as in the contiguous memory and Fortran is to be placed. When we call the ADD1 function is cumbersome put the ref to, but you can call this correctly the DLL of Fortran.

Try to call from F# !!!

Let’s rewrite more F#. This pointer which is export from dll is int& we declare as.

open System
open System.Runtime.InteropServices

type ARY2D = int[,]

[<AutoOpen>]
module FDLL =
  [<DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)>]
  extern int ADD1(int& a, int& b)
  [<DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)>]
  extern int SUM1(int[] v, int& count )
  // cannot use int[,], so we defined 'ARY2D' alias class.
  [<DllImport("FModule.dll", CallingConvention = CallingConvention.Cdecl)>]
  // extern int SUM2(int[,] v, int& cnti, int& cnt2 )
  extern int SUM2(ARY2D v, int& cnti, int& cnt2 )

let mutable a = 10
let mutable b = 20
let ans = ADD1( &a , &b )

Console.WriteLine( "ans {0}", ans )

let mutable v = [|1;2;3;4;5|]
let mutable cnt = v.Length
let ans2 = SUM1( v, &cnt )

Console.WriteLine( "ans2 {0}", ans2 )

// two-dimensional array is made by Array2D module.
let vv = [|
    [|1;2;3|];
    [|11;22;33|];
    [|10;20;30|];
    [|100;200;300|];
  |]
let v2 = Array2D.init 4 3 ( fun i j -> vv.[i].[j])

let mutable cnti = 3
let mutable cntj = 4
let ans3 = SUM2( v2, &cnti, &cntj )

Console.WriteLine( "ans3 {0}", ans3 )

C# is not enough … write simpler compared with F#, I do not change much. Than that, it’s worrisome is is where you must use let ‘mutable’ is to want to pass by const. This is a limitation of Fortran call, in order to pass a pointer value, the variable of a even if it is not changed content is not pass and do not ‘mutable’. Here is where I want to twist. Though it pains of immutable values, it is a place me.

C# 2-dimensional array in int [,] since the was an unclear writing is how I in F # … but, how was I understand. With Array2D int [,] you can make a considerable thing. This, what another twist necessary int when you extern [,] it can not be the type specified in, we will create the alias. Here, extern int SUM2 by creating a type alias that ARY2D and is doing. In this way, you will be passed as the same as a two-dimensional array with C++.

Oh, Order of two-dimensional array will not unlike those of C++, but this is that the charm ;)

At the end

That is why, one New Year’s Eve of projects? As, was able story article dealing with the Fortran from F#. This, or the existing Fortran library can make an app that was made from F#, Who’s the obtained technical information but, Well, it is possible to that wanted to do. Everyone, a good Christmas !!!

カテゴリー: 開発 パーマリンク