Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to make DLL's in fortran 2

Status
Not open for further replies.

GerritGroot

Technical User
Nov 3, 2006
291
ES
Hi,

I want to compile some fotran code to a DLL to be used by a commercial code and to be tested by my own fortran executable.

How do I do this?

Where can I find how to do this in gfortran and in CVF (but I'm especially interested in the gfortran option)?

Once compiled to a DLL, how do I call my DLL from my fortran code? (This is only to test it, in reality it will be used and called by some commercial code)

Any info or link to a manual is welcome, I couldn't find any hit on the search "DLL" in the user unfriendly manual at: gcc.gnu.org/onlinedocs/gcc-4.2.1/gfortran.pdf

Thanks,

Gerrit
 
With gfortran it works like with g95, which is described here:
I tried it:

1. I created a source for the dll named dll_foo.f95
Code:
[COLOR=#2e8b57][b]integer[/b][/color] [COLOR=#a020f0]function[/color] add2i(x, y)
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x, y
  add2i [COLOR=#804040][b]=[/b][/color] x [COLOR=#804040][b]+[/b][/color] y
[COLOR=#a020f0]end function[/color] add2i

[COLOR=#2e8b57][b]real[/b][/color] [COLOR=#a020f0]function[/color] add2r(x, y)
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x, y
  add2r [COLOR=#804040][b]=[/b][/color] x [COLOR=#804040][b]+[/b][/color] y
[COLOR=#a020f0]end function[/color] add2r

[COLOR=#2e8b57][b]real[/b][/color] [COLOR=#a020f0]function[/color] simpson(f, a, b, n)
  [COLOR=#0000ff]! Approximate the definite integral of f from a to b by the[/color]
  [COLOR=#0000ff]! composite Simpson's rule, using N subintervals[/color]
  [COLOR=#0000ff]! see: <a href="[URL unfurl="true"]http://en.wikipedia.org/wiki/Simpson%27s_rule">http://en.wikipedia.org/wiki/Simpson%27s_rule</a>[/URL][/color]
  [COLOR=#0000ff]! function arguments ---------------------[/color]
  [COLOR=#0000ff]! f: real function[/color]
  [COLOR=#a020f0]interface[/color]
[COLOR=#2e8b57][b]    real[/b][/color] [COLOR=#a020f0]function[/color] f(x)
[COLOR=#2e8b57][b]      real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x
    [COLOR=#a020f0]end function[/color] f 
  [COLOR=#a020f0]end interface[/color]
  [COLOR=#0000ff]! [a, b] : the interval of integration[/color]
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) ::  a, b
  [COLOR=#0000ff]! n : number of subintervals used[/color]
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: n
  [COLOR=#0000ff]! ----------------------------------------[/color]
  [COLOR=#0000ff]! temporary variables[/color]
  [COLOR=#2e8b57][b]integer[/b][/color] :: k
[COLOR=#2e8b57][b]  real[/b][/color] :: s
  s [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
  [COLOR=#804040][b]do[/b][/color] k[COLOR=#804040][b]=[/b][/color][COLOR=#ff00ff]1[/color], n[COLOR=#804040][b]-[/b][/color][COLOR=#ff00ff]1[/color]
    s [COLOR=#804040][b]=[/b][/color] s [COLOR=#804040][b]+[/b][/color] [COLOR=#ff00ff]2[/color][COLOR=#804040][b]**[/b][/color]([COLOR=#008080]mod[/color](k,[COLOR=#ff00ff]2[/color])[COLOR=#804040][b]+[/b][/color][COLOR=#ff00ff]1[/color]) [COLOR=#804040][b]*[/b][/color] f(a [COLOR=#804040][b]+[/b][/color] (b[COLOR=#804040][b]-[/b][/color]a)[COLOR=#804040][b]*[/b][/color]k[COLOR=#804040][b]/[/b][/color]n)
  [COLOR=#804040][b]end do[/b][/color]  
  simpson [COLOR=#804040][b]=[/b][/color] (b[COLOR=#804040][b]-[/b][/color]a) [COLOR=#804040][b]*[/b][/color] (f(a) [COLOR=#804040][b]+[/b][/color] f(b) [COLOR=#804040][b]+[/b][/color] s) [COLOR=#804040][b]/[/b][/color] ([COLOR=#ff00ff]3[/color][COLOR=#804040][b]*[/b][/color]n)
[COLOR=#a020f0]end function[/color] simpson
2. Then I compiled the dll source with
Code:
$ gfortran -c dll_foo.f95
$ gfortran -shared -mrtd -o dll_foo.dll dll_foo.o
The file dll_foo.dll was created.
Btw, when you want to look into the dll what functions it exports, then there is a free utility for it: Dependency Walker

3. Then I created the Fortran program, which should use the dll created in previous steps:
dll_foo_test.f95
Code:
[COLOR=#a020f0]program[/color] simpson_test
  [COLOR=#2e8b57][b]implicit[/b][/color] [COLOR=#2e8b57][b]none[/b][/color]
  [COLOR=#2e8b57][b]integer[/b][/color] :: add_i
[COLOR=#2e8b57][b]  real[/b][/color] :: integral, add_r
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]external[/b][/color] :: add2i
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]external[/b][/color] :: f1, add2r, simpson
  
  add_i [COLOR=#804040][b]=[/b][/color] add2i([COLOR=#ff00ff]1[/color], [COLOR=#ff00ff]2[/color])
  [COLOR=#804040][b]write[/b][/color] ([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) [COLOR=#ff00ff]"add 2 integers = "[/color], add_i  
  add_r [COLOR=#804040][b]=[/b][/color] add2r([COLOR=#ff00ff]2.71[/color], [COLOR=#ff00ff]3.14[/color])
  [COLOR=#804040][b]write[/b][/color] ([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) [COLOR=#ff00ff]"add 2 reals    = "[/color], add_r  
  integral[COLOR=#804040][b]=[/b][/color]simpson(f1, [COLOR=#ff00ff]0.0[/color], [COLOR=#ff00ff]1.0[/color], [COLOR=#ff00ff]1000[/color])
  [COLOR=#804040][b]write[/b][/color] ([COLOR=#804040][b]*[/b][/color],[COLOR=#804040][b]*[/b][/color]) [COLOR=#ff00ff]"Simpson's rule = "[/color], integral  
[COLOR=#a020f0]end program[/color] simpson_test

[COLOR=#2e8b57][b]real[/b][/color] [COLOR=#a020f0]function[/color] f1(x)
  [COLOR=#2e8b57][b]implicit[/b][/color] [COLOR=#2e8b57][b]none[/b][/color]
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x
  f1 [COLOR=#804040][b]=[/b][/color] x[COLOR=#804040][b]*[/b][/color]x
[COLOR=#a020f0]end function[/color] f1
and I compiled it into executable with
Code:
$ gfortran -o dll_foo_test dll_foo_test.f95 -L. dll_foo.dll
Now when I run it, the output is:
Code:
$ dll_foo_test                                 
 add 2 integers =            3
 add 2 reals    =    5.8500004    
 Simpson's rule =   0.33333328
It works.
 
Took a while to get PowerStation going. Forgot that it had to be installed in a directory with no spaces in the filename! The C1007 and F1019 really had me foxed for ages.

Anyway, Powerstation generates names with an argument count. You need to add an _ in front of the name, and an @ and the number of parameters * 4. Say you have a subroutine to add 2 numbers and return a result.
Code:
subroutine ADD (a, b, c)
real a, b, c
a = b + c
return
end
You need to create a .def file (say test.def). Note that all keywords must be in uppercase
Code:
LIBRARY testlib
EXPORTS
   _ADD@12
Click on settings. Under project options, add
Code:
/def:test.def
Build and you should get the DLL (ends with .dll) and its stub (ends with .lib). When you link with an exe, use the stub. Put the DLL in the same directory as the executable and you should be able to run it.

Another way of exporting if you do not wish to use a .def file is to add the name of the routine in the Project options
Code:
/export:_ADD@12
Note that this is /export but in the .def file it is EXPORTS
 
Hi,

Back again, sorry for reacting that late, I was on a trip, I used @mikrom and @xwb to answer.

@mikrom:

Thanks! Works great!

As well as the dll as the executable build and compile perfectly.

I tried to separate compiling and building (a matter of taste to see what I'm doing) and used a batch file like:
Code:
REM DLL compile
gfortran -c sub_twice.f90
REM DLL build
gfortran -shared -mrtd -o sub_twice.dll sub_twice.o
REM EXE compile
gfortran -c main.f90
REM EXE build
gfortran main.o -o main.exe -L. sub_twice.dll

With the simple code for the dll
Code:
SUBROUTINE Twice(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x
REAL, INTENT(OUT) :: y
y=2*x
END SUBROUTINE Twice

And for the executable
Code:
SUBROUTINE Twice(x,y)
IMPLICIT NONE
REAL, INTENT(IN) :: x
REAL, INTENT(OUT) :: y
y=2*x
END SUBROUTINE Twice

What amazes me a bit is that we don't need to use something in the dll like:
Code:
!DEC$ ATTRIBUTES DLLEXPORT::Twice
And in the exe something like:
Code:
!DEC$ ATTRIBUTES DLLIMPORT::Twice

...or !GCC$

It seems to work fine without these compiler commands that seem to be called "compiler directives"... ???
I suppose that the necessity to use these commands is compiler dependent

Thanks for the tip about Dependency Walker, I'll use it to check commercial dll's. Checking my own dll gives:
"twice_" as the function name indeed on the export window. You can't see which variables are going in or out, can you?
When I load the executable, it doesn't show "twice_" on the PI import window however, I suppose that Dependency Walker only works with DLL's, right?

Something that doesn't have to do anything with building DLL's but that I saw in your example and was new to me, is that you seem to use your function f1 as an argument to your function simpson ??
Is it therefore necessary to use the "INTERFACE" statement in the function "simpson"?? (Never knew what these interfaces were for anyway...) Amazing, I didnt know it existed!

@xwb:

Thanks for looking up your fortran power station compiler!!

The first thing I noticed is that you either used "compiler directives" like !DEC$ etc., are these not necessary?

About the def file, it's not clear to me whether the name of the library, in your case "testlib" should be the same as the name of your DLL, or the function in your DLL.

Using only 2 variables, I get indeed a message that _TWICE@8 is missing, which meets perfectly the name convention you mentioned.

So, I made a file "twice.def" that contains:

Code:
LIBRARY sub_twice
EXPORTS
   _TWICE@8

Adding that to my project options in the DLL workspace it looks like

Code:
/Ox /I "Release/" /c /nologo /MT /Fo"Release/  /def:twice.def "

But the lib file is not generated....???

Adding "/export:_TWICE@8" does not work either, I tried in the DLL workspace and in the EXE workspace before building, again I get the message that _TWICE@8 is missing...

After all, it seems very complicated and laborious in Developer Studio

P.S.: About the old power station, you mention the spaces in the directory name. Be careful using directory names longer than 8 characters with this compiler, the debug function only works for directory names upto 8 characters!!

Regards,

Gerrit
 
The !DEC stuff only works for the CVF/IVF compiler. On the CVF/IVF compilers, there are currently 3 ways of doing this - that happens to be the oldest and is supported by all the older compilers.
 
GerritGroot said:
It seems to work fine without these compiler commands that seem to be called "compiler directives"... ???I suppose that the necessity to use these commands is compiler dependent
I don't know, but I think too, that these directives are only for the another compiler. As I don't use fortran at professional level I don't have experience with commercial compilers.

GerritGroot said:
Thanks for the tip about Dependency Walker, I'll use it to check commercial dll
...
You can't see which variables are going in or out, can you?
...
I suppose that Dependency Walker only works with DLL's, right?
I discovered Dependency Walker as I tried to call my own dll using Python - but without success :-(
I thing the same as you about variables and that it works only with dll's, but maybe I don' have expereince.
(Maybe it's off topic, but if you know how to call a dll generated from g95 or gfortran in Perl, Python, Ruby, ... you could post it here.)
GerritGroot said:
Is it therefore necessary to use the "INTERFACE" statement in the function "simpson"?? (Never knew what these interfaces were for anyway...) Amazing, I didnt know it existed!
No!
I coded the Simpson's method here without interface
dll_foo2.f95
Code:
[COLOR=#2e8b57][b]integer[/b][/color] [COLOR=#a020f0]function[/color] add2i(x, y)
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x, y
  add2i [COLOR=#804040][b]=[/b][/color] x [COLOR=#804040][b]+[/b][/color] y
[COLOR=#a020f0]end function[/color] add2i

[COLOR=#2e8b57][b]real[/b][/color] [COLOR=#a020f0]function[/color] add2r(x, y)
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: x, y
  add2r [COLOR=#804040][b]=[/b][/color] x [COLOR=#804040][b]+[/b][/color] y
[COLOR=#a020f0]end function[/color] add2r

[COLOR=#2e8b57][b]real[/b][/color] [COLOR=#a020f0]function[/color] simpson(f, a, b, n)
  [COLOR=#0000ff]! Approximate the definite integral of f from a to b by the[/color]
  [COLOR=#0000ff]! composite Simpson's rule, using N subintervals[/color]
  [COLOR=#0000ff]! see: <a href="[URL unfurl="true"]http://en.wikipedia.org/wiki/Simpson%27s_rule">http://en.wikipedia.org/wiki/Simpson%27s_rule</a>[/URL][/color]
  [COLOR=#0000ff]! function arguments ---------------------[/color]
  [COLOR=#0000ff]! f: real function  [/color]
[COLOR=#2e8b57][b]  real[/b][/color] :: f
  [COLOR=#0000ff]! [a, b] : the interval of integration[/color]
[COLOR=#2e8b57][b]  real[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) ::  a, b
  [COLOR=#0000ff]! n : number of subintervals used[/color]
  [COLOR=#2e8b57][b]integer[/b][/color], [COLOR=#2e8b57][b]intent[/b][/color]([COLOR=#2e8b57][b]in[/b][/color]) :: n
  [COLOR=#0000ff]! ----------------------------------------[/color]
  [COLOR=#0000ff]! temporary variables[/color]
  [COLOR=#2e8b57][b]integer[/b][/color] :: k
[COLOR=#2e8b57][b]  real[/b][/color] :: s
  s [COLOR=#804040][b]=[/b][/color] [COLOR=#ff00ff]0[/color]
  [COLOR=#804040][b]do[/b][/color] k[COLOR=#804040][b]=[/b][/color][COLOR=#ff00ff]1[/color], n[COLOR=#804040][b]-[/b][/color][COLOR=#ff00ff]1[/color]
    s [COLOR=#804040][b]=[/b][/color] s [COLOR=#804040][b]+[/b][/color] [COLOR=#ff00ff]2[/color][COLOR=#804040][b]**[/b][/color]([COLOR=#008080]mod[/color](k,[COLOR=#ff00ff]2[/color])[COLOR=#804040][b]+[/b][/color][COLOR=#ff00ff]1[/color]) [COLOR=#804040][b]*[/b][/color] f(a [COLOR=#804040][b]+[/b][/color] (b[COLOR=#804040][b]-[/b][/color]a)[COLOR=#804040][b]*[/b][/color]k[COLOR=#804040][b]/[/b][/color]n)
  [COLOR=#804040][b]end do[/b][/color]  
  simpson [COLOR=#804040][b]=[/b][/color] (b[COLOR=#804040][b]-[/b][/color]a) [COLOR=#804040][b]*[/b][/color] (f(a) [COLOR=#804040][b]+[/b][/color] f(b) [COLOR=#804040][b]+[/b][/color] s) [COLOR=#804040][b]/[/b][/color] ([COLOR=#ff00ff]3[/color][COLOR=#804040][b]*[/b][/color]n)
[COLOR=#a020f0]end function[/color] simpson
It compiles and runs:
Code:
$ gfortran -c dll_foo2.f95
$ gfortran -shared -mrtd -o dll_foo2.dll dll_foo2.o
$ gfortran -o dll_foo_test dll_foo_test.f95 -L. dll_foo2.dll
$ dll_foo_test                                  
 add 2 integers =            3
 add 2 reals    =    5.8500004    
 Simpson's rule =   0.33333328
The above code works. But when the argument function should return something complicated as a number, e.g. vector then the declaration would be not so simple. So as a prophylactic, since the discussion given here I'm rather using interfaces. Maybe it's little bit verbose but I like it!
:)
 
(Maybe it's off topic, but if you know how to call a dll generated from g95 or gfortran in Perl, Python, Ruby, ... you could post it here.)

No, unfortunately I don't know that, I only know fortran and what I know about DLL's is what I read on this forum.

I have sort of the inverse problem, I'll have to find out how to make a DLL in fortran that fulfills certain commercial code call conventions (no idea how to do that?).

Thanks all for your answers!

 
Are you sure about this one
Code:
/Ox /I "Release/" /c /nologo /MT /Fo"Release/  /def:twice.def "
These are the flag for Fortran - the /def should not be added here. It should be added for the linker. Your Fortran flags should read
Code:
/Ox /I "Release/" /c /nologo /MT /F"Release/"
The linker flags should read something like
Code:
/nologo /subsystem:console /dll /def:"twice.def" /out:"release/twice.dll" /implib:"release/twice.lib"
The name after LIBRARY has to match that of the dll otherwise the linker will moan about it.
 
Once again, sorry for reacting so late.

It seems to work, thanks!

Nevertheless, it's very complicated in Microsoft's compiler, much more complicated than in gfortran.

Gerrit
 
The newer MS compilers have streamlined the process but you still need a few extras (ImportDLL etc) - basically the MS route isn't as streamlined as the *nix route. I found that when I first started using Windows in 1986. Powerstation has been around since 2000 - it was the replacement for MSFortran (I still have a copy of that somewhere!) but MS still needs some directive to tell it which routines to export.

The def table can be extended to hardcode the offset of the routine. It is basically a jump table so all exported routines will have a location. This means that if you send a DLL to a customer, you can be sure that there won't be any linkage problems because the routine has moved location in the jump table.

With the Import DLL technique, you can't just do a DLL swap. You need to export the whole suite, which can be costly. That is the only problem I had with *nix: if any new routines were added, you almost always had to export the whole suite.
 
Thanks! Being only a technical user, it's it bit hard for me to understand you, because of my lack of knowledge on the subject.

Anyway, I assume you mean that you can't just interchange DLL's for the same exe if they're compiled with gfortran, right?

Nevermind, my problems are solved and the stuff compiles.

(The only reason I use Microsoft's compiler, is because of their easy debugging mode and their colorful editor. As far as I know, debugging in gfortran can only be done in a very old-fashioned way, placing "WRITE(*,*)" everywhere... ...I, did that on those green screens in the 80's...)
 
GerritGroot said:
The only reason I use Microsoft's compiler, is because of their easy debugging mode and their colorful editor. As far as I know, debugging in gfortran can only be done in a very old-fashioned way, placing "WRITE(*,*)" everywhere...
Produces Micorsoft recently a Fortran compiler?
I searched a web, but found nothing. In the 90's I had a MS Fortran 5. This compiler was running on Windows, but in DOS-like IDE called Workbench.

It would not be generally so tragic with developing in gfortran or g95 on Windows as you think. There are some free tools available.
You can use for editing any of the free available editors, which supports syntax highlighting - im using vim.
For debugging I'm using gdb with GUI called insight. The complete binary insight-6.6-mingw.tar.bz2, which I have installed, is available here. But maybe there is newer version.
It's probably not so comfortable as your IDE, but I'm doing nothing big in Fortran.
For bigger projects I would try Photran.
 
Use the dependency walker and compare the offsets of the routines in both the original DLL and the new DLL. If they are the same then they are compatible and you can just do a DLL swap. If they are different, you might have to do the whole suite.

Sometimes you may think this is a painfully tedious task. The GNU suite has tedium saving tools for such things.

Also a lot depends on the company policies. Is it always full installs with no patches or do they allow patches. If it is always full installs, then don't worry about it. If they allow patches then you really need to keep the patches as small as possible otherwise you end up with some patch like VS2005 SP1 which in turn needs a patch on Windows 2003 before it can install.
 
Hi,

It took me long this time, I had a virus!! Well, not me, but my computer got the mexican flew.

Just wanted to say thanks to the last two posts.

@mikrom
Thanks for the editor info, I have been looking for that for quite some time.

@xwb
The DLL is supposed to be interchangeable, the commercial software provider wants the user to interchange them and describes what the dll should fullfill (in one page, without telling you how to make it...)

Regards,

Gerrit

P.S.: A patch that needs a patch... ...sounds like windows indeed. We need a windows compatible free OS (because the man on the street is unable to work with linux)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top