Есть ли альтернатива GETCWD() в Fortran 2003-2008?

Расширение GNU для компилятора GNU Fortran предоставляет подпрограмму GETCWD(), которая получает текущий рабочий каталог. Однако мой код также должен быть переносимым на компиляторы ifort и nagfor, и я использую функции F2003.

Итак, есть ли альтернатива GETCWD() для F2003 и выше?

У меня есть стандарт здесь, но он довольно большой, и я просматривал его некоторое время и не нашел ничего полезного...


person LienM    schedule 16.05.2015    source источник


Ответы (3)


Вы также можете использовать ISO_C_Binding и вызывать соответствующие функции C:

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;
  }
}

тест.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

Этот код действителен для Windows и Unix-производных (Linux, OSX, BSD и т. д.)

person Alexander Vogt    schedule 16.05.2015
comment
Спасибо за исчерпывающий ответ! :) У меня нет опыта взаимодействия с C, но я попробую. Правильно ли я скажу, что мой make-файл станет немного сложнее, так как мне нужно определить компилятор для C? - person LienM; 17.05.2015
comment
Это зависит... Чтобы скомпилировать этот пример, я просто использовал gfortran -Wall -Wextra -g cwd.c test.F90. - person Alexander Vogt; 17.05.2015

Как отмечено в комментариях, вы можете использовать get_environment_variable, который является стандартным Fortran (например, F2008 13.7.67). Этот пример программы запрашивает значение $PWD, которое должно содержать каталог, в котором находилась ваша оболочка, когда вы вызывали исполняемый файл.

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

И его вывод:

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

Это стандартный Fortran, но его переносимость будет ограничена Unix и Unix-подобными оболочками, которые устанавливают эту переменную.

Другим вариантом, хотя и стандартным, но уродливым (на мой взгляд), было бы использование execute_command_line для запуска команды, которая может вывести рабочий каталог во временный файл (например, pwd > /tmp/mypwd), а затем прочитать этот файл.

person casey    schedule 16.05.2015
comment
PWD недоступен для Windows, вам нужно запросить CD. - person Alexander Vogt; 16.05.2015
comment
Спасибо, Кейси! :) Поскольку переносимость в этом случае определенно является проблемой, я принял ответ Александра, но ваш определенно пригодится мне и другим в будущем. - person LienM; 17.05.2015

Принятый ответ содержит две ошибки (он передает неправильное значение длины строки в GETCWD и оставляет в C_NULL_CHAR). Этот ответ исправляет эти ошибки и делает интерфейс более удобным для использования с Fortran.

Основная идея та же самая: вызовите getcwd или _getcwd, используя C, и вызовите оболочку C, используя функции взаимодействия Fortran C. На стороне Фортрана для обработки длины строки используется подпрограмма-оболочка, поэтому ее не нужно передавать явно.

Кроме того, C_INT и C_CHAR не являются необходимыми, как целые числа по умолчанию и символы по умолчанию, которые нужны на стороне Фортрана (хотя на практике я не знаю ни одной системы, в которой C_CHAR и символ по умолчанию различаются). Обертка также преобразует их. Кроме того, строка, возвращаемая из C, содержит завершающий символ C_NULL_CHAR, который необходимо удалить, чтобы строку можно было использовать на стороне Fortran.

Код С:

#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;
}

Код Фортрана:

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

Тестовый код:

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

Сделать возвращаемое значение выделяемым и зациклить для получения достаточного размера мы оставляем читателю в качестве упражнения.

person rtoijala    schedule 17.05.2019