Как переопределить конструктор структуры в fortran
В настоящее время возможно переопределить конструктор структуры в 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
Это в основном генерирует кучу ошибок из-за избыточных имен переменных (например, Error: DERIVED атрибут "mytype" конфликтует с атрибутом PROCEDURE в (1)). Вербальная копия примера fortran 2003 генерирует аналогичные ошибки. Я пробовал это в gfortran 4.4, ifort 10.1 и 11.1, и все они вызывают те же ошибки.
Мой вопрос: это просто нереализованная функция fortran 2003? Или я неправильно реализую это?
Изменить: я столкнулся с сообщением об ошибке и объявленным патчем в gfortran по этой проблеме, Тем не менее, я пробовал использовать ноябрьскую сборку gcc46 без везения и подобных ошибок.
Изменить 2: приведенный выше код работает с использованием Intel Fortran 12.1.0.
Ответы
Ответ 1
Я обратился к моей копии стандарта Fortran 2008. Это позволяет вам определить общий интерфейс с тем же именем, что и производный тип. Мой компилятор (Intel Fortran 11.1) не будет компилировать код, поэтому я остаюсь подозревающим (без копии стандарта 2003 года), что это еще не реализованная функция стандарта Fortran 2003.
Кроме того, в вашей программе есть ошибка. Объявление функции:
type(mytype) function init_mytype
integer, intent(in) :: i
указывает существование и намерение аргумента, который отсутствует в спецификации функции, который, возможно, должен быть переписан как:
type(mytype) function init_mytype(i)
Ответ 2
В настоящее время возможно переопределить конструктор структуры в Fortran?
Нет. В любом случае даже использование вашего подхода полностью не связано с переопределением конструктора. Основная причина заключается в конструкторе конструктора # ООП. Существует некоторое сходство, но это еще одна идея.
Вы не можете использовать свою неинтерминированную функцию в выражении инициализации. Вы можете использовать только конструктор констант, массив или структуру, встроенные функции,... Для получения дополнительной информации см. 7.1.7. Инициализация в проекте Fortran 2003.
Учитывая этот факт, я полностью не понимаю, что представляет собой реальная разница между
type(mytype) :: x
x = mytype(0)
и
type(mytype) :: x
x = init_mytype(0)
и в чем смысл использования блока INTERFACE в модуле mymod.
Ну, честно говоря, есть разница, огромная - первый способ вводит в заблуждение. Эта функция не является конструктором (поскольку в Fortran нет конструкторов ООП), это инициализатор.
В mainstream OOP конструктор отвечает за выполнение двух действий:
- Распределение памяти.
- Инициализация члена.
Посмотрим на некоторые примеры экземпляров классов на разных языках.
В Java:
MyType mt = new MyType(1);
скрывается очень важный факт - факт, что объект фактически является указателем на varibale типа класса. Эквивалент в С++ будет распределением в куче, используя:
MyType* mt = new MyType(1);
Но на обоих языках видно, что две обязанности конструктора отражаются даже на уровне синтаксиса. Он состоит из двух частей: ключевое слово new (распределение) и имя конструктора (инициализация). В синтаксисе Objective-C этот факт еще более подчеркивается:
MyType* mt = [[MyType alloc] init:1];
Много раз, однако, вы можете увидеть другую форму вызова конструктора. В случае распределения в стеке С++ используется специальная (очень плохая) конструкция синтаксиса
MyType mt(1);
который на самом деле настолько вводит в заблуждение, что мы просто не можем его рассматривать.
В Python
mt = MyType(1)
и тот факт, что объект фактически является указателем, и тот факт, что выделение происходит сначала, скрыты (на уровне синтаксиса). И этот метод называется... __init__
! O_O Так вводит в заблуждение. C++ распределение стека исчезает по сравнению с этим. =)
Во всяком случае, идея иметь конструктор в языке подразумевает возможность сделать выделение инициализации в одном выражении с использованием какого-то особого метода. И если вы думаете, что это "истинный ООП", у меня плохие новости для вас. Даже Smalltalk не имеет конструкторов. Это просто соглашение, чтобы иметь метод new
на самих классах (это одноэлементные объекты мета классов). Factory шаблон дизайна используется на многих других языках для достижения той же цели.
Я где-то читал, что концепции модулей в Fortran были вдохновлены Modula-2. И мне кажется, что функции OOP вдохновлены Oberon-2. В Оберон-2 нет конструкторов. Но есть, конечно, чистое распределение с предопределенной процедурой NEW (например, ALLOCATE в Fortran, но ALLOCATE - это оператор). После выделения вы можете (на практике) вызвать некоторый инициализатор, который является обычным методом. Ничего особенного.
Таким образом, вы можете использовать какие-то фабрики для инициализации объектов. Это то, что вы на самом деле делали с использованием модулей вместо одноэлементных объектов. Или лучше сказать, что они (программисты Java/С#/...) используют методы одиночных объектов вместо обычных функций из-за отсутствия более поздней (без модулей - нет возможности иметь обычные функции, только методы).
Также вы можете использовать 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
INTENT(OUT)
для this
arg init
SUBROUTINE, кажется, в порядке. Потому что мы ожидаем, что этот метод будет вызван только один раз и сразу после выделения. Может быть хорошей идеей контролировать, что это предположение не будет ошибкой. Чтобы добавить некоторый логический флаг LOGICAL :: inited
в mytype
, проверьте, есть ли он .false.
и установите его при .true.
при первой инициализации и сделайте что-нибудь еще при попытке повторной инициализации. Я определенно помню какой-то поток об этом в группах Google... Я не могу найти его.