10.07.2015 Views

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 ...

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!