Есть ли альтернатива GETCWD() в Фортране 2003-2008
Расширение GNU для компилятора GNU Fortran предоставляет подпрограмму GETCWD()
это хорошо, получает текущий рабочий каталог. Тем не менее, мой код должен быть переносимым на ifort
а также nagfor
компилятор, и я использую функции F2003.
Итак, есть ли альтернатива GETCWD()
для F2003 и позже?
У меня здесь есть стандарт, но он довольно внушительный, и я уже давно его изучал и не нашел ничего полезного...
2 ответа
Вы также можете использовать 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;
}
}
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
Этот код действителен для Windows и Unix-производных (Linux, OSX, BSD и т. Д.)
Как отмечено в комментариях, вы можете использовать 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
), затем читая этот файл.
Принятый ответ содержит две ошибки (он передает неправильное значение в качестве длины строки GETCWD
и уходит в C_NULL_CHAR
). Этот ответ исправляет эти ошибки и делает интерфейс более удобным для использования в Fortran.
Основная идея та же: вызов getcwd
или же _getcwd
используя C, и вызовите оболочку C, используя возможности взаимодействия Fortran's C. На стороне Fortran подпрограмма-обертка используется для обработки длины строки, поэтому ее не нужно передавать явно.
Также, C_INT
а также C_CHAR
не нужны так же, как целые числа по умолчанию и символы по умолчанию, которые нужны на стороне Фортрана (хотя на практике я не знаю ни одной системы, где C_CHAR
и чар по умолчанию отличаются). Оболочка также конвертирует их. Кроме того, строка, возвращаемая из C, содержит завершающий C_NULL_CHAR
, который должен быть удален для использования строки на стороне Фортрана.
Код C:
#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
Делая возвращаемое значение размещаемым и циклическим, чтобы получить достаточный размер, оставляем читателю в качестве упражнения.