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

Equality comparison for unlimited polymorphic variables

Jacob_Williams
New Contributor III
2,046 Views

Fortran Gurus: say I have two unlimited polymorphic variables (CLASS(*)), and I want to check if they are equal. Does anyone know if there is a better way than having to write nested SELECT TYPE statements for all possible variable types? Basic example below. Assuming I only cared about built-in variables types (integers, etc.) I'd have to write cases for INT8, INT16, INT32, INT64, REAL32, REAL64, REAL128, and CHARACTERS kinds. This seems unsatisfactory to me, but I can't think of any other way to do it.

 

function equal(k1,k2)

implicit none

class(*),intent(in)    :: k1
class(*),intent(in)    :: k2
logical                :: equal

equal = .false.

if (same_type_as(k1,k2)) then

    select type (k1)
    type is (integer)

        select type (k2)
        type is (integer)
            equal = k1 == k2
        end select

    type is (character(len=*))

        select type (k2)
        type is (character(len=*))
            equal = k1 == k2
        end select

    !type is (...), etc.

    end select

end if

end function equal

 

0 Kudos
14 Replies
Les_Neilson
Valued Contributor II
2,046 Views

Mmm I would have thought that since you have already determined that k1 and k2 are the same type that you can just simply do:
equal = k1 == k2
But it is early and I haven't had my coffee yet so I may be wrong :-(
Les

0 Kudos
Simon_Geard
New Contributor I
2,046 Views

How do you establish that the two types are the same without using select type?

0 Kudos
LRaim
New Contributor I
2,046 Views

Get the number of bytes and compare each byte.

0 Kudos
Simon_Geard
New Contributor I
2,046 Views

Sorry but I don't really understand that, could you post a code snippet by way of explanation please. Unlike the OP I already have this problem with user-defined types so a way of removing hundreds of lines of what seems like unnecessary code would be very welcome. Thanks.
 

0 Kudos
IanH
Honored Contributor III
2,046 Views

There is no other way.  

(You can "cast" with unlimited polymorphic pointers to SEQUENCE or BIND(C) types without SELECT TYPE, but that is not the general case.)

I do not recommend comparing bytes, because polymorphic objects are internally managed via a descriptor of sorts that is likely to fail anyway, plus the same byte sequence can represent different values.

It would be worth testing the decision to use unlimited polymorphic objects in the first place.  It depends a great deal on what you are Trying to do, but if all the objects that you are trying to compare are an extension of a common base type, you can potentially eliminate one of the SELECT TYPE constructs, replacing it with a type bound procedure for each subject type that implements the equality comparison operator.

0 Kudos
FortranFan
Honored Contributor III
2,046 Views

IanH wrote:

.. 

It would be worth testing the decision to use unlimited polymorphic objects in the first place.  It depends a great deal on what you are Trying to do, but ..

Yes, this suggestion makes most sense.  OP may want to look back and consider whether the use of unlimited polymorphism is really necessary for the given situation - a code review perhaps using techniques in OO design and analysis might reveal better ways to go about achieving the needs.

0 Kudos
Jacob_Williams
New Contributor III
2,046 Views

My example is somewhat academic. I was experimenting with creating a dictionary class, where the key could be any numeric or string type (similar to the way Python works). It seemed reasonable to store this variable as an unlimited polymorphic. But in the instance when I need to check if two keys are equal, I encounter this issue (SELECT TYPE works fine, but I was just wondering if there was another way).

In this case, since k1 and k2 are only allowed to be numbers or strings, I wonder if comparing the bytes might be OK (assuming I keep the SAME_TYPE_AS check to ensure that they are really the same kind of variable). Perhaps using some TRANSFER magic and maybe also BTEST? I'll give that a try.

0 Kudos
FortranFan
Honored Contributor III
2,046 Views

Jacob Williams wrote:

My example is somewhat academic. I was experimenting with creating a dictionary class, where the key could be any numeric or string type (similar to the way Python works). It seemed reasonable to store this variable as an unlimited polymorphic. But in the instance when I need to check if two keys are equal, I encounter this issue (SELECT TYPE works fine, but I was just wondering if there was another way).

In this case, since k1 and k2 are only allowed to be numbers or strings, I wonder if comparing the bytes might be OK (assuming I keep the SAME_TYPE_AS check to ensure that they are really the same kind of variable). Perhaps using some TRANSFER magic and maybe also BTEST? I'll give that a try.

Well, if your interest is in something like a dictionary class, then you're on the right track by thinking of TRANSFER intrinsic - I do use something along the same lines in some of my classes.  See this thread and the code snippet in Message #22 for an example that may be of interest for what you are trying to do:

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/541989

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,046 Views

Luigi R. wrote:

Get the number of bytes and compare each byte.

A user defined type may contain POINTERs and/or ALLOCATABLEs therefore byte compare is not suitable excepting for identity and/or pointer to identity. Depending on your needs for operator==, you will generally want to dereference the pointer and/or array descriptor.

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,046 Views

How about something like this:

function equal(k1,k2)

implicit none

class(*),intent(in)    :: k1
class(*),intent(in)    :: k2
logical                :: equal

equal = k1%equal(k2)

end function equal

Each class/type will have the responsibility of having a contains function for equality of same type.

You can make a generic interface for equal, test the equality of native types:

interface equal
  function equal_class(k1,k2)
    implicit none
    class(*),intent(in)    :: k1
    class(*),intent(in)    :: k2
    logical                :: equal
  end function equal_class

  function equal_integer(k1,k2)
    implicit none
    integer,intent(in)    :: k1
    integer,intent(in)    :: k2
    logical               :: equal
  end function equal_integer
  ...
end interface equal

contains

function equal_class(k1,k2)
  implicit none
  class(*),intent(in)    :: k1
  class(*),intent(in)    :: k2
  logical                :: equal
  equal = k1%equal(k2)
end function equal

function equal_integer(k1,k2)
  implicit none
  integer,intent(in)    :: k1
  integer,intent(in)    :: k2
  logical               :: equal
  equal = (k1 == k2)
end function equal_integer

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor III
2,046 Views

jimdempseyatthecove wrote:

..  You can make a generic interface for equal, test the equality of native types: ..

Jim Dempsey

You cannot make a generic interface of class(*) dummy arguments along with native types - it creates ambiguous interfaces that cannot be resolved.

And the situation is no different than in the original post: OP will need to provide interfaces for each type to be supported.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,046 Views

>>You cannot make a generic interface of class(*) dummy arguments along with native types - it creates ambiguous interfaces that cannot be resolved.

It is not necessarily ambiguous. If the native types (without promotion/demotion/conversion) are resolvable with the generic interface, then the class(*) dummy arguments calling formats should not be considered. This said, the Fortran standards committee probably couldn't agree on how to do this. C++ managed to do this a long time ago.

Maybe Steve will comment on this.

Jim Dempsey

0 Kudos
IanH
Honored Contributor III
2,046 Views

Is a particular dictionary limited to only entries of a particular type (i.e. dictionary A has only CHARACTER, dictionary B has only INTEGER), or can they be mixed within the one dictionary?

(Further to FortranFan's comments, the function reference `k1%equal(k2)` where k1 is unlimited polymorphic will not compile - when the declared type is unlimited polymorphic, no components or bindings are available.  Also, generic resolution only considers declared type, but it is the dynamic type that you want to dispatch on.)
 

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,046 Views

IanH,

Thanks for pointing out that an arbitrary unlimited polymorphic is not required to have the same set of what amounts to member functions. You may be able to have a nested class, where the base class does, but then your outer program layer is then using these base classes. Also this would not reduce your coding effort, as my suggestion sought to achieve.

Jim Dempsey

0 Kudos
Reply