11.4 C语言捆绑 BACKWARD FORWARD


Fortran用户所面对的一个普通问题是需要调用以其它语言书写的过程,特别是那些用C书写或程序中的接口可用C原型描述的过程。虽然许多Fortran实现提供了解决这一问题的方法,但这些解答很少具有可扩展性。

本节定义了一个方法,该方法指定了同C中所定义的过程间的接口,去掉了大多数对于互操作性的障碍,同时保留了可扩展性。

11.4.1 同C中所定义过程的接口的规格说明

如果用户希望指定一个过程通过C过程定义,则这需要用C外部类关键字指定,见第五节

对于指定extrinsic-spec--arg为LANGUAGE=“C”的C子程序,与语法map-to(规则1101)相关联的约束被扩展为如下:

约束:函数应具有整型,实型或双精度型的标量结果。

约束:过程的哑参不能是一个假定形状的数组,不能具有POINTER属性,不能具有TARGET属性,它的子对象也不能具有POINTER属性。

约束:哑参的边界不能用一个除常数说明表达式之外的说明表达式来指定,这种过程的哑参字符长度参数也不能用一个除常数说明表达式之外的说明表达式来指定。

约束:子程序的dummy-arg-list中的dummy-arg不能是*或者哑过程。

EXTERNAL_NAME指示符中的char-literal-constant的值给出了C中所定义的过程名。这个值无须与function-stmt或subroutine-stmt所指定的过程名相同。如果没有指定EXTERNAL_NAME,则它的值就好象是用一个与过程名相同的小写字母指定的。

LANGUAGE="C"的extrinsic-spec-arg有助于编译器鉴别一个用C定义的过程,以便它可以采取适当的步骤保证过程按C编译器所要求的方式被引用。

对实现者的建议:如果系统可利用的不同C编译器提供了不同过程调用惯例或不同数据类型大小,则为多个C编译器提供支持是很有意义的。例如,厂家的编译器可在LANGUAGE=指示符中为GNU_C的值提供支持,或者可通过使用编译开关提供支持。(对实现者的建议结束)

11.4.2 C语言数据类型映射的规格说明

map-to属性是一个主要特征,该特征方便了在Fortran程序中引用用C定义的过程。它允许用户指定所需的变换以便将过程引用上所指定的实参同被引用过程所定义的形参关联起来。这个属性具有几个可选指示符。MAP_TO指示符指明了HPF数据通过编译器转换到的C数据类型;PASS_BY指明了是否传递指向哑参的一个C指针;LAYOUT指示符指明了对于一个数组是否需要从Fortran的数组元素次序转变到C的。

对于C,与map-to(规则1101)相关联的约束可被扩展为如下:

约束:应为所有哑参和LANGUAGE=“C”的显式接口的函数结果变量指定MAP_TO属性。

约束:与哑参相关联的map-to-type-spec应与哑参的类型相兼容。

约束:layout-spec应仅对数组哑参指定。

约束:layout-spec不应为假定大小的数组指定。

如果编译器能够同时描述大小写字母,则为map-to-type-spec,layout-spec或passby-spec所指定的值就无须关心大小写。为确定值,编译器将忽略为map-to-type-spec,layout-spec或passby-spec所指定的的空格。

一种实现将提供一种模块,ISO_C,该模块将定义一个派生类型,C_VOID_POINTER。C_VOID_POINTER类型的部件将是私有的。

对用户的建议:C_VOID_POINTER类型提供了在程序中使用void *指针的方法,但是没有为用户提供任何方式在程序的Fortran部分维护这种指针,这是因为如果具有私有部件的一个对象在定义类型的模块外面,则I/O不能它的上面执行,这种结构的部件或结构构造器也不能在定义类型的模块外面使用。(对用户的建议结束)

map-to-type所允许的值是下列值的任一个:INT,LONG,SHORT,CHAR,SINGED_CHAR,FLOAT,DOUBLE,LONG_DOUBLE,CHAR_PTR,VOID_PTR或者一个用逗号分隔的列表。下表中显示了同这些类型相兼容的HPF类型。

map-to-type-spec是一个括起来的值的列表,如果列表中的每个值与派生类型相应部件兼容,则它与派生类型的哑参兼容。

passby-spec所允许的值是VAL,*,**,***或****。如果不指定passby-spec,则假定值为VAL。如果将passby-spec指定为VAL,则哑参不具有指定的INTENT(OUT)或INTENT(IOUT)属性。如果将passby-spec的值指定为*,**,***,或****,则相关联的实参将是一个变量。

LANGUAGE="C"的过程接口体中为哑参所指定的map-to-type-spec的值应是这样的:至少一个允许的映射到类型与过程C定义中相应形参的C数据类型相同。过程C定义中函数的数据类型是具有LANGUAGE="C"的函数接口体中为函数结果变量所指定的所允许的映射到类型之一(或者与所允许的映射到类型等价)。如果子程序已被指定为LANGUAGE="C",则过程的C定义将被指定为void数据类型。

下表显示了内部类型或派生类型C_VOID_POINTER的变量哑参的所允许的映射到类型:

数组所允许的映射到类型与该类型标量变量的所允许的映射到类型相同,对于数组的每一维,映射到类型的后面紧接一个左中括号([),在往后接一个哑参相应维的范围,最后接一个右中括号(])。如果没有指定LAYOUT=指示符的值,则哑参的相应维从右到左确定;如果将LAYOUT=指示符的值指定为C_ARRAY,则哑参的相应维从左到右确定。

派生类型标量变量所允许的映射到类型是一些结构,该结构的成员是派生类型部件所允许的映射到类型之一。

如果在精度,描述方法,允许值的范围或(哑参类型和允许映射到的哑参类型之间的)存储顺序之间存在不匹配,则编译器将保证用LANGUAGE="C"所定义的过程引用期间,哑参的描述方式与一个允许映射到类型对象的C处理器的期望相兼容。当从过程返回时,编译器将保证实参变量的值被重新存储到指定的类型和类中。

如果类型和映射到类型的允许值范围不同且实参或一些实参子对象的值不在映射到类型的允许值范围之内,则相关联的哑参或哑参子对象的值变成未定义的。相反,如果哑参或一些哑参子对象的值不在相关联的哑参允许值范围之内,且相关联的实参是一个变量,则相关联实参或实参子对象的值变成未定义的。

对用户的建议:创建这些规则是为了保证互操作性的可扩展性。但是,应该注意,对于大的对象,如果在用于数据类型的描述方法和用于允许映射到类型的描述方法之间存在不匹配,则可能会发生很大的开销。(对用户的建议结束)

对用户的建议:在一些情况中,这可能导致实参的值未被过程引用修改就改变了。例如:

 PROGRAM P
  INTERFACE
   EXTRINSIC(LANGUAGE='C') SUBROUTINE C_SUB(R,I)
    REAL(KIND(1.0D0)), MAP_TO('FLOAT'), PASS_BY('*') :: R
    INTEGER, MAP_TO('INT', '*') :: I
   END SUBROUTINE C_SUB
  END INTERFACE
  REAL(KIND(0.0D0)) RR

  RR = 1.0D0 + 1.0D-10
  I = 123456789
  PRINT *, RR
  CALL C_SUB(RR, I)
  PRINT *, RR
 END PROGRAM P

 void c_sub(float *r, int *i)
 {
 }

虽然*r的值在c_sub中没有修改,还是会打印:

 1.00000000010000000
 1.00000000000000000

类似地,虽然*i没有修改,但引用c_sub后I的值可能会变成未定义的。

虽然避免将除缺省实型之外的任意类型的哑参指定为float映射到类型,或避免将除双精度实型之外的任意类型的哑参指定为双精度映射到类型是一个很好的经验,但为要求映射到类型为int或long的对象选定适当的哑参类型可能不是这样简单。(对用户的建议结束)

layout-spec所允许的值是C_ARRAY。

如果没有为哑数组参数指定layout-spec,则数组元素次序将与Fortran所指定的相同。如果layout-spce的值被指定为C_ARRAY,则数组元素次序将被转置以用于过程的引用。

11.4.2.1 数据类型映射的例子

一些例子有助于澄清在Fortran程序中给定一个接口体,允许何种C过程定义。例如,下列接口体:

  INTERFACE
   EXTRINSIC('C') SUBROUTINE C_SUB(I, R, DARR, STRUCT)
    INTEGER, MAP_TO('INT') :: I
    REAL, MAP_TO('FLOAT'), PASS_BY('*') :: R
    REAL(KIND(1.0D0)), MAP_TO('DOUBLE') :: DARR(10)
    TYPE DT
     SEQUENCE
     INTEGER :: I, J
    END TYPE DT
    TYPE(DT), MAP_TO('(INT, LONG)', '*') :: STRUCT
   END SUBROUTINE C_SUB
  END INTERFACE

可对应与具有下列原型的C过程:

  void c_sub(int i, float r*, double darr[10], struct {int i, long j} *)

在layout-spec的下列例子中,

  PROGRAM P
   INTERFACE
    EXTRINSIC('C') SUBROUTINE C_SUB(A, B)
     INTEGER, MAP_TO('INT') :: A(2,2)
     INTEGER, MAP_TO('INT'), LAYOUT('C_ARRAY') :: B(2,2)
    END SUBROUTINE C_SUB
   END INTERFACE

   INTEGER :: AA(2,2), BB(2,2)
   CALL C_SUB(AA, BB)
  END PROGRAM P

  void c_sub(int a[2][2], b[2][2])

元素AA和a之间以及元素BB和b之间的对应关系是:

  AA(1,1) a[0][0]   BB(1,1) b[0][0]
  AA(2,1) a[0][1]   BB(2,1) b[1][0]
  AA(1,2) a[1][0]   BB(1,2) b[0][1]
  AA(2,2) a[1][1]   BB(2,2) b[1][1]


Copyright: NPACT BACKWARD FORWARD