I have some matrix type that can either be real or complex:
type t_mat
logical :: is_real
real :: data_r(:,:)
complex :: data_c(:,:)
end type
and I would like to overload the =-operator as well as the (i,j) operator, so that I could conveniently say:
type(t_mat) :: a
...
a(2,3) = 3
I know in C++ it is possible to overload the subscript operator. Does Fortran allow for something similar?
Related
This question already has an answer here:
what's the meaning of "iostat" argument in open statement?
(1 answer)
Closed 1 year ago.
I want to overcome the lousy and non-intuitive string handling in fortran by writing a more pythonic string type, but I stumpled across a mean issue with derived-type (overloaded) assignment.
The main type should look like
TYPE t_string
CHARACTER(:), ALLOCATABLE :: str
contains
...
END TYPE t_string
with its power in the derived-type procedures. Of course the new string type should be as indistinguishable from the intrinsic CHARACTER(len=*) type as possible. Especially I want to use intrinsic routines (which use the character type) without any extra type conversions. Therefore I defined an assignment operator between CLASS(t_string) and CHARACTER(len=*). E.g. opening a file with the new type should look like this:
type(t_string) :: filename
filename = '...'
open(file = filename, ...)
! ^ assignment here
Since there is an assignment file=filename between t_string and CHARACTER(len=*) there should be no problem in the call to open. But I get an error due to mismatch in types.
I guess the problem is, that the assignment in the subroutine call is not really an assignment but just some syntax convention.
Any ideas how to fix this?
What is the reason (in term of design of the fortran language) for the "subroutine assignment" not to be a real assignment?
I do not want to call open(file = filename%str, ...)
Here is a mwe:
MODULE m_string
IMPLICIT NONE
SAVE
INTERFACE ASSIGNMENT(=)
MODULE PROCEDURE :: string_operator_equal_s, string_operator_equal_c
END INTERFACE ASSIGNMENT(=)
TYPE t_string
CHARACTER(:), ALLOCATABLE :: str
END TYPE t_string
CONTAINS
ELEMENTAL SUBROUTINE string_operator_equal_s(lhs,rhs)
IMPLICIT NONE
CLASS(t_string), INTENT(inout) :: lhs
CLASS(t_string), INTENT(in) :: rhs
lhs%str = rhs%str
END SUBROUTINE string_operator_equal_s
ELEMENTAL SUBROUTINE string_operator_equal_c(lhs,rhs)
IMPLICIT NONE
CLASS(t_string), INTENT(inout) :: lhs
CHARACTER(len=*), INTENT(in) :: rhs
lhs%str = rhs
END SUBROUTINE string_operator_equal_c
SUBROUTINE routine(char)
CHARACTER(len=*) :: char
END SUBROUTINE routine
END MODULE m_string
PROGRAM test
USE m_string
TYPE(t_string) :: str
CHARACTER(len=10) :: char
CALL routine(char) ! no error
CALL routine(char=str) ! error: #6633: The type of the actual argument differs from the type of the dummy argument. [STR]
END PROGRAM test
Since there is an assignment file=filename between t_string and CHARACTER(len=*) there should be no problem in the call to open.
No such assignment is present. You are only using the specifier name to specify which argument of the statement you are passing (similar to keyword/named arguments in Python, but not the same). open is in fact not a procedure, it is a statement, but it also has its "arguments" (specifiers) distinguished by their names.
Hence no derived assignment shall be invoked. You must convert to character yourself.
This question already has answers here:
A Fortran function/subroutine that could return either a real, an integer or a string.
(1 answer)
Fortran - setting kind/precision of a variable at run time
(1 answer)
Closed 1 year ago.
I have been looking around the internet for a solution to this problem, but I have to admit I am completely stuck.
What I have is a derived data type
TYPE :: MyType
REAL(KIND=MY_REAL_KIND) :: real_val
INTEGER :: int_val
END TYPE
I am overloading operators and intrinsic functions to work with it.
I need to overload the REAL cast to work with this type, so that when is called it gives back the real_val member. So fa so good:
FUNCTION real_mytype (a) RESULT (x)
IMPLICIT NONE
TYPE(MyType), INTENT(IN) :: a
REAL(KIND=MY_REAL_KIND) :: x
# Take advantage of the overloaded (=) operator
x = a
END FUNCTION real_mytype
But then I need to perform a cast with specified precision, let's say like
INTEGER, PUBLIC, PARAMETER :: sp = SELECTED_REAL_KIND( 6, 37)
INTEGER, PUBLIC, PARAMETER :: dp = SELECTED_REAL_KIND(12,307)
TYPE(MyType) :: a
call external_function_that_I_cant_change(REAL(a, sp))
call second_external_function_that_I_cant_change(REAL(a, dp))
I don't know how to write such interface, because the type of the return value (double real or single real) will depend on the value of the INTEGER input parameter (sp or dp) and not on its type, which is kind of forbidden in Fortran. The following does simply not make sense:
FUNCTION real_mytype_1d (a, p) RESULT (x)
IMPLICIT NONE
TYPE(MyType), INTENT(IN) :: a
INTEGER :: p
REAL(KIND=something_that_depends_on_p) :: x
x = a
END FUNCTION real_mytype_1d
I already took a look at the transfer function like suggested here but I was not able to figure out a solution (the type of return val there depends on the mold type).
Any suggestion?
I have a simple user-defined type
use, intrinsic :: iso_fortran_env
implicit none
type :: vector
real(real64) :: data(3)
end type
that has various interfaces defined as well as the assignment operator to and from arrays.
What I need is an abstract interface
interface assignment(=)
procedure v_to_a_assign, a_to_v_assign
end interface
which means I can do things like
type(vector) :: v
real(real64) :: a(3)
a = v
But what I want to do is an array-constructor such as
type(vector) :: v
real(real64) :: q(4)
q = [1d0, v]
! error #8209: The assignment operation or the binary expression operation is
! invalid for the data types of the two operands. [v]
which I could do if v was an array of real(real64). My question here is what binary operation do I need to define to make the above work?
The above is just one example of an implicit conversion of a user type to an array. I want to define the correct operator such that my user type is automaticall converted to an array when needed, like in function arguments, and/or other constructs.
Solution
define an interface for the conversion using the keyword real.
interface real
procedure v_to_array
end interface
contains
function v_to_array(v) result(a)
type(vector), intent(in) :: v
real(real64), dimension(3) :: a
a = v%data
end function
and use it as
q = [1d0, real(v)]
References Array Constructors
The language does not support the implicit conversion you are after.
Reference the array component, either directly using the component within the array constructor:
q = [1.0_real64, v%data]
or write an appropriate accessor function/unary operator.
q = [1.0_real64, .getdata. v]
The implicit conversion you seek would be problematic given the way generic resolution is defined by the language.
As a matter of style, explicit conversions are often preferred - for example user a structure constructor as the expression instead of the user defined assignment when assigning to an object of type vector, use an accessor function or unary operator when assigning to an array of real. Beyond clarity, user defined assignment does not invoke automatic (re)allocation of the variable on the left hand side of an assignment.
(Fortran does not have an assignment operator - it has an assignment statement.)
I have a code, which segfaults with all compilers I have at hand, when doing an assignment to an unallocted allocatable on the LHS with a structure constructor on the RHS. The structure (derived type) itself has an overloaded assignment. I thought, that automatic reallocation of the LHS should occur before the assignment routine is called, but it does not seem to be the case.
Below the code, demonstrating the issue. Uncommenting the allocate statement makes everything working, but I do not understand, why the explicit allocation is necessary in this case. Funny enough, if I remove the overloaded assignment, things work as well.
Any hints?
module dummy
implicit none
type :: DummyType
integer :: ii
contains
procedure :: assignDummyType
generic :: assignment(=) => assignDummyType
end type DummyType
interface DummyType
module procedure DummyType_init
end interface DummyType
contains
function DummyType_init(initValue) result(this)
integer, intent(in) :: initValue
type(DummyType) :: this
this%ii = initValue
end function DummyType_init
subroutine assignDummyType(this, other)
class(DummyType), intent(out) :: this
type(DummyType), intent(in) :: other
this%ii = other%ii + 1
end subroutine assignDummyType
end module dummy
program test_dummy
use dummy
implicit none
type(DummyType), allocatable :: aa
!allocate(aa) ! Should be covered via automatic reallocation...
aa = DummyType(42)
end program test_dummy
There is a recent discussion on comp.lang.fortran dealing with this topic.
An assignment statement is either an intrinsic assignment or a defined assignment. Intrinsic assignment permits [re]allocation of the left hand side, defined assignment does not.
When you provide a procedure for the assignment generic identifier, your assignment is defined assignment. The characteristics of the argument that corresponds to the left hand side then require that the left hand side be allocated.
I have a derived type (wrapper) containing an other derived type (over). For the latter the assignment operator have been overloaded. As the assignment of derived types happens per default componentwise, I'd expect that assigning two instances of wrapper would invoke the overloaded assignment for over at some point. However, using the program below, it does not seem to be the case. The overloaded assignment is only invoked if I also overload the assignment for wrapper containing an explicit assignment between instances of over (by uncommenting the commented code lines). Why? I find it somewhat counter intuitive. Is there any way to avoid the overloading in the wrapping type?
module test_module
implicit none
type :: over
integer :: ii = 0
end type over
type :: wrapper
type(over) :: myover
end type wrapper
interface assignment(=)
module procedure over_assign
!module procedure wrapper_assign
end interface assignment(=)
contains
subroutine over_assign(other, self)
type(over), intent(out) :: other
type(over), intent(in) :: self
print *, "Assignment of over called"
other%ii = -1
end subroutine over_assign
!subroutine wrapper_assign(other, self)
! type(wrapper), intent(out) :: other
! type(wrapper), intent(in) :: self
!
! other%myover = self%myover
!
!end subroutine wrapper_assign
end module test_module
program test
use test_module
implicit none
type(wrapper) :: w1, w2
print *, "Assigning wrapper instances:"
w2 = w1
end program test
This [unfortunate] situation is a consequence of the rules of the language (F90+) for intrinsic assignment of derived types. The details are spelled out in F2008 7.2.1p13. As a summary, intrinsic assignment of derived types (the assignment that happens with the wrapper_assign specific commented out) does not invoke non-type bound defined assignment for any components that are of derived type. In F90/F95, if you want defined assignment at some lower level of the component hierarchy then you need to have defined assignment for all the parent components up to the base object.
F2003 added type bound defined assignment to the language and this is invoked by intrinsic assignment of derived types. Use that instead of the stand-alone generic form of specifying defined assignment. (This also avoids a potential problem with the type name being accessible but the defined assignment procedure not being accessible.)
Just to complete the thread: the concrete realisation of IanH's suggestion (please upvote his original answer rather than this one) which worked for me was the following one:
module test_module
implicit none
type :: over
integer :: ii = 0
contains
procedure :: over_assign
generic :: assignment(=) => over_assign
end type over
type :: wrapper
type(over) :: myover
end type wrapper
contains
subroutine over_assign(other, self)
class(over), intent(out) :: other
class(over), intent(in) :: self
print *, "Assignment of over called"
other%ii = -1
end subroutine over_assign
end module test_module
program test
use test_module
implicit none
type(wrapper) :: w1, w2
print *, "Assigning wrapper instances:"
w2 = w1
end program test