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

Callforward from DLL

mattsdad
Beginner
911 Views
Is it possible to pass a function pointer to a dll and use it to call a routine in the .exe?

Or is there a way to do anything similar?

I have a simulator kernal that makes calls to callback routines in a user provided dll. I do not know which variables these callback routines need from the kernal. So, I want to provide a function that the dll can call that is in the simulator kernal to get the data it needs.
0 Kudos
7 Replies
Steven_L_Intel1
Employee
911 Views
Sure, you can do this. Just make sure the DLL uses the correct calling convention (if it's Fortran, that's easy.) The simple approach is to declare the routine EXTERNAL and the dummy argument EXTERNAL.
0 Kudos
mattsdad
Beginner
911 Views
Sure, you can do this. Just make sure the DLL uses the correct calling convention (if it's Fortran, that's easy.) The simple approach is to declare the routine EXTERNAL and the dummy argument EXTERNAL.

I tried that and it is insufficient for my needs. I do not simply want to call a function that I pass to the dll through the argument list of the function that would be using it. What I want is to register it to a pointer in the dll and call it from any routine in the dll.

For instance (For logging messages):

MODULE Routine_Registration_m

INTERFACE
SUBROUTINE messenger (message, i_scalar, r_scalar)
CHARACTER(LEN=*), INTENT(IN) :: message ! text, message to be written
INTEGER(KIND=4), OPTIONAL, INTENT(IN) :: i_scalar ! integer one-dimensional array to be written
REAL(KIND=8), OPTIONAL, INTENT(IN):: r_scalar ! real variable to be written
END SUBROUTINE messenger
END INTERFACE
POINTER (p_Messenger, Messenger)

END MODULE Routine_Registration_m


SUBROUTINE Routine_Registration(Msg)
USE Routine_Registration_m
IMPLICIT NONE
INTERFACE
SUBROUTINE Msg (message, i_scalar, r_scalar)
CHARACTER(LEN=*), INTENT(IN) :: message ! text, message to be written
INTEGER(KIND=4), OPTIONAL, INTENT(IN) :: i_scalar ! integer one-dimensional array to be written
REAL(KIND=8), OPTIONAL, INTENT(IN):: r_scalar ! real variable to be written
END SUBROUTINE
END INTERFACE
EXTERNAL :: Msg

p_Messenger = Msg

END SUBROUTINE Routine_Registration


But that doesn't compile because Msg is on the right of an assignment.
Also I am having trouble getting the EXTERNAL statement attached to a subroutine inside a module contains area.
0 Kudos
Steven_L_Intel1
Employee
911 Views
Ok - I didn't understand everything you wanted.

Here is a complete, working example that demonstrates using Fortran 2003 procedure pointers and abstract interfaces. You'll need 11.0 or later for this. I cheated a bit and made My_Messenger a contained procedure. Fortran 2003 doesn't allow you to pass this as an actual argument, but Fortran 2008 (and Intel Fortran) does. I moved Routine_Registration into the module because I figured this would be part of the DLL. It doesn't HAVE to be, but it makes more sense there.

Let me know if you have further questions.

[plain]MODULE Routine_Registration_m

ABSTRACT INTERFACE
SUBROUTINE messenger (message, i_scalar, r_scalar)
CHARACTER(LEN=*), INTENT(IN) :: message ! text, message to be written
INTEGER(KIND=4), OPTIONAL, INTENT(IN) :: i_scalar ! integer one-dimensional array to be written
REAL(KIND=8), OPTIONAL, INTENT(IN):: r_scalar ! real variable to be written
END SUBROUTINE messenger
END INTERFACE
PROCEDURE(messenger), POINTER :: p_Messenger

CONTAINS

SUBROUTINE Routine_Registration(Msg)
IMPLICIT NONE
PROCEDURE(messenger) :: Msg

p_Messenger => Msg
END SUBROUTINE Routine_Registration

SUBROUTINE Call_Registration

CALL p_messenger ('This is a message', 1, 2.0_8)
END SUBROUTINE Call_Registration

END MODULE Routine_Registration_m


program test
USE Routine_Registration_m

CALL Routine_Registration(My_Messenger)
CALL Call_Registration

CONTAINS
SUBROUTINE My_Messenger (message, i_scalar, r_scalar)
CHARACTER(LEN=*), INTENT(IN) :: message ! text, message to be written
INTEGER(KIND=4), OPTIONAL, INTENT(IN) :: i_scalar ! integer one-dimensional array to be written
REAL(KIND=8), OPTIONAL, INTENT(IN):: r_scalar ! real variable to be written

PRINT *, message, i_scalar, r_scalar
END SUBROUTINE My_Messenger

END PROGRAM TEST[/plain]

0 Kudos
mattsdad
Beginner
911 Views
Ok - I didn't understand everything you wanted.

The example uses Fortran 2003 procedure pointers and abstract interfaces. You'll need 11.0 or later for this. I cheated a bit and made My_Messenger a contained procedure. Fortran 2003 doesn't allow you to pass this as an actual argument, but Fortran 2008 (and Intel Fortran) does. I moved Routine_Registration into the module because I figured this would be part of the DLL. It doesn't HAVE to be, but it makes more sense there.

Let me know if you have further questions.


Since the Fortran 2008 Standard is not yet officially approved, Do you have a version that works without the Intel extensions or 2008 Standard features? (My product must be portable to systems that you do not make a compiler for.)
0 Kudos
Steven_L_Intel1
Employee
911 Views
Just move the callback procedure into a module and USE the module from the main program.

I will warn you that some other compilers may not support procedure pointers, and the "integer pointer" extension you tried to use before is also not widely implemented for procedures.
0 Kudos
mattsdad
Beginner
911 Views
Thanks!

I have had problems separating your example into a DLL and a kernal part. The copy of the dll_m module in the kernal will not allow the"PROCEDURE(messenger) :: Msg" in the Routine_Registrationinternfaceto compile.

MODULE dll_m

ABSTRACT INTERFACE
SUBROUTINE messenger (message, i_scalar, r_scalar)
CHARACTER(LEN=*), INTENT(IN) :: message ! text, message to be written
INTEGER(KIND=4), OPTIONAL, INTENT(IN) :: i_scalar ! integer one-dimensional array to be written
REAL(KIND=8), OPTIONAL, INTENT(IN):: r_scalar ! real variable to be written
END SUBROUTINE messenger
END INTERFACE

ABSTRACT INTERFACE
SUBROUTINE Routine_Registration(Msg)
PROCEDURE(messenger) :: Msg
END SUBROUTINE Routine_Registration
END INTERFACE

ABSTRACT INTERFACE
SUBROUTINE Call_Test()
END SUBROUTINE Call_Test
END INTERFACE

END MODULE dll_m


error #8169: The specified interface is not declared. [MESSENGER]
0 Kudos
Steven_L_Intel1
Employee
911 Views

1. Remove "ABSTRACT" from the Routine_Registration and Call_Test declarations. ABSTRACT INTERFACE is when you want to define a procedure interface without an actual corresponding external procedure. I don't think you really want these as INTERFACE blocks at all - they should be module procedures.

2. Add IMPORT following "SUBROUTINE RoutineRegistration(msg)" to make the declaration of messenger visible inside the interface block (assuming you keep it as an interface block.) See Domestic or Imported
0 Kudos
Reply