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

Generic Procedure export problem in creating DLL

chshaoning
Beginner
1,201 Views
In our project we have created a generic procedure like this:

INTERFACE SET_VARIABLE

MODULE PROCEDURE SET_VARIABLE_REAL

MODULE PROCEDURE SET_VARIABLE_INTEGER

MODULE PROCEDURE SET_VARIABLE_REAL_ARRAY

MODULE PROCEDURE SET_VARIABLE_INTEGER_ARRAY

END INTERFACE SET_VARIABLE
this interface is defined in a Module and also the module procedure. We want to export the SET_VARIABLE as the dll-function for main_programm. But we don't know how. I want to know if it's possible to export it and HOW?

0 Kudos
10 Replies
mecej4
Honored Contributor III
1,201 Views
When the compiler processes Fortran code that calls a procedure by its generic name, it resolves the specific name to be used by examining the types of the arguments. Therefore, there need to be exported routines in the DLL matching all the specific names that are needed to satisfy the calls. One (and only one) of the specific names is allowed to coincide with the generic name.

For example, with the four specific subroutines that are involved in your example, withe module named as SETVMOD, the following module definition file will work.

[bash]LIBRARY mproc.dll
EXPORTS
SETVMOD_mp_SET_VARIABLE_INTEGER
SETVMOD_mp_SET_VARIABLE_INTEGER_ARRAY
SETVMOD_mp_SET_VARIABLE_REAL
SETVMOD_mp_SET_VARIABLE_REAL_ARRAY
[/bash]
0 Kudos
chshaoning
Beginner
1,201 Views
hi mecej4:

I tried your methode. But it semms not working. I tried to use the general name "SET_VARIABLE", but i got a bug.
0 Kudos
mecej4
Honored Contributor III
1,201 Views
It certainly works. If you show what you tried and what the error messages were, we could get things working. Simply saying "I got a bug" takes us nowhere.
0 Kudos
chshaoning
Beginner
1,201 Views
Thank you for the answer.
OK. I try to explain my problem in detail.
As in the question mentioned we put a INTERFACE in a MODULE for different types of parameter. The MODULE PROCEDURE are defined in the MODULE as PRAVITE. I defined in the .def file the EXPORTS as below.
...

EXPORTS
SET_VARIABLE_REAL = M_ID_AGMA2101_mp_SET_VARIABLE_REAL

SET_VARIABLE_INTEGER = M_ID_AGMA2101_mp_SET_VARIABLE_INTEGER

SET_VARIABLE_REAL_ARRAY = M_ID_AGMA2101_mp_SET_VARIABLE_REAL_ARRAY

SET_VARIABLE_INTEGER_ARRAY = M_ID_AGMA2101_mp_SET_VARIABLE_INTEGER_ARRAY

...
In a MODULE of the main programm I try to call the exported functions directly use CALL SET_VARIABLE without using a !DEC$ ATTRIBUTE DLLIMPORT :: [the function Name]. And I got the error at Compiling like this:
Fehler1 error LNK2019: Verweis auf nicht aufgelstes externes Symbol "_SET_VARIABLE" in Funktion "_M_IF_AGMA2101_mp_IF_AGMA2101".M_IF_AGMA2101.obj
And then I tried another way. I puted a INTERFACE in the MODULE of the main programm like this:

INTERFACE
SUBROUTINE SET_VARIABLE_INTEGER(NM_VARIABLE,VL_VARIABLE)
!DEC$ ATTRIBUTES DLLIMPORT :: SET_VARIABLE_INTEGER
END SUBROUTINE SET_VARIABLE_INTEGER
SUBROUTINE SET_VARIABLE_REAL(NM_VARIABLE,VL_VARIABLE)
!DEC$ ATTRIBUTES DLLIMPORT :: SET_VARIABLE_REAL
END SUBROUTINE
...
END INTERFACE

and I got same error like before. At last I tried to put SET_VARIABLE behind the INTERFACE keyword and got the error below.
Fehler1 error #6285: There is no matching specific subroutine for this generic subroutine call. [SET_VARIABLE]

I think I made somewhere a mistake. Can you tell me the right way to do that?
0 Kudos
mecej4
Honored Contributor III
1,201 Views
I think that you ran into problems because the substitution of generic names by specific names, name substitution by DEC$ directives and name substitution through the .DEF all have the potential to interact and, if not done correctly and in the proper order, will cause clashes.

Here is a simpler solution, which does not need renaming DLL symbols or directives.

First, the source for building the DLL and the .mod file, mproc.f90:

[fortran]module setvmod
  INTERFACE SET_VARIABLE
    module procedure SET_VARIABLE_REAL
    module procedure SET_VARIABLE
    module procedure SET_VARIABLE_REAL_ARRAY
    module procedure SET_VARIABLE_INTEGER_ARRAY
  END INTERFACE
contains
  subroutine set_variable(x,v)
  integer,intent(out) :: x
  integer,intent(in) :: v
  x=v
  return
  end subroutine set_variable

  subroutine set_variable_real(x,v)
  real,intent(out) :: x
  real,intent(in) :: v
  x=v
  return
  end subroutine set_variable_real

  subroutine set_variable_integer_array(x,v)
  integer,intent(out) :: x(:)
  integer,intent(in) :: v(:)
  x=v
  return
  end subroutine set_variable_integer_array

  subroutine set_variable_real_array(x,v)
  real,intent(out) :: x(:)
  real,intent(in) :: v(:)
  x=v
  return
  end subroutine set_variable_real_array
end module setvmod
[/fortran]
The .def file, mproc.def :

[bash]LIBRARY mproc.dll
EXPORTS
SETVMOD_mp_SET_VARIABLE
SETVMOD_mp_SET_VARIABLE_INTEGER_ARRAY
SETVMOD_mp_SET_VARIABLE_REAL
SETVMOD_mp_SET_VARIABLE_REAL_ARRAY
[/bash]
Building the DLL and the import library for it:

[bash]s:> lib /def:mproc.def /machine:i386
s:> ifort /LD mproc.f90 mproc.exp
[/bash]
The main program which uses the module and will call the DLL for the module procedures, tmproc.f90:

[fortran]program tmproc
use setvmod
integer :: i,ia(5),j,ja(5)
real :: x,xa(5),y,ya(5)

do i=1,5
  ia(i)=i*2-3
  xa(i)=i*2.5-4.5
end do

j=3; call set_variable(i,j); write(*,*)i
call set_variable(ja,ia); write(*,*)ja
y=3.5; call set_variable(x,y); write(*,*)x
call set_variable(ya,xa); write(*,*)ya
end program tmproc
[/fortran]
Build and run the program:
[bash]s:LANG>ifort tmproc.f90 mproc.lib
s:LANG>tmproc
           3
          -1           1           3           5           7
   3.500000
  -2.000000      0.5000000       3.000000       5.500000       8.000000 [/bash]
0 Kudos
IanH
Honored Contributor III
1,201 Views
Quoting chshaoning
...
In a MODULE of the main programm I try to call the exported functions directly use CALL SET_VARIABLE without using a !DEC$ ATTRIBUTE DLLIMPORT :: [the function Name]. And I got the error at Compiling like this:
Fehler1 error LNK2019: Verweis auf nicht aufgelstes externes Symbol "_SET_VARIABLE" in Funktion "_M_IF_AGMA2101_mp_IF_AGMA2101".M_IF_AGMA2101.obj

This mising symbol isn't decorated with the _MODULENAME_mp_ prefix, which indicates that the compiler thinks that procedure represented by the symbol is just a normal external procedure (not a module procedure). As per mecej4's example, the SET_VARIABLE interface needs to be PUBLIC in the module that defines it (everything in the module in his example is PUBLIC by default) and you need the appropriate USE M_ID_AGMA2101 statement in the scope where CALL SET_VARIABLE appears.

Note that it is illegal code to separately specify an interface for a module procedure (like you've done in the second stretch of code in post four with the subroutine statements inside the interface block). The interfaces for a module procedure must only be accessed by a USE statement.

Rather than tying my code to a particular compiler's name mangling scheme, I prefer to use the BIND suffix on the definitions of the individial procedures to give the procedure a linker name of my choosing. If the procedure has an external linker name, making it private seems a bit counter-intuitive to me, but I guess it has to work.

0 Kudos
mecej4
Honored Contributor III
1,201 Views
There was no mention of PUBLIC/PRIVATE before your reply, so I am somewhat puzzled by why you brought it up.

Anyway, the accessibility of the generic routine is separate from the accessibility of the specific routines, so one could add
[fortran]  private
  public :: set_variable
[/fortran]
after Line-1 in #5. The linker, however, usually does not inherit (courtesy of the compiler) PUBLIC/PRIVATE attributes, so the module definition file needs to contain entries for even the private procedure names.
0 Kudos
IanH
Honored Contributor III
1,201 Views
Agreed - it's just the OP had said that he'd marked the specific procedures as private (in post 4). If he's done that by setting the default accessibility for the module to private, but then forgotten to mark the generic as public, you'd get the symptoms that he's describing (you'd also get them if you simply forgot to USE the defining module).

Marking procedures that must be exported in a DLL private bothers me a little because you've got supposedly internal implementation detail (private procedure names and entry points) "visible" to the outside world. Normally such externally visible interfaces (in the non-fortran sense) to bits of code need to be well controlled - changes to the name or argument list require everything that dynamically links to the interface to be recompiled. When something is private to a module I take it as a cue that I can interfere with it within the confines of the module as much as I please, but that's not the case here.

(Some compilers do not emit a external link name for private entities when they are sure that the procedure can not be referenced from outside the module (not the case here with ifort's implementation - if the generic interface is public then the private procedure must have an external link name). Some compilers (including ifort at one stage, might be fixed now) omit the external name in corner cases where it is still required, causing link errors.)
0 Kudos
Steven_L_Intel1
Employee
1,201 Views
It's fine to have the specific procedures private - the compiler has to know how to deal with that. Accessibility is about whether source code can reference the name, not whether it is invisible to the compiler. You do, however,
have to DLLEXPORT all the specific procedures that may be referenced in a DLL.
0 Kudos
chshaoning
Beginner
1,201 Views
Thanks to all. With your professional answer I solved my problem.
0 Kudos
Reply