Get length of HDF5 filename in Fortran?

Viewed 72

I'm trying to get the filename of an HDF5 object in Fortran, for which I do not know the length of the filename in advance. The HDF5 documentation says this can be accomplished by passing NULL to h5fget_name:

If the length of the name, which determines the required value of size, is unknown, a preliminary H5Fget_name call can be made by setting name to NULL.

However, it is unclear how to accomplish this from Fortran code. Per the above language in the documentation, something like this should work:

  function get_hdf5_filename(obj_id) result(filename)

    use hdf5
    use iso_c_binding, ONLY: c_null_ptr

    ! Arguments
    integer(HID_T), intent(in)::obj_id
        !! Handle of HDF5 object

    ! Result
    character(:), allocatable::filename

    integer(SIZE_T)::size = 0
        !! Length of filename

    integer::hdferr
        !! HDF5 error code

    call h5fget_name_f(obj_id, c_null_ptr, size, hdferr)

    allocate (character(size)::filename)

    call h5fget_name_f(obj_id, filename, size, hdferr)

  end function get_hdf5_filename

However, gfortran won't compile the above and gives a type mismatch error:

Error: Type mismatch in argument 'buf' at (1); passed TYPE(c_ptr) to CHARACTER(1)
2 Answers

A null can be passed by declaring the filename variable as a pointer and nullifying it before the initial call to h5fget_name_f:

  function get_hdf5_filename(obj_id) result(filename)

    use hdf5
    use iso_c_binding, ONLY: c_null_ptr

    ! Arguments
    integer(HID_T), intent(in)::obj_id
        !! Handle of HDF5 object

    ! Result
    character(:), pointer::filename

    integer(SIZE_T)::size = 50
        !! Length of filename

    integer::hdferr
        !! HDF5 error code

    nullify(filename)

    call h5fget_name_f(obj_id, filename, size, hdferr)

    ! HDF5 needs one more character (probably for the null character terminating the string), so we allocate filename to length size+1
    allocate (character(size+1)::filename)

    call h5fget_name_f(obj_id, filename, size, hdferr)

    ! Remove last character from the returned string
    filename => filename(1:size)

  end function get_hdf5_filename

Note that h5fget_name_f needs an extra character in the passed string (probably for the terminating null character), so the name pointer has to be allocated one character longer than the value set in the size argument. The terminating null can be removed by redirecting the file pointer to a subset that excludes the last character using array slicing syntax (filename => filename(1:size) in the above example).

A possible solution is to create a C function that returns the filename length for a given HDF5 object:

#include "hdf5.h"
#include "H5f90i.h"

int_f get_hdf5_filename_length(hid_t_f *obj_id){
  return H5Fget_name((hid_t)*obj_id, NULL, 0);
}

which can be called from Fortran in the following way:

  function get_hdf5_filename(obj_id) result(filename)

    use hdf5
    use iso_c_binding, ONLY: c_null_ptr

    interface
       function get_hdf5_filename_length(obj_id) bind(c) result(length)
         use hdf5
         integer(HID_T)::obj_id
         integer(SIZE_T)::length
       end function get_hdf5_filename_length
    end interface

    ! Arguments
    integer(HID_T), intent(in)::obj_id
        !! Handle of HDF5 object

    ! Result
    character(:), pointer::filename

    integer(SIZE_T)::size = 50
        !! Length of filename

    integer::hdferr
        !! HDF5 error code

    integer::i
        !! Loop counter

    size = get_hdf5_filename_length(obj_id)

    ! filename has to be allocated to size+1 to allow for the terminating null
    ! of the filename string in C
    allocate (character(size+1)::filename)

    ! h5fget_name_f uses len_trim to determine the buffer length,
    ! which requires the buffer be filled with non-whitespace characters
    ! in order to work correctly
    do i = 1, size + 1
      filename(i:i) = 'a'
    end do

    ! Get the filename
    call h5fget_name_f(obj_id, filename, size, hdferr)

    ! Remove the null character from the end of the string
    filename => filename(1:size)

  end function get_hdf5_filename

Note that it is not enough to allocate the string buffer to the correct size; it also needs to be populated with non-whitespace characters before being passed to h5fget_name_f, because h5fget_name_f calls len_trim on the passed buffer and uses the result to determine the maximum permissible filename length.

Related