如何在 Fortran 中重写结构构造函数

发布于 2024-10-04 14:33:43 字数 974 浏览 10 评论 0原文

目前是否可以重写 Fortran 中的结构构造函数?我见过这样的建议示例(例如在 Fortran 2003 规范中):

module mymod

  type mytype
    integer :: x
    ! Other stuff
  end type

  interface mytype
    module procedure init_mytype
  end interface

contains
  type(mytype) function init_mytype(i)
    integer, intent(in) :: i
    if(i > 0) then
      init_mytype%x = 1
    else
      init_mytype%x = 2
    end if
  end function
end

program test
  use mymod
  type(mytype) :: x
  x = mytype(0)
end program

由于冗余变量名称,这基本上会生成一堆错误(例如错误:'mytype' 的 DERIVED 属性与 (1) 处的 PROCEDURE 属性冲突)。 Fortran 2003 示例的逐字副本会生成类似的错误。我已经在 gfortran 4.4、ifort 10.1 和 11.1 中尝试过,它们都会产生相同的错误。

我的问题:这只是 fortran 2003 未实现的功能吗?或者我错误地执行了此操作?

编辑:我遇到了 错误报告针对此问题发布了 gfortran 补丁。但是,我尝试使用 11 月版本的 gcc46,但没有成功并出现类似错误。

编辑 2:上述代码似乎可以使用 Intel Fortran 12.1.0 运行。

Is it currently possible to override the structure constructor in Fortran? I have seen proposed examples like this (such as in the Fortran 2003 spec):

module mymod

  type mytype
    integer :: x
    ! Other stuff
  end type

  interface mytype
    module procedure init_mytype
  end interface

contains
  type(mytype) function init_mytype(i)
    integer, intent(in) :: i
    if(i > 0) then
      init_mytype%x = 1
    else
      init_mytype%x = 2
    end if
  end function
end

program test
  use mymod
  type(mytype) :: x
  x = mytype(0)
end program

This basically generates a heap of errors due to redundant variable names (e.g. Error: DERIVED attribute of 'mytype' conflicts with PROCEDURE attribute at (1)). A verbatim copy of the fortran 2003 example generates similar errors. I've tried this in gfortran 4.4, ifort 10.1 and 11.1 and they all produce the same errors.

My question: is this just an unimplemented feature of fortran 2003? Or am I implementing this incorrectly?

Edit: I've come across a bug report and an announced patch to gfortran regarding this issue. However, I've tried using a November build of gcc46 with no luck and similar errors.

Edit 2: The above code appears to work using Intel Fortran 12.1.0.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

翻了热茶 2024-10-11 14:33:43

当前是否可以重写 Fortran 中的结构构造函数?

不。无论如何,即使使用你的方法也完全不是关于构造函数重写。主要原因是结构构造函数#OOP构造函数。有一些相似之处,但这只是另一个想法。

您不能在初始化表达式中使用非内在函数。您只能使用常量、数组或结构体构造函数、内部函数……有关详细信息,请参阅 Fortran 2003 草案中的 7.1.7 初始化表达式。

考虑到这一事实,我完全不明白

type(mytype) :: x
x = mytype(0)

type(mytype) :: x
x = init_mytype(0)

之间的真正区别是什么,以及在 mymod MODULE 中使用 INTERFACE 块的全部意义是什么。

好吧,老实说,这是有区别的,而且是巨大的区别——第一种方法是误导性的。这个函数不是构造函数(因为 Fortran 中根本没有 OOP 构造函数),它是一个初始值设定项。


在主流的 OOP 构造函数中,它负责顺序执行两件事:

  1. 内存分配。
  2. 成员初始化。

让我们看一下用不同语言实例化类的一些示例。

Java中:

MyType mt = new MyType(1);

隐藏了一个非常重要的事实 - 对象实际上是指向类类型变量的指针。 C++ 中的等效项将是在堆上分配,使用:

MyType* mt = new MyType(1);

但在两种语言中,我们都可以看到即使在语法级别也反映了两个构造函数职责。它由两部分组成:关键字new(分配)和构造函数名称(初始化)。在Objective-C语法中,这一事实得到了进一步强调:

MyType* mt = [[MyType alloc] init:1];

但是,很多时候,您可以看到一些其他形式的构造函数调用。在在堆栈上分配的情况下,C++ 使用特殊的(非常差的)语法构造

MyType mt(1);

,这实际上非常具有误导性,我们可以不考虑它。

Python中,

mt = MyType(1)

对象实际上是一个指针的事实和首先发生分配的事实都被隐藏了(在语法级别)。而这个方法被称为...__init__! O_O 太误导人了。与那个相比,С++ 堆栈分配逐渐减弱。 =)


无论如何,在语言中拥有构造函数的想法意味着能够使用某种特殊的方法在一个语句中分配初始化。如果您认为这是“真正的 OOP”方式,那么我有坏消息要告诉您。甚至 Smalltalk 没有构造函数。它只是一个约定,在类本身上有一个 new 方法(它们是元类的单例对象)。许多其他语言都使用工厂设计模式来实现相同的目标。

我在某处读到,Fortran 中的模块概念受到 Modula-2 的启发。在我看来,OOP 功能的灵感来自于 Oberon-2。 Oberon-2 中也没有构造函数。但当然也有预声明过程 NEW 的纯分配(类似于 Fortran 中的 ALLOCATE,但 ALLOCATE 是语句)。分配后,您可以(实际上应该)调用一些初始化程序,这只是一个普通的方法。那里没什么特别的。

所以你可以使用某种工厂来初始化对象。这就是您使用模块而不是单例对象实际所做的事情。或者更好的说法是,他们(Java/C#/...程序员)使用单例对象方法而不是普通函数,因为缺少后者(没有模块 - 无法拥有普通函数,只有方法)。

您也可以使用类型绑定的 SUBROUTINE 来代替。

MODULE mymod

  TYPE mytype
    PRIVATE
    INTEGER :: x
    CONTAINS
    PROCEDURE, PASS :: init
  END TYPE

CONTAINS

  SUBROUTINE init(this, i)
    CLASS(mytype), INTENT(OUT) :: this
    INTEGER, INTENT(IN) :: i

    IF(i > 0) THEN
      this%x = 1
    ELSE
      this%x = 2
    END IF
  END SUBROUTINE init

END

PROGRAM test

  USE mymod

  TYPE(mytype) :: x

  CALL x%init(1)

END PROGRAM

init SUBROUTINE 的 this arg 的 INTENT(OUT) 似乎没问题。因为我们希望该方法仅在分配后调用一次。控制这个假设不会是错误的可能是个好主意。要向 mytype 添加一些布尔标志 LOGICAL :: inited,请检查它是否为 .false. 并将其设置为 .true。 在第一次初始化时,并在尝试重新初始化时执行其他操作。我肯定记得 Google 网上论坛中有一些关于它的帖子...我找不到它。

Is it currently possible to override the structure constructor in Fortran?

No. Anyway even using your approach is completely not about constructor overriding. The main reason is that structure constructor # OOP constructor. There is some similarity but this is just another idea.

You can not use your non-intrinsic function in initialization expression. You can use only constant, array or structure constructor, intrinsic functions, ... For more information take a look at 7.1.7 Initialization expression in Fortran 2003 draft.

Taking that fact into account I completely do not understand what is the real difference between

type(mytype) :: x
x = mytype(0)

and

type(mytype) :: x
x = init_mytype(0)

and what is the whole point of using INTERFACE block inside mymod MODULE.

Well, honestly speaking there is a difference, the huge one - the first way is misleading. This function is not the constructor (because there are no OOP constructors at all in Fortran), it is an initializer.


In mainstream OOP constructor is responsible for sequentially doing two things:

  1. Memory allocation.
  2. Member initialization.

Let's take a look at some examples of instantiating classes in different languages.

In Java:

MyType mt = new MyType(1);

a very important fact is hidden - the fact the object is actually a pointer to a varibale of a class type. The equivalent in C++ will be allocation on heap using:

MyType* mt = new MyType(1);

But in both languages one can see that two constructor duties are reflected even at syntax level. It consists of two parts: keyword new (allocation) and constructor name (initialization). In Objective-C syntax this fact is even more emphasized:

MyType* mt = [[MyType alloc] init:1];

Many times, however, you can see some other form of constructor invocation. In the case of allocation on stack C++ uses special (very poor) syntax construction

MyType mt(1);

which is actually so misleading that we can just not consider it.

In Python

mt = MyType(1)

both the fact the object is actually a pointer and the fact that allocation take place first are hidden (at syntax level). And this method is called ... __init__! O_O So misleading. С++ stack allocation fades in comparison with that one. =)


Anyway, the idea of having constructor in the language imply the ability to do allocation an initialization in one statement using some special kind of method. And if you think that this is "true OOP" way I have bad news for you. Even Smalltalk doesn't have constructors. It just a convention to have a new method on classes themselves (they are singleton objects of meta classes). The Factory Design Pattern is used in many other languages to achieve the same goal.

I read somewhere that concepts of modules in Fortran was inspired by Modula-2. And it seems for me that OOP features are inspired by Oberon-2. There is no constructors in Oberon-2 also. But there is of course pure allocation with predeclared procedure NEW (like ALLOCATE in Fortran, but ALLOCATE is statement). After allocation you can (should in practice) call some initializer, which is just an ordinary method. Nothing special there.

So you can use some sort of factories to initialize objects. It's what you actually did using modules instead of singleton objects. Or it's better to say that they (Java/C#/... programmers) use singleton objects methods instead of ordinary functions due to the lack of the later one (no modules - no way to have ordinary functions, only methods).

Also you can use type-bound SUBROUTINE instead.

MODULE mymod

  TYPE mytype
    PRIVATE
    INTEGER :: x
    CONTAINS
    PROCEDURE, PASS :: init
  END TYPE

CONTAINS

  SUBROUTINE init(this, i)
    CLASS(mytype), INTENT(OUT) :: this
    INTEGER, INTENT(IN) :: i

    IF(i > 0) THEN
      this%x = 1
    ELSE
      this%x = 2
    END IF
  END SUBROUTINE init

END

PROGRAM test

  USE mymod

  TYPE(mytype) :: x

  CALL x%init(1)

END PROGRAM

INTENT(OUT) for this arg of init SUBROUTINE seems to be fine. Because we expect this method to be called only once and right after allocation. Might be a good idea to control that this assumption will not be wrong. To add some boolean flag LOGICAL :: inited to mytype, check if it is .false. and set it to .true. upon first initialization, and do something else on attempt to re-initialization. I definitely remember some thread about it in Google Groups... I can not find it.

场罚期间 2024-10-11 14:33:43

我查阅了 Fortran 2008 标准的副本。这确实允许您定义与派生类型同名的通用接口。我的编译器(Intel Fortran 11.1)不会编译代码,所以我怀疑(手头没有 2003 标准的副本)这是 Fortran 2003 标准尚未实现的功能。

除此之外,你的程序有错误。您的函数声明:

  type(mytype) function init_mytype
    integer, intent(in) :: i

指定函数规范中不存在的参数的存在和意图,该参数也许应该重写为:

  type(mytype) function init_mytype(i)

I consulted my copy of the Fortran 2008 standard. That does allow you to define a generic interface with the same name as a derived type. My compiler (Intel Fortran 11.1) won't compile the code though so I'm left suspecting (without a copy of the 2003 standard to hand) that this is an as-yet-unimplemented feature of the Fortran 2003 standard.

Besides that, there is an error in your program. Your function declaration:

  type(mytype) function init_mytype
    integer, intent(in) :: i

specifies the existence and intent of an argument which is not present in the function specification, which should perhaps be rewritten as:

  type(mytype) function init_mytype(i)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文