cppProgramStructure_Lect(1)
cppProgramStructure_Lect(1)
1 Separate Compilation
Working with Large Programs
• Each file of source code (programming language text) is compiled to produce a file of object
code.
• All object code files are linked to produce the executable
Object Code
The preprocessor:
• #include
o insert a file
• #define
o define a macro
• #ifdef, #ifndef, #endif
o check to see if a macro has been defined
2.1 #include
#include
#include <headerName>
inserts a system header file from a location defined when the compiler was
installed
#include "fileName"
• We ask the compiler to only run the preprocessor and save the result:
g++ -E C.cpp > C.i
# 1 "A.h" 1
# 1 "A.h" 1
In real programs, most of the code actually seen by the compiler may come from #includes
int main() {
cout << "Hello World" << endl;
return 0;
}
• the compiler sees the file “helloWorld.i”, which is posted under the “Supporting files” folder of
this week
Deja-Vu
• Code that is in headers (.h files) may actually be compiled many times
• Code that is in compilation unit (.cpp) files will be compiled only once
#define
• Used to define macros (symbols that the preprocessor will later substitute for)
o Sometimes used to supply constants
int main() {
cout << "Running version "
<< VersionNumber
<< endl;
o Much more elaborate macros are possible, including ones with parameters
Used to select code based upon whether a macro has been defined:
#ifdef __GNUG__
/* Compiler is gcc/g++ */
#endif
#ifdef _MSC_VER
/* Compiler is Microsoft Visual C++ */
#endif
• All of these macros are used to reduce the amount of code seen by the actual compiler
• Suppose we have three files:
#ifndef A2_H
#define A2_H
#endif
,
#ifndef B2_H
#define B2_H
#endif
,
//This is file C.cpp
#include "A2.h"
#include "B2.h"
code from C2.cpp
• We ask the compiler to only run the preprocessor and save the result:
g++ -E C2.cpp > C2.i
# 1 "A2.h" 1
A declaration in C++
Definitions
A definition in C++
Decls&Defs: Variables
Decls&Defs: Functions
• Declaration:
int myFunction (int x, int y);
• Definition
int myFunction (int x, int y)
{
return x + y;
}
• The declaration provides only the header. The definition adds the body.
How often does the compiler process each line of code from a file?
Coupling
In something with high cohesion, all the pieces contribute to a well-defined, common goal.
In something with low cohesion, the pieces have little relation to one another
Online Auction
main (fileNames[]){
readItems;
readBidders;
readBids;
for each item {
resolveAuction (item, bidders, bids)
}
}
We read in all the information about the auction, then resolve the bidding on each item, one at a
time (in order by closing time of the bidding for the items).
///////////////////////////////////////
// Basic Data Types
///////////////////////////////////////
/**
* An amount of U.S. currency
*/
struct Money {
int dollars;
int cents; //< @invariant should be in the range 0..99, inclusive
};
/**
* The time of the day, to the nearest second.
*/
struct Time {
int hours;
int minutes;
int seconds;
};
/**
* A Bid received during an auction
*/
struct Bid {
std::string bidderName;
Money amount;
std::string itemName;
Time bidPlacedAt;
};
/**
* Someone registered to participate in an auction
*/
struct Bidder {
std::string name;
Money balance;
};
/**
* An item up for auction
*/
struct Item {
/// Name of the item
std::string name;
/////////////////////////////////////
// Sequence Data Types
/////////////////////////////////////
/////////////////////////////////////
// Global Variables
/////////////////////////////////////
/**
* The collection of all items being auctioned off for the day
*/
ItemSequence items;
/////////////////////////////////////
// Function Declarations
/////////////////////////////////////
/**
* Adds two Money amounts together
*
* @param left 1st value to be added
* @param right 2nd value to be added
* @return sum of the two amounts
*/
Money add (const Money& left, const Money& right);
/**
* Find the index of the bidder with the given name. If no such bidder exists,
* return bidders.size.
*/
int findBidder (std::string name, const BidderSequence& bidders);
/**
* Compare two Money amounts to see if the 1st is smaller
* than the second
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff left is a smaller amount than right
*/
bool lessThan (const Money& left, const Money& right);
/**
* Compare two times. Return true iff time1 is earlier than or equal to
* time2
*
* Pre: Both times are normalized: sconds and minutes are in the range 0..59,
* hours are non-negative
*/
bool noLaterThan(const Time& time1, const Time& time2);
/**
* Print a monetary amount.The output format will always
* include a decimal point and a two-digit cents amount.
*
* @param out the stream to which to print
* @param money the value to be printed
*/
void print (std::ostream& out, const Money& money);
/**
* Compare two Money amounts to see if they are equal
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff the two amounts are equal
*/
/**
* Print a time in the format HH:MM:SS (two digits each)
*/
void print (std::ostream& out, const Time& time);
/**
* Read a bid from the indicated file
*/
void read (istream& in, Bid& bid);
/**
* Read all bids from the indicated file
*/
void read (istream& in, BidSequence& bids);
/**
* Read a bidder from the indicated file
*/
void read (istream& in, Bidder& bidder);
/**
* Read all bidders from the indicated file
*/
void read (istream& in, BidderSequence& bidders);
/**
* Read one item from the indicated file
*/
void read (istream& in, Item& item);
/**
* Read all items from the indicated file
*/
void read (istream& in, ItemSequence& items);
/**
* Read a money value from the input. Acceptable formats are
*
* ddd.cc or ddd
*
* where ddd is any positive/negative integer of
* one or more digits denoting dollars, and cc, if
* supplied, is a two-digit integer.
*
* @param in stream from which to read
* @param money the value read in. Result is unpredictable if an I/O error occur
s
*/
void read (std::istream& in, Money& money);
/**
* Read a time (in the format HH:MM:SS) after skipping any
* prior whitespace.
*/
void read (std::istream& in, Time& time);
/**
* Determine the winner of the auction for item number i.
* Announce the winner and remove money from winner's account.
*/
void resolveAuction (const BidSequence& bids, BidderSequence& bidders, const Ite
m& item);
/**
* Subtract one Money amount from another
*
* @param left the minuend
* @param right the subtrahend
* @return difference of the two amounts
*/
Money subtract (const Money& left, const Money& right);
/////////////////////////////////////
// Function Bodies
/////////////////////////////////////
/**
* Adds two Money amounts together
*
* @param left 1st value to be added
* @param right 2nd value to be added
* @return sum of the two amounts
*/
Money add (const Money& left, const Money& right)
{
Money result;
result.dollars = left.dollars + right.dollars;
result.cents = left.cents + right.cents;
while (result.cents > 99)
{
result.cents -= 100;
++result.dollars;
}
while (result.cents < 0)
{
result.cents += 100;
--result.dollars;
}
return result;
}
/**
* Compare two Money amounts to see if they are equal
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff the two amounts are equal
*/
bool equal (const Money& left, const Money& right)
{
return (left.dollars == right.dollars)
&& (left.cents == right.cents);
}
/**
* Find the index of the bidder with the given name. If no such bidder exists,
* return bidders.size.
*/
int findBidder (std::string name, const BidderSequence& bidders)
{
int found = bidders.size;
for (int i = 0; i < bidders.size && found == bidders.size; ++i)
{
if (name == bidders.data[i].name)
found = i;
}
return found;
}
/**
* Compare two Money amounts to see if the 1st is smaller
* than the second
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff left is a smaller amount than right
*/
bool lessThan (const Money& left, const Money& right)
{
if (left.dollars < right.dollars)
return true;
else if (left.dollars == right.dollars)
return left.cents < right.cents;
return false;
}
/**
* Compare two times. Return true iff time1 is earlier than or equal to
* time2
*
* Pre: Both times are normalized: sconds and minutes are in the range 0..59,
* hours are non-negative
*/
bool noLaterThan(const Time& time1, const Time& time2)
{
// First check the hours
if (time1.hours > time2.hours)
return false;
if (time1.hours < time2.hours)
return true;
// If hours are the same, compare the minutes
if (time1.minutes > time2.minutes)
return false;
if (time1.minutes < time2.minutes)
return true;
// If hours and minutes are the same, compare the seconds
if (time1.seconds > time2.seconds)
return false;
return true;
}
/**
* Print a monetary amount.The output format will always
* include a decimal point and a two-digit cents amount.
*
* @param out the stream to which to print
* @param money the value to be printed
*/
void print (std::ostream& out, const Money& money){
out << money.dollars;
out << '.' ;
if (money.cents < 10)
out << '0' ;
out << money.cents;
}
/**
* Print a time in the format HH:MM:SS (two digits each)
*/
void print (std::ostream& out, Time& t)
{
if (t.hours < 10)
out << '0' ;
out << t.hours << ':' ;
if (t.minutes < 10)
out << '0' ;
out << t.minutes << ':' ;
if (t.seconds < 10)
out << '0' ;
out << t.seconds;
}
/**
* Read a bid from the indicated file
*/
void read (istream& in, Bid& bid)
{
in >> bid.bidderName;
read (in, bid.amount);
/**
* Read all bids from the indicated file
*/
void read (istream& in, BidSequence& bids)
{
int nBids;
in >> nBids;
bids.size = nBids;
for (int i = 0; i < nBids; ++i)
{
Bid bid;
read (in, bid);
bids.data[i] = bid;
}
}
/**
* Read a bidder from the indicated file
*/
void read (istream& in, Bidder& bidder)
{
in >> bidder.name;
read (in, bidder.balance);
}
/**
* Read all bidders from the indicated file
*/
void read (istream& in, BidderSequence& bidders)
{
int nBidders;
in >> nBidders;
bidders.size = nBidders;
for (int i = 0; i < nBidders; ++i)
{
read (in, bidders.data[i]);
}
}
/**
* Read one item from the indicated file
*/
void read (istream& in, Item& item)
{
read (in, item.reservedPrice);
read (in, item.auctionEndsAt);
/**
* Read all items from the indicated file
*/
void read (istream& in, ItemSequence& items)
{
int nItems;
in >> nItems;
items.size = nItems;
for (int i = 0; i < nItems; ++i)
read (in, items.data[i]);
}
/**
* Read a money value from the input. Acceptable formats are
*
* ddd.cc or ddd
*
* where ddd is any positive/negative integer of
* one or more digits denoting dollars, and cc, if
* supplied, is a two-digit integer.
*
* @param in stream from which to read
* @param money the value read in. Result is unpredictable if an I/O error occur
s
*/
void read (std::istream& in, Money& money)
{
if (!in) return;
in >> money.dollars;
if (!in) return;
if (in.peek() == '.' ) // if next input is a '.'
{
char decimal;
in >> decimal;
in >> money.cents;
} else
money.cents = 0;
}
/**
* Determine the winner of the auction for item number i.
* Announce the winner and remove money from winner's account.
*/
void resolveAuction (const BidSequence& bids, BidderSequence& bidders, const Ite
m& item)
{
Money zero = {0, 0};
Money highestBidSoFar = zero;
string winningBidderSoFar;
for (int bidNum = 0; bidNum < bids.size; ++bidNum)
{
Bid bid = bids.data[bidNum];
if (noLaterThan(bid.bidPlacedAt, item.auctionEndsAt))
{
if (bid.itemName == item.name
&& lessThan(highestBidSoFar, bid.amount)
&& !lessThan(bid.amount, item.reservedPrice)
)
{
int bidderNum = findBidder(bid.bidderName, bidders);
Bidder bidder = bidders.data[bidderNum];
// Can this bidder afford it?
if (!lessThan(bidder.balance, bid.amount))
{
highestBidSoFar = bid.amount;
winningBidderSoFar = bid.bidderName;
}
}
}
}
/**
* Read a time from the indicated stream after skipping any
* leading whitespace
*/
void read (istream& in, Time& t)
{
char c;
in >> t.hours >> c >> t.minutes >> c >> t.seconds;
}
/**
* Subtract one Money amount from another
*
* @param left the minuend
* @param right the subtrahend
* @return difference of the two amounts
*/
Money subtract (const Money& left, const Money& right)
{
Money result;
result.dollars = left.dollars - right.dollars;
result.cents = left.cents - right.cents;
while (result.cents > 99)
{
result.cents -= 100;
++result.dollars;
}
while (result.cents < 0)
{
result.cents += 100;
--result.dollars;
}
return result;
}
A Possible Modularization
From the description of the program and from a glance through the code, we might guess that the
key modules would be
Items
Data and functions related to the items up for auction
Bidders
Data and functions related to the people bidding in the auction
Bids
Data and functions related to the bids placed by those people
Money
Data and functions related to money
Time
Data and functions related to time.
(In later lessons we’ll talk about other ways to identify good modules.)
Module Files
And we would then expect to divide the program into files corresponding to those modules:
Types
Money
Time
Data
Bid
bidders
Bidder
bids
Item
items
BidSequence
BidderSequence
ItemSequence
Making a List…
Functions
Functions
read (bid sequence)
add
read (bidder)
equal
read (bidder sequence)
findBidder
read (item)
lessThan
read (item sequence)
noLaterThan
read (money)
print (money)
read (time)
print (time)
resolveAuction
read (bid)
subtract
Bids
Bid, BidSequence, bids, read bid, read bid seq.
Bidders
Bidder, BidderSequence, bidders, findBidder, read bidder, read bidder seq.
Items
Item, ItemSequence, items, read item, read item seq.
Money
Money, add, equal, lessThan, print, read, subtract
Time
Time, noLaterThan, print, read
??
resolveAuction, main
The “application”
The final two functions, resolveAuction and main, constitute the core algorithm, the "main application"
for this particular program, and so can be kept in the main cpp file.
We can reduce possible coupling, though, by looking at the actual function bodies and
• checking to see if any of these declarations would only be used internally within a single
module.
o A declaration only needs to appear in the header file if some code outside the module is
using it.
o If it is only used from within its own module. it can be kept "hidden" inside that
module’s .cpp file.
Not Shared
For example, in the Items module, the function read that reads a single item is only called from
inside the function read that reads an entire sequence of items.
• Because that first read is only called by a function within its own module, it does not need to be
listed in items.h.
Possible Division
• Bids
#ifndef BIDS_H
#define BIDS_H
#include <iostream>
#include <string>
#include "money.h"
#include "times.h"
/**
* A Bid received during an auction
*/
struct Bid {
std::string bidderName;
Money amount;
std::string itemName;
Time bidPlacedAt;
};
/**
* Read all bids from the indicated input
*/
void read (std::istream& in, BidSequence& bids);
#endif
#include <string>
#include <fstream>
//
// Bids Received During Auction
//
#include "bids.h"
// Bids received
BidSequence bids;
/**
* Read a bid from the indicated file
*/
void read (istream& in, Bid& bid)
{
in >> bid.bidderName;
read (in, bid.amount);
/**
* Read all bids from the indicated file
*/
void read (istream& in, BidSequence& bids)
{
int nBids;
in >> nBids;
bids.size = nBids;
for (int i = 0; i < nBids; ++i)
{
Bid bid;
read (in, bid);
bids.data[i] = bid;
}
}
• Bidders
#ifndef BIDDERS_H
#define BIDDERS_H
#include <iostream>
#include <string>
#include "money.h"
//
// Bidders Registered for auction
//
/**
* Someone registered to participate in an auction
*/
struct Bidder {
std::string name;
Money balance;
};
/**
* Read all bidders from the indicated file
*/
void read (std::istream& in, BidderSequence& bidders);
/**
* Find the index of the bidder with the given name. If no such bidder exists,
* return bidders.size.
*/
int findBidder (std::string name, const BidderSequence& bidders);
#endif
#include <string>
#include <fstream>
#include <iostream>
//
// Bidders Registered for auction
//
#include "bidders.h"
/**
* Read a bidder from the indicated file
*/
void read (istream& in, Bidder& bidder)
{
in >> bidder.name;
read (in, bidder.balance);
}
/**
* Read all bidders from the indicated file
*/
void read (istream& in, BidderSequence& bidders)
{
int nBidders;
in >> nBidders;
bidders.size = nBidders;
for (int i = 0; i < nBidders; ++i)
{
read (in, bidders.data[i]);
}
}
/**
* Find the index of the bidder with the given name. If no such bidder exists,
* return bidders.size.
*/
int findBidder (std::string name, const BidderSequence& bidders)
{
int found = bidders.size;
for (int i = 0; i < bidders.size && found == bidders.size; ++i)
{
if (name == bidders.data[i].name)
found = i;
}
return found;
}
• Items
#ifndef ITEMS_H
#define ITEMS_H
#include <string>
#include "money.h"
#include "times.h"
/**
* An item up for auction
*/
struct Item {
/// Name of the item
std::string name;
/**
* The collection of all items being auctioned off for the day
*/
extern ItemSequence items;
/**
* Read all items from the indicated input
*/
void read (std::istream& in, ItemSequence& items);
#endif
#include <iostream>
#include <fstream>
#include "items.h"
//
// Items up for auction
//
ItemSequence items;
/**
* Read one item from the indicated file
*/
void read (istream& in, Item& item)
{
read (in, item.reservedPrice);
read (in, item.auctionEndsAt);
/**
* Read all items from the indicated file
*/
void read (istream& in, ItemSequence& items)
{
int nItems;
in >> nItems;
items.size = nItems;
for (int i = 0; i < nItems; ++i)
read (in, items.data[i]);
}
• Money
/*
* money.h
*
* Created on: Aug 23, 2013
* Author: zeil
*/
#ifndef MONEY_H_
#define MONEY_H_
#include <iostream>
/**
* An amount of U.S. currency
*/
struct Money {
int dollars;
int cents; //< @invariant should be in the range 0..99, inclusive
};
/**
* Read a money value from the input. Acceptable formats are
*
* ddd.cc or ddd
*
* where ddd is any positive/negative integer of
* one or more digits denoting dollars, and cc, if
* supplied, is a two-digit integer.
*
* @param in stream from which to read
* @param money the value read in. Result is unpredictable if an I/O error occ
urs
*/
void read (std::istream& in, Money& money);
/**
* Print a monetary amount.The output format will always
* include a decimal point and a two-digit cents amount.
*
* @param out the stream to which to print
* @param money the value to be printed
*/
void print (std::ostream& out, const Money& money);
/**
* Compare two Money amounts to see if they are equal
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff the two amounts are equal
*/
bool equal (const Money& left, const Money& right);
/**
* Compare two Money amounts to see if the 1st is smaller
* than the second
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff left is a smaller amount than right
*/
bool lessThan (const Money& left, const Money& right);
/**
* Adds two Money amounts together
*
* @param left 1st value to be added
* @param right 2nd value to be added
* @return sum of the two amounts
*/
Money add (const Money& left, const Money& right);
/**
* Subtract one Money amount from another
*
* @param left the minuend
* @param right the subtrahend
* @return difference of the two amounts
*/
Money subtract (const Money& left, const Money& right);
#endif /* MONEY_H_ */
/*
* money.h
*
* Created on: Aug 23, 2013
* Author: zeil
*/
#include "money.h"
#include <iostream>
/**
* Read a money value from the input. Acceptable formats are
*
* ddd.cc or ddd
*
* where ddd is any positive/negative integer of
* one or more digits denoting dollars, and cc, if
* supplied, is a two-digit integer.
*
* @param in stream from which to read
* @param money the value read in. Result is unpredictable if an I/O error occ
urs
*/
void read (std::istream& in, Money& money)
{
if (!in) return;
in >> money.dollars;
if (!in) return;
if (in.peek() == '.' ) // if next input is a '.'
{
char decimal;
in >> decimal;
in >> money.cents;
} else
money.cents = 0;
}
/**
* Print a monetary amount.The output format will always
* include a decimal point and a two-digit cents amount.
*
* @param out the stream to which to print
* @param money the value to be printed
*/
void print (std::ostream& out, const Money& money){
out << money.dollars;
out << '.' ;
if (money.cents < 10)
out << '0' ;
out << money.cents;
}
/**
* Compare two Money amounts to see if they are equal
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff the two amounts are equal
*/
bool equal (const Money& left, const Money& right)
{
return (left.dollars == right.dollars)
&& (left.cents == right.cents);
}
/**
* Compare two Money amounts to see if the 1st is smaller
* than the second
*
* @param left 1st value to be compared
* @param right 2nd value to be compared
* @return true iff left is a smaller amount than right
*/
bool lessThan (const Money& left, const Money& right)
{
if (left.dollars < right.dollars)
return true;
else if (left.dollars == right.dollars)
return left.cents < right.cents;
return false;
}
/**
* Adds two Money amounts together
*
* @param left 1st value to be added
* @param right 2nd value to be added
* @return sum of the two amounts
*/
Money add (const Money& left, const Money& right)
{
Money result;
result.dollars = left.dollars + right.dollars;
result.cents = left.cents + right.cents;
while (result.cents > 99)
{
result.cents -= 100;
++result.dollars;
}
while (result.cents < 0)
{
result.cents += 100;
--result.dollars;
}
return result;
}
/**
* Subtract one Money amount from another
*
* @param left the minuend
* @param right the subtrahend
* @return difference of the two amounts
*/
Money subtract (const Money& left, const Money& right)
{
Money result;
result.dollars = left.dollars - right.dollars;
result.cents = left.cents - right.cents;
while (result.cents > 99)
{
result.cents -= 100;
++result.dollars;
}
while (result.cents < 0)
{
result.cents += 100;
--result.dollars;
}
return result;
}
• Time
#ifndef TIMES_H
#define TIMES_H
#include <iostream>
/**
* The time of the day, to the nearest second.
*/
struct Time {
int hours;
int minutes;
int seconds;
};
/**
* Read a time (in the format HH:MM:SS) after skipping any
* prior whitespace.
*/
void read (std::istream& in, Time& time);
/**
* Print a time in the format HH:MM:SS (two digits each)
*/
void print (std::ostream& out, const Time& time);
/**
* Compare two times. Return true iff time1 is earlier than or equal to
* time2
*
* Pre: Both times are normalized: sconds and minutes are in the range 0..59,
* hours are non-negative
*/
bool noLaterThan(const Time& time1, const Time& time2);
#endif // TIMES_H
#include "times.h"
/**
* Times in this program are represented by three integers: H, M, & S, representing
* the hours, minutes, and seconds, respecitvely.
*/
/**
* Read a time from the indicated stream after skipping any
* leading whitespace
*/
void read (istream& in, Time& t)
{
char c;
in >> t.hours >> c >> t.minutes >> c >> t.seconds;
}
/**
* Print a time in the format HH:MM:SS (two digits each)
*/
void print (std::ostream& out, Time& t)
{
if (t.hours < 10)
out << '0' ;
out << t.hours << ':' ;
if (t.minutes < 10)
out << '0' ;
out << t.minutes << ':' ;
if (t.seconds < 10)
out << '0' ;
out << t.seconds;
}
/**
* Compare two times. Return true iff time1 is earlier than or equal to
* time2
*
* Pre: Both times are normalized: sconds and minutes are in the range 0..59,
#include "items.h"
#include "bidders.h"
#include "bids.h"
#include "times.h"
#include "sequence.h"
/**
* Determine the winner of the auction for item number i.
* Announce the winner and remove money from winner's account.
*/
void resolveAuction (const BidSequence& bids, BidderSequence& bidders, const Item& i
tem);
{
ifstream itemInput (argv[1]);
read (itemInput, items);
}
{
ifstream bidderInput (argv[2]);
read (bidderInput, bidders);
}
{
ifstream bidInput (argv[3]);
read (bidInput, bids);
}
/**
* Determine the winner of the auction for item number i.
* Announce the winner and remove money from winner's account.
*/
void resolveAuction (const BidSequence& bids, BidderSequence& bidders, const Item& i
tem)
{
Money zero = {0, 0};
Money highestBidSoFar = zero;
string winningBidderSoFar;
for (int bidNum = 0; bidNum < bids.size; ++bidNum)
{
Bid bid = bids.data[bidNum];
if (noLaterThan(bid.bidPlacedAt, item.auctionEndsAt))
{
if (bid.itemName == item.name
&& lessThan(highestBidSoFar, bid.amount)
&& !lessThan(bid.amount, item.reservedPrice)
)
{
int bidderNum = findBidder(bid.bidderName, bidders);
Bidder bidder = bidders.data[bidderNum];
// Can this bidder afford it?
if (!lessThan(bidder.balance, bid.amount))
{
highestBidSoFar = bid.amount;
winningBidderSoFar = bid.bidderName;
}
}
}
}