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

DLL export with bind C

DataScientist
Valued Contributor I
990 Views

Dear Fortranners, consider the following function,

module Square_mod
    implicit none
contains
    function getSquare(val) result(valSquared) bind(C, name="getSquare")
        !DEC$ ATTRIBUTES DLLEXPORT :: getSquare
        use iso_c_binding
        use, intrinsic :: iso_fortran_env, only: RK => real64
        real(RK), intent(in) :: val
        real(RK)             :: valSquared
        valSquared = val ** 2
    end function getSquare
end module Square_mod

Now generating the DLL,

ifort /dll Square_mod.f90 /exe:Square

and here is a program that can successfully call the function in the DLL,

program Square_prog
    use, intrinsic :: iso_fortran_env, only: RK => real64
    use Square_mod, only: getSquare
    implicit none
    real(RK) :: val = 100._RK
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSquare(val)
end program Square_prog

However, consider the following modified module that gives its function a different bind(c) name, getSq,

module Square_mod
    implicit none
contains
    function getSquare(val) result(valSquared) bind(C, name="getSq")
        !DEC$ ATTRIBUTES DLLEXPORT :: getSquare
        use iso_c_binding
        use, intrinsic :: iso_fortran_env, only: RK => real64
        real(RK), intent(in) :: val
        real(RK)             :: valSquared
        valSquared = val ** 2
    end function getSquare
end module Square_mod

Note that the only difference with the original function is in the binding name. I expected this function to be callable with the new name from the main program, but it is not,

program Square_prog
    use, intrinsic :: iso_fortran_env, only: RK => real64
    use Square_mod, only: getSq
    implicit none
    real(RK) :: val = 100._RK
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSq(val)
end program Square_prog

Why is ifort able to "use Square_mod, only: getSquare" in the first version with a binding name that is the same as the function name, while in the latter case with a binding name different from the function name, it gives the following compile error?

Square_prog.f90(3): error #6580: Name in only-list does not exist or is not accessible.   [GETSQ]
    use Square_mod, only: getSq
--------------------------^
Square_prog.f90(6): error #6404: This name does not have a type, and must have an explicit type.   [GETSQ]
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSq(val)
--------------------------------------------------------^
compilation aborted for Square_prog.f90 (code 1)

 

0 Kudos
5 Replies
DataScientist
Valued Contributor I
990 Views

I think I have got a reasonable understanding of the issue here. The following code, nicely compiles and runs,

program Square_prog
    use, intrinsic :: iso_fortran_env, only: RK => real64
    use Square_mod, only: getSquare
    implicit none
    real(RK) :: val = 100._RK
    interface
    function getSq(val) result(valSquared)
        !DEC$ ATTRIBUTES DLLIMPORT, DECORATE, ALIAS: 'getSq' :: getSq
        use, intrinsic :: iso_fortran_env, only: RK => real64
        real(RK), intent(in) :: val
        real(RK)             :: valSquared
    end function getSq
    end interface
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSquare(val)
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSq(val)
end program Square_prog

with the following module compiled to a DLL file that is used by the above program,

module Square_mod
    implicit none
contains
    function getSquare(val) result(valSquared) bind(C, name="getSq")
        !DEC$ ATTRIBUTES DLLEXPORT :: getSquare
        use, intrinsic :: iso_fortran_env, only: RK => real64
        real(RK), intent(in) :: val
        real(RK)             :: valSquared
        valSquared = val ** 2
    end function getSquare
end module Square_mod

So it seems like, when the bind name (getSq) and the DLLEXPORT (getSquare) name are different, both of them are exported, one as a module procedure which can be "USE"d in Fortran program, and the other as a global identifier "getSq" which can be called by other languages as well as Fortran, as done in the above example program.

0 Kudos
Steve_Lionel
Honored Contributor III
990 Views

I'd say that was a compiler bug. As you found, the ALIAS attribute works; BIND(C, NAME=) should work the same. Please submit a bug report to Intel Support. Ignore - see below.

0 Kudos
JVanB
Valued Contributor II
990 Views

I think the original program shouldn't compile. The binding label is just a name visible to the linker or in a *.DLL and isn't syntactically the same as the name of a function. You could use it in a Fortran program by explicitly linking to it (XXX in my example) or using LoadLibrary, GetProcAddress, and C_F_PROCPOINTER (YYY in my example, that I can't seem to get to work just now), but there is no way to just invoke it as a function.

program Square_prog
    use, intrinsic :: iso_fortran_env, only: RK => real64
    use Square_mod, only: getSquare
    use IFWIN, only: LoadLibrary, HANDLE, GetProcAddress
    use ISO_C_BINDING, only: C_FUNPTR, C_F_PROCPOINTER
    implicit none
    procedure(getSquare), bind(C,name='getSq') :: XXX
    integer(HANDLE) hModule
    type(C_FUNPTR) :: ProcAddress
    procedure(getSquare), pointer :: YYY
    real(RK) :: val = 100._RK
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", XXX(val)
    hModule = LoadLibrary('Square.dll'//achar(0))
    ProcAddress = transfer(GetProcAddress(hModule,'getSq'//achar(0)),ProcAddress)
    call C_F_PROCPOINTER(ProcAddress,YYY)
    write(*,"(*(g0.13,:,' '))") "Square of", val, "is", YYY(val)
end program Square_prog

 

0 Kudos
DataScientist
Valued Contributor I
990 Views

Thanks Repeat Offender, very interesting third approach, that I had no idea about. Any resources to learn more about IFWIN module would be greatly appreciated.

Steve, thanks. Which implementation is a bug in your opinion? original, or the alternative using `getSq` as the bind name, or the last solution in my response to the question (via DLLIMPORT)?

0 Kudos
Steve_Lionel
Honored Contributor III
990 Views

Sorry, I missed something again.

The original program shows correct behavior. As RO correctly says, you are not supposed to use the binding label for a reference in Fortran. getSq is not a Fortran identifier defined by the module. The compiler correctly DLLEXPORTs 'getSq' as the global symbol and correctly references it when you call getSquare in the main program. There is no compiler bug.

Your revised program that "works" basically does an end-run around the module interface. You should never have to declare an explicit interface to a Fortran routine (generics and submodules sometimes excepted.)

0 Kudos
Reply