Passing address of a C++ function to Fortran - syntax??

A

Adrian

I am trying to pass the address of a C++ function into a Fortran
routine to enable the Fortran routine to call this C++ function. I
have to do it this way as our build process does not allow circular
dependencies of DLL's.

Does anyone know how to do this, I have tried everything in my book.

I have a C++ function GetP:

DllExport void GetP()
{
...code...
return;
}

I am trying to pass the address of this function into a Fortran
routine called BIND_FTN. The call is made from another area of the
C++ code as follows:

void (GetP)();
DllImport void __stdcall BIND_FTN(int* GetP);

void Initialize()
{
...code...
BIND_FTN(GetP); // inform Fortran of address of callback
function
return;
}

The C++ will not compile, I get:
error C2664: 'BIND_FTN' : cannot convert parameter 1 from 'void
(__cdecl *)(void)' to 'int *'

Can anyone see the problem here?

Here is the Fortran code for BIND_FTN:

subroutine BIND_FTN_EXTERNALS[DLLEXPORT](ext)
external ext

interface
subroutine cb_GetP()
!DEC$ ATTRIBUTES STDCALL :: cb_GetP
end subroutine cb_GetP
end interface

pointer (p_GetP, cb_GetP)
p_GetP = loc(ext)

call cb_GetP ! call the C++ function from Fortran

return
end

(I haven't got to the Fortran yet - there may be syntax errors in
there)

Adrian
 
E

Evan Carew

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Adrian,
I am trying to pass the address of a C++ function into a Fortran
routine to enable the Fortran routine to call this C++ function. I
have to do it this way as our build process does not allow circular
dependencies of DLL's.

Does anyone know how to do this, I have tried everything in my book.

Well, last time I did this with a C function calling a C++ library
function, I had to write a wrapper interface for the C++ which performed
any object initialization & type conversions. In addition to this
requirement, you also needed to mark the wrapper interface in an extern
"C"{} block in order that the linker be able to deal with the two
differing languages.

Evan
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFBo/s8pxCQXwV2bJARAt/2AJ9zSVfV4gc8dZH1zSayPa1zOwC4dgCfceHG
i9xCYAuLURLqt7Id4dVMrkM=
=Xiu3
-----END PGP SIGNATURE-----
 
G

glen herrmannsfeldt

Adrian said:
I am trying to pass the address of a C++ function into a Fortran
routine to enable the Fortran routine to call this C++ function. I
have to do it this way as our build process does not allow circular
dependencies of DLL's.
Does anyone know how to do this, I have tried everything in my book.

I would first learn how to call C++ from Fortran, though I
believe that it is best to call C from Fortran and C++ from C.

If you can call C or C++ from Fortran then I wouldn't expect
passing the address to be too hard. I would look at the
calling convention for both, but C can pass function addresses,
and so can Fortran. There aren't so many ways to do it.

I do suggest a C intermediate, though.

-- glen
 
J

Jugoslav Dujic

Adrian wrote:
| I am trying to pass the address of a C++ function into a Fortran
| routine to enable the Fortran routine to call this C++ function. I
| have to do it this way as our build process does not allow circular
| dependencies of DLL's.
|
| Does anyone know how to do this, I have tried everything in my book.
|
| I have a C++ function GetP:
|
| DllExport void GetP()
| {
| ...code...
| return;
| }
|
| I am trying to pass the address of this function into a Fortran
| routine called BIND_FTN. The call is made from another area of the
| C++ code as follows:
|
| void (GetP)();
| DllImport void __stdcall BIND_FTN(int* GetP);
|
| void Initialize()
| {
| ...code...
| BIND_FTN(GetP); // inform Fortran of address of callback
| function
| return;
| }
|
| The C++ will not compile, I get:
| error C2664: 'BIND_FTN' : cannot convert parameter 1 from 'void
| (__cdecl *)(void)' to 'int *'
|
| Can anyone see the problem here?

You're passing a function as the actual argument, but the declaration
says it should be an int*. Obviously, the declaration is wrong. You
need either void* and cast GetP in the call to void*, or, more nice,
spell it out:

typedef void __stdcall GETP(void);

DllImport void __stdcall BIND_FTN(GETP);

| Here is the Fortran code for BIND_FTN:
|
| subroutine BIND_FTN_EXTERNALS[DLLEXPORT](ext)
| external ext
|
| interface
| subroutine cb_GetP()
| !DEC$ ATTRIBUTES STDCALL :: cb_GetP
| end subroutine cb_GetP
| end interface
|
| pointer (p_GetP, cb_GetP)
| p_GetP = loc(ext)
|
| call cb_GetP ! call the C++ function from Fortran
|
| return
| end

No need to use Cray pointers here. Instead, just declare the argument
as external and spell out its interface:

subroutine BIND_FTN_EXTERNALS(cb_GetP)
!DEC$ATTRIBUTES DLLEXPORT:: BIND_FTN_EXTERNALS

interface
subroutine cb_GetP()
!DEC$ ATTRIBUTES STDCALL :: cb_GetP
end subroutine cb_GetP
end interface

call cb_GetP ! call the C++ function from Fortran

return
end

Here's the complete working C++ code:

#include "stdafx.h"
#include <stdio.h>

#define FORTRAN_DLL_CALL __stdcall

typedef void __stdcall GETP(void);
void __stdcall GetP(void);
extern "C" void FORTRAN_DLL_CALL BIND_FTN_EXTERNALS(GETP);

int main(int argc, char* argv[])
{
BIND_FTN_EXTERNALS(GetP); // inform Fortran of address of callback
return 0;
}

void __stdcall GetP(void)
{
printf("Hello World");
}


--
Jugoslav
___________
www.geocities.com/jdujic

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
 
A

Adrian

Jugoslav Dujic said:
You're passing a function as the actual argument, but the declaration
says it should be an int*. Obviously, the declaration is wrong. You
need either void* and cast GetP in the call to void*, or, more nice,
spell it out:

typedef void __stdcall GETP(void);

DllImport void __stdcall BIND_FTN(GETP);

.... FTN code snipped
Here's the complete working C++ code:

#include "stdafx.h"
#include <stdio.h>

#define FORTRAN_DLL_CALL __stdcall

typedef void __stdcall GETP(void);
void __stdcall GetP(void);
extern "C" void FORTRAN_DLL_CALL BIND_FTN(GETP);

Question: here you have the definition of BIND_FTN with extern "C"
before it whereas above you had it with DllImport before it - which
should I use?


I still get compilation / link errors. Here is what I have now:

In File1.cpp:

#define FORTRAN_DLL_CALL __stdcall
typedef void __stdcall GetP(void);
extern "C" void FORTRAN_DLL_CALL BIND_FTN(GetP);
void Initialize()
{
...code...
BIND_FTN(GetP); // inform Fortran of address of callback
function
return;
}


In File2.cpp:

DllExport void GetP() // function whose address I want to pass to
FTN code
{
...code...
return;
}

the error is:
phi_simulator.cpp(337) : error C2275: 'GetP' : illegal use of this
type as an expression
phi_simulator.cpp(152) : see declaration of 'GetP'

line 337 is the call to BIND_FTN(GetP)

(I get the same error if I use DllImport instead of extern "C")

Adrian
 
R

Richard E Maine

glen herrmannsfeldt said:
I would first learn how to call C++ from Fortran, though I
believe that it is best to call C from Fortran and C++ from C. ....
I do suggest a C intermediate, though.

Not that I've done scarecely any C++ interfacing myself, but my
understanding, from here and elsewhere, is that the biggest "trick" of
calling C++ from C is to make sure that the C++ routine is
defined with "extern C" (exact syntax might not be right, but
something like that).

Once the C++ routine is callable from C, then it essentially is a
C routine, so no further intermediary is needed unless there are
independent reasons for one.

In fact, this is pretty much the approach implied in the F2003
C interop stuff. The F2003 interop stuff carefully avoids specifying
that the "companion processor" is actually a C compiler. Instead, it
is just required that the interface "look like" (that's not the actual
words of the standard, but it will do for here) a C compiler's would.
This was intended to provide an avenue for such things as C++ and
Ada (both of which have defined ways of interoperating with C).
 
J

Jugoslav Dujic

Adrian wrote:
| || You're passing a function as the actual argument, but the declaration
|| says it should be an int*. Obviously, the declaration is wrong. You
|| need either void* and cast GetP in the call to void*, or, more nice,
|| spell it out:
||
|| typedef void __stdcall GETP(void);
||
|| DllImport void __stdcall BIND_FTN(GETP);
||
|
| ... FTN code snipped
|
||
|| Here's the complete working C++ code:
||
|| #include "stdafx.h"
|| #include <stdio.h>
||
|| #define FORTRAN_DLL_CALL __stdcall
||
|| typedef void __stdcall GETP(void);
|| void __stdcall GetP(void);
|| extern "C" void FORTRAN_DLL_CALL BIND_FTN(GETP);
||
|
| Question: here you have the definition of BIND_FTN with extern "C"
| before it whereas above you had it with DllImport before it - which
| should I use?

What's DllImport defined as? _declspec(dllimport)? If so, you need both.
(Although it will work even without _declspec(dllimport))

| I still get compilation / link errors. Here is what I have now:
|
| In File1.cpp:
|
| #define FORTRAN_DLL_CALL __stdcall
| typedef void __stdcall GetP(void);
| extern "C" void FORTRAN_DLL_CALL BIND_FTN(GetP);
| void Initialize()
| {
| ...code...
| BIND_FTN(GetP); // inform Fortran of address of callback
| function
| return;
| }
|
|
| In File2.cpp:
|
| DllExport void GetP() // function whose address I want to pass to
| FTN code
| {
| ...code...
| return;
| }
|
| the error is:
| phi_simulator.cpp(337) : error C2275: 'GetP' : illegal use of this
| type as an expression
| phi_simulator.cpp(152) : see declaration of 'GetP'

Oh, no, you misread my post. I used TWO types of identifiers:

GETP - a typedef (prototype) for a function with no arguments returning
void. You need this only in order to declare the prototype
of BIND_FTN.
GetP - the actual declaration and body of function GetP (which matches
the prototype of GETP).

--
Jugoslav
___________
www.geocities.com/jdujic

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
 
G

glen herrmannsfeldt

Richard E Maine wrote:

(snip)
Not that I've done scarecely any C++ interfacing myself, but my
understanding, from here and elsewhere, is that the biggest "trick" of
calling C++ from C is to make sure that the C++ routine is
defined with "extern C" (exact syntax might not be right, but
something like that).
Once the C++ routine is callable from C, then it essentially is a
C routine, so no further intermediary is needed unless there are
independent reasons for one.

OK, I agree with this one. I thought it was calling an existing
C++ library (or DLL) function, but maybe not.

(snip)

-- glen
 
D

David T. Croft, Ph.D.

It sounds like you want to pass a function pointer from C/C++ to a Fortran
code. The Fortran code then calls the C/C++ function from a Fortran routine.

In general there are two things to consider. (1) naming conventions or
function decoration and (2) calling conventions. The function decoration is
highly dependent on the specific compilers that are being used. As far as
calling conventions are concerned the call into Fortran will require using
the standard call mechanism. The call from a Fortran External function will
use the C calling convention.

The function pointer can be passed as a void* from C/C++. If you want type
checking it can be defined differently.

I would be happy to help you with the details if you contact me at
(e-mail address removed)
 
G

Gerry Thomas

[...]

You're undoubtedly aware that you can omit the DLLEXPORT metacommand in the
Fortran code and make the sample a straightforward mixed language one
instead of having to create a Fortran DLL.


--
Ciao,
Gerry T.
______
"Today's C++ programs will be tomorrow's unmaintainable legacy code." --Ian
Joyner, in a Critique of C++, 3rd ed., 1996.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top