How to Support Inheritance and Run-TIme Polymorphism in Fortran ...
How to Support Inheritance and Run-TIme Polymorphism in Fortran ...
How to Support Inheritance and Run-TIme Polymorphism in Fortran ...
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>How</strong> <strong>to</strong> <strong>Support</strong> <strong>Inheritance</strong> <strong>and</strong> <strong>Run</strong>-<strong>TIme</strong> <strong>Polymorphism</strong> <strong>in</strong> <strong>Fortran</strong> 90byVik<strong>to</strong>r K. Decyk 1,2 , Charles D. Nor<strong>to</strong>n 2 , <strong>and</strong> Boleslaw K. Szymanski 31 Department of Physics <strong>and</strong> AstronomyUniversity of California, Los AngelesLos Angeles, CA 90095-15472 Jet Propulsion Labora<strong>to</strong>ryCalifornia Institute of Technology4800 Oak Grove DrivePasadena, CA 91109-80993 Department of Computer ScienceRensselaer Polytechnic InstituteTroy, NY 12180-3590Abstract<strong>Fortran</strong> 90 does not support au<strong>to</strong>matic <strong>in</strong>heritance <strong>and</strong> run-timepolymorphism as language mechanisms. This paper discusses techniquesfor software emulation of <strong>in</strong>heritance <strong>and</strong> polymorphism <strong>in</strong> <strong>Fortran</strong> 90,which simplifies the implementation of an object-oriented programm<strong>in</strong>gstyle <strong>in</strong> <strong>Fortran</strong> 90.1
I. IntroductionIn recent years, a number of papers have appeared discuss<strong>in</strong>g <strong>Fortran</strong>90 <strong>and</strong> Object-Oriented Programm<strong>in</strong>g (OOP) [1-6]. <strong>Fortran</strong> 90 clearly hassome language features which are useful for OOP (derived types, modules,generic <strong>in</strong>terfaces), but clearly lacks some others (<strong>in</strong>heritance, run-timepolymorphism). Is OOP possible or practical <strong>in</strong> such a situation? Cary et.al. [5] believe that <strong>Fortran</strong> 90 allows it “<strong>to</strong> some degree,” but that anobject hierarchy cannot be constructed <strong>and</strong> polymorphic types are notpossible. Gray <strong>and</strong> Roberts [6] argue that <strong>in</strong>heritance can be “faked” (sic),but the effort required <strong>to</strong>o much duplication of code <strong>to</strong> be practical. Inthis paper we will show how <strong>to</strong> emulate <strong>in</strong>heritance <strong>and</strong> polymorphictypes by software constructs without duplication of code. This allowsone <strong>to</strong> implement all the important concepts <strong>in</strong> OOP, but with more effortthan would be required <strong>in</strong> an object-oriented language. To keep theexposition of this paper clear <strong>and</strong> focused, we will exp<strong>and</strong> on thes<strong>to</strong>pwatch example used by Gray <strong>and</strong> Roberts.What is <strong>in</strong>heritance? Gray <strong>and</strong> Roberts quote Rumbaugh [7] <strong>in</strong> def<strong>in</strong><strong>in</strong>g<strong>in</strong>heritance <strong>to</strong> be “shar<strong>in</strong>g of structure <strong>and</strong> behavior among classes <strong>in</strong> ahierarchical relationship.” They also def<strong>in</strong>e polymorphism <strong>to</strong> be“differentiation of behavior of the same operation on different classes.”In many object-oriented languages, <strong>in</strong>heritance <strong>and</strong> run-timepolymorphism use the same language mechanism so these concepts areglued <strong>to</strong>gether. S<strong>in</strong>ce <strong>Fortran</strong> 90 does not have such a unify<strong>in</strong>g mechanism,it is helpful <strong>to</strong> keep these concepts separate while develop<strong>in</strong>g emulationtechniques. Another useful dist<strong>in</strong>ction is the difference between static(ad hoc) <strong>and</strong> run-time polymorphism. Static polymorphism means that theactual type be<strong>in</strong>g used at any po<strong>in</strong>t <strong>in</strong> the program is known at compiletime, while run-time polymorphism means that a s<strong>in</strong>gle type can refer <strong>to</strong>one of several possible actual types, <strong>and</strong> only at run-time can the correcttype be determ<strong>in</strong>ed.When Gray <strong>and</strong> Roberts “fake” <strong>in</strong>heritance with their s<strong>to</strong>pwatchexample, they appear <strong>to</strong> conflate the concepts of <strong>in</strong>heritance <strong>and</strong>polymorphism <strong>to</strong>gether. This results <strong>in</strong> a situation where “developers ofnew classes derived from the s<strong>to</strong>pwatch class must modify the bases<strong>to</strong>pwatch class <strong>to</strong> accommodate the new type,” <strong>and</strong> where developers“must duplicate identical code <strong>in</strong> each of the child classes <strong>in</strong> order <strong>to</strong>allow future classes <strong>to</strong> override the default behavior.” Our techniques forimplement<strong>in</strong>g <strong>in</strong>heritance allow us <strong>to</strong> avoid these difficulties <strong>and</strong> replace2
the pejorative “fake” with the more neutral “emulate.”3
II. Implement<strong>in</strong>g an <strong>in</strong>heritance hierarchy.The real value <strong>in</strong> us<strong>in</strong>g <strong>in</strong>heritance is <strong>to</strong> avoid duplicat<strong>in</strong>g code whencreat<strong>in</strong>g types (classes) which are similar <strong>to</strong> one another. Gray <strong>and</strong>Roberts create a base type called s<strong>to</strong>pwatch, which is composed of anumber of timers <strong>and</strong> has two ma<strong>in</strong> procedures: split (<strong>to</strong> <strong>to</strong>ggle a timer on<strong>and</strong> off) <strong>and</strong> report (<strong>to</strong> display the results). They then create a classcalled parallel_s<strong>to</strong>pwatch, which is derived from s<strong>to</strong>pwatch <strong>and</strong> sharesits procedures <strong>and</strong> <strong>in</strong>terfaces, but adds new functionality for parallelcomput<strong>in</strong>g. Unfortunately, they do not show how this derivation is done.Instead they focus on how <strong>to</strong> implement run-time polymorphism for thesetwo classes. We will beg<strong>in</strong> by show<strong>in</strong>g how <strong>to</strong> create the derived class<strong>and</strong> later show how <strong>to</strong> implement run-time polymorphism.We can def<strong>in</strong>e a parallel s<strong>to</strong>pwatch class with the follow<strong>in</strong>g derivedtype:type parallel_s<strong>to</strong>pwatchprivatetype (s<strong>to</strong>pwatch) :: sw<strong>in</strong>teger :: idprocend type parallel_s<strong>to</strong>pwatch! base class component! processor idwhich conta<strong>in</strong>s exactly one <strong>in</strong>stance of the base class type s<strong>to</strong>pwatch, <strong>and</strong>an additional <strong>in</strong>teger type which conta<strong>in</strong>s the processor id def<strong>in</strong>ed on adistributed memory parallel computer. A construc<strong>to</strong>r for the derivedclass is implemented by call<strong>in</strong>g the construc<strong>to</strong>r of the base class <strong>to</strong>construct the base class component, <strong>and</strong> the procedure gidproc <strong>to</strong> obta<strong>in</strong>the processor id <strong>to</strong> <strong>in</strong>itialize the <strong>in</strong>teger component:subrout<strong>in</strong>e s<strong>to</strong>pwatch_construct(self,n)type (parallel_s<strong>to</strong>pwatch), <strong>in</strong>tent(out) :: self<strong>in</strong>teger, <strong>in</strong>tent(<strong>in</strong>), optional :: ncall construct(self%sw,n) ! call base class construc<strong>to</strong>rcall gidproc(self%idproc) ! assign processor idend subrout<strong>in</strong>e s<strong>to</strong>pwatch_constructThis is functionally equivalent <strong>to</strong> the memberwise <strong>in</strong>itialization list used<strong>in</strong> C++ <strong>to</strong> <strong>in</strong>itialize a derived class object. A similar procedure is used <strong>to</strong>create the destruc<strong>to</strong>r for the derived class. We want the method split <strong>in</strong>the derived class <strong>to</strong> work the same way as <strong>in</strong> the base class. In <strong>Fortran</strong> 90this is implemented by writ<strong>in</strong>g a procedure with exactly one executablel<strong>in</strong>e which merely calls the base class procedure on the base class4
component of the derived type, as follows:subrout<strong>in</strong>e s<strong>to</strong>pwatch_split(self,name)type (parallel_s<strong>to</strong>pwatch), <strong>in</strong>tent(<strong>in</strong>out) :: selfcharacter(len=*), <strong>in</strong>tent(<strong>in</strong>) :: namecall split(self%sw,name)! delegate <strong>to</strong> base classend subrout<strong>in</strong>e s<strong>to</strong>pwatch_splitIn C++, such a procedure would be au<strong>to</strong>matically available <strong>and</strong> would nothave <strong>to</strong> be explicitly written.Static polymorphism is implemented <strong>in</strong> <strong>Fortran</strong> 90 by us<strong>in</strong>g an<strong>in</strong>terface block <strong>in</strong> the derived class, as follows:<strong>in</strong>terface splitmodule procedure s<strong>to</strong>pwatch_splitend <strong>in</strong>terfaceso that the name split can be used with either type correctly.We want the method report, on the other h<strong>and</strong>, <strong>to</strong> be implementeddifferently <strong>in</strong> the derived class <strong>to</strong> accommodate the new features ofparallel process<strong>in</strong>g (e.g., it will report the maximum time returned by thes<strong>to</strong>pwatches on each node.) This method would have <strong>to</strong> be rewritten <strong>in</strong>both C++ <strong>and</strong> <strong>Fortran</strong> 90, <strong>and</strong> the two languages are equivalent here.Thus we can create either a normal s<strong>to</strong>pwatch or a parallel s<strong>to</strong>pwatch<strong>and</strong> run one or the other as follows:program ma<strong>in</strong>! get s<strong>to</strong>pwatch type def<strong>in</strong>ition <strong>and</strong> methodsuse parallel_s<strong>to</strong>pwatch_class#ifdef MPPtype (parallel_s<strong>to</strong>pwatch) sw ! declare a parallel s<strong>to</strong>pwatch#elsetype (s<strong>to</strong>pwatch) sw! declare a s<strong>to</strong>pwatch#endifcall construct(sw) ! construct s<strong>to</strong>pwatchcall split(sw,'bar') ! turn "bar" split oncall bar()! execute bar subrout<strong>in</strong>ecall split(sw,'bar') ! turn "bar" split offcall report(sw,6) ! report <strong>to</strong>tal <strong>and</strong> split timescall destruct(sw) ! destroy s<strong>to</strong>pwatchNote that the parallel_s<strong>to</strong>pwatch class did not have <strong>to</strong> “modify the bases<strong>to</strong>pwatch class <strong>to</strong> accommodate the new type,” nor did we have <strong>to</strong>“duplicate identical code” <strong>in</strong> the child class.So far, our emulation of <strong>in</strong>heritance consists of two techniques. First,5
<strong>in</strong>heritance of data members is implemented by <strong>in</strong>clud<strong>in</strong>g exactly one<strong>in</strong>stance of the base class data member <strong>in</strong> a derived class. Second,<strong>in</strong>heritance of methods is implemented by delegation (or subcontract<strong>in</strong>g)<strong>to</strong> the base class the responsibility of carry<strong>in</strong>g out the operation on thebase class component of the derived class object. These techniques foremulat<strong>in</strong>g <strong>in</strong>heritance have appeared earlier [3-5].<strong>Inheritance</strong> is sometimes referred <strong>to</strong> as an “is-a” relation (a parallels<strong>to</strong>pwatch is a k<strong>in</strong>d of s<strong>to</strong>pwatch). Another relation which occurs <strong>in</strong>object-oriented programm<strong>in</strong>g is the “has-a” relation (a s<strong>to</strong>pwatch hastimers). Our emulation is based on the observation that a parallels<strong>to</strong>pwatch can function as a s<strong>to</strong>pwatch precisely because it conta<strong>in</strong>s as<strong>to</strong>pwatch <strong>in</strong>side itself. This idea is not entirely new. Meyer [8] alsopo<strong>in</strong>ts out that “when the "is" view is legitimate, one can always take the"has" view <strong>in</strong>stead.“What we have done is create an <strong>in</strong>heritance hierarchy but without runtimepolymorphism (that is, without the use of virtual functions <strong>in</strong> C++).In many cases, this is sufficient. For example, by use of a preprocessorone can chose either a s<strong>to</strong>pwatch or a parallel s<strong>to</strong>pwatch at compile time.This is always more efficient at execution time than run-timepolymorphism, even <strong>in</strong> C++. Another way <strong>to</strong> avoid requir<strong>in</strong>g run-timepolymorphism is <strong>to</strong> ensure that parallel s<strong>to</strong>pwatches run correctly onserial computers. In our case this was implemented by requir<strong>in</strong>g that theprocedure which calculates a global maximum produces the correct resulteven if only a s<strong>in</strong>gle node is be<strong>in</strong>g used. Then it is always safe <strong>to</strong> useparallel s<strong>to</strong>pwatches even on non-parallel computers.6
III. Implementation of run-time polymorphismNevertheless, sometimes one does desire the functionality of run-timepolymorphism, <strong>and</strong> this is often considered a part of the mean<strong>in</strong>g of<strong>in</strong>heritance. In the case of our s<strong>to</strong>pwatches, the parallel s<strong>to</strong>pwatchreports one global result, whereas the normal s<strong>to</strong>pwatches report a resultfor each node separately. In normal operation, parallel s<strong>to</strong>pwatches areused, but if unusual behavior is occurr<strong>in</strong>g, it may be desirable <strong>to</strong> switch <strong>to</strong>the normal s<strong>to</strong>pwatch (possibly <strong>in</strong>teractively) on the parallel computer <strong>to</strong>obta<strong>in</strong> <strong>in</strong>formation about the variation of tim<strong>in</strong>gs across processors. Weimplement run-time polymorphism by creat<strong>in</strong>g a polymorphic type calledpoly_s<strong>to</strong>pwatch, which conta<strong>in</strong>s a po<strong>in</strong>ter for each possible type <strong>in</strong> the<strong>in</strong>heritance hierarchy:type poly_s<strong>to</strong>pwatchprivatetype (s<strong>to</strong>pwatch), po<strong>in</strong>ter :: stype (parallel_s<strong>to</strong>pwatch), po<strong>in</strong>ter :: pend type poly_s<strong>to</strong>pwatchIf s<strong>to</strong>pwatches have already been created, then one can write a conversionfunction <strong>to</strong> assign one of the po<strong>in</strong>ters <strong>in</strong> the polymorphic type <strong>to</strong> thes<strong>to</strong>pwatch one desires <strong>to</strong> use (nullify<strong>in</strong>g the others), as follows:function convert_s<strong>to</strong>pwatch(s) result(sw)! convert s<strong>to</strong>pwatch <strong>to</strong> poly_s<strong>to</strong>pwatchtype (poly_s<strong>to</strong>pwatch) :: swtype (s<strong>to</strong>pwatch), target, <strong>in</strong>tent(<strong>in</strong>) :: ssw%s => snullify(sw%p)end function convert_s<strong>to</strong>pwatchIf we further write a similar conversion function for parallel s<strong>to</strong>pwatches<strong>and</strong> create the <strong>in</strong>terface name poly <strong>to</strong> refer <strong>to</strong> them:<strong>in</strong>terface polymodule procedure convert_s<strong>to</strong>pwatchmodule procedure convert_parallel_s<strong>to</strong>pwatchend <strong>in</strong>terfacethen we can use the poly_s<strong>to</strong>pwatch type <strong>to</strong> refer <strong>to</strong> either type:7
!!type (s<strong>to</strong>pwatch) s ! declare a s<strong>to</strong>pwatchtype (parallel_s<strong>to</strong>pwatch) p ! declare a parallel s<strong>to</strong>pwatchtype (poly_s<strong>to</strong>pwatch) sw ! declare polymorphic s<strong>to</strong>pwatchcall construct(s)call construct(p)sw = poly(s)....sw = poly(p)....! construct a s<strong>to</strong>pwatch! construct a parallel s<strong>to</strong>pwatch! sw is a normal s<strong>to</strong>pwatch now! sw is a parallel s<strong>to</strong>pwatch nowIn addition <strong>to</strong> creat<strong>in</strong>g a polymorphic type, a dispatch mechanism must beconstructed <strong>to</strong> execute the correct procedure. This procedure merelychecks which po<strong>in</strong>ter has been associated, <strong>and</strong> executes the correspond<strong>in</strong>gprocedure.subrout<strong>in</strong>e s<strong>to</strong>pwatch_report(self,u)type (poly_s<strong>to</strong>pwatch), <strong>in</strong>tent(<strong>in</strong>out) :: self<strong>in</strong>teger, <strong>in</strong>tent(<strong>in</strong>) :: uif (associated(self%s)) thencall report(self%s,u)elseif (associated(self%p)) thencall report(self%p,u)endifend subrout<strong>in</strong>e s<strong>to</strong>pwatch_reportwhich is similar <strong>to</strong> the one shown by Gray <strong>and</strong> Roberts. When this functionis added <strong>to</strong> the <strong>in</strong>terface report, it corresponds <strong>to</strong> creat<strong>in</strong>g a virtualfunction <strong>in</strong> C++. The follow<strong>in</strong>g example illustrates how <strong>to</strong> use run-timepolymorphism:!sw = poly(s)call split(sw,'bar')call bar()call split(sw,'bar')call report(sw,6)sw = poly(p)call split(sw,'foo')call foo()call split(sw,'foo')call report(sw,6)! use normal s<strong>to</strong>pwatch! turn "bar" split on! execute bar subrout<strong>in</strong>e! turn "bar" split off! report <strong>to</strong>tal <strong>and</strong> split times! use parallel s<strong>to</strong>pwatch! turn "foo" split on! execute foo subrout<strong>in</strong>e! turn "foo" split off! report <strong>to</strong>tal <strong>and</strong> split timesThis use of the poly function with the poly_s<strong>to</strong>pwatch type corresponds <strong>to</strong>the assignment of derived class objects <strong>to</strong> base class po<strong>in</strong>ters <strong>in</strong> C++.This polymorphic class functions like an abstract base class or8
<strong>in</strong>terface class <strong>in</strong> C++. <strong>How</strong>ever, it is constructed after all the classes <strong>in</strong>the <strong>in</strong>heritance hierarchy are known, <strong>and</strong> it is the only place where suchknowledge is concentrated. Such a situation corresponds <strong>to</strong> a polymorphic<strong>in</strong>stance set which can occur <strong>in</strong> object-oriented programm<strong>in</strong>g as describedby Meyer [8]. The polymorphic class knows only about the types <strong>and</strong><strong>in</strong>terfaces <strong>in</strong> the hierarchy <strong>and</strong> noth<strong>in</strong>g whatsoever about theirimplementation. None of the classes <strong>in</strong> the hierarchy that we have createdwith static polymorphism have <strong>to</strong> be modified <strong>to</strong> implement run-timepolymorphism. Therefore it should be possible <strong>to</strong> write a software <strong>to</strong>ol <strong>to</strong>au<strong>to</strong>matically create such a polymorphic class. This would be a usefulproject for some enterpris<strong>in</strong>g computer science student!One <strong>in</strong>terest<strong>in</strong>g feature of this approach <strong>to</strong> add<strong>in</strong>g run-timepolymorphism <strong>in</strong> <strong>Fortran</strong> 90 is that the polymorphic class can consist ofany types whatsoever, not necessarily those related by <strong>in</strong>heritance as <strong>in</strong>C++. Although this is not exactly the same as templates <strong>in</strong> C++, it servesa similar purpose: the ability <strong>to</strong> write one function which can be used withdifferent actual types. This answers the concern of Cary et. al. that“there is no way <strong>to</strong> refer <strong>to</strong> a group of ... objects collectively <strong>and</strong> have[functions] return what is appropriate for each object.”If a new type of s<strong>to</strong>pwatch is added <strong>to</strong> the <strong>in</strong>heritance hierarchy,derived from one of the other s<strong>to</strong>pwatches, one first implements an<strong>in</strong>heritance relationship with static polymorphism, which does not requirechang<strong>in</strong>g any of the previous classes, as we have shown. If one furtherdesires <strong>to</strong> add run time-polymorphism <strong>to</strong> this third class, an additionalpo<strong>in</strong>ter must be added <strong>to</strong> the polymorphic type, an additional l<strong>in</strong>e or twomust be added <strong>to</strong> the conversion functions <strong>and</strong> methods. F<strong>in</strong>ally, a newconversion function must be created. In this example, this corresponds <strong>to</strong>add<strong>in</strong>g about a dozen l<strong>in</strong>es of new code.As an alternative <strong>to</strong> (or <strong>in</strong> addition <strong>to</strong>) creat<strong>in</strong>g conversion functions,which requires one <strong>to</strong> first create a specific s<strong>to</strong>pwatch, then assign it <strong>to</strong>the polymorphic type, one can create a construc<strong>to</strong>r which <strong>in</strong>ternallycreates one of the s<strong>to</strong>pwatches <strong>and</strong> associates the correspond<strong>in</strong>g po<strong>in</strong>ter<strong>in</strong> the polymorphic type, as follows:9
!<strong>in</strong>teger, save, private :: platform = PARALLELsubrout<strong>in</strong>e s<strong>to</strong>pwatch_construct(self,n,timer_type)type (poly_s<strong>to</strong>pwatch), <strong>in</strong>tent(out) :: self<strong>in</strong>teger, <strong>in</strong>tent(<strong>in</strong>), optional :: n, timer_typeif (present(timer_type)) platform = timer_typeif (platform==PARALLEL) thenallocate(self%p)call construct(self%p,n)nullify(self%s)elseallocate(self%s)call construct(self%s,n)nullify(self%p)endifend subrout<strong>in</strong>e s<strong>to</strong>pwatch_constructThis is similar <strong>to</strong> the construc<strong>to</strong>r shown by Gray <strong>and</strong> Roberts, <strong>and</strong> thefollow<strong>in</strong>g example illustrates its use:!program ma<strong>in</strong>use poly_s<strong>to</strong>pwatch_classtype (poly_s<strong>to</strong>pwatch) swcall construct(sw)call split(sw,'bar')call bar()call split(sw,'bar')call report(sw,6)call destruct(sw)! declare a polymorphic type! construct s<strong>to</strong>pwatch! turn "bar" split on! execute bar subrout<strong>in</strong>e! turn "bar" split off! report <strong>to</strong>tal <strong>and</strong> split times! destroy s<strong>to</strong>pwatch10
IV. DiscussionExcept for the discussion of <strong>in</strong>heritance, Gray <strong>and</strong> Roberts have anexcellent discussion of how <strong>to</strong> model object-oriented concepts <strong>in</strong> <strong>Fortran</strong>90. There are only a small number of other improvements which we cansuggest <strong>to</strong> their exposition. One m<strong>in</strong>or po<strong>in</strong>t is that the implicit nonestatement does not have <strong>to</strong> be repeated <strong>in</strong> each subrout<strong>in</strong>e. It can bedeclared just once <strong>in</strong> each module <strong>and</strong> will au<strong>to</strong>matically apply <strong>to</strong> eachprocedure conta<strong>in</strong>ed <strong>in</strong> that module. Another m<strong>in</strong>or po<strong>in</strong>t is that it is notnecessary <strong>to</strong> make a complete list of the public <strong>and</strong> private entities <strong>in</strong> amodule separately. One can make the default public or private <strong>and</strong> thenlist only the exceptions. Another improvement which one can takeadvantage of is that po<strong>in</strong>ters <strong>in</strong> <strong>Fortran</strong> 90 are more “<strong>in</strong>telligent” thanpo<strong>in</strong>ters <strong>in</strong> C++, because they know how much memory has been allocated<strong>to</strong> them. Therefore class data members such as max_splits <strong>in</strong> thes<strong>to</strong>pwatch class described by Gray <strong>and</strong> Roberts are not needed, s<strong>in</strong>ce onecan always obta<strong>in</strong> the size from the <strong>Fortran</strong> 90 size <strong>in</strong>tr<strong>in</strong>sic, forexample:type (s<strong>to</strong>pwatch) :: swallocate(sw%name(20))max_splits = size(sw%name)A further useful feature <strong>in</strong> <strong>Fortran</strong> 90 is the idea of an optional argument.Gray <strong>and</strong> Roberts implement two construc<strong>to</strong>rs for the base s<strong>to</strong>pwatchclass, s<strong>to</strong>pwatch_construct <strong>and</strong> s<strong>to</strong>pwatch_construct_1. The onlydifference between them is that <strong>in</strong> the former case the number of timersdefaults <strong>to</strong> 20, <strong>and</strong> <strong>in</strong> the latter case it is explicitly given as an argument.Two construc<strong>to</strong>rs are not required if one uses optional arguments, asfollows:subrout<strong>in</strong>e s<strong>to</strong>pwatch_construct(self,n)type (s<strong>to</strong>pwatch), <strong>in</strong>tent(out) :: self<strong>in</strong>teger, <strong>in</strong>tent(<strong>in</strong>), optional :: n<strong>in</strong>teger i, max_splits! make n names, splitsif (present(n)) thenmax_splits = nelsemax_splits = 20endif....11
These optional arguments are more powerful than default arguments <strong>in</strong>C++, because they can be referenced by keyword. For example, theconstruc<strong>to</strong>r for the poly_s<strong>to</strong>pwatch type previously discussed abovesubrout<strong>in</strong>e s<strong>to</strong>pwatch_construct(self,n,timer_type)type (poly_s<strong>to</strong>pwatch), <strong>in</strong>tent(out) :: self<strong>in</strong>teger, <strong>in</strong>tent(<strong>in</strong>), optional :: n, timer_typeif (present(timer_type)) platform = timer_type....can be called with the second optional argument but not the first asfollows:type (poly_s<strong>to</strong>pwatch) sw ! declare a polymorphic typecall construct(sw,timer_type=0) ! construct SERIAL s<strong>to</strong>pwatchGray <strong>and</strong> Roberts use the term “object-based programm<strong>in</strong>g” becausetheir emulation of <strong>in</strong>heritance “required far <strong>to</strong>o much work for far <strong>to</strong>olittle benefit” <strong>to</strong> use. We feel that our techniques make object-orientedprogramm<strong>in</strong>g more practical <strong>in</strong> <strong>Fortran</strong> 90. Indeed, we have translated agreat many object-oriented examples from C++ <strong>to</strong> <strong>Fortran</strong> 90, <strong>and</strong> have notyet found any object-oriented concept as def<strong>in</strong>ed by Gray <strong>and</strong> Robertswhich could not be implemented. Many of these examples are available onour web site [9], <strong>in</strong>clud<strong>in</strong>g a complete list<strong>in</strong>g of our implementation ofs<strong>to</strong>pwatches. For an extended discussion of the use of polymorphic types<strong>in</strong> <strong>Fortran</strong> 90, see ref. [3].Cary et. al. give a good explanation of <strong>in</strong>heritance by delegation, butthey are clearly less proficient <strong>in</strong> <strong>Fortran</strong> 90 than <strong>in</strong> C++, <strong>and</strong> as a resulttheir paper has a number of errors, misunderst<strong>and</strong><strong>in</strong>gs, <strong>and</strong> <strong>in</strong>efficiencies.Many of the errors would be caught by a compiler, so we will not dwell onthem here. <strong>How</strong>ever, there are some po<strong>in</strong>ts that are more subtle that wefeel should be explicitly addressed. In their discussion, they have aprocedure <strong>to</strong> return the k<strong>in</strong>etic energy of a particle, similar <strong>to</strong>:real function K<strong>in</strong>eticEnergy(p)type (particle), <strong>in</strong>tent(<strong>in</strong>) :: preal :: ke = 0.0ke = ke + (p%velocity)**2K<strong>in</strong>eticEnergy = p%mass*keend function K<strong>in</strong>eticEnergy12
The problem here is the <strong>in</strong>itialization of the variable ke. In <strong>Fortran</strong>90, when a variable is declared with an <strong>in</strong>itial value <strong>in</strong> a procedure, itau<strong>to</strong>matically has the save attribute, so that on second entry, the oldvalue of ke would have been used, <strong>in</strong>stead of be<strong>in</strong>g re<strong>in</strong>itialized <strong>to</strong> zero.This is a common trap when translat<strong>in</strong>g C++ code <strong>to</strong> <strong>Fortran</strong> 90.A misunderst<strong>and</strong><strong>in</strong>g they have <strong>in</strong>volves the parameter attribute, as <strong>in</strong>the follow<strong>in</strong>g declaration:real, parameter, private :: elemCharge = 1.6e-19saveThe authors state that “The SAVE qualifier <strong>in</strong>dicates that only one copy ofthe parameter ... is used.” Parameters are not variables, they are symbolicconstants. There is no danger of hav<strong>in</strong>g multiple constants, but the savestatement is harmless. Other m<strong>in</strong>or improvements one could make <strong>to</strong> theirexposition is that return statements are not needed at the end of aprocedure, they are au<strong>to</strong>matic. Also, they do not take advantage of arraysyntax, but cont<strong>in</strong>ue <strong>to</strong> write loops <strong>in</strong> the C++ style.There are three ma<strong>in</strong> disadvantages <strong>to</strong> us<strong>in</strong>g object-orientedtechniques <strong>in</strong> <strong>Fortran</strong> 90 compared <strong>to</strong> do<strong>in</strong>g so <strong>in</strong> a true object-orientedlanguage. The first is that more code must be written, especially <strong>in</strong>implement<strong>in</strong>g the polymorphic class. This situation could be improved bythe development of an appropriate software <strong>to</strong>ol. The second is thatemulation of <strong>in</strong>heritance requires an extra layer (or more) of procedurecalls. This can lead <strong>to</strong> performance degradation if the amount of workbe<strong>in</strong>g done <strong>in</strong> the procedure is small. F<strong>in</strong>ally, procedures which usepolymorphic types must be recompiled if a new derived class is added <strong>to</strong>the <strong>in</strong>heritance hierarchy. This can be a nuisance for very large projectswhere compilation time is long.We have been programm<strong>in</strong>g <strong>in</strong> <strong>Fortran</strong> 90 <strong>and</strong> C++ for several years now[10-11], <strong>and</strong> have found that there are <strong>in</strong>deed many useful ideas <strong>in</strong> objec<strong>to</strong>rientedanalysis <strong>and</strong> design. The two concepts which we have found mostuseful are the notion of creat<strong>in</strong>g simple, stable <strong>in</strong>terfaces for proceduresby data hid<strong>in</strong>g <strong>and</strong> encapsulation with types <strong>and</strong> the idea of avoid<strong>in</strong>greplication of code.Other ideas seem less compell<strong>in</strong>g. Gray <strong>and</strong> Roberts argue that“<strong>in</strong>heritance is ... the most important concept <strong>in</strong> science. Know<strong>in</strong>g that anemu is a k<strong>in</strong>d of bird tells me many th<strong>in</strong>gs about its behavior.” But<strong>in</strong>heritance can also be misused. For example, by <strong>in</strong>heritance one mightconclude that emus fly. (Oops, they don’t!)13
The <strong>in</strong>heritance relationship as traditionally def<strong>in</strong>ed <strong>in</strong> objec<strong>to</strong>rientedlanguages requires that exactly one copy of the base class datamembers are conta<strong>in</strong>ed <strong>in</strong>side a derived class. The relationship is likethat of the Russian matrioska dolls, where one fits snugly <strong>in</strong>side another.We have not found very many examples where such a relationship occurs <strong>in</strong>our scientific programm<strong>in</strong>g, although we recognize that such examplesexist <strong>in</strong> other doma<strong>in</strong>s. Instead, the notion of composition, where one ormore objects are conta<strong>in</strong>ed <strong>in</strong>side another (such as timers <strong>in</strong> s<strong>to</strong>pwatches)occurs more often. The notion of delegation which we used <strong>to</strong> model<strong>in</strong>heritance can also be used here <strong>to</strong> avoid replicat<strong>in</strong>g code, just as <strong>in</strong> C++.Even Rumbaugh[7] notes that “Many applications do not require <strong>in</strong>heritance.Many other applications have only a few classes requir<strong>in</strong>g <strong>in</strong>heritance.”The few times when run-time polymorphism seemed useful oftenoccurred for families of data types which were not related by <strong>in</strong>heritance,while object-oriented languages supported polymorphism only when thedata was related by <strong>in</strong>heritance. Object-oriented programm<strong>in</strong>g seems <strong>to</strong>be based on the idea that types are more important than procedures. Weare still not conv<strong>in</strong>ced this is true, nor are we conv<strong>in</strong>ced that the oppositeis true. But we do have some nagg<strong>in</strong>g doubts whether nouns are morecentral than verbs <strong>in</strong> the scheme of th<strong>in</strong>gs.<strong>Fortran</strong> 90 is a language designed for scientific comput<strong>in</strong>g <strong>and</strong> webelieve it does this well. C++ is designed as a more general purposelanguage which deliberately avoids specialization <strong>to</strong> specific doma<strong>in</strong>s.Each language has both advantages <strong>and</strong> disadvantages. <strong>Fortran</strong> 90 hasspecialized features useful for scientific calculations, such as powerfularray classes, but requires one <strong>to</strong> write polymorphic classes. C++ has apowerful <strong>in</strong>heritance mechanism, but requires one <strong>to</strong> write array classes.We have found that it takes significantly longer <strong>to</strong> debug code written <strong>in</strong>C++ than <strong>in</strong> <strong>Fortran</strong> 90. One reason is that <strong>Fortran</strong> 90 is more restricted <strong>in</strong>its features <strong>and</strong> more strict <strong>in</strong> its type check<strong>in</strong>g (no au<strong>to</strong>maticconversions across arguments, for example). Another reason has <strong>to</strong> dowith style. Meyer describes a “picture of the software developer asfireworks expert or arsonist. He prepares a giant conflagration ... thenlights up a match <strong>and</strong> watches the blaze.” Such code can be very difficult<strong>to</strong> debug when some unexpected problem arises, especially <strong>in</strong> C++ whenpolymorphism <strong>and</strong> au<strong>to</strong>matic type conversions obscure the flow of logic.It makes one long for the simple days of spaghetti code, when at leastthere were statement labels <strong>to</strong> help one f<strong>in</strong>d ones way through all thatpasta!14
Acknowledgments:The research of Vik<strong>to</strong>r K. Decyk was carried out <strong>in</strong> part at UCLA <strong>and</strong>was sponsored by USDOE <strong>and</strong> NSF. It was also carried out <strong>in</strong> part at theJet Propulsion Labora<strong>to</strong>ry, California Institute of Technology, under acontract with the National Aeronautics <strong>and</strong> Space Adm<strong>in</strong>istration. Theresearch of Charles D. Nor<strong>to</strong>n was supported by a National ResearchCouncil Associateship, <strong>and</strong> that of Boleslaw K. Szymanski was sponsoredunder grants CCR-9216053 <strong>and</strong> CCR-9527151.15
References:[1] B. J. Dupee, “Object Oriented Methods us<strong>in</strong>g <strong>Fortran</strong> 90,” ACM <strong>Fortran</strong> Forum 13(1),21 (1994).[2] L. Machiels <strong>and</strong> M. O. Deville, "<strong>Fortran</strong> 90: An Entry <strong>to</strong> Object-OrientedProgramm<strong>in</strong>g for the Solution of Partial Differential Equations" ACM Transactions onMathematical Software, 23, 32 (1997).[3] V. K. Decyk, C. D. Nor<strong>to</strong>n, <strong>and</strong> B. K. Szymansk, “<strong>How</strong> <strong>to</strong> Express C++Concepts <strong>in</strong> <strong>Fortran</strong>90,” <strong>to</strong> be published <strong>in</strong> Scientific Programm<strong>in</strong>g, 1998.[4] V. K. Decyk, C. D. Nor<strong>to</strong>n, <strong>and</strong> B. K. Szymanski, “Express<strong>in</strong>g Object-Oriented Concepts <strong>in</strong> <strong>Fortran</strong> 90,” ACM <strong>Fortran</strong> Forum 16(1),13 (1997).[5] J. R. Cary, S. G. Shashar<strong>in</strong>a, J. C. Cumm<strong>in</strong>gs, J. V. W. Reynders, <strong>and</strong> P. J. H<strong>in</strong>ker,“Comparison of C++ <strong>and</strong> <strong>Fortran</strong> 90 for object-oriented scientific programm<strong>in</strong>g,”Computer Phys. Comm. 105, 20 (1997).[6] Mark G. Gray <strong>and</strong> R<strong>and</strong>y M. Roberts, “Object-Based Programm<strong>in</strong>g <strong>in</strong><strong>Fortran</strong> 90,” Computers <strong>in</strong> Physics, 11, 355 (1997).[7] J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, <strong>and</strong> W. Lorensen, Object-Oriented Model<strong>in</strong>g <strong>and</strong> Design (Prentice Hall, Englewood Cliffs, NJ, 1991).[8] B. Meyer, Object-Oriented Software Construction [Prentice Hall, UpperSaddle River, NJ, 1997].[9] http://www.cs.rpi.edu/~szymansk/oof90.html[10] C. D. Nor<strong>to</strong>n, B. K. Szymanski, <strong>and</strong> V. K. Decyk, “Object-OrientedParallel Computation for Plasma Simulation,” Comm. ACM, 38(10), 88(1995).[11] C. D. Nor<strong>to</strong>n, V. K. Decyk, <strong>and</strong> B. K. Szymanski, “High performanceobject-oriented scientific programm<strong>in</strong>g <strong>in</strong> <strong>Fortran</strong> 90,” Proc. Eighth SIAMConf. on Parallel Process<strong>in</strong>g for Scientific Comput<strong>in</strong>g, M<strong>in</strong>neapolis, MN,March, 1997, ed. by M. Heath et. al, [SIAM, Philadelphia, PA, 1997], CD-ROM.16