Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29280 Discussions

Fortran calling C, odd attribute requirement

bsoplinger
Beginner
855 Views
I'm porting some HP-UX code and have hit a snag with a construct that seemed ok on the Unix boxes but causes me nothing but trouble with Visual Fortran.

Here's a snippet of what's causing me trouble:

logical function parse( a, b, c, d, e )
integer*4 a,d
character*(*) b
logical e

call access_dataf( a, b, c, d, e )

access_dataf is a C routine, and its the second parameter that's the problem. Its declared as a void ** and the variable length character parameter got me the right type of arbitrary pointer to pointer on HP-UX, but what can I use with Visual Fortran?

access_dataf is a general data reading function, returning real, int, string, etc.

Or will I end up needing to write a access_data_int and access_data_real and access_data_int_array and access_data_real_array and ...?

I thought I was on the right track with the !DEC$ ATTRIBUTES REFERENCE :: variable_name, but I'm not allowed to use that on variable length strings.
0 Kudos
7 Replies
Jugoslav_Dujic
Valued Contributor II
855 Views
It's not all clear to me. First, why void** (why not just void*)? Void** would be required if access_dataf allocates memory (or changes the address) for the argument b -- does it? Second, which argument is of variant type? Can you post some code of access_dataf an some original HP-UX calling code?

Jugoslav
0 Kudos
bsoplinger
Beginner
855 Views
Oops typo on my part, I didn't notice I wrote void **, it should just be void *. My problem still occurs, I want a way from Fortran to call C with an arbitrary pointer to anything. On HP-UX, I could use the variable length character mechanism to get this, even if the actual parameter was a real or an int.

With the VF compiler, it complains, and I need a way to pass the address (which is the default in Fortran I thought) of actual parameters of differing types.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
855 Views
LOC() (or %LOC()) function returns the address of the argument. There are several ways to do what you want:

1) Enclose all actual arguments into LOC(). It's simplest, but it sounds like a cludge to me.

2) Leave the calling source as-is. Create a header file (include or module) containing generic interface for the C function, with different types of argument b, but all physically mapped to the same C function:
interface access_dataf
  subroutine access_data_string(a,b,c,d,e)
  !DEC$ATTRIBUTES C, ALIAS: "_access_dataf" :: access_data_real
  !DEC$ATTRIBUTES REFERENCE: b !suppress passing length
  integer a
  character(*) b
  whatever c, d, e
  end subroutine access_data_string
  !----
  subroutine access_data_int(a,b,c,d,e)
  !DEC$ATTRIBUTES C, ALIAS: "_access_dataf" :: access_data_int
  !DEC$ATTRIBUTES REFERENCE: b !call by reference
  integer a
  integer b
  ...
  end subroutine access_data_int
  !----
  subroutine access_data_real(a,b,c,d,e)
  !...
  end subroutine access_data_real
end interface access_dataf
The same technique is used occasionally in CVF headers. Look for "interface qsort" in ...DF98IncludeDFPORT.f90. Of course, you'll have to INCLUDE that file or USE that module wherever required.

3) Similar to 2), except that it's most portable and all Fortran. Rewrite access_dataf into several Fortran routines access_data_int etc. Place it in a module and define a generic interface (no changes to rest of the code except having to USE that module wherever required):
module m_access
interface access_dataf
   module procedure access_data_real
   module procedure access_data_int
   module procedure access_data_string
end interface
!------------
contains
!------------
subroutine access_data_real(a, b, c, d, e)
!...code of access_data_real here
end subroutine access_data_real
!-------------------------------
subroutine access_data_int(...
...
end subroutine access_data_string
!-------------------------------
end module m_access
Jugoslav

0 Kudos
bsoplinger
Beginner
855 Views
Ah, I actually use a #define:
#define PARSE(a, b, c, d, e ) if( parse( a, LOC( b ), c, e, d ) ) goto 999

So I just added the LOC to the #define, looks like that will do the trick, thanks!
0 Kudos
bsoplinger
Beginner
855 Views
OK, now I'm just plain stumped.

I used the define based on what you said. I've compiled, but when I like I'm told missing symbol PARSE@20. Why would the compiler think the function either 1) Only has 4 parameters and a return value, or 2) has 5 parameters and no return value?

Seems pretty obvious to me that parse returns a value, because I've got a
logical parse
in the Fortran when my #define is used, and that there are 5 parameters to the call, so why is the object being produced looking for PARSE@20?

Please help clue me in.

Thanks
0 Kudos
Jugoslav_Dujic
Valued Contributor II
855 Views
Return value (at least scalar one) doesn't count in number of arguments, so, _PARSE@20 is OK...

...but you didn't adjust calling conventions right; I was a bit surprised that LOC solution worked immediately without INTERFACE block, which you didn't mention, so I assumed it was there.

Default calling convention in CVF is stdcall, which produces symbols like _PARSE@20. Default calling convention in MSVC++ (is that what you used to compile C code?) is cdecl; it produces symbols like _parse. Calling conventions must match. On HP-UX there's only cdecl for both Fortran and C (and cdecl is and will be the default in Intel Fortran), so there are no problems.

One solution is to make an interface for parse:
interface
   logical function Parse(a, b, c, d, e)
   !dec$attributes C:: parse
   integer:: a 
   integer:: b
   whatever:: c, d, e
   end function
end interface
Another solution is to compile Fortran code with "C, by reference" calling convention (/iface:cref). Yet another is to declare parse __stdcall in C code (if the file has extension .cpp, you have to use extern "C" as well:
extern "C" bool __stdcall PARSE(a, b, c, d, e)
If you use interface, take care to add !dec$attributes reference:: c for every argument which is a pointer -- but not for b, since LOC() already adds one level of indirection.

Jugoslav
0 Kudos
TimP
Honored Contributor III
855 Views
Thanks, Jugoslav, for excellent explanations. Since IFL has been mentioned in passing, I'll note that /iface:cref will not be supported until the 8.0 release. IFL 7.1 should support the other features mentioned, but I have an Intel Premier issue filed on a problem with use of /Gz (__stdcall) in a case much like this.
0 Kudos
Reply