If I'm understanding you correctly, you'd like to write a Perl binding to a Fortran lib containing a function such as

real function evapol(tu,nu,tv,nv,c,rad,x,y)

from Dierckx's fitpack, where rad is the user-defined callback routine in question, which the user of the Perl binding of course wants to write in Perl

c rad : real function subprogram, defining the boundary of the c approximation domain. must be declared external in the c calling (sub)-program

Although this is possible, it's a bit involved, as the flow of execution has to go through a layer of C glue code in both directions...

Anyway, I tried to put together a minimal example, which will hopefully get you started. Unfortunately, it's still rather lengthy...  I've tested it with the g77 GNU Fortran compiler, but things should in principle work similarly with other compilers.

Let's assume we have the following Fortran code

double precision function myfunc(x, y, func) double precision x, y, z, v double precision func v = 1.5 z = func(v) myfunc = x * y * z end

This defines a function myfunc which we're going to call from Perl. It takes three arguments, the last one of which is normally expected to be a reference to a user-defined Fortran function (declared as external in the calling Fortran code). The latter function is supposed to be implemented as a user-defined Perl routine here.

Ok, the first step is to compile the Fortran code:

$ g77 -c myfunc.f -o myfunc.o

The resulting object file myfunc.o is what you need to link the Perl extension against.

Next, create a project working directory for the extension and populate it with the necessary files. There are several ways to do this... one of them would be to use h2xs. For example

$ h2xs -Afn P2f2p

where "P2f2p" is the name of the extension ("Perl-to-Fortran-to-Perl" — couldn't think of a better name). This creates several files and directories, most importantly P2f2p.xs and Makefile.PL.

However, P2f2p.xs is just a default stub file like this

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = P2f2p PACKAGE = P2f2p

so you need to modify it to actually do something:

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" static SV * userfunc; double userfunc_wrapper(double *x) { int n; double r; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSVnv(*x))); PUTBACK; n = call_sv(userfunc, G_SCALAR); SPAGAIN; if (n != 1) croak("wrong number of args returned!\n"); r = POPn; PUTBACK; FREETMPS; LEAVE; return r; } // the Fortran function double myfunc_(double *x, double *y, void* func); double myfunc(double x, double y) { double r; r = myfunc_(&x, &y, &userfunc_wrapper); return r; } MODULE = P2f2p PACKAGE = P2f2p double myfunc(x, y, ufunc) double x double y SV * ufunc CODE: userfunc = newSVsv(ufunc); RETVAL = myfunc(x, y); OUTPUT: RETVAL

(To keep things as concise as possible, I chose to put all code in this .xs file... In a larger project, you could of course do it proPerly and put stuff in separate .h/.c files...)

Then, modify Makefile.PL to have the generated Makefile issue commands to link against the Fortran object (myfunc.o) and g77 runtime support lib (libg2c.so), i.e. change

LIBS => [''], # e.g., '-lm' OBJECT => '$(O_FILES)', # link all the C files too

in WriteMakefile(...) to read:

LIBS => ['-lg2c -lm'], # e.g., '-lm' OBJECT => '$(O_FILES) myfunc.o', # link all the C files + too

Lastly, don't forget to copy myfunc.o into the P2f2p project directory.

Having completed those steps, you're ready to do the usual

perl Makefile.PL make make test

(The make test isn't doing much — without any other tests having been written, it just checks if the extension can be loaded.)

Now, if you're like many people, you probably don't want to install the newly created extension system-wide yet, in order to just play around with it. Of course, you could've messed with PREFIX= and friends, but the easiest way might be to simply copy the important two files into some testing directory:

$ mkdir -p /path/to/working/directory/auto/P2f2p $ cp blib/arch/auto/P2f2p/P2f2p.so /path/to/working/directory/auto/P2f +2p/ $ cp blib/lib/P2f2p.pm /path/to/working/directory/

Then you can write a little test script (and place it in the same test directory):

#!/usr/bin/perl use P2f2p; sub userfunc { my $v = shift; print "userfunc()\n called from Fortran with arg: $v\n"; my $r = $v + 0.5; print " returning: $r\n"; return $r; } my $r = P2f2p::myfunc(3, 7, \&userfunc ); printf "myfunc() returned: %.2f\n", $r;

When you run it, it should print

userfunc() called from Fortran with arg: 1.5 returning: 2 myfunc() returned: 42.00

So what's happening in detail?  P2f2p::myfunc is calling XS_P2f2p_myfunc under the hood, which is the XS wrapper function as expanded by xsubpp. This function stores the reference to the passed in user-defined Perl routine, and then calls myfunc, which in turn calls the actual Fortran function myfunc_ (note the underscore, as automatically appended by g77), passing it a pointer to the callback wrapper function userfunc_wrapper. The wrapper is responsible for calling the real Perl routine (via call_sv) and handling its arguments (see perlcall for what all those macros are doing). It is being called from within the Fortran code, and is expected to return some value computed by Perl.  (I'll leave it as an exercise for the reader to figure out how this all results in the final 42.)  Good luck!


In reply to Re: perl calling FORTRAN calling perl? by almut
in thread perl calling FORTRAN calling perl? by Anonymous Monk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.