swig-fortran / swig

This fork of SWIG creates Fortran wrapper code from C++ headers.
http://www.swig.org
Other
42 stars 11 forks source link

Fortran-generated bindings do not correctly perform string handling #180

Closed sskutnik closed 1 year ago

sskutnik commented 1 year ago

When converting a C interface like the following:

void my_function(const std::string& my_string)

this generates the following FORTRAN function:

function my_function(my_string) &
result(swig_result)
use, intrinsic :: ISO_C_BINDING
integer(C_INT) :: swig_result
character(len=*), target :: my_string
integer(C_INT) :: fresult
character(kind=C_CHAR), dimension(:), allocatable, target :: farg1_temp
type(SwigArrayWrapper) :: farg1

call SWIGTM_fin_char_Sm_(my_string, farg1, farg1_temp)
fresult = swigc_my_function(farg1)
swig_result = fresult
end function

The following FORTRAN helper function is likewise generatedL

subroutine SWIGTM_fin_char_Sm_(finp, iminp, temp)
  use, intrinsic :: ISO_C_BINDING
  character(len=*), intent(in) :: finp
  type(SwigArrayWrapper), intent(out) :: iminp
  character(kind=C_CHAR), dimension(:), target, allocatable, intent(out) :: temp
  integer :: i

  allocate(character(kind=C_CHAR) :: temp(len(finp) + 1))
  do i=1,len(finp)
    temp(i) = char(ichar(finp(i:i)), kind=C_CHAR)
  end do
  i = len(finp) + 1
  temp(i) = C_NULL_CHAR ! C finp compatibility
  iminp%data = c_loc(temp)
  iminp%size = len(finp, kind=C_SIZE_T)
end subroutine

We are noticing two problems in particular with this on some platforms.

  1. The string length should be calculated using len_trim(finp)+1 rather than len(finp) to trim any trailing whitespace
  2. Because the character array is passed into the SWIG helper function within the wrapped function, this needs to have an associated ~intent(INOUT)~ intent(IN) for some (stricter) compiler settings to avoid being implicitly assumed as intent(INOUT)

Specifically, if the function is called using a string constant, we observe the following error:

error #6638: An actual argument is an expression or constant; this is not valid since the associated dummy argument has the explicit INTENT(OUT) or INTENT(INOUT) attribute.   ['decaylib.def']

i.e., appending an explicit intent attribute fixes this issue for Intel Fortran.

sethrj commented 1 year ago

It's the caller's responsibility to pass in a trimmed string with my_function(trim(x)) if that's needed, since it's acceptable to have and pass strings with trailing whitespace and some applications may need/wish to do that. The missing decoration is a bug that I will fix!