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

c++ function pointer

Martin__Paul
New Contributor I
2,282 Views
I have a C++ function which is called by Fortran. The function allocates a pointer to a dll function, casting the function pointer to stdcall or cdecl as required. Is there a way that I can pass the function pointer back to Fortran, so I only need to do the cast/GetProcAddress once, and the caller (Fortran) maintains the dll handle thereafter? This would be convenient as the handle could then be passed to any other C++ dlls without having to call loadlibrary again.

typedef void (__stdcall *f_func_type_stdcall)(float*, unsigned char[]);
typedef void (__cdecl *f_func_type_cdecl)(float*, unsigned char[]);

f_func_type_stdcall pfunc_stdcall = NULL;
f_func_type_cdecl pfunc_cdecl = NULL;

int LoadDLL (const string& dllname, const bool convCdecl)
{
HMODULE mydll = NULL;
mydll = LoadLibrary(dllname.c_str());

if (mydll)
{
if (conv)
{
pfunc_cdecl = (f_func_type_cdecl) GetProcAddress(mydll, "FUNCNAME");
}
else
{
pfunc_stdcall = (f_func_type_stdcall) GetProcAddress(mydll, "_FUNCNAME@8");
}
}
return(0);
}

What type should I pass from Fortran to contain the value of f_func_type_cdecl or f_func_type_stdcall? I couldn't find an equivalent to void*, and have tried various permutation of INT(KIND=4) and int POINTER without success.
0 Kudos
7 Replies
Steven_L_Intel1
Employee
2,282 Views

The type C_FUNPTR from ISO_C_BINDING would be the appropriate type to pass and return the function pointer from C. You could then use C_F_PROCPTR to create a Fortran procedure pointer that can call the function.

As for void *, there is no current standard way to express this in Fortran, though one has been proposed. If you use C_F_PROCPTR, you can convert the C pointer to a Fortran function pointer of whatever type you want.

To get STDCALL, you will need to insert !DEC$ ATTRIBUTES STDCALL into the abstract interface for the procedure pointer (see sample below).

[plain]abstract interface
  subroutine subint (x)
  !dec$ attributes stdcall :: subint
  real x
  end subroutine subint
end interface

procedure(subint), pointer :: p[/plain]

0 Kudos
Martin__Paul
New Contributor I
2,282 Views

Thanks. I have declared an interface with a C_FUNPTR parameter, although when control returns to Fortran after calling the dll, the variable dfunc shows "undefined address".

INTERFACE
FUNCTION FuncInit [C, ALIAS: '_FuncInit'] (sArr, inFile, outFile, conv, dfunc)
import :: C_FUNPTR
INTEGER FuncInit
REAL sArr [REFERENCE]
INTEGER(KIND=1) inFile [REFERENCE]
INTEGER(KIND=1) outFile [REFERENCE]
CHARACTER conv [REFERENCE]
TYPE(C_FUNPTR) dfunc [REFERENCE]
END
END INTERFACE

INTEGER(KIND=1) L_INI(256), L_OUTI(1024)
CHARACTER L_INC*256, L_OUTC*1024
EQUIVALENCE (L_INI, L_INC), (L_OUTI, L_OUTC)

TYPE(C_FUNPTR) :: C_DFUNC = C_NULL_FUNPTR

INTEGER retVal

!INFILE, OUTNAME, C_CONV, R_ARR populated elsewhere

L_INC = INFILE
L_OUTC = OUTNAME

retVal = FuncInit(R_ARR(1),L_INI(1),L_OUTI(1),C_CONV,C_DFUNC)

---------

Callee:


typedef void (__stdcall *f_func_type_stdcall)(float*, unsigned char[], unsigned char[]);
typedef void (__cdecl *f_func_type_cdecl)(float*, unsigned char[], unsigned char[]);

f_func_type_stdcall pfunc_stdcall = NULL;
f_func_type_cdecl pfunc_cdecl = NULL;

int FuncInit(float *rs, unsigned char infile[], unsigned char outfile[], char callingConv[], void (*inptr)(float*, unsigned char[], unsigned char[]))
{
//Calls function to set calling convention...

// set inptr = function ptr
inptr = pfunc_cdecl;

return(0);
}

After the C++ call C_DFUNC contains undefined address. What can I do to ensure this value is correctly passed back?

Thanks.

0 Kudos
Steven_L_Intel1
Employee
2,282 Views
First, I'll rap your knuckles with a ruler for using obsolete, undocumented Microsoft Fortran PowerStation syntax in your interface block. Please replace this with the standard Fortran 2003 syntax. You can remove the [REFERENCE] tags and replace the function declaration with:

FUNCTION FuncInit (sArr, inFile, outFile, conv, dfunc) BIND(C,NAME='FuncInit')

The problem you have is on the C side. The assignment to inptr isn't doing what you want - I'm not enough of a C expert to understand why. The Fortran code is passing the correct thing.
0 Kudos
IanH
Honored Contributor III
2,282 Views
Your C function is taking the pointer-to-function argument by value, so changes made to the argument are local to the function. You want it to take a pointer to a pointer-to-function (which is what the Fortran code is passing dfunc as unless you've given the relevant argument the VALUE attribute) so it can change what the pointer-to-function points to in the calling routine.

Whack an extra level of indirection in on the C inptr argument declaration and the appropriate *inptr dereference on the left hand side of the C inptr assignment expression and see how you go. Alternatively, make the C function return the pointer-to-function. Similar level-of-indirection issue (but going the other way) discussed here.


0 Kudos
Martin__Paul
New Contributor I
2,282 Views

Steve - lesson learnt! I've tidied up the interface, and will purge other examples of Microsoft pollution I come across.

Adding an extra layer of indirection did the trick with the function pointer.

A more useful design for my purposes would be to return the dll handle (HMODULE) from the initialisation function, and then pass the handle back to a C++ dll function loader/caller when required. To acheive this I am passing an 8-byte int ref from Fortran to the callee (C++ long). This works ok but I get a C++ warning related to 64-bit portability when I cast the long back to an HMODULE.

I understand I can call LoadLibrary from Fortran but the only examples I can find use a 4-byte int as the handle -is this the closest equivalent to HMODULE? Might this also have repurcussions for 64-bit portability?

Thanks.

0 Kudos
Steven_L_Intel1
Employee
2,282 Views

Use INTEGER(HANDLE) for Windows handles - HANDLE is defined if you use any of the Win32 modules.

A sample using LoadLibrary is included with the compiler, under the DLL subfolder.
0 Kudos
Martin__Paul
New Contributor I
2,282 Views
Thanks, that worked nicely.
0 Kudos
Reply