Is there an alternative to GETCWD() in Fortran 2003-2008

此生再无相见时 提交于 2019-12-19 07:09:21

问题


The GNU Extension to the GNU Fortran compiler provides the subroutine GETCWD() that well, gets the current working directory. However, my code has to be portable to the ifort and nagfor compiler as well and I use F2003 features.

So, is there an alternative to GETCWD() for F2003 and later?

I have the standard here but it's quite sizeable and I've been going through it for a while now and haven't found anything useful...


回答1:


You can also use the ISO_C_Binding and call the corresponding C functions:

cwd.c:

#ifdef _WIN32
/* Windows */
#include <direct.h>
#define GETCWD _getcwd

#else
/* Unix */
#include <unistd.h>
#define GETCWD getcwd

#endif

void getCurrentWorkDir( char *str, int *stat )
{
  if ( GETCWD(str, sizeof(str)) == str ) {
    *stat = 0;
  } else {
    *stat = 1;
  }
}

test.F90:

program test
 use ISO_C_Binding, only: C_CHAR, C_INT
 interface
   subroutine getCurrentWorkDir(str, stat) bind(C, name="getCurrentWorkDir")
     use ISO_C_Binding, only: C_CHAR, C_INT
     character(kind=C_CHAR),intent(out) :: str(*)
     integer(C_INT),intent(out)         :: stat
    end subroutine
  end interface
  character(len=30)   :: str
  integer(C_INT)      :: stat

  str=''
  call getCurrentWorkDir(str, stat)
  print *, stat, trim(str)

end program

This code is valid for Windows and Unix-derivates (Linux, OSX, BSD, etc. )




回答2:


As noted in the comments, you can make use of get_environment_variable which is standard Fortran (e.g. F2008 13.7.67). This example program queries the value of $PWD, which should contain the directory your shell is in when you invoked the executable.

program test
 implicit none
 character(len=128) :: pwd
 call get_environment_variable('PWD',pwd)
 print *, "The current working directory is: ",trim(pwd)
end program

And its output:

casey@convect code % pwd
/home/casey/code
casey@convect code % so/getpwd 
 The current working directory is: /home/casey/code

This is standard Fortran, but its portability will be limited to Unix and Unix-like shells that set this variable.

Another option while standard but ugly (in my opinion) would be to use execute_command_line to run a command that can output the working directory to a temporary file (e.g. pwd > /tmp/mypwd), then reading that file.




回答3:


The accepted answer contains two errors (it passes the wrong value as the length of the string to GETCWD, and leaves in the C_NULL_CHAR). This answer corrects those mistakes and makes the interface more usable from Fortran.

The basic idea is the same: call getcwd or _getcwd using C, and call the C wrapper using Fortran's C interoperability features. On the Fortran side, a wrapper subroutine is used to handle the string length, so it does not have to be passed explicitly.

Also, C_INT and C_CHAR are not necessary the same as default integers and default characters, which are wanted on the Fortran side (though in practice I am not aware of any system where C_CHAR and default char differ). The wrapper also converts those. Also, the string returned from C contains the terminating C_NULL_CHAR, which must be removed for the string to be usable on the Fortran side.

The C code:

#ifdef _WIN32
#include <direct.h>
#define GETCWD _getcwd
#else
#include <unistd.h>
#define GETCWD getcwd
#endif

/* Return 0 on success, 1 on error. */
int getCWDHelper(char *str, int len)
{
    return GETCWD(str, len) != str;
}

The Fortran code:

module cwd
    use iso_c_binding, only: C_INT, C_CHAR, C_NULL_CHAR
    implicit none
    private
    public :: getCWD

    interface
        function getCWDHelper(str, len) bind(C, name="getCWDHelper")
            use iso_c_binding, only: C_INT, C_CHAR
            integer(kind=C_INT) :: getCWDHelper
            character(kind=C_CHAR), intent(out) :: str(*)
            integer(kind=C_INT), value :: len
        end function getCWDHelper
    end interface

contains

    ! Writes the current working directory path into str.
    ! Returns 0 on success, or 1 on error.
    function getCWD(str)
        integer :: getCWD
        character(*), intent(out) :: str

        integer :: i, length
        character(len=len(str), kind=C_CHAR) :: str_copy

        ! Call the C helper, passing the length as the correct int kind
        getCWD = getCWDHelper(str_copy, len(str_copy, kind=C_INT))

        if (getCWD /= 0) then
            str = '' ! Error, clear the string
            return
        end if

        ! Copy the C_CHAR string to the output string,
        ! removing the C_NULL_CHAR and clearing the rest.
        length = index(str_copy, C_NULL_CHAR) - 1
        do i = 1, length
            str(i:i) = char(ichar(str_copy(i:i)))
        end do
        str(length+1:) = ''
    end function getCWD

end module

Test code:

program test
    use cwd, only: getCWD
    implicit none

    character(len=255) :: path
    integer :: error

    error = getCWD(path)

    print *, error
    if (error == 0) print *, path
end program

Making the return value allocatable and looping to get a sufficient size is left as an exercise to the reader.



来源:https://stackoverflow.com/questions/30279228/is-there-an-alternative-to-getcwd-in-fortran-2003-2008

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!