6

新しいプロジェクトで、Fortran2003 のオブジェクト指向機能を使用することを検討しています。私が試したことの 1 つは、多相型へのポインターを返す関数 (サブルーチンではない) を指すプロシージャー ポインターです。さまざまなコンパイラからさまざまな結果が得られるため、そのような構成が合法であるかどうか疑問に思います (以下を参照)。

具体的な例として、次の関数インターフェイスを考えてみましょう。

abstract interface
   function if_new_test(lbls) result(t)
      import :: test_t
      class(test_t),pointer       :: t
      character(len=*),intent(in) :: lbls(:)
   end function if_new_test
end interface

また、使用するコードには、このインターフェイスを使用して関数を指すことができるプロシージャ ポインターが必要です。

procedure(if_new_test),pointer :: nt

gfortran (4.7.2) がこのプロシージャ ポインタ宣言について次のメッセージで不平を言うため、これが合法かどうかを尋ねています。

エラー: (1) の CLASS 変数 'nt' は、ダミー、割り当て可能、またはポインターでなければなりません

このエラーメッセージ自体がポインターであるため、私は理解できませんnt。また、それが指す関数が返すものもポインターです。

参考までに、この例の完全なソース コードを次に示します。まず、私の派生型、インターフェイス、および関数/サブルーチンを含むモジュール:

module test_m

   implicit none

   type :: test_t
      character(len=10) :: label
      contains
      procedure :: print => print_test
   end type test_t

   type,extends(test_t) :: test2_t
      character(len=10) :: label2
      contains
      procedure :: print => print_test2
   end type test2_t

   abstract interface
      function if_new_test(lbls) result(t)
         import :: test_t
         class(test_t),pointer       :: t
         character(len=*),intent(in) :: lbls(:)
      end function if_new_test
      subroutine if_make_test(t,lbls)
         import :: test_t
         class(test_t),pointer       :: t
         character(len=*),intent(in) :: lbls(:)
      end subroutine if_make_test
   end interface

   contains

   subroutine print_test(self)
      implicit none
      class(test_t),intent(in) :: self
      print *, self%label
   end subroutine print_test

   subroutine print_test2(self)
      implicit none
      class(test2_t),intent(in) :: self
      print *, self%label, self%label2
   end subroutine print_test2

   function new_test(lbls) result(t)
      implicit none
      class(test_t),pointer       :: t
      character(len=*),intent(in) :: lbls(:)
      call make_test(t,lbls)
   end function new_test

   function new_test2(lbls) result(t)
      implicit none
      class(test_t),pointer       :: t
      character(len=*),intent(in) :: lbls(:)
      call make_test2(t,lbls)
   end function new_test2

   subroutine make_test(t,lbls)
      implicit none
      class(test_t),pointer       :: t
      character(len=*),intent(in) :: lbls(:)
      allocate(test_t::t)
      t%label = lbls(1)
   end subroutine make_test

   subroutine make_test2(t,lbls)
      implicit none
      class(test_t),pointer       :: t
      character(len=*),intent(in) :: lbls(:)
      allocate(test2_t::t)
      select type(t) ! so the compiler knows the actual type
         type is(test2_t)
            t%label  = lbls(1)
            t%label2 = lbls(2)
         class default
            stop 1
      end select
   end subroutine make_test2  

end module test_m

そして、このモジュールを使用するメインプログラム:

program test

   use test_m
   implicit none

   class(test_t),pointer           :: p
   procedure(if_make_test),pointer :: mt
   procedure(if_new_test),pointer  :: nt

   mt => make_test
   call mt(p,["foo"])
   call p%print
   deallocate(p)

   mt => make_test2
   call mt(p,["bar","baz"])
   call p%print
   deallocate(p)

   p => new_test(["foo"])
   call p%print
   deallocate(p)

   p => new_test2(["bar","baz"])
   call p%print
   deallocate(p)

   nt => new_test
   p => nt(["foo"])
   call p%print
   deallocate(p)

   nt => new_test2
   p => nt(["bar","baz"])
   call p%print
   deallocate(p)

end program test

プログラムは最初にサブルーチン make_testとを介してオブジェクトを作成しますmake_test2。私のテストでは、これは私が試したすべてのコンパイラで動作します。次に、関数 new_testandを直接呼び出してオブジェクトを作成しnew_test2ます。これは私のテストでも機能します。最後に、これらの関数を介してオブジェクトを再度作成する必要がありますが、手続きポインタを介して間接的に作成する必要がありますnt

上記のように、gfortran (4.7.2) は の宣言をコンパイルしませんnt

ifort (12.0.4.191) は、行で内部コンパイラ エラーを生成しますnt => new_test

pgfortran (12.9) は警告なしでコンパイルされ、実行可能ファイルは期待される結果を生成します。

Fortran2003 によると、私がやろうとしていることは違法ですか、それともそのような機能に対するコンパイラのサポートはまだ不十分ですか? 関数の代わりにサブルーチンを使用する必要がありますか (動作しているようです)。

4

1 に答える 1

2

あなたのコードは問題ないようです。Intel 13.0.1 と NAG 5.3.1 の両方で問題なくコンパイルできました。古いコンパイラには、Fortran 2003 のより「派手な」機能に問題がある可能性があります。

問題によっては、ポインターの代わりに割り当て可能な型を使用することもできます。一方、関数の結果として多相型を返すことはできません。

module test_m
  implicit none

  type :: test_t
    character(len=10) :: label
  contains
    procedure :: print => print_test
  end type test_t

  type,extends(test_t) :: test2_t
    character(len=10) :: label2
  contains
    procedure :: print => print_test2
  end type test2_t

  abstract interface
    function if_new_test(lbls) result(t)
      import :: test_t
      class(test_t), allocatable :: t
      character(len=*),intent(in) :: lbls(:)
    end function if_new_test

    subroutine if_make_test(t,lbls)
      import :: test_t
      class(test_t), allocatable :: t
      character(len=*),intent(in) :: lbls(:)
    end subroutine if_make_test
  end interface

contains

  subroutine print_test(self)
    class(test_t), intent(in) :: self
    print *, self%label
  end subroutine print_test

  subroutine print_test2(self)
    class(test2_t), intent(in) :: self
    print *, self%label, self%label2
  end subroutine print_test2

  subroutine make_test(t,lbls)
    class(test_t), allocatable :: t
    character(len=*),intent(in) :: lbls(:)
    allocate(test_t::t)
    t%label = lbls(1)
  end subroutine make_test

  subroutine make_test2(t,lbls)
    class(test_t), allocatable :: t
    character(len=*),intent(in) :: lbls(:)
    allocate(test2_t::t)
    select type(t) ! so the compiler knows the actual type
    type is(test2_t)
      t%label  = lbls(1)
      t%label2 = lbls(2)
    class default
      stop 1
    end select
  end subroutine make_test2

end module test_m


program test
   use test_m
   implicit none

   class(test_t), allocatable :: p
   procedure(if_make_test), pointer :: mt

   mt => make_test
   call mt(p, ["foo"])
   call p%print
   deallocate(p)

   mt => make_test2
   call mt(p, ["bar","baz"])
   call p%print
   deallocate(p)

end program test

もう 1 つ注意: モジュール レベルの暗黙的な none ステートメントは、モジュール プロシージャによって "継承" されるため、すべてのサブルーチン エクストラでそれを発行する必要はありません。

于 2013-02-15T16:56:54.657 に答える