- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I want to pass callback routines in to the DLL I have compiled from Fortran source. One of the parameters is already a pointer to an object that includes the pointers to the callback functions.
How do I dereference the function pointer fields from the pointer to the structure that was passed in? (The structure is actually an object but that shouldn't make any difference, right?)
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Are these TYPE(C_FUNPTR) function pointers? If so, use C_F_PROCPOINTER to associate a Fortran procedure pointer with the procedure address that is in the C_FUNPTR object.
(If they are already Fortran procedure pointers, then they are dereferenced automatically in any context that expects a procedure, e.g. if they are used as the procedure designator in a call statement or function statement.)
If they are the non-standard integer pointer (sometimes called cray pointers) things, then you use the POINTER(pointer, pointee) statement, where pointee is the name of a specific procedure that has been described in an interface block or similar.
(The standard C interoperability stuff (C_PTR, C_FUNPTR, etc) should be preferred to the integer pointer extension stuff.)
If you showed some code that might encourage more specific examples.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is the current interface spec for one of the functions I want the fortran DLL to be able to call. It is currently being passed to it by way of an additional pascal dll that essentiually duplicates the code that is in the main executable (also written in pascal)
interface
Integer FUNCTION FREF18(base,leg,i1,i2,ain)
!DEC$ ATTRIBUTES STDCALL, DLLIMPORT :: FREF18
!DEC$ ATTRIBUTES REFERENCE :: leg, ain
Integer base,i1,i2
Character leg*(*), ain*(*)
end FUNCTION
end interface
Then there is the association of the pointer with the function call to be used later in the code.
Pointer(pFREF18, FREF18)
The main entry point for the fortran DLL that is called from the main pascal executable looks like this:
SUBROUTINE CORETG(nfunci, erri, pblui)
!DEC$ ATTRIBUTES STDCALL, DLLEXPORT, REFERENCE,
1 ALIAS:'_CORETG' :: CORETG
The parameters are defined like this:
INTEGER*4 nfunci, erri, pblui
And part of the initial code in this function is the following:
dllaux = 'WTFuncAux.DLL'C
pdllDelphi = LoadLibrary(dllaux)
IF (pDLLDelphi.ne.NULL) THEN
pFREF18 = GetProcAddress(pDLLDelphi, 'FREF18'C)
Else ...
Then later there is a call to this function:
NRC = FREF18(BASE, BLOCK, I1, I2, AIN)
What I want to do is pass the pointer to the function FREF18 directly to the DLL function CORETG instead of using the intermediate DLL written in pascal that duplicates a lot of the code in the main program. (That seems wasteful and unnecessary and makes the job of maintaining program state more complicated.)
In the main code, FREF18 is declared like this:
function FREF18(Base: integer; leg: pansichar; i1, i2: integer; ainx: pansichar): Integer; stdcall;
And it has a normal looking definition which I won't bother to put here.
I hope that is detailed enough.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That Fortran code is using the integer pointer extension. For consistency I'll also use that below, but for new code I would prefer to use the standard F2003 C interoperability language features.
I don't know delphi from a bar of soap - so you will need to sort out what the interface of the modified Fortran procedure looks like to delphi, and how you go about acquiring the address of a procedure on the delphi side (@xxx?) - but the modifications to the Fortran code are quite straight forward. You need to add a dummy argument to allow the address of the procedure of interest to be passed, and delete the code that currently gets that address from the DLL.
SUBROUTINE CORETG(nfunci, erri, pblui, fun_iptr) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL, DLLEXPORT, REFERENCE, ALIAS:'_CORETG' :: CORETG INTEGER*4 :: nfunci, erri, pblui INTEGER(INT_PTR_KIND()), INTENT(IN) :: fun_iptr INTERFACE INTEGER FUNCTION FREF18(base, leg, i1, i2, ain) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL :: FREF18 !DEC$ ATTRIBUTES REFERENCE :: leg, ain INTEGER :: base,i1,i2 CHARACTER :: leg*(5), ain*(5) END FUNCTION FREF18 END INTERFACE POINTER(fun_iptr, FREF18) ... r = FREF18(base, leg, i1, i2, ain) END SUBROUTINE CORETG
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I tried that and now I am getting this:
error #6401: The attributes of this name conflict with those made accessible by a USE statement. [FREF18]
I should tell you that the interface spec you show above as defined within the scope of the subroutine CORETG is actually in my ase defined in a separate module which is included here with a USE statement. Other than that, I changed all my code to mirror what you show above. Is there something else maybe I am missing? I know you probably typed out your suggestion without trying to compile it so maybe there is something missing?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Remember - I can't see your code.
Bar some scope-local attributes not relevant here, once an entity is defined in a module its definition is complete - you can't then add attributes to the entity in a scope that accesses the entity via a use statement that fundamentally change the nature of the entity.
Without the integer POINTER statement the interface definition defines an external procedure identified externally by its name, but in combination with the integer POINTER statement it defines a procedure identified by the address in the given integer object.
Move the interface definition into the same scope that has the integer POINTER statement. If that creates problems for the organisation of your code elsewhere, then some other changes might be required, such as making FREF18
in the module a Fortran procedure pointer that gets associated to the address passed from delphi in CORETG. As part of these changes I would suggest abandoning the integer pointer extension, and moving to standard Fortran's C interoperability feature.
This was the original test case I compiled at the time using ifort 16.0.4. I don't have access to (or know how to write) delphi, so I mocked up a main program and the FREF18 function.
SUBROUTINE CORETG(nfunci, erri, pblui, fun_iptr) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL, DLLEXPORT, REFERENCE, ALIAS:'_CORETG' :: CORETG INTEGER*4 :: nfunci, erri, pblui INTEGER(INT_PTR_KIND()), INTENT(IN) :: fun_iptr INTERFACE INTEGER FUNCTION FREF18(base, leg, i1, i2, ain) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL :: FREF18 !DEC$ ATTRIBUTES REFERENCE :: leg, ain INTEGER :: base,i1,i2 CHARACTER :: leg*(5), ain*(5) END FUNCTION FREF18 END INTERFACE POINTER(fun_iptr, FREF18) INTEGER :: base, i1, i2, r CHARACTER(5) :: leg, ain ! Dummy values. base = nfunci i1 = erri i2 = pblui leg = 'leg' ain = 'ain' r = FREF18(base, leg, i1, i2, ain) END SUBROUTINE CORETG MODULE m IMPLICIT NONE CONTAINS INTEGER FUNCTION FREF18(base, leg, i1, i2, ain) !DEC$ ATTRIBUTES STDCALL :: FREF18 INTEGER :: base, i1, i2 CHARACTER :: leg*(5), ain*(5) !DEC$ ATTRIBUTES REFERENCE :: leg, ain PRINT *, 'Hello' FREF18 = 0 END FUNCTION FREF18 END MODULE m PROGRAM you_have_to_write_this_bit_in_delphi USE m IMPLICIT NONE INTERFACE SUBROUTINE CORETG(nfunci, erri, pblui, fun_iptr) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL, REFERENCE, ALIAS:'_CORETG' :: CORETG INTEGER*4 :: nfunci, erri, pblui INTEGER(INT_PTR_KIND()), INTENT(IN) :: fun_iptr END SUBROUTINE CORETG END INTERFACE INTEGER*4 :: nfunci, erri, pblui ! Dummy values. nfunci = 1 erri = 2 pblui = 3 CALL CORETG(nfunci, erri, pblui, LOC(FREF18)) END PROGRAM you_have_to_write_this_bit_in_delphi
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am having no luck.
The parameter passed to the CORETG function from the pascal code seems right to me. When I look at it from the fortran debugger, the other parameters have the values I expect. But it looks like the function pointer has, instead of the address of the function, the value of the first four bytes of the code at that address. It calls the function and throws an access violation exception, which I would expect because the first four bytes interpreted as an address are way outside valid memory range!
I suspect the problem is on the calling side, and not the called side. Any ideas? Anyone?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
With the attributes specified for the CORETG
procedure, the thing passed for the fun_iptr argument should be the address of the address of the procedure to call, i.e. a pointer to a pointer, if delphi has that sort of concept.
There is a redundant level of indirection with that argument. Ordinarily I would expect that could be removed by applying ATTRIBUTES VALUE to the fun_iptr argument, but in this instance I cannot get it to work. From investigating the generated assembly it appears that the procedure reference is constructed still assuming the integer argument is passed by reference. Perhaps other have better ideas (delphi-with value, compiled on x86 using /debug /check:all /warn:all /Od), my !DEC$ limit has been reached for the day.
But I might have made this more complicated than it needs to be. If you are just passing a few procedures, then you can pretty much just specify each procedure directly as a dummy argument. Try the following Fortran variant, which should just require a single level of indirection on the delphi side.
SUBROUTINE CORETG(nfunci, erri, pblui, FREF18) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL, DLLEXPORT, REFERENCE, ALIAS:'_CORETG' :: CORETG INTEGER*4 :: nfunci, erri, pblui INTERFACE INTEGER FUNCTION FREF18(base, leg, i1, i2, ain) IMPLICIT NONE !DEC$ ATTRIBUTES STDCALL :: FREF18 INTEGER :: base, i1, i2 CHARACTER :: leg*(5), ain*(5) !DEC$ ATTRIBUTES REFERENCE :: leg, ain END FUNCTION FREF18 END INTERFACE ... END SUBROUTINE CORETG
If you have lots of procedures, then you may need to consider passing an array of procedure pointers - which will look like an array of pointer sized integers or an array of C_FUNPTR objects across the EXE/DLL boundary.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page