Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29243 Discussions

Can a call to a subroutine be specified in F95 at run time?

j0e
New Contributor I
1,334 Views
Hi All,
I'd like to have a program call a subroutine that is specified at run from user (or file) input. That is, something like this.
character subname*20
read(5,*) subname
call subname (...)
The above obviously won't work, but can it be made to work in principle using newer features of F95?
The procedure 'subname' would be from a list of routines that all have the same argument list (that is, an abstract interface could be used).
I've been playing around a little with procedure pointers, but this is all new to me and I don't even know if it is possible.
Thanks for any help!
-joe
0 Kudos
8 Replies
mecej4
Honored Contributor III
1,334 Views
What you want to do, if taken literally, is incompatible with the properties of a compiled language. Even if it can be done, it is probably not worth the effort and, at this point, beyond your reach unless you know interlanguage calling conventions, C-interoperability and DLL loading.

If you can, settle for something less ambitious: to choose one out of a set of known, precompiled and linked subroutines/functions, here is how

[fortran]integer, parameter :: NSUBS =10
character(len=5):: subname, knowns(NSUBS)=(/'name1','name2',.../)
...
read(5,*) subname

! Find index of selected subroutine

do i=1,NSUBS
   if(subname.eq.knowns(i))exit
end do
select case(i)
   case (1 )
     call sub1(arg1,...)
   case (2)
     call sub2(arg1,...)
   ...
   case(NSUBS+1)
     call error('Desired subroutine not provided')
end select[/fortran]
0 Kudos
John4
Valued Contributor I
1,334 Views

Well, it can be done through dynamic loading, in which case, in order to lookup the symbol, you either have to keep in mind the compiler's rules of mangling, or override the linking name (through the ALIAS attribute or BIND(C)). After that, you use C_F_PROCPOINTER to convert the C pointer to a Fortran procedure pointer.

I agree with mecej4 in that it's probably not worth the effort in your case.

0 Kudos
Arjen_Markus
Honored Contributor II
1,334 Views

You can find an example of how that can be done in my Flibs project - http://flibs.sf.net, look for the "src/dynlib" directory in the repository. Whether it is worth the trouble, is another matter :).

If the routines you want to call are simply part of your program, you might do:

read(10,*) name

select case (name)

case('sub1')

call sub1(..)

case('sub2')

call sub2(..)

case default

write(*,*) 'Unknown routine: ', name

end select

Or store the names and the pointers in a derived type:

type name_routine

character(len=32) :: name

procedure, pointer :: routine

end type name_routine

and initialise an array of such a type with the names and the pointers to these routines.


Regards,

Arjen

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,334 Views
As mecj4 and John have pointed out you can use the DLL (Dynamic Loaded Library) functions on Windows. This requires your subroutines to be located in a DLL which you create. The advantage of this is the main PROGRAM need not know the subroutine to be called in advance. It does need to be provided at run time the name of the DLL library and subroutine entry point name. Their are a few different ways of doing this

[cpp]// C code example
typedef BOOL (WINAPI *LPFN_GLPI)(
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
    PDWORD);
...
    LPFN_GLPI Glpi = NULL;
...
    void	DoGlpi(LPFN_GLPI Glpi);
...
    Glpi = (LPFN_GLPI)GetProcAddress(
                GetModuleHandle(TEXT("kernel32")), // library name
                "GetLogicalProcessorInformation"); // function name
...
    if(Glpi != NULL) DoGlpi(Glpi);
[/cpp]

You can make those calls from FORTRAN

Now then, if you want to have all the subroutines compiled in/with the main application then your programarchetecture is more that of an interpreter. In this case your main program woud, at start, initialize a table containing the list of subroutine names and entry points. Then this list is consulted (in your interpreter loop).

Jim Dempsey
0 Kudos
j0e
New Contributor I
1,334 Views
Thanks everyone for your fast and detailed responses! My initial guess was that if it was at all possible, it would not be straight forward, and this appears to be the case, but I wanted to make sure there wasn't something I was missing in the newer releases of Fortran.

My objective was to see if there would be an easy way of adding new routines without having to modify various parts of the code manually. While it looks this may be technically possible, it appears to add more complexity than it is worth, at least for my needs.

Thanks again, it this saved me a lot of time exploring a dead end (for what I need).
cheers,
-joe
0 Kudos
Arjen_Markus
Honored Contributor II
1,334 Views
Well, what you could do as a kind of intermediate solution is to put the select case that I posted
into a DLL together with all the subroutines you want to call that way.

Using this technique your overall application does not need to change, only the DLL, if you add
a new feature.

Regards,

Arjen
0 Kudos
j0e
New Contributor I
1,334 Views
Yes, I may implement something like you (or possiblymecej4) describe. This part of the code is not that critical, but I was curious if (and how) it might be done, so the ideas presented are useful.
0 Kudos
John4
Valued Contributor I
1,334 Views

As mecj4 and John have pointed out you can use the DLL (Dynamic Loaded Library) functions on Windows. This requires your subroutines to be located in a DLL which you create. The advantage of this is the main PROGRAM need not know the subroutine to be called in advance. It does need to be provided at run time the name of the DLL library and subroutine entry point name.

Just to clarify what I mentioned before, the link in my previous post points to a section in the dynamic loading Wikipedia page that explains how you can access the symbol table of the running process ---i.e., there's no need to put everything in a DLL or shared object.

0 Kudos
Reply