www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fortran DLL and D

reply "Michael" <pr m1xa.com> writes:
Hi everyone)

dmd 2.058
os: win 7 64 bit
fortran compilers: gfortran, ftn95

I have a fortran code that compiled into dll:

SUBROUTINE fsu (i)
     real :: x
     integer :: i
     x = 0.025
     print *, 'The answer is x = ', x , i
END SUBROUTINE fsu

and simple D code

import std.stdio;
import core.runtime;
import std.c.windows.windows;
import core.memory;

alias void function(int) MyHandler;

void main()
{
     GC.disable;
     FARPROC fp;
     HMODULE lib = cast(HMODULE)Runtime.loadLibrary("testf.dll");
     MyHandler mh;

     if (lib is null)
     {
         writeln("Lib!");
         return;
     }

     fp = GetProcAddress(lib, "FSU");

     if (fp is null)
     {
             writeln("Proc!");
             writeln(GetLastError());
             return;
     }
     mh = cast(MyHandler) fp;
     (*mh)(1);
     Runtime.unloadLibrary(lib);
}

and its output

The answer is x =     2.500000E-02  1407551829

It's should be an 1 value instead 1407551829.

I think, trouble in param passing.
How it can be fixed? Or where to look?

Thanks)
Mar 13 2012
next sibling parent Tobias Brandt <tob.brandt googlemail.com> writes:
Fortran uses pass-by-ref by default. You could try

    integer, value :: i

in the Fortran function declaration, OR

    *int

in the MyHandler declaration.
Mar 13 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I don't think this can work:
alias void function(int) MyHandler;

maybe:
alias extern(C) void function(int) MyHandler;

And there's no need to call it like this: '(*mh)(1)', call it mh(1).
Mar 13 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
On Tuesday, 13 March 2012 at 22:30:02 UTC, Tobias Brandt wrote:
 Fortran uses pass-by-ref by default. You could try

     integer, value :: i

 in the Fortran function declaration, OR

     *int

 in the MyHandler declaration.

same results in case int* int * i; *i=5; (*mh)(i); object.Error: Access Violation ---------------- 409960 4097D7 402BA8 402BE7 4027F7 413635 ----------------
Mar 13 2012
prev sibling next sibling parent Tobias Brandt <tob.brandt googlemail.com> writes:
On 13 March 2012 23:53, Michael <pr m1xa.com> wrote:
 On Tuesday, 13 March 2012 at 22:30:02 UTC, Tobias Brandt wrote:
 Fortran uses pass-by-ref by default. You could try

 =A0 =A0integer, value :: i

 in the Fortran function declaration, OR

 =A0 =A0*int

 in the MyHandler declaration.

in case integer, value :: i or integer, intent(in) :: i same results in case int* int * i; *i=3D5; (*mh)(i); object.Error: Access Violation ---------------- 409960 4097D7 402BA8 402BE7 4027F7 413635 ----------------

You could use the C binding syntax: SUBROUTINE fsu (i) bind(C, name =3D "FSU") real :: x integer, value :: i x =3D 0.025 print *, 'The answer is x =3D ', x , i END SUBROUTINE fsu and then use extern(C) in D. That should work, but you need a newish Fortran compiler.
Mar 13 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
On Tuesday, 13 March 2012 at 22:42:38 UTC, Andrej Mitrovic wrote:
 I don't think this can work:
 alias void function(int) MyHandler;

 maybe:
 alias extern(C) void function(int) MyHandler;

 And there's no need to call it like this: '(*mh)(1)', call it 
 mh(1).

I know, it's short version. Anyway, object.Error: Access Violation ---------------- 409960 4097D7 402BA8 402BE7 4027F7 413635 ----------------
Mar 13 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
Thanks, but i still get the same.
Mar 13 2012
prev sibling next sibling parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/13/2012 05:15 PM, Michael wrote:
 Hi everyone)

 dmd 2.058
 os: win 7 64 bit
 fortran compilers: gfortran, ftn95

Here's apples to oranges, since I'm on linux 64 bit, but with your code and this: pragma(lib, "flib"); extern(C) void fsu_(int*i); void main(){ int i = 1; fsu_(&i); } when compiled on my box gives The answer is x = 2.50000004E-02 1 If something similar doesn't work for you, can you post disassembly dumps of your dll function and calling function?
Mar 14 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
Guys, thanks for advices.
After all I have proper code.

simple.d

import core.runtime;
import std.stdio;
import std.string;
import std.conv;

version(Windows)
{
     import core.sys.windows.windows;
     alias GetProcAddress GetProc;
}
else
     version(Linux) // not tested
     {
         import core.sys.linux.linux;
         alias dlsym GetProc;
     }

alias extern(C) void function (double *, int) func_One;
alias extern(C) void function (double *, int, ref int) func_Two;

void main(string[] args)
{
     if (args.count != 2)
         return;

     size_t size = parse!int(args[1]);

     writeln("Accept: ", size);

     double[] arr = new double[size];

     auto dllfile = "simple.dll";

     void * lib = Runtime.loadLibrary(dllfile);

     if (!lib)
     {
         writeln("Lib!");
         return;
     }

     func_One f_One = cast(func_One) GetProc(lib, 
"fOne".toStringz);
     if (f_One is null)
     {
         writeln("f_One!");
         return;
     }

     func_Two f_Two = cast(func_Two) GetProc(lib, 
"fTwo".toStringz);
     if (f_Two is null)
     {
         writeln("f_Two!");
         return;
     }

     f_One(arr.ptr, arr.length);

     writeln("array size: ", arr.length);

     for(int k = 0; k < arr.length; k++)
     {
         writeln(k + 1, '\t', arr[k]);
     }

     int ans = 0;
     f_Two(arr.ptr, arr.length, ans);

     writeln("fTwo = ", ans);

     Runtime.unloadLibrary(lib);
}

simple.f95 compiled with gfortran -shared -O3 -o simple.dll 
simple.f95

subroutine fOne(a, i) bind(C, name = "fOne")
     integer*4, value, intent(in) :: i
     real*8, dimension(i), intent(out) :: a
     print *, 'entry fortran dll'
     print *, 'size', i
     do j = 1, i
         a(j) = j * j;
         print *, j
     end do
     print *, 'exit fortran dll'
end

subroutine fTwo(a, i, ans) bind(C, name = "fTwo")
     integer*4, value, intent(in) :: i
     real*8, dimension(i), intent(in) :: a
     integer*4, intent(out) :: ans
     print *, 'entry fortran dll'
     print *, 'size', i
     if (a(1) .lt. 100) then
         print *, 'inside if'
         ans = 1
         print *, 'end if'
         return
     end if
     ans = 0
     print *, 'exit fortran dll'
end
Mar 21 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Michael wrote:

 Guys, thanks for advices.
 After all I have proper code.

Why don't you write how to do it in the D wiki? Bye, bearophile
Mar 21 2012
prev sibling parent "Michael" <pr m1xa.com> writes:
Bad English, and I don't have experiense with Wiki.

On Wednesday, 21 March 2012 at 22:21:11 UTC, bearophile wrote:
 Michael wrote:

 Guys, thanks for advices.
 After all I have proper code.

Why don't you write how to do it in the D wiki? Bye, bearophile

Mar 21 2012