Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29393 Discussions

Assignment overloading for intrinsic types?

Jacob_Williams
New Contributor III
1,488 Views

Consider the following toy example:

module test_module
  
  implicit none
     
  type :: myint
  
    integer :: i = 0
    
    contains
     
    generic :: assignment(=) => int_to_myint
    procedure :: int_to_myint

  end type myint
    
contains

  subroutine int_to_myint(me,i)  !myint = integer assignment
  
  implicit none
  
  class(myint),intent(inout) :: me
  integer,intent(in)         :: i
    
  me%i = i
  
  end subroutine int_to_myint
  
end module test_module

The type(myint)=integer assignment works great.  My question is this: is it possible to have the reverse assignment (integer=type(myint)) work as well?  This would be like overloading the assignment operator for the integer type, I guess?  Is such a thing possible?

0 Kudos
3 Replies
FortranFan
Honored Contributor III
1,488 Views

Jacob Williams wrote:

 ...

The type(myint)=integer assignment works great.  My question is this: is it possible to have the reverse assignment (integer=type(myint)) work as well?  This would be like overloading the assignment operator for the integer type, I guess?  Is such a thing possible?

Yes, at some price.  The generic assignment overload needs to be unambiguous, so one needs to come up with a scheme to distinguish from your myint_to_int procedure.  You can do so using the unlimited polymorphic object option as shown below.  "Casting" the unlimited polymorphic object to an integer frequently in a compute-intensive environment may introduce a compute cost, so one has to weigh it against the benefits:

module test_module

   implicit none

   type :: myint

      integer :: i = 0

   contains

      generic :: assignment(=) => int_to_myint, myint_to_int
      procedure, pass(me) :: int_to_myint
      procedure, pass(me) :: myint_to_int

   end type myint

contains

   subroutine int_to_myint(me, i)  !myint = integer assignment

      implicit none

      class(myint), intent(inout) :: me
      integer, intent(in)         :: i

      me%i = i

   end subroutine int_to_myint

   subroutine myint_to_int(i, me)  !integer = myint assignment

      implicit none

      class(*), intent(inout)  :: i
      class(myint), intent(in) :: me

      !.. process based on input type
      select type (i)
         type is (integer)
            i = me%i
         class default
            !.. insert some error handling here
            return
      end select

   end subroutine myint_to_int

end module test_module

 

0 Kudos
IanH
Honored Contributor III
1,488 Views

Using class(*) as the output argument makes the "casting" assignment universally applicable at compile time, but issues with mismatches become a runtime issue.  If you only want to support assignment to integer, just declare the i argument of myint_to_int as type integer, an unsupported type on the left hand side of an assignment is then identified at compile time and the runtime overhead of the SELECT TYPE construct is removed.

   subroutine myint_to_int(i, me)  !integer = myint assignment
      integer, intent(out)  :: i
      class(myint), intent(in) :: me

      i = me%i
   end subroutine myint_to_int

If you want to support other types on the left hand side, just add additional specific procedures with the dummy argument for the left hand side declared appropriately.  Alternatively, consider a type bound operator or function that extracts the numeric value as an integer.

Ambiguity restrictions on specific procedures in the generic interface for ASSIGNMENT(=) only consider position, the general requirements on the interfaces of such specific procedures and the nature in which that generic is referenced removes the need for consideration of the other aspects that might otherwise make a generic reference ambiguous.

0 Kudos
Jacob_Williams
New Contributor III
1,488 Views

Now that is interesting.  I always thought that the first dummy argument of a type bound procedure with the pass attribute had to be the class where it was contained.  I didn't know it was allowed to put something before it.  

0 Kudos
Reply