- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
void closf_ (int *ihandle, int *istat)
My wrapper function on the Fortran side looks like this:
SUBROUTINE closf(ihandl,istat)
USE,INTRINSIC :: iso_c_binding , ONLY : C_LOC
IMPLICIT NONE
INTERFACE
SUBROUTINE closf_can(ihandl,istat) BIND(C , NAME='closf_')
USE,INTRINSIC :: iso_c_binding , ONLY : C_PTR
IMPLICIT NONE
TYPE(C_PTR),VALUE :: ihandl,istat
END SUBROUTINE closf_can
END INTERFACE
INTEGER,TARGET :: ihandl,istat
CALL closf_can(C_LOC(ihandl),C_LOC(istat))
END SUBROUTINE closf
When compiled, I get "error LNK2019: unresolved external symbol closf_ referenced in function _CLOSF". I tried different ways of adding the C component into my Fortran project: a) added the C++ project into the solution and made my Fortran project dependant on it, b) added the compiled C library (.lib file) to the Fortran project, c) added the closf_.obj file to the Fortran project. None of them worked.
I am using IVF 10.0.27 with VS2005 Professional.
Any help will greatly be appreciated.
Thanks,
Jon
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
extern "C"
otherwise you get C++ name mangling.
The other thing that comes to mind is that on IA-32, the actual external name will be _closf_ so you may need to add the leading underscore. I'll have to reread that part of the standard to see if that in fact ought to be necessary.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The C source code is in a ".c" file. I will try adding the leading underscore and hopefully that will work. This has been the only thing that is stopping me to migrate from CVF to IVF.
Thanks,
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
From a Fortran Build Environment command prompt, do a:
dumpbin -symbols ccode.obj
on your C onbject file to see what the "decoration" of the symbol is. I haven't done the test myself to see if BIND is doing the right thing - I will do that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your suggestion of adding underscore in front of the C function names seemed to work. At least I got it compiled, although I haven't tested the library for produced results yet.
Thanks,
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A workaround that will not break once the compiler is fixed is to add:
!DEC$ ATTRIBUTES DECORATE :: routine_name
to the interface block.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have another problem regarding Fortran calling C procedures using ISO_C_BINDING intrinsic module. It would be great if somebody could help.
The issue is related to passing a character variable to a C procedure:
#include
#include
void
isonlocaldrive_ (char *pathName, int *isLocal, int len_pathName)
{
char absPath[_MAX_PATH];
char *filePart;
GetFullPathName(pathName, _MAX_PATH, absPath, &filePart);
.
.
.
}
The Fortran wrapper for this procedure is
SUBROUTINE isonlocaldrive(pathName,isLocal)
USE,INTRINSIC :: iso_c_binding
IMPLICIT NONE
CHARACTER(LEN=*) :: pathName
INTEGER :: isLocal
INTERFACE
SUBROUTINE isonlocaldrive_can(c_pathName,isLocal,len_pathName) BIND(C , NAME='_isonlocaldrive_')
USE,INTRINSIC :: iso_c_binding
IMPLICIT NONE
CHARACTER(C_CHAR),DIMENSION(*) :: c_pathName
INTEGER(C_INT) :: isLocal
INTEGER(C_INT),VALUE :: len_pathName
END SUBROUTINE isonlocaldrive_can
END INTERFACE
CALL isonlocaldrive_can(pathName,isLocal,LEN(pathName))
END SUBROUTINE isonlocaldrive
This set-up seems to work fine until the GetFullPathName (a WIN32 API) function in the C code is invoked. In the debugger I can see the values of *isLocal, len_pathName and the first character of *pathName correctly. But after the GetFullPathName is called *pathName and *isLocal seem to loose the address they are pointing to (as much as I can tell with my very limited C knowledge). Later in the C code *isLocal is assigned a value and at this stage I get an access violation error.
I would very much appreciate if somebody can help me on this. I read whatever information I could find on passing character variables from Fortran to C, but I still can't figure out what the problem is.Thanks,
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Run-Time Check Failure #3 - The variable 'filePart' is being used without being initialized.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here's the MSDN sample excerpt - maybe try its method?
DWORD retval=0;
BOOL success; char buffer[BUFSIZE]="";
char * lpPart[BUFSIZE]={NULL};
// Retrieve a full path name for a file. The file does not need to
// exist.
retval = GetFullPathName("c:testfile.txt", BUFSIZE, buffer, lpPart);
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Jon,
I do not know if this is your problem or not but GetFullPathName takes as the 2nd argument the size of the destination buffer in TCHARs. A TCHAR is two bytes. So either declare your buffer as TCHAR Buffer[[_MAX_PATH] (not char) or request the number of chars in the array declaration as char Buffer[_MAX_PATH*sizeof(TCHAR)].
As to wether char's or TCHAR's are returned it is not clear by the documentation.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I tried your suggestion of passing NULL instead of filePart. It didn't work. I haven't tried Jim's suggestion yet.
I have another C function that expects char *cname that I wrap with Fortran using the exact same approach. That C function calls CreateFile API to open file with *cname. Although the pointers don't loose their targets, the CreateFile function fails. This makes me wonder if there is indeed a problem with the Fortran side of my code. I inhereted the C code from someone else and supposedly it worked fine when linked to CVF-compiled (without using the iso_c_binding modulke, of course) Fortran code.
Below are the part of C code that uses CreateFile and its Fortran wrapper:
void
openf_ (char *cname, int *iaccess, int *ihandl, int *istat, int len_cname)
{
HANDLE hFile;
hFile = CreateFile (cname,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
SUBROUTINE openf(cname,iaccess,ihandl,istat)
USE,INTRINSIC :: iso_c_binding , ONLY : C_LOC
IMPLICIT NONE
CHARACTER(LEN=*) :: cname
INTEGER :: iaccess,ihandl,istat
INTERFACE
SUBROUTINE openf_can(cname,iaccess,ihandl,istat,len_cname) BIND(C , NAME='_openf_')
USE,INTRINSIC :: iso_c_binding , ONLY : C_INT , C_CHAR , C_PTR
IMPLICIT NONE
CHARACTER(C_CHAR),DIMENSION(*) :: cname
INTEGER(C_INT) :: iaccess,ihandl,istat
INTEGER(C_INT),VALUE :: len_cname
END SUBROUTINE openf_can
END INTERFACE
CALL openf_can(cname,iaccess,ihandl,istat,LEN(cname))
END SUBROUTINE openf
Thanks,
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
JimDempseyAtTheCove:
A TCHAR is two bytes.
No, a TCHAR is a "generic" for either char (1 byte) or WCHAR (2 bytes); the appropriate version will be picked up depending on whether UNICODE is #defined; additionally, it will pick up GetFullPathNameA (ANSI) or GetFullPathNameW (UNICODE) export from the Kernel32.dll. So, unless there's UNICODE defined for the C++ project, that's not a problem.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
dogrul@water.ca.gov:
StAlthough the pointers don't loose their targets, the CreateFile function fails. This makes me wonder if there is indeed a problem with the Fortran side of my code.
Have you terminated the cname with CHAR(0) in the caller (trim(clen)//char(0))? C expects that for (almost) all strings, and will also normally return char(0)-terminated strings (rather than Fortran-style blank-padded). You have to do the conversion yourself.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yes, cname is null-terminated using CHAR(0).
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve,
Attached is a C-static libraryproject and a Fortran console application project. The openf_ function should create a file named "Test.txt" but creates a file with a name full of strange characters (in my full application nothing is created, I get an invalid handle); isonlocaldrive_ function kills the executable.
The projects are compiled by VS2005.
Jon
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I know nothing about 'iso-c-binding' and use Compaq Visual Fortran 6.6c. But I translated your Fortran project as shown below, compiled your three C-files (with no changes)into a static library using Visual C++ and included the .LIB file into my fortran project and built a console project using your two Fortran files as modified below and ran it. The output on the console was as follows:
iaccess= 10
ihandl= 10
istat from openf= 0
istat from isonlocaldrive= 1
Press any key to continue
and an empty file called 'Test.txt' was created. So the code appears OK from the point of view of CVF!
The modified Fortran code follows, using compiler directives to match the calling convention and C-naming (the .C file extension meant that the Visual C++ did not mangle the names).
First, your 'C-wrapper':
-------------------------
SUBROUTINE openf(cname,iaccess,ihandl,istat)
IMPLICIT NONE
CHARACTER(LEN=*) :: cname
INTEGER*4 :: iaccess,ihandl,istat, length
INTERFACE
SUBROUTINE openf_can(cname,iaccess,ihandl,istat,len_cname)
!DEC$ ATTRIBUTES C,REFERENCE :: openf_can
!DEC$ ATTRIBUTES REFERENCE :: cname, iaccess, ihandl, istat
!DEC$ ATTRIBUTES VALUE :: len_cname
!DEC$ ATTRIBUTES ALIAS:'_openf_' :: openf_can
IMPLICIT NONE
CHARACTER(*) cname
INTEGER(4) iaccess,ihandl,istat
INTEGER(4) len_cname
END SUBROUTINE openf_can
END INTERFACE
length=LEN(cName)
CALL openf_can(cname,iaccess,ihandl,istat,length)
END SUBROUTINE openf
!---------------------------------------------------------------------------
SUBROUTINE isonlocaldrive(pathName,isLocal)
IMPLICIT NONE
CHARACTER(LEN=*) :: pathName
INTEGER :: isLocal, length
INTERFACE
SUBROUTINE isonlocaldrive_can(c_pathName,isLocal,len_pathName)
!DEC$ ATTRIBUTES C,REFERENCE :: isonlocaldrive_can
!DEC$ ATTRIBUTES REFERENCE :: c_pathname, islocal
!DEC$ ATTRIBUTES VALUE :: len_pathname
!DEC$ ATTRIBUTES ALIAS:'_isonlocaldrive_' :: isonlocaldrive_can
IMPLICIT NONE
CHARACTER(*) c_pathName
INTEGER(4) isLocal
INTEGER(4) len_pathName
END SUBROUTINE isonlocaldrive_can
END INTERFACE
length=LEN(pathName)
CALL isonlocaldrive_can(pathName,isLocal,length)
END SUBROUTINE isonlocaldrive
Then your test program:
---------------------------
PROGRAM Test_Fortran_C
IMPLICIT NONE
CHARACTER :: cna me*20
INTEGER :: iaccess,ihandl,istat,len_cname
cname = ADJUSTL('Test.txt' // CHAR(0) )
len_cname = LEN_TRIM(cname)+1
iaccess = 10
CALL openf(cname(1:len_cname),iaccess,ihandl,istat)
write(*,*) 'iaccess= ',iaccess
write(*,*) 'ihandl= ',iaccess
write(*,*) 'istat from openf= ',istat
CALL isonlocaldrive(cname(1:len_cname),istat)
write(*,*) 'istat from isonlocaldrive= ',istat
END
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
From my testing, it looks as if UNICODE is the default when you create a C++ project in VS2005, so this is something to watch out for.
There is a second minor bug in the Fortran code where you have:
cname = ADJUSTL('Test.txt' // CHAR(0) )
len_cname = LEN_TRIM(cname)+1
You want that +1 to be -1.
I will comment that there is a bug in the compiler regarding BIND(C,NAME=) in that, at present, it does not apply the corresponding C compiler's decoration (leading underscore) to the name. You have correctly worked around this, but a future update, when the bug is fixed, will require that you remove the leading underscore from the NAME value.
I will also comment that, at least from the C code I see here, all of this could be done in Fortran quite easily. I don't know why you want to call out to C.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Changing the UNICODE character set to "Not set" worked like a charm.
Jon

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page