- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am building and running the following in Windows using the Intel Fortran compiler. My program uses eccodes.lib and eccodes.mod, both of these are correctly compiled and my program (grib_copy_msg) compiles correctly. But when I try to run it an error is thrown:
I have got the following:
In the top part of my program (grib_copy_msg.f90) I have:
program copy
use eccodes
implicit none
integer :: infile
print *,'I am here 0'
call codes_open_file(infile,'msl_000032_me.grb','r')
print *,'I am here 1'
.....
In eccodes.f90 there is:
subroutine codes_open_file ( ifile, filename, mode, status )
integer(kind=kindOfInt),intent(out) :: ifile
character(len=*), intent(in) :: filename
character(LEN=*), intent(in) :: mode
integer(kind=kindOfInt),optional, intent(out) :: status
!DIR$ ATTRIBUTES DLLEXPORT :: codes_open_file
print *, "I am here 2"
call grib_open_file ( ifile, filename, mode, status )
end subroutine codes_open_file
grib_open_file is in a file called grib_f90.f90:
subroutine grib_open_file ( ifile, filename, mode, status )
integer(kind=kindOfInt),intent(out) :: ifile
character(len=*), intent(in) :: filename
character(LEN=*), intent(in) :: mode
integer(kind=kindOfInt),optional, intent(out) :: status
integer(kind=kindOfInt) :: iret
!DIR$ ATTRIBUTES DLLEXPORT :: grib_open_file
interface
integer(c_int) function grib_f_open_file(ifile,filename,mode) bind(c)
use iso_c_binding, only: c_char,c_int
integer(c_int), intent(out) :: ifile
character(c_char), intent(in) :: filename,mode
end function grib_f_open_file
end interface
print *, "I am here 3"
iret=grib_f_open_file(ifile, filename, mode )
if (present(status)) then
status = iret
else
call grib_check(iret,'open_file','('//filename//')')
endif
end subroutine grib_open_file
When I try to run it, I get the output:
I am here 0
I am here 2
The program then crashes.
I have added in the two !DIR$ ATTRIBUTES lines, however have I got them right and do I need to add more !DIR$ lines?
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Th compiler directives are only of importance if you put the routines in a DLL - they export the routines, so that they can be used by a program.
You do not tell HOW it crashes: any error messages, for instance? But from the code I think I see the problem: grib_f_open_file is a C function (or acts like one) and one of its arguments is a string - the name of the file to open. Typically, strings in C are terminate with a NUL character. But you also pass it a single character, instead of what you should do an array of characters.
Try:
character(len=1), dimension(*), intent(in) :: filename, mode
and change the arguments to "trim(filename) // c_nul_char" and "trim(mode) // c_nul_char" (without the quotation marks of course)
No guarantee, as I do not know the library and therefore do not know what the C interface actually is - perhaps you can copy that in a reply - but this is the direction you should be looking at.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Is the compile of eccodes.f90 producing a DLL? the line !DIR$ ATTRIBUTES DLLEXPORT :: grib_open_file would suggest so. And grib_open_file in grib_f90.f90, is that going in the same dll or a different dll? And Finally, do you expect to call grib_open_file from your main program or only from within the eccodes DLL?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Andrew,
eccodes.f90 produces a dll, lib, and a mod. It is only the lib and mod that I need.
grib_open_file is only called from within eccodes.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
No, the "lib" file is a so-called import library and is needed at link time. The "mod" file is required by the compiler to import the module. You need the DLL at run-time.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
HI Arjen,
I am afraid no other message when it crashes. So are you suggesting that I replace the line:
character(c_char), intent(in) :: filename,mode
With the line:
character(len=1), dimension(*), intent(in) :: (trim(filename)//c_nul_char), (trim(mode)//c_nul_char)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ah, no, I meant: change the call to grib_f_open_file to:
iret=grib_f_open_file(ifile, trim(filename)//c_nul_char, trim(mode)//c_nul_char )
and change the declaration for filename and mode in the interface block.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Arjen,
Thank you. I now get:
I am here 0
I am here 2
I am here 3
However at that point it crashes with the message:
forrtl: severe (157): Program Exception - access violation
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Can you compile the program and the library using the compile option /traceback? Or run it in a debugger? The print statements in your program do not allow us to determine whether it failed in grib_f_open_file or later. The option should enable the program to print a traceback so that it becomes clearer where things go wrong.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Will it be possible for you to post here the C function prototype for `grib_f_open_file`? As alluded to by others, the Fortran interface to this function needs to be corrected. It may be as follows, however the actual C "signature" can confirm this:
interface
integer(c_int) function grib_f_open_file(ifile, filename, mode) bind(C)
use iso_c_binding, only: c_char, c_int
integer(c_int), intent(out) :: ifile
character(c_char), intent(in) :: filename(*), mode(*) !<-- note the assumed-length declaration
end function grib_f_open_file
end interface
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi FortranFan,
Here are the prototypes:
int grib_f_open_file(int *fid, char *name, char *op, int lname, int lop);
int grib_f_open_file_(int *fid, char *name, char *op, int lname, int lop);
int grib_f_open_file__(int *fid, char *name, char *op, int lname, int lop);
And here are the functions (in grib_fortran.c):
int grib_f_open_file_(int* fid, char* name , char* op, int lname, int lop) {
FILE* f = NULL;
int ioerr=0;
char oper[1024]; /* GRIB-576: open mode */
char *p;
char fname[1024];
int ret=GRIB_SUCCESS;
char* iobuf=NULL;
/*TODO Proper context passed as external parameter */
grib_context* context=grib_context_get_default();
cast_char(oper,op,lop);
p=oper;
while (*p != '\0') { *p=tolower(*p);p++;}
f = fopen(cast_char(fname,name,lname),oper);
if(!f) {
ioerr=errno;
grib_context_log(context,(GRIB_LOG_ERROR)|(GRIB_LOG_PERROR),"IO ERROR: %s: %s",strerror(ioerr),cast_char(fname,name,lname));
*fid = -1;
ret=GRIB_IO_PROBLEM;
} else {
if (context->io_buffer_size) {
/* printf("setting vbuf = %d\n",context->io_buffer_size); */
#ifdef POSIX_MEMALIGN
if (posix_memalign((void **)&iobuf,sysconf(_SC_PAGESIZE),context->io_buffer_size) ) {
grib_context_log(context,GRIB_LOG_FATAL,"grib_f_open_file_: posix_memalign unable to allocate io_buffer\n");
}
#else
iobuf = (void*)malloc(context->io_buffer_size);
if (!iobuf) {
grib_context_log(context,GRIB_LOG_FATAL,"grib_f_open_file_: Unable to allocate io_buffer\n");
}
#endif
setvbuf(f,iobuf,_IOFBF,context->io_buffer_size);
}
*fid = push_file(f,iobuf);
ret=GRIB_SUCCESS;
}
return ret;
}
int grib_f_open_file__(int* fid, char* name , char* op, int lname, int lop){
return grib_f_open_file_( fid, name , op, lname, lop);
}
int grib_f_open_file(int* fid, char* name , char* op, int lname, int lop){
return grib_f_open_file_( fid, name , op, lname, lop);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Oh, that makes it clearer! The library you are using uses the OLD style interfacing between Fortran and C and as a consequence takes into account that Fortran passes string arguments with a hidden argument. In that case, the solution should be to leave out the interface block. The burden of the interfacing is on the C side.
Note: there are three versions to take care of common name mangling schemes found with Fortran compilers.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Arjen,
I did originally try to build it without the interface blocks, however the compiler didn't recognise the C function names, so in order to get it compiling I had to add interface blocks throughout the Fortran code. I am using the Intel Fortran 2019 Windows compiler. I would really like to not have to use them! I had to add 112 interface blocks in, in order to get the code compiling!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well the first thing I note is that grib_f_open_file on the c side is expecting 5 parameters but your interface to it defines 3. In Fortran to Fortran passing there are hidden length parameters that get passed in a way the can be specific to the compiler. When you add bind C to the interface it stops that and does things in the way the companion C compiler would expect. You need to define the length parameters in the interface (passed by value) and actually pass them in the call.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The interface you have in the C code does not acknowledge the Intel style of name mangling - the Fortran names are converted to uppercase. One way to proceed here is to add an uppercase version in the same way as there are the versions with two underscores and with no underscores.
In the long term I would say the interface blocks are better, but then you need to add those hidden arguments that are visible on the C-side. You can then do away with the c_nul_chars I suggested, as that is all taken care of on the C-side.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you Andrew and Arjen.
One other consideration I have, is that this code needs to be able to be compiled in Linux and macOS. Will that mean interfaces blocks are better or uppercase versions of the function are the way to go?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As a quick test, you can try the following:
interface
integer(c_int) function grib_f_open_file(ifile, filename, mode, lname, lop) bind(C, name="grib_f_open_file_")
use iso_c_binding, only: c_char, c_int
integer(c_int), intent(out) :: ifile
character(c_char), intent(in) :: filename(*), mode(*)
integer(c_int), intent(in), value :: lname, lop
end function grib_f_open_file
end interface
You have a couple of options after that, to go with compiler-specific directives or with standard Fortran approach - the above interface faces the latter.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi FortranFan,
Thanks, so what would the call to grib_f_open_file be in this case with the additional two parameters?
iret=grib_f_open_file(ifile, (trim(filename))//c_null_char, (trim(mode))//c_null_char, ...... , ...... )
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You should be able to call it as:
iret = grib_f_open_file( ifile, filename, mode, len(filename), len(mode) )
(no need for the explicit NUL character)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Great, thank you that works.
Will these changes work in Linux and macOS?
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page