Introduction to Object-Oriented Concepts in Fortran95

Similar documents
Inheritance parent child contains

Object Oriented Programming. Assistant Lecture Omar Al Khayat 2 nd Year

Chapter 1 Getting Started

High Level Classes in UPIC Framework

QUIZ on Ch.5. Why is it sometimes not a good idea to place the private part of the interface in a header file?

What does it mean by information hiding? What are the advantages of it? {5 Marks}

OBJECT ORIENTED PROGRAMMING USING C++ CSCI Object Oriented Analysis and Design By Manali Torpe

Lists. Michael P. Fourman. February 2, 2010

Week 8: Operator overloading

Object Oriented Programming

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

QUIZ Friends class Y;

COMS W3101 Programming Language: C++ (Fall 2016) Ramana Isukapalli

A simplified method for implementing run-time polymorphism in Fortran95

Module 10 Inheritance, Virtual Functions, and Polymorphism

Programming Tips for Plugins

Lecture 18 Tao Wang 1

Client Code - the code that uses the classes under discussion. Coupling - code in one module depends on code in another module

VIRTUAL FUNCTIONS Chapter 10

C++ Inheritance and Encapsulation

Module 10A Lecture - 20 What is a function? Why use functions Example: power (base, n)

G Programming Languages - Fall 2012

Object Oriented Programming

CIS 110: Introduction to Computer Programming

Outline. CIS 110: Introduction to Computer Programming. Any questions? My life story. A horrible incident. The awful truth

Welcome to Design Patterns! For syllabus, course specifics, assignments, etc., please see Canvas

Chapter 12 Object-Oriented Programming. Starting Out with Games & Graphics in C++ Tony Gaddis

PROGRAMMING LANGUAGE 2

Object oriented programming Concepts

Introduction to Object- Oriented Programming

Midterm 2. 7] Explain in your own words the concept of a handle class and how it s implemented in C++: What s wrong with this answer?

Assignment 1: grid. Due November 20, 11:59 PM Introduction

Data Abstraction. Hwansoo Han


Inf1-OP. Inf1-OP Exam Review. Timothy Hospedales, adapting earlier version by Perdita Stevens and Ewan Klein. March 20, School of Informatics

Inheritance and Polymorphism

Table 2 1. F90/95 Data Types and Pointer Attributes. Data Option. (Default Precision) Selected-Int-Kind

VB.NET. Exercise 1: Creating Your First Application in Visual Basic.NET

Introduction to Programming Using Java (98-388)

ENCAPSULATION AND POLYMORPHISM

Reusing this material

Ch. 12: Operator Overloading

Lecture 13: Object orientation. Object oriented programming. Introduction. Object oriented programming. OO and ADT:s. Introduction

Chapter 11. Categories of languages that support OOP: 1. OOP support is added to an existing language

Data Structure. Lecture#2: Data Structures and Algorithms. U Kang Seoul National University. U Kang (2016) 1

Lecturer: William W.Y. Hsu. Programming Languages

Chapter 10 Introduction to Classes

Modern Fortran OO Features


CGS 2405 Advanced Programming with C++ Course Justification


COMS W3101 Programming Language: C++ (Fall 2015) Ramana Isukapalli

UPIC Framework designed to help construct Plasma PIC calculations by student programmers.

COMS W3101 Programming Language: C++ (Fall 2015) Ramana Isukapalli

Zhifu Pei CSCI5448 Spring 2011 Prof. Kenneth M. Anderson

OOPS Viva Questions. Object is termed as an instance of a class, and it has its own state, behavior and identity.

VALLIAMMAI ENGINEERING COLLEGE

Chapter 4. Fortran Arrays

CS313D: ADVANCED PROGRAMMING LANGUAGE

2559 : Introduction to Visual Basic.NET Programming with Microsoft.NET

CS201 - Introduction to Programming Glossary By

6.001 Notes: Section 6.1

UMBC CMSC 331 Final Exam

Object Oriented Design

Operator overloading

3.Constructors and Destructors. Develop cpp program to implement constructor and destructor.

Create a Java project named week9

QUIZ. Can you find 5 errors in this code?

Ch. 11: References & the Copy-Constructor. - continued -

6.005 Elements of Software Construction Fall 2008

Classwide Programming 1

Defining Your Own Functions/Methods

9/21/2010. Based on Chapter 2 in Advanced Programming Using Visual Basic.NET by Bradley and Millspaugh

Lesson Plan. Subject: OBJECT ORIENTED PROGRAMMING USING C++ :15 weeks (From January, 2018 to April,2018)

Programming 2. Object Oriented Programming. Daniel POP

Absolute C++ Walter Savitch

Programming Data Structures and Algorithms Prof. Shankar Balachandran Department of Computer Science Indian Institute of Technology, Madras

Objects, Subclassing, Subtyping, and Inheritance

CPS 506 Comparative Programming Languages. Programming Language

Cpt S 122 Data Structures. Introduction to C++ Part II

CS304 Object Oriented Programming Final Term

HST 952. Computing for Biomedical Scientists Lecture 5

Welcome. Modern Fortran (F77 to F90 and beyond) Virtual tutorial starts at BST

Programming, numerics and optimization

SRM ARTS AND SCIENCE COLLEGE SRM NAGAR, KATTANKULATHUR

What is an algorithm?

Object-Oriented Programming. Lecture 16 CS 565 4/10/08

1. Write two major differences between Object-oriented programming and procedural programming?

CS313D: ADVANCED PROGRAMMING LANGUAGE

Chapter 12. OOP: Creating Object- Oriented Programs. McGraw-Hill. Copyright 2011 by The McGraw-Hill Companies, Inc. All Rights Reserved.

Programming II. Modularity 2017/18

EL2310 Scientific Programming

Data Structures (list, dictionary, tuples, sets, strings)

CS1150 Principles of Computer Science Objects and Classes

Lecture Contents CS313D: ADVANCED PROGRAMMING LANGUAGE

Object Oriented Programming: In this course we began an introduction to programming from an object-oriented approach.

CORE JAVA TRAINING COURSE CONTENT

Lecture Contents CS313D: ADVANCED PROGRAMMING LANGUAGE. What is Inheritance?

Object Oriented Programming

Lesson 10A OOP Fundamentals. By John B. Owen All rights reserved 2011, revised 2014

Transcription:

Introduction to Object-Oriented Concepts in Fortran95 Object-Oriented Programming (OOP) is a design philosophy for writing complex software. Its main tool is the abstract data type, which allows one to program in terms of higher level concepts than just numbers and arrays of numbers. In their mathematics, physicists are quite familiar with the power of abstraction, e.g., we express physics equations using the curl operator, rather than writing out all the components. But we have not used such abstractions very much in our programming. OOP includes a number of concepts which have proved useful in programming large projects. These are: 1. Information Hiding and Data Encapsulation 2. Function Overloading or Static Polymorphism 3. Abstract Datatypes, Classes and Objects 4. Inheritance 5. Dynamic Dispatch or Run-Time Polymorphism

Information Hiding and Data Encapsulation Perhaps the most important concept is that of information hiding. This means that information which is required in only one procedure should not be made known to other procedures which do not need this information. Like the CIA, procedures should be informed of data only on a need to know basis. This philosophy simplifies programming, because there is less detail one must be concerned about in programming and less opportunities to make mistakes. One way to achieve this is to encapsulate the data inside a derived type, and then allow only certain procedures (sometime called methods) to modify the data. One is prevented from modifying the data by any other means not provided by the programmer. Such encapsulation permits separation of concerns. One can separately write and debug pieces of a large program, without worrying about a new procedure causing inadvertent damage to an older procedure. Writing complex program becomes an order N problem, rather than an order N 2 problem.

Let s look at an example of what this means. Consider the following interface to a legacy Fortran77 fft procedure: subroutine fft1r(f,t,isign,mixup,sct,indx,nx,nxh) integer isign, indx, nx, nxh, mixup(nxh) real f(nx) complex sct(nxh), t(nxh)... rest of procedure goes here In this procedure, f is the data to be transposed, t is a temporary work array, mixup is a bit reversed table, sct is a sine/cosine table, indx is the power of 2 defining the length of the transpose, nx is the size of the f, and nxh is size of the remaining data, and isign is either the direction of the transform (-1,1) or a request to initialize the tables (0). To use this fft, one must get all of this data correct, there are many opportunities for mistakes. However, most of this data is relevant only internal details of performing the fft. The programmer only wants to worry about the data f and the direction of the transpose. Life would be much simpler if one could merely call call fft1(f,isign) without having to worry about the other details.

One of the reason all these details are exposed is that Fortran77 did not allow dynamic arrays. By using automatic and allocatable arrays, one can easily hide the scratch array t and the tables mixup and sct inside a wrapper function: subroutine fft1(f,indx,isign,nx,nxh) integer indx, isign, nx, nxh real f(nx) complex, dimension(nxh) :: t integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct if (isign==0) allocate(mixup(nxh),sct(nxh)) call fft1r(f,t,isign,mixup,sct,indx,nx,nxh) Thus the programmer does not have to worry about these things anymore and there is less opportunity for error. Fortran95 arrays encapsulate dimension information, and we can use this feature to remove all the dimension information from the interface: subroutine fft1(f,indx,isign) integer :: indx, isign, nx, nxh real, dimension(:) :: f complex, dimension(size(f)/2) :: t integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct nx = size(f); nxh = nx/2 if (isign==0) allocate(mixup(nxh),sct(nxh)) call fft1r(f,t,isign,mixup,sct,indx,nx,nxh)

We have successfully hidden from the programmer details about the fft that are not necessary to know to use the fft. Now the interface is much simpler and less error prone: call fft1(f,indx,isign) If one gets the interface down to its bare essentials, then it is unlikely to change in the future, even if the internal details of the procedure do change. For example, suppose on a given computer, there was an optimized fft which was much faster than the legacy fft1r. One could now replace the call to fft1r inside the wrapper function, and the users of the wrapper function would not have to change anything in their code. subroutine fft1(f,indx,isign)... call faster_fft1r(f,...)! different internal arguments end subroutine call fft1(f,indx,isign)! Note the call does not change Thus encapsulation allows one to change the implementation details of a procedure without impacting the rest of the program. This also allows concurrent development: different programmers can be modifying different pieces of a large program, without worrying about getting in each other s way, so long as the interfaces do not change.

We can improve this interface even further by noting that the argument indx which determines the length of the fft and the internal tables mixup and sct need to be consistent with one another. The tables are created when the parameter isign = 0: call fft1r(f,indx,isign=0)! create tables But when the fft is called later, the indx parameter might different. call fft1(f,kndx,isign=1)! wrong value of indx. Part of the problem here is that the legacy fft1r is actually used to perform two completely different operations, table initialization and transposition. It is better to have two different functions perform two different operations. But the tables are private arrays stored inside the wrapper function fft1. How can another procedure initialize that table? There are several ways to do that. One way is to put the tables inside a module which is shared by all the procedures in the module, as follows:

module fft1 integer, save :: saved_indx integer, dimension(:), allocatable, save :: mixup complex, dimension(:), allocatable, save :: sct contains subroutine new_fft_table(indx) integer :: indx, isign, nx, nxh saved_indx = indx isign = 0 nx = 2**saved_indx; nxh = nx/2 allocate(mixup(nxh),sct(nxh)) call fft1r(f,t,isign,mixup,sct,indx,nx,nxh) end subroutine new_fft_table! create fft tables subroutine fft1(f,isign)! perform fft integer :: indx, isign, nx, nxh real, dimension(:) :: f complex, dimension(size(f)/2) :: t nx = 2**saved_indx; nxh = nx/2 call fft1r(f,t,isign,mixup,sct,indx,nx,nxh) end subroutine fft1 end module fft1 One can use these procedures as follows: use fft1 call new_fft_table(indx) call fft1(f,isign=1)! create new fft table! indx no longer in argument

We should also create a third procedure in this module to deallocate the tables if we will no longer perform any ffts. subroutine delete_fft_table() deallocate(mixup,sct) end subroutine delete_fft_table! delete fft tables One other feature we can add is access control. If we add the following lines to the beginning of the module: module fft1 private public: new_fft_table, delete_fft_table, fft1 then the fft tables cannot be accessed from outside the module. The only way to manipulate the table is via the new_fft_table and delete_fft_table procedures. As a student exercise, think about additional error checks one can add inside these procedures, e.g., how to prevent calling an fft if the table has not been created. Thus we have grouped together all operations related to ffts into a single module. Such grouping is also part of the concept of encapsulation. Information hiding and data encapsulation are arguably the most useful and important concepts in object-oriented programming.

Function Overloading or Static Polymorphism We have already encountered this concept in earlier lectures. Function overloading refers to using the same procedure name but performing different operations based on argument type. Fortran77 has always had this feature. For example, the function real() means different things depending on its type. integer :: i real :: a complex :: z a = real(i) a = real(z)! converts integer to real! takes real part of complex z In Fortran95, generic functions allow user defined functions to also have this feature. For example, there are many different types of FFTs, real to complex, complex to complex, one dimensional, two dimensional, single precision, double precision, etc. In Fortran77 one had remember different names for each of these FFTs. Since it is unambiguous what each of these do, Fortran95 allows one to use the same name for all of them, using generic interfaces: interface fft module procedure fft1rc module procedure fft1cc... end interface! define generic name fft

so long as each of these functions have different argument types. subroutine fft1rc(f) real, dimension(:) :: f subroutine fft1cc(f) complex, dimension(:) :: f subroutine fft2rc(f) real, dimension(:,:) :: f! argument is real array! argument is complex array! argument is real 2D array and so on. It is easy to overdo function overloading, however. You should use it only when it is obvious what you intend to happen. You should avoid using it if it obfuscates your intention. For example, by overloading different procedures with a generic name such as solve, you may not remember later which solver you actually intended, without carefully studying all the argument types in all your modules. You still want a human being to be able to read your code and easily determine what it is supposed to be doing. Function overloading is sometimes called static polymorphism because the actual function being called is determined (resolved) at compile time and not at run time.

Abstract Datatypes, Classes and Objects An abstract data type or class encapsulates a user defined data type along with the operations that one can perform on that type. For example, consider a class called Personnel designed to manipulating personnel records in a database. (This example comes from Henderson and Zorn). The data we encapsulate are a person s social security number and name. The functions we provide create and delete a record, print a record and obtain a social security number from a record. In Fortran95 a class looks like: module Personnel_class type Personnel private integer :: ssn character*12 :: firstname, lastname end type Personnel contains subroutine new_personnel(this,s,fn,ln)... subroutine delete_personnel(this)... subroutine print_personnel(this,printssn)... function getssn_personnel(this) result(ssn)... end module Personnel_class

A variable of this type is called an object. It is declared as follows: type (Personnel) :: person The components of a derived type are called the class data members. They are often declared private, so that individual components are not accessible outside the class. The procedures defined in the class are called class member functions. Generally, they provide the only means by which one can manipulate Personnel objects. One function which is always necessary is the constructor, to initialize a record. For example: subroutine new_personnel(this,s,fn,ln)! Constructor type (Personnel), intent (out) :: this integer, intent (in) :: s character(len=*), intent (in) :: fn, ln this%ssn = s! store social security number this%firstname = fn! store first name this%lastname = ln! store last name end subroutine new_personnel We can then create a person record for Paul as follows: program database use Personnel type (Personnel) :: person call new_personnel(person,012345678, Paul, Jones )

By convention, the first argument in each method is the class type, and is commonly called this (in C++) or self (in other OO languages). In most OO languages, the first argument is not explicitly declared, but is available. A destructor is often defined to delete a Personnel object. In Fortran95, this is only necessary if the Personnel type has a pointer component. In our case, we will define a destructor to merely nullify the data: subroutine delete_personnel(this)! Destructor type (Personnel), intent (inout) :: this this%ssn = 0! nullify social security number this%firstname =! nullify first name this%lastname =! nullify last name end subroutine delete_personnel One can then delete the contents of Paul s person record as follows: call delete_personnel(person) Since the components of the personnel type are private, one cannot print them out directly: print *, person%ssn! Cannot print out ss number

Instead one has to provide a method to obtain the private components, for example print *, getssn_personnel(person)! this is OK. where we define a function to obtain the social security number: function getssn_personnel(this) result(ssn) type (Personnel), intent (in) :: this integer :: ssn ssn = this%ssn! extract the private component end function getssn_personnel We can also provide a function the print out the entire record: subroutine print_personnel(this) type (Personnel), intent (in) :: this print *, this%ssn, this%firstname, this%lastname end subroutine print_personnel

Although it may seem a unnecessarily complicated to make the components private, it has the advantage that one can change the components and those using this class do not need to modify their old code. For example, suppose that we decided at a later date to add an optional age field to the Personnel type: type Personnel private integer :: ssn, age character*12 :: firstname, lastname end type Personnel We create a new constructor: subroutine new_personnel_age(this,s,fn,ln,a) type (Personnel), intent (out) :: this integer, intent (in) :: s, a character(len=*), intent (in) :: fn, ln this%ssn = s! store social security number this%age = a! store age this%firstname = fn! store first name this%lastname = ln! store last name end subroutine new_personnel_age

We can continue to use the older constructor and the new one by using generic functions. First we rename the original constructor: subroutine new_personnel_orig(this,s,fn,ln) Then we define the generic interface interface new_personnel module procedure new_personnel_orig module procedure new_personnel_age end interface so that the name new_personnel now has two meanings. All of the old code works as before, and new code can use the new feature: type (Personnel), dimension(2) :: person call new_personnel(person(1),012345678, Paul, Jones ) call new_personnel(person(2),123456789, Pat, Smith,21)... call delete_personnel(person(1))! delete first record Pat Smith s age is recorded, Paul s is not. We might also wish to change how the print method works, e.g., suppose we wish to print to a file instead of to a console. The print method can be modified accordingly.

The public interface to a class presents an abstract type to the outside world. By requiring the outside world to use only these interfaces, keeping the internal details of a class private, the internal data cannot be corrupted, and the implementation of methods can be changed without impacting others.