- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have yet another problem with accessing C from Fortran. This follows on from my two earlier problems that were solved.
So the program compiles but crashes on running. I am building and running the following in Windows using the Intel Fortran compiler. My program compiles eccodes.f90 and grib_f90.f90 correctly and grib_copy_msg.f90 compiles correctly. However when I try to run it an error is thrown:
I have the following:
In the top part of the program grib_copy_msg.f90 I have:
program copy
use eccodes
implicit none
integer :: err, centre
integer :: byte_size
integer :: infile,outfile
integer :: igrib_in
integer :: igrib_out
character(len=1), dimension(:), allocatable :: message
character(len=32) :: product_kind
integer :: status
call codes_open_file(infile,'msl_000032_me.grb','r')
call codes_open_file(outfile,'copy.grb','w')
call codes_grib_new_from_file(infile, igrib_in, status)
call codes_get_message_size_int(igrib_in, byte_size)
allocate(message(byte_size), stat=err)
call codes_copy_message(igrib_in, message)
call codes_new_from_message(igrib_out, message)
print *,'I am here 1'
call codes_get(igrib_out, 'kindOfProduct', product_kind)
print *,'I am here 2'
.....
In eccodes.f90 there is:
interface codes_get
module procedure codes_get_int, &
codes_get_real4, &
codes_get_real8, &
codes_get_string, &
codes_get_byte_array, &
codes_get_int_array, &
codes_get_real4_array, &
codes_get_real8_array
end interface codes_get
subroutine codes_get_string ( msgid, key, value, status )
integer(kind=kindOfInt), intent(in) :: msgid
character(len=*), intent(in) :: key
character(len=*), intent(out) :: value
integer(kind=kindOfInt),optional, intent(out) :: status
!DIR$ ATTRIBUTES DLLEXPORT :: codes_get_string
print *,'I am here 1.1'
call grib_get_string ( msgid, key, value, status )
end subroutine codes_get_string
grib_get_string is in grib_f90.f90:
subroutine grib_get_string ( gribid, key, value, status )
integer(kind=kindOfInt), intent(in) :: gribid
character(len=*), intent(in) :: key
character(len=*), intent(out) :: value
integer(kind=kindOfInt),optional, intent(out) :: status
integer(kind=kindOfInt) :: iret
interface
integer(c_int) function grib_f_get_string(gribid,key, &
value,lkey,lval) bind(C,name="grib_f_get_string_")
use iso_c_binding, only: c_int,c_char
integer(c_int), intent(in) :: gribid
integer(c_int) :: lkey,lval
character(c_char), intent(in) :: key
character(c_char), intent(out) :: value
end function grib_f_get_string
end interface
interface
subroutine grib_f_write_on_fail( &
gribid) bind(C,name="grib_f_write_on_fail_")
use iso_c_binding, only: c_int
integer(c_int), intent(in) :: gribid
end subroutine grib_f_write_on_fail
end interface
print *,'I am here 1.1.1'
iret=grib_f_get_string ( gribid, key, value, len(key), len(value) )
print *,'I am here 1.1.2'
if (iret /= 0) then
call grib_f_write_on_fail(gribid)
endif
if (present(status)) then
status = iret
else
call grib_check(iret,'get',key)
endif
end subroutine grib_get_string
grib_f_get_string is in a C file called grib_fortran.c:
int grib_f_get_string_(int* gid, char* key, char* val,int len, int len2){
grib_handle *h = get_handle(*gid);
int err = GRIB_SUCCESS;
char buf[1024];
size_t lsize = len2;
if(!h) return GRIB_INVALID_GRIB;
fort_char_clean(val,len2);
printf("%s\n","I am here 1.1.1.1");
err = grib_get_string(h, cast_char(buf,key,len), val, &lsize);
printf("%s\n","I am here 1.1.1.2");
czstr_to_fortran(val,len2);
return err;
}
When I try to run it, I get the output:
I am here 1
I am here 1.1
I am here 1.1.1
The program then crashes.
What mistake have I made with this interface?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for your help on this Arjen. Based on your comments I have a solution to this now.
The solution to this is to have in the interface:
integer(c_int), value :: lkey,lval
And in the C file:
int grib_f_get_string_(int* gid, char* key, char* val,int* len, int* len2){
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I can see several mistakes:
- The declaration on the Fortran side of the key and value string is wrong. It should be:
character(len=*,kind=c_char), intent(in) :: key character(len=*,kind=c_char), intent(out) :: value
otherwise the strings are c_char characters long (the kind and the length are confused here)
- The lengths of the strings are declared as "integer(c_int)", but as Fortran passes arguments by reference by default, this would mean "int*" on the C side. You probably want:
unless the length changes. Alternatively: declare them as pointer on the C side.integer(c_int), value :: lkey,lval
- Be aware that C works best with nul-terminated strings, so you need to arrange for that extra character. That may be done in fort_char_clean() but you do not show the implementation.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you Arjen.
When I substitute in:
character(len=*,kind=c_char), intent(in) :: key
character(len=*,kind=c_char), intent(out) :: value
I get a compile error:
error 6683: A kind type parameter must be a compile-time constant. [C_CHAR]
When I substitute into the interface (without the 'kind=c_char' change):
integer(c_int), value :: lkey,lval
It compiles and runs and crashes in fort_char_clean
fort_char_clean is defined as:
static void fort_char_clean(char* str,int len)
{
char *p,*end;
p=str; end=str+len-1;
while (p != end) *(p++)=' ';
*p=' ';
}
How should I change the interface to get this working?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That is odd. Could you attach your complete source code (omit the code for the eccodes library) or the relevant part, so that we can reproduce this compile-time error? Since you import c_char from iso_c_binding, the compiler message is odd. Hence my request for a reproducer program, rather than the fragments.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Arjen,
Would you like the code just for grib_copy_msg.f90? The rest of code comes from eccodes which is available online from ECMWF.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well, the code you wrote :). I should be able to download the ECMWF library myself.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is the grib_copy_msg.f90 code:
program copy
use eccodes
implicit none
integer :: err, centre
!integer(kind=kindOfSize) :: byte_size
integer :: byte_size
integer :: infile,outfile
integer :: igrib_in
integer :: igrib_out
character(len=1), dimension(:), allocatable :: message
character(len=32) :: product_kind
integer :: status
call codes_open_file(infile,'msl_000032_me.grb','r')
call codes_open_file(outfile,'copy.grb','w')
call codes_grib_new_from_file(infile, igrib_in, status)
call codes_get_message_size_int(igrib_in, byte_size)
allocate(message(byte_size), stat=err)
call codes_copy_message(igrib_in, message)
call codes_new_from_message(igrib_out, message)
print *,'I am here 1'
call codes_get(igrib_out, 'kindOfProduct', product_kind)
print *,'I am here 2'
write(*,*) 'kindOfProduct=',product_kind
centre=80
call codes_set(igrib_out, 'centre', centre)
call codes_write(igrib_out, outfile)
call codes_release(igrib_out)
call codes_release(igrib_in)
call codes_close_file(infile)
call codes_close_file(outfile)
deallocate(message)
end program copy
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Arjen,
To build eccodes in Windows is a bit of a pain at the moment. It is currently set up to build in Linux and Mac. There are a number of steps to build in it in Windows:
- There are hard-coded symlinks that don't work and need to be replaced. I replaced with a copy of the actual file that was symlinked.
- There are instances of 'type' and 'echo' that need to be replaced.
- I had to replace the contents of eccodes/fortran/grib_types.f90
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
To:
eccodes/fortran/CMakeFiles/eccodes_f90.dir/build.make
On line 70, change:
type <your_path_to_eccodes>/eccodes/fortran/grib_f90_head.f90 <your_path_to_eccodes>/eccodes/fortran/grib_f90_int.f90 <your_path_to_eccodes>/eccodes/fortran/grib_f90_long_size_t.f90 <your_path_to_eccodes>/eccodes/fortran/grib_f90_tail.f90 > grib_f90.f90
To:
copy /y grib_f90_head.f90+grib_f90_int.f90+grib_f90_long_size_t.f90+grib_f90_tail.f90 grib_f90.f90
On line 80, change:
type <your_path_to_eccodes>/eccodes/fortran/eccodes_f90_head.f90 <your_path_to_eccodes>/eccodes/fortran/eccodes_f90_int.f90 <your_path_to_eccodes>/eccodes/fortran/eccodes_f90_long_size_t.f90 <your_path_to_eccodes>/eccodes/fortran/eccodes_f90_tail.f90 > eccodes_f90.f90
To:
copy /y eccodes_f90_head.f90+eccodes_f90_int.f90+eccodes_f90_long_size_t.f90+eccodes_f90_tail.f90 eccodes_f90.f90
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
To line 127 of:
<your_path_to_eccodes>/eccodes/fortran/CMakeFiles/grib_types.dir/build.make
Change:
echo >nul && "C:\Program Files (x86)\CMake\bin\cmake.exe" -E remove <your_path_to_eccodes>/eccodes/fortran/grib_types.exe
To:
"C:\Program Files (x86)\CMake\bin\cmake.exe" -E remove <your_path_to_eccodes>/eccodes/fortran/grib_types.exe
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
To the file:
<your_path_to_eccodes>/eccodes/fortran/CMakeFiles/eccodes_f90.dir/build.make
On line 181 change:
echo >nul && "C:\Program Files (x86)\CMake\bin\cmake.exe" -E remove <your_path_to_eccodes>/eccodes/bin/eccodes_f90.dll
To:
"C:\Program Files (x86)\CMake\bin\cmake.exe" -E remove <your_path_to_eccodes>/eccodes/bin/eccodes_f90.dll
Change lines 190 to 192:
echo >num && "C:\Program Files (x86)\CMake\bin\cmake.exe" -E make_directory <your_path_to_eccodes>/eccodes/include
echo >num && "C:\Program Files (x86)\CMake\bin\cmake.exe" -E copy <your_path_to_eccodes>/eccodes/fortran/modules/./eccodes.mod <your_path_to_eccodes>/eccodes/include
echo >num && "C:\Program Files (x86)\CMake\bin\cmake.exe" -E copy <your_path_to_eccodes>/eccodes/fortran/modules/./grib_api.mod <your_path_to_eccodes>/eccodes/include
To:
"C:\Program Files (x86)\CMake\bin\cmake.exe" -E make_directory <your_path_to_eccodes>/eccodes/include
"C:\Program Files (x86)\CMake\bin\cmake.exe" -E copy <your_path_to_eccodes>/eccodes/fortran/modules/./eccodes.mod <your_path_to_eccodes>/eccodes/include
"C:\Program Files (x86)\CMake\bin\cmake.exe" -E copy <your_path_to_eccodes>/eccodes/fortran/modules/./grib_api.mod <your_path_to_eccodes>/eccodes/include
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You will need a copy of bash.exe (this can be obtained from the Windows version of Git)
And then build eccodes in Windows using the following CMake command:
cmake -G "NMake Makefiles" -DBASH_EXE=<your_path_to_bash_exe>/bash.exe <your_path_to_eccodes> -DCMAKE_BUILD_TYPE=Release -DCMAKE_Fortran_COMPILER=<your_path_to_intel_fortran>/compilers_and_libraries_2020.0.166/windows/bin/intel64/ifort.exe"
Follow that by the nmake commands:
nmake clean
nmake
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for your help on this Arjen. Based on your comments I have a solution to this now.
The solution to this is to have in the interface:
integer(c_int), value :: lkey,lval
And in the C file:
int grib_f_get_string_(int* gid, char* key, char* val,int* len, int* len2){
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page