【问题标题】:Propagate error string: Fortran > C传播错误字符串:Fortran > C
【发布时间】:2015-09-22 06:51:35
【问题描述】:

问题陈述

我的代码的主要部分是 C(从 Python 调用)。 C 部分调用用 Fortran 编写的函数。使用错误代码和带有错误描述的错误字符串传播可能的错误。

问题是我似乎无法获得正确的接口来在 Fortran 中写入字符串并在 C 中读取/复制/操作它。下面的代码概述了我想要做的事情,标有 * ... * 的 cmets 表示需要扩展的地方。

C

// global variable: read from Python if an error is encountered
char* error_string;

// template for the Fortan-subroutine
void fortran_calculation_( double* , int* );


int heavy_calculation( double* x )
{

  int error_code;


  // ... some code ...


  // * should accept and write "error_string" *
  fortran_calculation_( x , &error_code );

  if ( error_code ) 
  {
    error_string = "TO BE WRITTEN BY FORTRAN > REMOVE!!";
    return 1;
  }


  // ... some code ...


  return 0;

}

Fortran

subroutine fortran_calculation_(x,error_code)

implicit none

! * include "error_string" as argument *
real*8  :: x
integer :: error_code


! ... some code ...


if ( ... ) then
  ! * write "error_string" *
  error_code = 1
  return
end if


return 
end subroutine

我已经尝试了很多东西,但我似乎无法让它工作......

【问题讨论】:

  • 您的错误字符串是否需要是全局的,还是可以作为参数传递给您的 Fortran 例程?
  • 要传递字符串,也可以看看这个问题:stackoverflow.com/q/9972743/577108
  • 也可以作为参数传递给 Fortran 例程,但最后 error_string 指针应该指向字符串。这与我如何从 Python 中读取它有关

标签: c fortran


【解决方案1】:

你有两个问题。一、如何从 Fortran 中访问一个 C 全局变量。这个比较简单,用iso_c_binding在一个模块中创建一个接口。示例见https://gcc.gnu.org/onlinedocs/gfortran/Interoperable-Global-Variables.html

但是,更棘手的问题是您将 error_string 定义为指向 char 的指针。这意味着您的 Fortran 代码必须在写入之前分配字符串。 Fortran 可分配变量和指针变量使用描述符,而不是原始指针,因此您必须首先创建 C malloc 函数的接口。只有在那之后,你才能写信给它。比如:


module my_error_string
  use iso_c_binding
  interface
     type(c_ptr) function c_malloc(size) bind(C, name="malloc")
       use iso_c_binding
       integer(kind=c_size_t), value :: size
     end function c_malloc
  end interface
  type(c_ptr), bind(C) :: error_string

contains
  subroutine write_error(str)
    character(len=*) :: str
    character, pointer :: fstr(:)
    integer(c_size_t) :: strlen
    integer :: i

    strlen = len(str, kind=c_size_t) + 1_c_size_t
    error_string = c_malloc(strlen)
    if (.not. c_associated(error_string)) then
       call perror("error_string is a null pointer => malloc failed?!")
       stop 1
    end if
    call c_f_pointer(error_string, fstr, shape=[strlen])
    do i = 1, len(str)
       fstr(i) = str(i:i)
    end do
    fstr(strlen) = c_null_char
  end subroutine write_error
end module my_error_string

(更改接口可能很简单,而是将分配的 C 字符串传递给 Fortran 函数以进行填充,或者使用回调函数。但如果这是你想要的,上述方法有效。)

【讨论】:

  • 感谢您的解决方案!原则上这更优雅,但我想我会使用@Gilles 的解决方案,因为它会产生更简单的 Fortran 代码。这是一个实际的选择,主要与我如何重用代码的 Fortran 部分有关。
  • @Tom:修复了示例,因此它现在可以正常工作了。但是,是的,不管你想怎么做,跨 C-Fortran 边界的字符串处理都很棘手。
【解决方案2】:

根据您提供的设计,这是针对您的问题的丑陋的“解决方案”。

main.c:

#include <stdio.h>
#include <string.h>

char error_string_[512];

void fortan_calculation_( double*, int*, int* );

int main() {
   double d = 2.5;
   int l, i = 3;
   memset( error_string_, 0, 512 );

   fortan_calculation_( &d, &i, &l );
   error_string_[l] = 0;
   printf( "After call: '%s'\n", error_string_ );

}

error.f90:

subroutine fortan_calculation( d, i, l )
    implicit none
    character(512) str
    common /error_string/ str
    double precision d
    integer i, l

    str = "Hello world!"
    l = len_trim( str )
end subroutine fortan_calculation

编译测试:

$ gcc -c main.c
$ gfortran -c error.f90
$ gcc main.o error.o -lgfortran
$ ./a.out 
After call: 'Hello world!'

但这只是令人作呕的代码:它假定了 Fortran 编译器的许多(可以说)常见做法,而它存在一些使用 iso_c_binding Fortran 模块正确链接 C 和 Fortran 的方法。

我会看看我是否能想出一个适当的解决方案。


编辑:实际上,有一个nice SO page about that 可用。

【讨论】:

  • 尽管它很丑,但它完全符合我的要求,谢谢!一个好处是没有“交叉依赖”。我没有说的是我将 Fortran 代码也用作本机 Fortran 代码的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-23
相关资源
最近更新 更多