The Ideal GasPump!

Alex Edwards 0 Tallied Votes 210 Views Share

Hmm this implementation is a bit bulky and unnecessary, but it was required as a project for one of my classes.


Programming perspective:

You'll notice in the code that I'm still fairly new to manipulating streams directly in C++, but I am trying my best =P.

The StandardIncremental overhead could have been omitted by simply extracting the value using the extraction operator, though I'm still new to using that so I did things the hard way (initially by accident, but I've learned my lesson! =) )

The rest is up to you other programmers to inspect =P.


User perspective:

Whoo! Have fun! =)

/**
 * GasPump.h
 * 
 * This header is for representing a real-life GasPump.
 */
#pragma once
#include "StandardIncremental.h"
#include <ctime>
#include <string>
#include <sstream>
#include <exception>

using std::size_t;
using std::string;
using std::exception;
const double Conv_Factor = .03; // $3.00 = 1 gallon... yes I know, wishful thinking @_@

string conversionFactor();

namespace D{
	enum Direction {OUT, IN};
}

using D::Direction;
using D::OUT;
using D::IN;

class GasPump{
	
	StandardIncremental money;
	StandardIncremental moneySpent;
	StandardIncremental gas;
	StandardIncremental currentGasDispensed;
	size_t capacity;
	string buff;
	void reset();

	public:
		class GasPumpException : public exception{
			public:
				GasPumpException();
				virtual const char* what() const throw();
		} gpe;

		GasPump(size_t val = 0, size_t cap = 500);
		void insertMoney(const StandardIncremental&);
		void insertGas(const StandardIncremental&);
		void pumpOnce(Direction);
		void pumpGas(Direction);
		void showTotalGas();
		void showTotalMoney();
		void showCurrentGasDispensed();
		void showCurrentMoneyUsed();
		void showSession();
		StandardIncremental dispenseChange();
};

//////////////////////////

/**
 * StandardIncremental.h
 * 
 * This header is for representing real-life StandardIncremental
 * things (i.e. Money, Volume, etc).
 */
#pragma once
#include <iostream>
#include <string>

using std::string;

class StandardIncremental{
	double value;
	void truncate(); // truncates the double, should only happen once
	void truncate() const;

	public:
		StandardIncremental(); // constructor (void)
		StandardIncremental(double); // constructor (double)
		StandardIncremental(const string&); // constructor (string)
		string getValue(); // returns the value of this money object
		StandardIncremental operator+(const StandardIncremental&);
		StandardIncremental& operator++(int);
		StandardIncremental operator-(const StandardIncremental&);
		StandardIncremental& operator--(int);
		StandardIncremental& operator+=(const StandardIncremental&);
		StandardIncremental& operator-=(const StandardIncremental&);
		StandardIncremental operator *(const StandardIncremental&);
		bool operator==(const StandardIncremental&);
		bool operator>(const StandardIncremental&);
		bool operator>=(const StandardIncremental&);
		bool operator<=(const StandardIncremental&);
		bool operator<(const StandardIncremental&);
		friend std::ostream& operator<< (std::ostream&, const StandardIncremental&);
		friend std::istream& operator>> (std::istream&, const StandardIncremental&);
		operator double ();
		operator double () const;
};

//////////////////

/**
 * GasPump.cpp
 * 
 * This is the implementation file that follows the GasPump.h interface.
 */
#include "GasPump.h"
#include <cmath>
#include <iostream>
#include <sstream>
#include <cmath>

using std::cin;
using std::cout;
using std::endl;
using std::stringstream;
using std::ios;
using D::OUT;
using D::IN;

/**
 * Constructor for the GasPump
 *
 * If the value supplied is greater than the capacity provided,
 * the value becomes the capacity.
 */
GasPump::GasPump(size_t val, size_t cap) : gas(val * 1.00), capacity(cap), money(0.00),
				 moneySpent(0.00), currentGasDispensed(0.00){
	if(val >= cap){
		gas = static_cast<double>(capacity);
	}
}

/**
 * Default Constructor for th GasPumpException inner class of GasPump
 */
GasPump::GasPumpException::GasPumpException() : exception(){}

/**
 * In the event an illegal operation occurs on the GasPump, such as attempting to supply
 * a negative amount of cash for the pump.
 */
const char* GasPump::GasPumpException::what() const throw(){
	return "An illegal operation occured on the GasPump!";
}

/**
 * Attempts to insert money into the GasPump.
 * 
 * This function will fail if the GasPump doesn't have enough gas to support
 * the amount of money inserted.
 */
void GasPump::insertMoney(const StandardIncremental& mon){
	if( const_cast<StandardIncremental&>(mon) // i.e. if $3.00 < (gas * 100 * Conv_Factor)
			<  static_cast<const StandardIncremental>( gas * 
					static_cast<const StandardIncremental>(100 * Conv_Factor) ) ){
		
		if( fabs(static_cast<double>(mon)) != static_cast<double>(mon) ) throw gpe; // no stealing allowed!

		money += mon;
	}
}

/**
 * Attempts to increase the current amount of gas available
 */
void GasPump::insertGas(const StandardIncremental& g){
	StandardIncremental cap = static_cast<double>(capacity);
	if( cap > (const_cast<StandardIncremental&>(g) + gas) 
		&& ((static_cast<double>(g) + static_cast<double>(gas)) >= 0  )){ // if the capacity is greater than
																	      // the sum of the current amount and incoming amount
																	      // and the amount is not negative
		gas += g; // increment the current amount by incoming amount
	}
}

/**
 * Increases or Decreases the current amount of gas once and only once.
 */
void GasPump::pumpOnce(Direction dir){
	if(dir == IN){
		if( ( gas + static_cast<StandardIncremental>(1.00) ) <=
					static_cast<const StandardIncremental>(capacity) ){ // if its possible to increment gas withouuuu
																		// exceeding the capacity...
			gas++; // increment the gas
		}
	}else{ // if dir == OUT
		StandardIncremental lowest = 0.03;
		bool gasCanBePumped = ( lowest <= ( gas * static_cast<const StandardIncremental&>(Conv_Factor * 100) ) ); // is there gas left 
																										          // to spend money on?
		if( (money > moneySpent) && (gasCanBePumped == true) ){ // if the amount of money inserted is greater than the amount spent...
														        // and there is gas left to be extracted
			moneySpent++; // increment the amount of money spent
			double dMod = fmod(moneySpent, Conv_Factor); // obtain the remainder of moneySpent / Conv_Factor
			stringstream ss  (stringstream::in | stringstream::out); // input/output stringstream
			ss.precision(5); // knock-off factor for single/double precision floating point types
			ss.setf(ios::fixed, ios::floatfield); // sets the format of the stringstream
			ss << dMod; // place untruncated value in streammm
			ss >> dMod; // truncate the value

			if( dMod == 0.01 ){ // if the remainder of the amount spent and the conversion
								// factor is 0, increment the currentGasDispensed and
								// decrement the current amount of gas
				// edit: fmod is pretty funky... it wont return 0.00, but instread 0.01-to-arg ??
				currentGasDispensed++;
				gas--;
			}
		}
	}
}

/**
 * Pumps gas in or out, depending on the direction specified.
 * Continuously pumps so long as the user confirms the input
 * of the char 'p'.
 */
void GasPump::pumpGas(Direction dir){
	while(true){
		buff.clear();
		std::cin >> buff;

		if(buff[0] == 'p'){
			pumpOnce(dir);
		}else return;
	}
}

/**
 * Shows the current gas dispensed during a session.
 */
void GasPump::showCurrentGasDispensed(){
	cout << " " << currentGasDispensed << "gal" << endl;
}

/**
 * Shows the money used during a session.
 */
void GasPump::showCurrentMoneyUsed(){
	cout << "$" << moneySpent << endl;
}

/**
 * Shows the money inserted during a session.
 */
void GasPump::showTotalMoney(){
	cout << "$" << money << endl;
}

/**
 * Shows the current amount of gas available
 * out of the maximum capacity.
 */
void GasPump::showTotalGas(){
	cout << gas << "gal/" << capacity << "gal" << endl;
}

/**
 *  Simply invokes both the current gas dispensed and money used.
 */
void GasPump::showSession(){
	showCurrentMoneyUsed();
	showCurrentGasDispensed();
}

/**
 * Changes the money inserted, the money spent and the current gas dispenesed to be
 * of value 0. This is used so that any new customers of the Gas Station will be able
 * to see and use their own sessions.
 */
void GasPump::reset(){
	currentGasDispensed = 0.00;
	money = 0.00;
	moneySpent = 0.00;
}

/**
 * Dispenses change and invokes reset
 */
StandardIncremental GasPump::dispenseChange(){
	StandardIncremental s = (money - moneySpent);
	reset();
	return s;
}

/**
 * Returns the string object version of the Conv_Factor
 */
string conversionFactor(){
	stringstream ss (stringstream::in | stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << Conv_Factor;
	return ss.str();
}

///////////////////////

/**
 * StandardIncremental.cpp
 * 
 * This is the implementation file for the StandardIncremental.h interface.
 */
#include "StandardIncremental.h"
#include <sstream>
#include <iostream>
#include <exception>

using std::stringstream;
using std::ios;

StandardIncremental::StandardIncremental() : value(0.0) {
	truncate();
}

void StandardIncremental::truncate(){
	stringstream ss (stringstream::in | stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << value;
	ss >> value; // push new truncated value into the double
}

void StandardIncremental::truncate() const{
	const_cast<StandardIncremental&>(*this).truncate();
}

StandardIncremental::StandardIncremental(double v = 0.0) : value(v) {
	truncate();
}

StandardIncremental::StandardIncremental(const std::string& val){
	try{
		stringstream ss (stringstream::in | stringstream::out);
		ss << val;
		ss >> value;
		truncate();
	}catch(std::exception& e){
		std::cout << "ERROR!\n" << e.what() << "\n";
		exit(1);
	}
}

string StandardIncremental::getValue(){
	stringstream ss (stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << value;
	return ss.str();
}


StandardIncremental StandardIncremental::operator+(const StandardIncremental& other){
	StandardIncremental temp  = *this;
	temp.value += other.value;
	return temp;
}

StandardIncremental& StandardIncremental::operator++(int){
	return ((*this) += 0.01);
}

// this will msot likely be changed
StandardIncremental StandardIncremental::operator-(const StandardIncremental& other){
	StandardIncremental temp = *this;
	temp.value -= other.value;
	return temp;
}

StandardIncremental& StandardIncremental::operator--(int){
	return ((*this) -= 0.01);
}

StandardIncremental& StandardIncremental::operator+=(const StandardIncremental& other){
	this->value += other.value;
	return *this;
}

// this will most likely be changed
StandardIncremental& StandardIncremental::operator-=(const StandardIncremental& other){
	this->value -= other.value;
	return *this;
}

StandardIncremental StandardIncremental::operator *(const StandardIncremental& other){
	StandardIncremental temp = *this;
	temp.value *= other.value;
	return temp;
}

StandardIncremental::operator double(){
	return this->value;
}

StandardIncremental::operator double() const{
	return this->value;
}

bool StandardIncremental::operator>(const StandardIncremental& other){
	return this->value > other.value;
}

bool StandardIncremental::operator<(const StandardIncremental& other){
	return this->value < other.value;
}

bool StandardIncremental::operator>=(const StandardIncremental& other){
	return this->value >= other.value;
}

bool StandardIncremental::operator<=(const StandardIncremental& other){
	return this->value <= other.value;
}

std::ostream& operator<<( std::ostream& out, const StandardIncremental& mon ){
	return out << mon.value;
}

std::istream& operator>>(std::istream& in, const StandardIncremental& mon){
	double& d = const_cast<double&>(mon.value);
	if( !(in >> d) ){
		std::cout << "ERROR!\n";
		exit(1);
	}
	mon.truncate();
	return in;
}

////////////////////////

/**
 * GasPump Project
 * Author: Alex Edwards
 * Date: 10/07/08
 *
 * Intent: The intent of this program is to simulate a typical
 * GasPump that we encounter when we have a need to fuel our automobiles,
 * or anything that requires gas.
 */

/*includes needed for this program*/
#include "GasPump.h"
#include "StandardIncremental.h"
#include <windows.h>
#include <iostream>
#include <ctime>
#include <string>
#include <sstream>

/*using directive for namespaces of objects and types commonly used*/
using std::string;
using std::size_t;
using std::clock_t;
using D::Direction;
using std::cout;
using std::cin;
using std::endl;
using std::ios;
using std::stringstream;

/*Prototypes. See method definitions under main for full descriptions*/
VOID keyboard(KEY_EVENT_RECORD&); // Handles key input-events received from the EDT
VOID fadeOut(); // Blacks out the screen
void pause(size_t); // Pauses a process
void unsetFlags(); // Initializes mask


typedef INPUT_RECORD QUEUE; // renamed for more clerity
typedef COORD POINT2D; // renamed for clerity


enum {PUMP_OUT_ONCE = 80, EXIT = 69, SEE_DESCRIPTIONS = 83, REFILL = 82, 
	  INSERT = 73, LOAD_ONCE = 76, SEE_MENU = 77, DISPENSE_CHANGE = 67, C_FACTOR = 70}; // convenience-constants
enum {SHOW_BANNER = 1, SHOW_MONEY_SPENT = 2, SHOW_MONEY = 4, SHOW_GAS_DISPENSED = 8, 
	  SHOW_GAS = 16, SHOW_MENU_DESCRIPTION = 32, SHOW_C_FACTOR = 64}; // Flags for bit-wise operations
enum {MAX_BIT = 6}; // The maximum number of bits


GasPump gp (50, 500); // Our GasPump with initial amount of 50 gallons, with a maximum capacity of 500 gallons!
POINT2D loc = {0, 0}; // Specifying a point of return once we're finished manipulating the ConsoleScreen
bool wasMenuKey = true; // might be removed
bool finishedLoop = true; // for re-use of exiting loops
unsigned char mask = 1; // mask for flags
string menuBanner (
"************************************************\n\
***        Welcome to the Gas Station!       ***\n\
***                                          ***\n\
***  'p': pump out gas |  's': menu info     ***\n\
***  'r': refill pump  |  'e': exit program  ***\n\
***  'i': insert money |  'l': load pump     ***\n\
***  'c': get change   |  'm': show menu     ***\n\
***  'f': conv factor  |                     ***\n\
************************************************\n"
); // The Menu

string menuDescription (
"-----------------------------------------------------------------\n\
|'p' - Gradually pumps gas out of the GasPump\t\t\t|\n\
|'r' - Gradually refills the GasPump \t\t\t\t|\n\
|'i' - Insert cash if you wish to purchase gas from the GasPump\t|\n\
|'c' - Obtain the money you didn't spend in a session \t\t|\n\
|'f' - Displays the conversion factor\t\t\t\t|\n\
|'s' - Displays this help menu on the screen \t\t\t|\n\
|'e' - Shuts down the program \t\t\t\t\t|\n\
|'l' - Gives the GasPump a bulk amount of gas \t\t\t|\n\
|'m' - Displays the standard menu\t\t\t\t|\n\
-----------------------------------------------------------------\n"
); // The Instructions for using the commands specified in the GasPump menu.

// OUT seems to already be defined. I don't want OUT or IN in some header to overlap with my
// constants declared in a namespace. Since im not using the 
#ifdef OUT
#undef OUT
#endif

#ifdef IN
#undef IN
#endif

HANDLE standardInputHandle; // Handle to the Standard Input
HANDLE outputHandle; // Handle to the Standard Output
DWORD pendingReq; // Amount of requests read
QUEUE requestQueue[256]; // Holds up to 256 requests
DWORD count; // Stores information for methods that accept DWORD references
CONSOLE_SCREEN_BUFFER_INFO csbi; // Contains information of the Console Screen

/**
 * Entry-point for the program.
 */
int main(){

    standardInputHandle = GetStdHandle(STD_INPUT_HANDLE); // Attempt to get handle to the Standard Input
	if (standardInputHandle == INVALID_HANDLE_VALUE){ // If unsuccessful...
		cout << "Unable to get the handle to the standard input! Closing program!\n";
		exit(1); // exit program with an irregular exit code (1)
	}

	outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); // also attempt to get handle to the Standard Output
	if (outputHandle == INVALID_HANDLE_VALUE){ // If unsuccessful...
		cout << "Unable to get the handle to the standard output! Closing program!\n";
		exit(1); // exit program with an irregular exit code (1)
	}
	
	cout.precision(2); // convenience for gas and cash display
	cout.setf( ios::fixed, ios::floatfield ); // formats the standard output to show what we want to see

    while(true){ 
		if(wasMenuKey){ // if a menu key was pressed, re-display the menu (??, most likely will be changed)
			fadeOut(); // clear the screen
			if(mask & SHOW_BANNER) // show banner mask
				cout << menuBanner << endl;
			if(mask & SHOW_MONEY_SPENT) // show money spent mask
				gp.showCurrentMoneyUsed();
			if(mask & SHOW_MONEY) // show money mask
				gp.showTotalMoney();
			if(mask & SHOW_GAS_DISPENSED) // show gas dispensed mask
				gp.showCurrentGasDispensed();
			if(mask & SHOW_GAS) // show gas mask
				gp.showTotalGas();
			if(mask & SHOW_MENU_DESCRIPTION) // show menu description mask
				cout << menuDescription << endl;
			if(mask & SHOW_C_FACTOR){ // show the conversion factor
				cout << "The conversion factor of $-to-gallons is: ";
				cout << conversionFactor() << ":0.01" << endl;
			}

			wasMenuKey = false;
		}

        if (! ReadConsoleInput( standardInputHandle, requestQueue, 
			256, &pendingReq) ){ // if the input to the console couldn't be read
			cout << "An error occurred while attempting to read input. Closing program!\n";
			exit(1); // exit the program with an irregular exit code (1)
		}
 
        for (DWORD i = 0; i < pendingReq; i++){ // For every request pushed in the Queue
            switch(requestQueue[i].EventType){ // get the Event type of the request
                case KEY_EVENT: 
                    keyboard(requestQueue[i].Event.KeyEvent); 
                    break;
                default: // Not really concerned with any other event type.
                    break; 
            }
        }
    }
    return 0; 
};

/**
 * Convenience method for takking a KEY_EVENT_RECORD and
 * performing an action based on the key pressed.
 */
VOID keyboard(KEY_EVENT_RECORD& key){
	
	if(key.bKeyDown){
		switch(key.wVirtualKeyCode){ // in the event the user presses the appropriate menu button...
			case PUMP_OUT_ONCE:
				gp.pumpOnce(D::OUT);
				unsetFlags();
				mask |= SHOW_MONEY_SPENT;
				mask |= SHOW_GAS_DISPENSED;
				pause(100);
				break;
			case EXIT:
				cout << "Thank you, come again! =)\nPress any key to continue...";
				cin.get();
				cout << "\n";
				exit(0);
				break; // probably unnecessary, since the program will end before this can happen
			case SEE_DESCRIPTIONS:
				unsetFlags();
				mask |= SHOW_MENU_DESCRIPTION;
				break;
			case REFILL:
				unsetFlags();
				cout << "Fill'er up!" << endl;
				gp.pumpOnce(D::IN);
				mask |= SHOW_GAS;
				pause(200);
				break;
			case INSERT:
				do{
					finishedLoop = true;
					fadeOut();
					unsetFlags();
					try{
						cout << "Submit cash into the machine? y/n ";
						string choice;
						cin >> choice;
						
						if(choice[0] == 'y'){
							double cash;
							cout << "Enter the amount you wish to submit.\n";
							stringstream ss (stringstream::in | stringstream::out);
							string val;
							cin >> val;
							ss << val;
							if(!(ss >> cash)) throw ss.str();
							gp.insertMoney(cash);
							cout << "You can spend up to ";
							gp.showTotalMoney();
							cout << "Submit any key to continue. ";
							cin.ignore();
							cin.get();
							mask |= SHOW_BANNER;
							mask |= SHOW_MONEY;
						}else{
							mask |= SHOW_BANNER;
						}
					}catch(string& errval){
						cout << "\a\a" << errval << ": IS AN ILLEGAL AMOUNT!" << endl;
						//cin.clear();
						finishedLoop = false;
					}catch(...){
						cout << "Thief! Shutting down program!" << endl;
						cin.ignore();
						cin.get();
						exit(1);
					}
				}while(!finishedLoop);
				break;
			case LOAD_ONCE:
				do{
					finishedLoop = true;
					fadeOut();
					unsetFlags();
					try{
						cout << "Would you like to bulk load gas into the gas pump? y/n ";
						string theChoice;
						cin >> theChoice;
						if(theChoice[0] == 'y'){
							cout << "Specify the amount of gas you would like to bulk-load" << endl;
							double gasAmount;
							stringstream ss (stringstream::in | stringstream::out);
							string val;
							cin >> val;
							ss << val;
							if(!(ss >> gasAmount)) throw ss.str();
							gp.insertGas(gasAmount);
							cout << "Submit any key to continue.";
							cin.ignore();
							cin.get();
							mask |= SHOW_BANNER;
							mask |= SHOW_GAS;
						}else{
							mask |= SHOW_BANNER;
						}
					}catch(string& illegalAmount){ // if illegal input, user has to try again
						cout << "\a\a" << illegalAmount << ": IS AN ILLEGAL AMOUNT!" << endl;
						//cin.clear();
						finishedLoop = false;
					}catch(...){ // if another exception occurred, end the program
						cout << "An error occurred! Closing program!\n";
						exit(1);
					}
				}while(!finishedLoop);
				break;
			case SEE_MENU:
				unsetFlags();
				mask |= SHOW_BANNER;
				break;
			case C_FACTOR:
				unsetFlags();
				mask |= SHOW_C_FACTOR;
				break;
			case DISPENSE_CHANGE:
				unsetFlags();
				cout << "You received: $" << gp.dispenseChange() << " as change." << endl;
				cout << "Resetting the pump." << endl;
				cout << "Press any key to continue...";
				cin.ignore();
				cin.get();
				mask |= SHOW_BANNER;
				break;
			default:
				break;
		}
		
		// this might be changed
		if(key.wVirtualKeyCode == PUMP_OUT_ONCE	|| key.wVirtualKeyCode == EXIT
		   || key.wVirtualKeyCode == SEE_DESCRIPTIONS || key.wVirtualKeyCode == REFILL
		   || key.wVirtualKeyCode == INSERT || key.wVirtualKeyCode == LOAD_ONCE
		   || key.wVirtualKeyCode == SEE_MENU || key.wVirtualKeyCode == DISPENSE_CHANGE
		   || key.wVirtualKeyCode == C_FACTOR)
			wasMenuKey = true; // =/
	}
}

/**
 * Convenience method for pausing an independant process without pausing
 * any others. Pauses for a specified amount of milliseconds.
 */
void pause(size_t milli){
	clock_t t1 = clock() + (milli * (CLOCKS_PER_SEC/1000)); // t1 is x milliseconds in the future
	while(clock() < t1); // keep re-evaluating the time until it catches up with t1
}

/**
 * Sets the mask to 0
 */
void unsetFlags(){
	for(size_t i = MAX_BIT + 1; i > 0; i--)mask &= ~(1 << (i - 1)); // Performs bitwise arithmetic to set mask to 0 (to say the least)
}

/**
 * Fills the Console with blank characters, emulating a "wipe"
 * or screen-clearing of the Console screen.
 *
 * To be fair, I had to do some research on how to clear the screen without using
 * system(cls) so I used a similar approach to one submitted on msdn
 */
VOID fadeOut(){
	if(GetConsoleScreenBufferInfo(outputHandle, &csbi)){ // if the program can obtain the Console Screen Buffer Info...
		FillConsoleOutputCharacter(outputHandle, (TCHAR) 32, csbi.dwSize.X * csbi.dwSize.Y, loc, &count); // Fill the
																									      // console with opaque chars
		FillConsoleOutputAttribute(outputHandle, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, loc, &count );
		SetConsoleCursorPosition(outputHandle, loc); // Sets the cursor to the location loc, relative to he console
	}
	return;
}

Dani AI

Generated

This is a neat class project from and a good sandbox for learning C++ patterns. The implementation works as a console demo, but it exposes a few maintainability and correctness issues worth fixing before reusing the code in production or as a teaching example: global state and UI mixed with core logic, floating-point rounding used for currency/quantity, unsafe const_casts, non-idiomatic operator overloads, and busy-wait timing. The following notes focus on practical, low-risk improvements that preserve the original behavior while making the code safer and easier to test.

First, separate concerns. Move all console- and Windows-specific code out of the GasPump class so the class only models pump state and business rules. Replace global objects (for example the single global pump and console handles) with objects created in main() or wrapped in a small application class. Replace the busy-wait pause loop with a blocking sleep (Sleep or std::this_thread::sleep_for) to avoid pegging the CPU.

Second, stop using doubles for money and fractional quantities. Floating arithmetic causes the fmod and comparison surprises seen in the pump logic. Use an integral smallest-unit type (cents, or hundredths of a gallon) and do arithmetic on integers; format for display only when printing. Also fix I/O and operator signatures (input operator must take a non-const reference; postfix ++ should follow the usual value-return convention). Remove or replace const_cast usage with clearly designed APIs that expose value accessors.

A minimal replacement pattern for money (illustrative):

struct Money {
    std::int64_t cents{0};                     // always store whole cents
    explicit Money(long double dollars=0.0L)   // safe conversion
      : cents(static_cast<std::int64_t>(std::llround(dollars*100.0L))) {}
    Money& operator+=(const Money& o){ cents += o.cents; return *this; }
    long double as_double() const { return cents/100.0L; }
    std::string str() const { std::ostringstream os; os<<std::fixed<<std::setprecision(2)<<as_double(); return os.str(); }
};

Finally, prefer exceptions (or error returns) over calling exit() from library code, avoid putting using-directives/aliases in headers, and add small unit tests for the arithmetic and pump boundary cases. These changes keep the original learning intent while making the code robust, portable, and easier to extend.

Alex Edwards 321 Posting Shark

Oh no O_O

Everything is in global scope, so the program is easily hijackable @_@

>_<

Alex Edwards 321 Posting Shark

I guess this is no longer the ideal gas-pump!

Gas prices (in America) have dropped down to $2.35-$2.75 per gallon O_O

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.