【问题标题】:Using C pointers to track Fortran module data members使用 C 指针跟踪 Fortran 模块数据成员
【发布时间】:2017-05-30 19:10:19
【问题描述】:

我们有一个旧的 Fortran 90 代码,它对一组数据数组进行操作,并希望扩展代码以便我们可以拥有多组数据。不幸的是,没有一个子程序接受输入参数,而是通过模块评估所有数据。以下代码类似于我们的 Fortran 代码。

MODULE test_mod
    IMPLICIT NONE

    double precision, pointer :: f_x(:)
    integer :: n = 5
END MODULE test_mod

SUBROUTINE alloc_x()
    use test_mod
    IMPLICIT NONE

    allocate(f_x(n))
END SUBROUTINE alloc_x

SUBROUTINE init_x()
    USE test_mod
    IMPLICIT NONE

    f_x = 1.0
END SUBROUTINE init_x

SUBROUTINE dealloc_x()
    use test_mod
    IMPLICIT NONE

    deallocate(f_x)
END SUBROUTINE dealloc_x

由于 Fortran 代码足够复杂(大约有一百个数组,并且都具有不同的形状和大小),因此对代码的修改越少越好。我们提出了以下解决方案,并对该解决方案是否被视为与 Fortran 90 标准兼容感兴趣:

我们创建了两个额外的 Fortran 子例程,用于存储分配的 Fortran 数组的位置并将 c 指针复制回模块。

SUBROUTINE store_ptrs(c_x)
    use iso_c_binding
    use test_mod
    IMPLICIT NONE

    TYPE(c_ptr) :: c_x

    c_x = c_loc(f_x)
END SUBROUTINE

SUBROUTINE copy_ptrs2mod(c_x)
    use iso_c_binding
    use test_mod
    IMPLICIT NONE

    TYPE(c_ptr) :: c_x
    CALL c_f_pointer(c_x,f_x,[n])
END SUBROUTINE

通过这两个子程序,我们可以拥有多于一份的数据(以下代码中为 10 份),而无需更改 Fortran 代码 --

#include <stdio.h>
#include <stdlib.h>

void alloc_x_();
void init_x_();
void dealloc_x_();
void store_ptrs_(double **c_x);
void copy_ptrs2mod_(double **c_x);

void output_result(int entry, double* array);

int main()
{
    int i;
    double *x[10];

    /* allocate array */
    for(i=0; i<10; i++){
        alloc_x_();
        store_ptrs_(&x[i]);
    }

    /* initialize array */
    for(i=0; i<10; i++){
        copy_ptrs2mod_(&x[i]);
        init_x_();
        output_result(i,x[i]);
    }

    /* deallocate the array */
    for(i=0; i<10; i++){
        copy_ptrs2mod_(&x[i]);
        dealloc_x_();
    }
}

void output_result(int entry, double* array){
    int j;
    printf("x[%2d] = [", entry);
    for (j = 0; j < 5; ++j)
    {
        if (j == 4)
        {
            printf("%3.1f",array[j]);
            continue;
        }
        printf("%3.1f, ",array[j]);
    }
    printf("]\n");
}

输出 -

x[ 0] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 1] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 2] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 3] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 4] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 5] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 6] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 7] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 8] = [1.0, 1.0, 1.0, 1.0, 1.0]
x[ 9] = [1.0, 1.0, 1.0, 1.0, 1.0]

虽然我们还没有看到这方面的任何问题,但我们有点担心我们可能(隐式地)依赖于各种标准的编译器特定实现,或者与最新标准的其他一些不兼容可能会阻止它工作。我们非常感谢任何有关此方法的 cmets 或反馈,以处理传统 Fortran 90 代码的常见问题。

【问题讨论】:

  • 模块 iso_c_binding 直到 Fortran 2003 才标准化。
  • Fortran 90 已完全过时。算了,不需要 Fortran 90 合规性。 至少使用解决了最大问题的 Fortran 95,但使用 Fortran 2003 或 2008 更好。

标签: fortran


【解决方案1】:

我不会说您的代码/方法是正确的,但我会指出一件引起我最初关注的事情。你可以简单地解决这个问题。

事实上,在 C 端,你有名字修饰来给出原型

void alloc_x_();
void init_x_();
void dealloc_x_();
void store_ptrs_(double **c_x);
void copy_ptrs2mod_(double **c_x);

这表明 Fortran 过程在任何模块之外(子例程语句中没有 bind(c,name='alloc_x_') 等)。在 Fortran 2008 之前,当模块不再在活动范围内被引用时,模块变量变为未定义的规则。

在 C 执行中,alloc_x 被输入 test_mod 被引用。但是,一旦子例程完成,test_mod 就不再在任何活动范围内引用。在 Fortran 90 到 Fortran 2003 规则下,模块变量 f_x 在此时变为未定义。之后,在store_ptrs 中,c_loc(f_x) 中的引用不合规。

save 属性添加到模块局部变量,或遵循 Fortran 2008 标准,可以解决此问题。


话虽如此,根据 Fortran 2003 C 互操作性规则,f_x 不是c_loc 的有效参数:指针参数必须是标量。

【讨论】:

  • 你知道今天仍在使用的任何编译器实际上在某些旧版本中的行为方式吗?
  • 没有。这是“你可能是安全的”之一,但这个问题本质上是在语言律师层面提出的。无论如何,我的编辑可能没有实际意义......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
  • 1970-01-01
  • 2012-05-21
  • 2012-02-29
  • 2013-05-02
相关资源
最近更新 更多