在Fortran 03/08(gfortran编译器)中使用无限多态类型进行数组操作

时间:2014-11-27 08:08:00

标签: arrays class fortran gfortran

我想通过class(*)功能(无限多态)实现有用的数组操作(添加元素,删除元素,通过可分配/指针/二进制树结构实现不同的实现)。我使用gfortran 5.0来处理这样的功能。我需要它不为我使用的每种类型重复相同的代码。

这应该类似于

function add_element(array,element)
  class(*),intent(in)::array(:)
  class(*),intent(in)::element
  class(*)::add_element(size(array)+1)
       add_element=[array,element]
end function

问题是当我尝试使用具有某种确定类型的函数时,返回结果时出错。我不能在没有class(*)的情况下将select type分配给某个明确的类型变量,我肯定不希望每次使用时都选择类型结构。在子程序中,我不应该知道任何我想要使用的类型,因为我会创建很多类型。

我尝试使用move_alloc的一些变体,source,尝试使用带有intent(out)参数的子例程等。它不起作用。我认为它应该在参数属性中定义,与size(使用source关键字?)相同,但是在标准中没有找到这样的结构的示例或定义。当然我会更多地学习这个标准(我不是专业的程序员,但是物理学家试图使我的程序可测试,可检查并且更容易更改)并且现在只需重复此代码等待更好的解决方案,但也许任何人都知道在哪里在标准或一些书中搜索它?我认为这不仅仅是关于数组而是使用class(*),因为我认为应该有不知道类型的方法......

不知道我是否应该添加此子例程的其他非工作形式的示例或它对错误的说法 - 或者问题将不重点。它可以编译,但在所有情况下,在调用中分配确定类型不起作用。对于参数intent(out)(inout),它不能从伪参数变为实际参数。从源重新分配使得一个对象具有类型(以及在我的示例中也分配的结果),但是类型是隐藏的......我不能在子例程中使用select类型,因为我不知道类型。 / p>

此外,我不知道可以检查“与此类型相同”的构造或在此上下文中的某些内容......

2 个答案:

答案 0 :(得分:3)

这不是一个简单的问题您可以使用select type,但Fortran没有type is(type_of(x))之类的内容。另一方面,有SAME_TYPE_AS()EXTENDS TYPE_OF()内在函数,但您不能将它们用作类型保护。

有必要确保arrayelement的动态类型相同。

我认为这是标准的缺陷。

但是,你的方法仍然存在错误。您应该使函数结果可分配,以便能够将其分配给正确的动态类型:

class(*), allocatable ::add_element(:)

您可能会想到以下内容:(未测试!使用gfortran-4.9 ifort14编译)

allocate(add_element(size(array)+1), mold=array)

但是如何实际转移我不知道的价值观,我担心如果不诉诸一些肮脏的伎俩可能是不可能的。

你甚至无法使用transfer,这就是我看到真正缺陷的地方。尽管你可以用多态模具调用转移

transfer(element, add_element(1))

您无法将其分配给数组元素

add_element(1) = transfer(element, add_element(1))

我的观点是,Fortran缺少类型保护选项,只能确保两个变量具有相同的动态类型。

您可能会想到以下内容:(未测试!使用gfortran-4.9 ifort14编译)

function add_element(array,element)
  use iso_c_binding
  implicit none
  class(*),intent(in)::array(:)
  class(*),intent(in)::element
  class(*), allocatable ::add_element(:)
  type(c_ptr) :: tmp

  interface
    function memcpy(dest, src, n) bind(c)
      use iso_c_binding
      integer(c_intptr_t),value :: dest, src
      integer(c_size_t) :: n
      type(c_ptr) :: memcpy
    end function
  end interface

  allocate(add_element(size(array)+1), mold=array)

  tmp = memcpy(loc(add_element(size(array)+1)), &
               loc(array), &
               size(array, kind=c_size_t) * storage_size(array, c_size_t)/8_c_size_t )
  tmp = memcpy(loc(add_element(size(array)+1)), &
               loc(array(1)), &
               storage_size(element, c_size_t)/8_c_size_t )

end function

答案 1 :(得分:2)

CLASS(*)是一种基本上允许运行时类型安全但类型不可知存储的工具。您正在尝试将其用作编译时类型参数化机制。它并不十分合适,语言并不直接支持其他方法。

传统的类型参数化是通过将要参数化的过程的公共部分放在单独的文件中,然后根据需要包含该文件来完成的,可能在使用隐式类型的模块中指定要参数化的类型。

如果必须使用CLASS(*),则实际上需要编写并使用包装类型。如果你所有的包装都是基本的阵列操作,那么这将比它的价值更麻烦。

在客户端代码(与常用程序相比)中提取已存储的东西,通常需要使用SELECT TYPE(如果数据类型有BIND(C)或SEQUENCE,则可以使用指针赋值,但这不是't type safe)。

TYPE :: Wrapper
  CLASS(*), ALLOCATABLE :: item
END TYPE Wrapper

FUNCTION add_element(array, element)
  TYPE(Wrapper), INTENT(IN) :: array(:)
  CLASS(*), INTENT(IN) :: element
  TYPE(Wrapper), INTENT(OUT) :: add_element(SIZE(array)+1)
  ! If you want to enforce type consistency (at runtime)...
  IF (SIZE(array) > 0) THEN
    IF (.NOT. SAME_TYPE_AS(array(1)%item, element)) THEN
      STOP 'Objects not of same type!'
    END IF
  END IF
  add_element(:SIZE(array)) = array
  add_element(SIZE(add_element))%item = element
END FUNCTION add_element

FUNCTION get(scalar)
  TYPE(Wrapper), INTENT(IN) :: scalar
  CLASS(*), ALLOCATABLE :: get
  get = scalar%item
END FUNCTION get

...

TYPE(Wrapper), ALLOCATABLE :: array(:)
array = [ Wrapper :: ]
array = add_element(array, 'cat')
array = add_element(array, 'dog')

DO i = 1, SIZE(array)
  SELECT TYPE (item => get(array(i)))
  TYPE IS (CHARACTER(*))
    PRINT "(A)", item
  END SELECT
END DO