Создание разнородных массивов в Fortran

Я пытаюсь создать разнородные массивы, содержащие переменные разных типов, например, [ 1.0, 7, "hi" ]. Я попытался включить class(*) или type(*) в конструктор массива (см. конец следующего кода), но gfortran5.2 просто считает это синтаксической ошибкой. Есть ли способ создать такой массив с помощью конструктора массива или необходимо использовать другой подход (например, определить тип, содержащий каждый элемент отдельно)?


Подробнее:

Следующий код является примером того, почему я хочу создать такой массив. Подпрограмма checktype_multi получает несколько аргументов с помощью ключевого слова optional, но этот подход явно ограничен из-за фиксированного количества аргументов. Чтобы разрешить произвольное количество аргументов, я попробовал процедуру checktype_array, но кажется невозможным передать массив с разными типами... Более практичный case может заключаться в создании подпрограммы для печати переменного количества аргументов с различными типами.

module mymod
    implicit none
contains

    subroutine checktype ( x )
        class(*) :: x

        select type ( x )
            type is ( integer )      ; print *, "int    : ", x
            type is ( real )         ; print *, "real   : ", x
            type is ( character(*) ) ; print *, "string : ", x
        endselect
    end subroutine

    subroutine checktype_multi ( x1, x2, x3 )
        class(*), optional :: x1, x2, x3

        print *
        if ( present( x1 ) ) call checktype ( x1 )
        if ( present( x2 ) ) call checktype ( x2 )
        if ( present( x3 ) ) call checktype ( x3 )
    end subroutine

    subroutine checktype_array ( a )
        class(*) :: a(:)
        integer :: k

        print *
        do k = 1, size( a )
            call checktype ( a( k ) )
        enddo
    end subroutine

end module

program main
    use mymod

    call checktype_multi ( 1.0 )
    call checktype_multi ( 1.0, 7 )
    call checktype_multi ( 1.0, 7, "hi" )

    ! call checktype_array ( [ 1.0, 7, "hi" ] )  !! error (this is to be expected)

    !>>> Here is the problem.
    ! call checktype_array ( [ type(*)  :: 1.0, 7, "hi" ] )  !! this is also an error
    ! call checktype_array ( [ class(*) :: 1.0, 7, "hi" ] )  !! this too
end program

person roygvib    schedule 10.11.2015    source источник


Ответы (1)


Элементы массива могут отличаться друг от друга только по значению. Они не могут отличаться по типу или любому другому атрибуту.

Вместо этого используйте оболочку производного типа вокруг неограниченного полиморфного выделяемого компонента. Затем динамический тип компонента считается частью значения объекта типа-оболочки.

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

CALL sub([wrapper(1), wrapper(2.0), wrapper('3')])

(Конструктор массива (или конструктор структуры) задает значение. Само значение не может быть полиморфным, тип значения всегда является просто типом значения. Синтаксис для необязательного начального спецификация типа в конструктор массива отражает это, поскольку это просто спецификация типа, а не спецификация типа объявления.)

person IanH    schedule 10.11.2015
comment
Привет @IanH, большое спасибо за информацию. Поскольку подпрограмма checktype_multi могла принимать литералы разных типов (например, 1.0 и 7) непосредственно в качестве фактических аргументов, я предположил, что они могут быть преобразованы внутри (внутри компилятора) в некоторые внутренние объекты C, которые представляют эти данные (например, что-то например struct, состоящий из указателя на необработанные данные плюс некоторая информация о времени выполнения). (продолжение) - person roygvib; 10.11.2015
comment
Поэтому я задался вопросом, существуют ли какие-либо удобные способы прямого создания массива гетероданных Fortran, например, массива, состоящего из внутренних объектов, на которые ссылается class(*) (как некоторая более новая функция Fortran). Но, судя по вашему ответу, это не так. Спасибо большое :) - person roygvib; 10.11.2015
comment
И еще один вопрос: gfortran5.2 все еще выдает ошибку с вашим кодом (невозможно преобразовать целое число в класс (*) и т. д.), но это потому, что gfortran5.2 все еще отстает от последнего стандарта? Поскольку у меня есть только ifort14 (и я не могу установить более новые версии в своей среде), я задался вопросом, компилируется ли код напрямую с более новыми компиляторами. (Если необходимы более явные коды, я сделаю это вопросом.) - person roygvib; 10.11.2015
comment
Другой вопрос (извините за многих...): представляет ли class(*) :: a(:) полиморфный массив, который может ссылаться на семейство type(T) :: a(:) (с произвольным T), а не массив, состоящий из скаляров класса (*)? Если это так, то я боюсь, что тоже совершаю большую ошибку с class(*) :: a(:)... (Это может быть похоже на тот факт, что type(T), pointer :: a(:) представляет собой указатель на type(T) :: a(...), а не массив указателей на скаляры type(T).) - person roygvib; 10.11.2015
comment
gfortran еще не является компилятором Fortran 2003 (или Fortran 2008). Похоже, что текущий ствол не поддерживает определение неограниченного полиморфного компонента через конструктор структуры. Я думаю, что первое предложение моего ответа также отвечает на другой вопрос - все элементы массива должны иметь один и тот же [динамический и объявленный] тип. - person IanH; 11.11.2015
comment
Хорошо, теперь я думаю, что полностью понимаю. Спасибо большое! :) - person roygvib; 11.11.2015