Page 1 of 1

Zen and The Art of Using Functors With STL Algorithms This tutorial presents the use of function objects using Standard Temp Rate Topic: -----

#1 jwwicks  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 24
  • View blog
  • Posts: 162
  • Joined: 31-July 08

Post icon  Posted 10 October 2009 - 02:05 AM


Introduction

This tutorial presents the use of function objects using Standard Template algorithm functions. The tutorial uses a simple inventory program to illustrate the use of these functors.

The first function object, or functor, is used to facilitate the find_if algorithm on a data structure. We'll create a simple product structure for an inventory program. The user can pick what information to update and which one to update. To do so the user needs to identify which product to update. They might know the product id or just the product description. The functor allows us to check for both possibilities.

The second function object is used to store the product to a data file. Normally this would be simple since all we have is a structure but this structure stores a pointer to a Date object which handles obviously the date for the product. Storing the pointer wouldn't make any sense so we use the functor to serialize the object into a readable format. We also want to store the product in a tab delimited format. You could write specialized loops or a iostream friend to do this, infact we do this for loading, but the functor makes it trivial and we get to use the algorithms in the Standard Template Library.

The Product Structure

Each product is stored using the following structure.
struct product {
	string id;
	string description;
	int quantity;
	double wholesale;
	double retail;
	Date* date_added;
    friend std::istream& operator >>( istream& is, product& p );
};



Date is just a simple class for dates. A vector of products is used to store the inventory for our program.

Program Flow

The program starts out by checking to see if there is an inventory file created in the current directory. If not it then walks the user through the process of creating it etc... Main then moves on to loading the newly created inventory into our vector. Next a menu is presented to the user that allows the user to modify or search for a item. Once the user exits the main menu loop the product information is once again stored.

Function Object Bases

Function objects are so common in C++ that the Standard Template Library supplies bases classes for their definition. These definitions help you name your function object return types and parameters so they can seemlessly be used with the algorithms in the Library.

The first base class supplied by the Standard Template Library is the std::unary_function template. To derive from this class all that is needed is to supply an operator() that returns bool. For our purposes we'll define three functors using this template as a base. The first we'll use to test equality of the product structures based on the ID or DESCRIPTION field. We'll use this functor with the find_if algorithm to locate the product in our vector of products.

class prod_eq:public std::unary_function<product, bool>
{
private:
	string m_id;
public:
	prod_eq( const string& n ): m_id( n ){}
	bool operator()(const product& element )
	{
		bool ret_val = true;
        typedef string::const_iterator SI;
        SI p1 = element.id.begin();
        SI p2 = m_id.begin();

        while( (p1!=element.id.end() && p2!=m_id.end()) && ret_val )
        {
            if( toupper(*p1)!= toupper(*p2) ) ret_val = false;
            ++p1;
            ++p2;
        }

	    if(!ret_val){
		    SI p1 = element.description.begin();
       	    SI p2 = m_id.begin();
            
            ret_val = true;
            while( (p1!=element.description.end() && p2!=m_id.end()) && ret_val )
            {
                if( toupper(*p1)!= toupper(*p2) ) ret_val = false;
                ++p1;
                ++p2;
            }
	    }
        return ret_val;
	}
};



The second we'll use to store the product structures using a ostream to a file. We'll use this functor with the for_each algorithm to save each of the products in our vector of products to a data file in tab delimited format.

class save_product:public std::unary_function<product, bool>
{
private:
	ostream* m_os;
public:
	save_product( ostream* os ){ m_os = os; }
	bool operator()( const product& element )
	{
		bool ret_val = false;
		// save element in tab delimited format
		 
		*m_os << setprecision(2) << fixed << element.id.c_str() 
			<< "\t" << element.description.c_str() 
			<< "\t" << element.quantity 
            << "\t" << element.wholesale 
            << "\t" << element.retail 
			<< "\t" << element.date_added->get(2) // write date in month/day/year format
			<< "/" << element.date_added->get(1) 
			<< "/" << element.date_added->get(3)
			<< "\t";
		if(m_os)
			ret_val = true;
		return ret_val;
	}
};



The last we'll use to load the product structures from an istream. We'll use this functor with the copy algorithm to load each of the products from our data file into our vector of products.

class load_product:public std::unary_function<product, bool>
{
private:
    istream* m_is;
public:
    load_product( istream* is ){ m_is = is; }
    bool operator()( product& element )
    {
        bool ret_val = false;
	    char buffer[maxStringSize];
	    unsigned int fieldCnt = 0;

	    while(m_is && fieldCnt < REC_FIELD_COUNT )
	    {
		    m_is->getline(buffer,(streamsize)maxStringSize, '\t');
		    if(m_is)
		    {
			    switch( fieldCnt )
			    {
				    case 0: element.id = buffer; ++fieldCnt; break;
				    case 1: element.description = buffer; ++fieldCnt; break;
				    case 2: element.quantity = ioConv<int>(buffer); ++fieldCnt; break;
				    case 3: element.retail = ioConv<double>(buffer); ++fieldCnt; break;
				    case 4: element.wholesale = ioConv<double>(buffer); ++fieldCnt; break;
				    default:
                        element.date_added = new Date();
					    element.date_added->set(buffer, "/");
					    ret_val = true;
                        break;
			    }
		    }
	    }
        return ret_val;
    }
};



The Source

main cpp
///////////////////////////////////////
/// \file main cpp
/// \brief Main Module for Product project
/// 
/// \author John 'Ghost' Wicks
/// \version 01.00.01
/// \date 2007-10-16
///
//////////////////////// 
// History 
// JWW 2007-10-03 v00.99.00 Initial release 
// JWW 2007-10-16 v01.00.01 Eliminated transform calls with string
//                          since gnu fails to compile with them

// Includes 
#include <iostream>
#include <iomanip>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

#include "product.h"
#include "date.h"

// Global constants and variables 
const char DATA_FILE[] = {"data_file.dat"};
const char REPORT_FILE[] = {"report_file.txt"};
const char PRG_NAME[] = {"MyInventory Program - v 1.00.01"}; 
const char CPY_RGHT[] = {"CopyMePlease 2007 - "}; 
const char AUTHOR[] = {"John Wicks"};
#ifdef _WIN32
	const char CLRSCR[] = {"cls"};
#else
	const char CLRSCR[] = {"clear"};
#endif

const char STR_ERR_DATAFILE_EMPTY[] = {"Error - Data file is empty, using standard input instead..."};
const char STR_ERR_OPEN_RPTFILE[] = {"Error - Unable to open report file, using standard out instead..."};
const char STR_ERR_OPEN_DATAFILE[] = {"Error - Unable to open data file, using manual input instead..."};
const int QUIT = 0;
const int CONTINUE = 1;

// function prototypes 
void show_copyright( void );
int get_choice( const string& prompt ); 
bool is_valid_choice( unsigned int val, size_t min, size_t max );
string get_string( const string& prompt, int maxSize );
void show_main_menu( void );
void do_update( vector<product>& p );
void do_report( vector<product>& p );

// Uncomment to run just the testing main
//#define TEST 1

/////////////////////////
/// \fn main(int argc, char** argv)
/// \brief Main module for inventory program.
///
/// \param argc - integer number of arguments passed from system to program
/// \param argv - pointer to array of characters of arguments passed to program from system
///
/// \return integer value for success or failure
/////////////////////////
#ifndef TEST
int main(int argc, char* argv[])
{
	int done = 0, choice = 0, nProducts = 0;
	bool flag_inFile = false;
	bool flag_outFile = false;
	ifstream inFile; //Input data file
	ofstream outFile; //Output report file
    ostream* out;
	vector<product> Inventory;

	system(CLRSCR);
	show_copyright();

	//Check for existance of datafile or if it's empty
	inFile.open(DATA_FILE, ios::in|ios::out|ios::binary|ios::app);
	if(flag_inFile = inFile.is_open()){
		if(inFile.peek()==EOF){
			cerr << STR_ERR_DATAFILE_EMPTY << endl;
			flag_inFile = false;
			inFile.close();
			outFile.open(DATA_FILE, ios::in|ios::out|ios::binary|ios::app);
			flag_outFile = outFile.is_open();
            if(flag_outFile)
			    out = &outFile;
            else
                out = &cout;
		}
		else
			out = &cout;
	}
	else{
		out = &cout; // No data file output should be to standard out
		cerr << STR_ERR_OPEN_DATAFILE << endl;
	}
	
	//Input file doesn't exist or is empty, Let's create some data
	if(!flag_inFile){ 
		do{
			cout << endl << endl;
			do{
				create_product( Inventory );
				show_product( Inventory[nProducts] );
				nProducts++;
				choice = get_choice("Press 1 to continue entering data or 0 to quit: ");
			}while(!is_valid_choice( choice, 0, 1));
			if (choice == QUIT) done = true;
		}while (!done);
        if(!flag_outFile)
		    *out << "The following Inventory items were entered..." << endl;
		for_each(Inventory.begin(), Inventory.end(), show_product);
		
        if(!flag_outFile)
            *out << "Storing data..." << endl << endl;
		for_each(Inventory.begin(), Inventory.end(), save_product(out));
	}
	
	if( Inventory.size() ==0 ){
	    copy(std::istream_iterator<product>(inFile), std::istream_iterator<product>(), std::back_inserter(Inventory));	
	}
	
	// Main Program loop
	do{
        show_main_menu();
		do{ 
			choice = get_choice("Press 1 to continue or 0 to quit: "); 
		}while(!is_valid_choice( choice, 0, 3 )); 
	
		switch( choice ){
			case 1: create_product( Inventory );
				show_product( Inventory[nProducts] );
				nProducts++;
				break;
			case 2: do_update( Inventory );
 				break;
			case 3: do_report( Inventory );
				break;
			default:
				break;
		}
	}while(choice!= QUIT);
		
	if(flag_inFile) inFile.close();
    if(!flag_outFile){
        outFile.open(DATA_FILE, ios::out|ios::binary|ios::trunc);
        for_each(Inventory.begin(), Inventory.end(), save_product(&outFile));
        outFile.close();
    }

	return 0;	
}
#else
int main( int argc, char** argv)
{
	return 0;
}

#endif

///////////////////////// 
/// \fn show_copyright(void) 
/// \brief Show program name and copyright notice 
/// 
/// \b SideEffects : None\n
/// \b Output : couts PRG_NAME, CPY_RGHT and AUTHOR defines\n
///
/// \return None
//////////////////////// 
void show_copyright( void ) 
{ 
	cout << PRG_NAME << endl; 
	cout << CPY_RGHT << AUTHOR << endl << endl; 
	return; 
} 

///////////////////////// 
/// \fn get_choice( const string& prompt )
/// \brief Check to see if user wants to continue 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to continue program\n
/// 
/// \return integer of choice entered by user 
///////////////////////// 
int get_choice( const string& prompt ) 
{ 
	int ret_val;
	stringstream ioConv;
	
	ioConv << get_string(prompt, maxStringSize);
	ioConv >> ret_val;
	
	return ret_val; 
} 

///////////////////////// 
/// \fn is_valid_choice( int val, size_t min, size_t max ) 
/// \brief Checks for valid entry for menu 
/// 
/// \b SideEffects : None\n 
/// \b Output : Displays an error message if an invalid choice is passed \n
/// 
/// \param val - Current choice from menu
/// \param min - minimum acceptable value
/// \param max - maximum acceptable value
/// 
/// \return boolean true if choice is valid and false otherwise 
//////////////////////// 
bool is_valid_choice( unsigned int val, size_t min, size_t max ) 
{ 
	bool ret_val = false;
	if( val >= min && val <= max )
	{
		ret_val = true;
	}
	if(!ret_val){
		cout << "Error: Invalid choice " << val << " selected." << endl; 
	}
		
	return ret_val; 
}

///////////////////////// 
/// \fn get_string( const string& prompt, int maxSize )
/// \brief Gets a user entered string
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter in string data\n
/// 
/// \param prompt - Display string that tells user what type of data string to enter 
/// \param maxSize - maximum string size
/// 
/// \return ret_val - User entered string
/////////////////////////
string get_string( const string& prompt, int maxSize )
{
	string ret_val;

	cout << prompt;
	getline(cin,ret_val);
	
	return ret_val;
}

///////////////////////// 
/// \fn show_main_menu( void ) 
/// \brief Show program menu 
/// 
/// \b SideEffects : None\n
/// \b Output : couts menu for user choice\n
///
/// \return None
//////////////////////// 
void show_main_menu( void ) 
{ 
	cout << "1. Enter new product data" << endl;
	cout << "2. Modify a product's data" << endl;
	cout << "3. Display a product's data" << endl;
	cout << "0. Exit the program" << endl;

	return; 
} 

///////////////////////// 
/// \fn show_update_menu( void ) 
/// \brief Show program update product menu 
/// 
/// \b SideEffects : None\n
/// \b Output : couts menu for user choice\n
///
/// \return None
//////////////////////// 
void show_update_menu( void ) 
{ 
	cout << "1. Update Product Description" << endl;
	cout << "2. Update Product Quantity on Hand" << endl;
    cout << "3. Update Product Wholesale Price" << endl;
	cout << "4. Update Product Retail Price" << endl;
    cout << "5. Update Product Date Added to Inventory" << endl;
	cout << "0. Return to Main Menu." << endl;

	return; 
} 

///////////////////////// 
/// \fn show_report_menu( void ) 
/// \brief Show program report product menu 
/// 
/// \b SideEffects : None\n
/// \b Output : couts menu for user choice\n
///
/// \return None
//////////////////////// 
void show_report_menu( void ) 
{ 
	cout << "1. Look up Product by Item Description" << endl;
	cout << "2. Look up Product by ID" << endl;
    cout << "3. Show all Products" << endl;
    cout << "0. Return to Main Menu." << endl;

	return; 
} 

/////////////////////////
// \fn do_update( vector<product>& p )
// \brief Handling routine for update menu
//
// \b Purpose: Processes update menu choices
// \b SideEffects: none
//
// \param p - vector containing product records
// \return none
/////////////////////////
void do_update( vector<product>& p )
{
	bool done = false;
	int choice;
	string t, id;
	product* uProd = new( product );
	product& pUpdate = *uProd;
	
	system(CLRSCR);
	show_copyright();

	do
	{
		id = get_product_id();
	}while(!is_valid_product_id( p, id ));

    typedef vector<product>::iterator VCI;
	VCI f = find_if( p.begin(), p.end(), prod_eq(id));
	
    do{
        cout << endl << "Modifying Product : " << (*f).id << endl << endl;

	    show_update_menu();
	    do{ 
		    choice = get_choice("Enter the choice of data to modify: "); 
	    }while(!is_valid_choice( choice, 0, 5 )); 
		
	    switch( choice ){
		    case 1: do_prod_descript( p, pUpdate );
				    (*f).description = pUpdate.description;
				    break;
		    case 2: do_prod_quantity( pUpdate );
				    (*f).quantity = pUpdate.quantity;
				    break;
		    case 3: do_prod_cost( pUpdate, PRD_TYPE_WHOLESALE );
				    (*f).wholesale = pUpdate.wholesale;
				    break;
		    case 4: do_prod_cost( pUpdate, PRD_TYPE_RETAIL );
				    (*f).retail = pUpdate.retail;
				    break;
		    case 5: do_prod_date_added( pUpdate );
				    (*f).date_added = pUpdate.date_added;
				    break;
		    default:
			    done = true;
			    break;
	    }
    }while(!done);
	

	delete uProd;
}

/////////////////////////
// \fn do_report( vector<product>& p )
// \brief Handling routine for report menu
//
// \b Purpose: Processes report menu choices
// \b SideEffects: none
//
// \param p - vector containing product records
// \return none
/////////////////////////
void do_report( vector<product>& p )
{
	bool done = false;
	int choice;
	string id;

	system(CLRSCR);
	show_copyright();

    do{
		show_report_menu();
		do{ 
			choice = get_choice("Enter your choice: "); 
		}while(!is_valid_choice( choice, 0, 3 )); 
		
		switch( choice ){
			case 1:
			case 2:{
					do{
						id = get_product_id();
					}while(!is_valid_product_id( p, id ));

					typedef vector<product>::iterator VCI;
					VCI f = find_if( p.begin(), p.end(), prod_eq(id));

					product& pUpdate = *f;
					show_product( pUpdate );
					break;
				   }
            case 3:
                for_each( p.begin(), p.end(), show_product);
                break;
			default:
				done = true;
			break;
		}

	}while(!done);
}



date.h
///////////////////////////////////////
/// \file date.h
/// \brief Date Class header
/// 
/// \author John 'Ghost' Wicks
/// \version 0.99.0
/// \date 2007-09-14
///
/// \section History
/// \brief Revision 0.99.0 14-Sept-2007 Initial release
///
///////////////////////////////////////

#ifndef __DATE_H__
#define __DATE_H__

#include <string>
#include <sstream>
#include <vector>

using namespace std;

const int YEAR = 3;
const int MONTH = 2;
const int DAY = 1;

class Date
{
	private:
		string Month( void );

	protected:
		int m_month, m_day, m_year;

	public:
		Date( int month=1, int day=1, int year=1 );
		Date( string d );
		Date( const Date& d );
		Date operator+( string &other );
		bool set( int m, int d, int y );
		bool set( const string& str, const string& delim = " " );
		bool is_valid( void );
		string get( void );
		string get( int which );
		virtual ~Date(){}
		
};

#endif



date cpp
///////////////////////////////////////
/// \file date cpp
/// \brief Date Class Module
/// 
/// \author John 'Ghost' Wicks
/// \version 0.99.0
/// \date 2007-09-14
///
/// \section History
/// \brief Revision 0.99.0 14-Sept-2007 Initial release
///
///////////////////////////////////////

#include "date.h"

Date::Date( int month, int day, int year )
{
	this->m_month = month;
	this->m_day = day;
	this->m_year = year;
}

Date::Date( string d )
{
	Date::set( d , "/" );
}

Date::Date( const Date& d )
{
	this->m_month = d.m_month;
	this->m_day = d.m_day;
	this->m_year = d.m_year;
}

Date Date::operator+( string &other )
{
	return Date( other );
}

string Date::Month( void )
{
	static char m_months[13][10] = {"", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
	string month(m_months[m_month]);
	return month;
}

string Date::get( void )
{
	stringstream d;
	d << Month() << " " << m_day << ", " << m_year;
	return d.str();
}

string Date::get( int which )
{
	stringstream d;
	switch( which )
	{
		case DAY: d << m_day; break;
		case MONTH: d << m_month; break;
		case YEAR: d << m_year; break;
		default:
			d << m_month <<"/"<< m_day <<"/"<< m_year;
			break;
	}
	
	return d.str();
}

bool Date::set( int m, int d, int y )
{
	m_day = d;
	m_month = m;
	m_year = y;

	return is_valid();
}

bool Date::set( const string& str, const string& delim )
{
	vector<string> tokens;

	string::size_type last = str.find_first_not_of( delim );
	string::size_type pos = str.find_first_of( delim );
	
	while (string::npos != pos || string::npos != last)
    {
        tokens.push_back(str.substr(last, pos - last));
        last = str.find_first_not_of(delim, pos);
        pos = str.find_first_of(delim, last);
    }
    
    this->m_month = atoi(tokens.at(0).c_str());
	this->m_day = atoi(tokens.at(1).c_str());
	this->m_year = atoi(tokens.at(2).c_str());

	//Corrects any two digit years
	if(m_year < 100 && m_year >= 80)
		m_year += 1900;
	else if( m_year < 100 && m_year < 80 )
		m_year +=2000;
	
	
	return Date::is_valid();
}

bool Date::is_valid( void )
{
	bool ret_val = false;
	int ny_days [13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	int ly_days [13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	if( m_year >= 1900 ){
		if( m_month >= 1 && m_month <= 12 ){
			if ( m_day >= 1 ){
				if (!(m_year % 4))
				{
					if (m_day <= ly_days[m_month])
						ret_val = true;
				}
				else{
					if (m_day <= ny_days[m_month])
						ret_val = true;
				}
			}
		}
	}
	return ret_val;
}



product.h
///////////////////////////////////////
/// \file product.h
/// \brief Inventory Product header
/// 
/// \author John 'Ghost' Wicks
/// \version 01.00.01
/// \date 2007-10-16
///
/// \section History
/// \brief Revision 0.99.0 03-Oct-2007 Initial release
// JWW 2007-10-16 v01.00.01 Eliminated transform calls with string
//                          since gnu fails to compile with them
///
///////////////////////////////////////

#ifndef __PRODUCT_H__
#define __PRODUCT_H__

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <string>
#include <sstream>
#include <limits>
#include <functional>
#include <cctype>
using namespace std;

#include "date.h"

const int maxStringSize = 1024;
const unsigned int REC_FIELD_COUNT = 6;
const unsigned int PRD_TYPE_WHOLESALE = 1;
const unsigned int PRD_TYPE_RETAIL = 2;
 
struct product {
    product(){ date_added = new Date(); }
    string id;
	string description;
	int quantity;
	double wholesale;
	double retail;
	Date* date_added;
    friend std::istream& operator >>( istream& is, product& p );
};

template <class oType, class iType>
oType ioConv( const iType& _T )
{
	stringstream _S;
	oType ret_val;

	_S << _T;
	_S >> ret_val;

	return ret_val;
}

class prod_eq:public std::unary_function<product, bool>
{
private:
	string m_id;
public:
	prod_eq( const string& n ): m_id( n ){}
	bool operator()(const product& element )
	{
		bool ret_val = true;
        typedef string::const_iterator SI;
        SI p1 = element.id.begin();
        SI p2 = m_id.begin();

        while( (p1!=element.id.end() && p2!=m_id.end()) && ret_val )
        {
            if( toupper(*p1)!= toupper(*p2) ) ret_val = false;
            ++p1;
            ++p2;
        }

	    if(!ret_val){
		    SI p1 = element.description.begin();
       	    SI p2 = m_id.begin();
            
            ret_val = true;
            while( (p1!=element.description.end() && p2!=m_id.end()) && ret_val )
            {
                if( toupper(*p1)!= toupper(*p2) ) ret_val = false;
                ++p1;
                ++p2;
            }
	    }
        return ret_val;
	}
};

class save_product:public std::unary_function<product, bool>
{
private:
	ostream* m_os;
public:
	save_product( ostream* os ){ m_os = os; }
	bool operator()( const product& element )
	{
		bool ret_val = false;
		// save element in tab delimited format
		 
		*m_os << setprecision(2) << fixed << element.id.c_str() 
			<< "\t" << element.description.c_str() 
			<< "\t" << element.quantity 
            << "\t" << element.wholesale 
            << "\t" << element.retail 
			<< "\t" << element.date_added->get(2) // write date in month/day/year format
			<< "/" << element.date_added->get(1) 
			<< "/" << element.date_added->get(3)
			<< "\t";
		if(m_os)
			ret_val = true;
		return ret_val;
	}
};

class load_product:public std::unary_function<product, bool>
{
private:
    istream* m_is;
public:
    load_product( istream* is ){ m_is = is; }
    bool operator()( product& element )
    {
        bool ret_val = false;
	    char buffer[maxStringSize];
	    unsigned int fieldCnt = 0;

	    while(m_is && fieldCnt < REC_FIELD_COUNT )
	    {
		    m_is->getline(buffer,(streamsize)maxStringSize, '\t');
		    if(m_is)
		    {
			    switch( fieldCnt )
			    {
				    case 0: element.id = buffer; ++fieldCnt; break;
				    case 1: element.description = buffer; ++fieldCnt; break;
				    case 2: element.quantity = ioConv<int>(buffer); ++fieldCnt; break;
				    case 3: element.retail = ioConv<double>(buffer); ++fieldCnt; break;
				    case 4: element.wholesale = ioConv<double>(buffer); ++fieldCnt; break;
				    default:
                        element.date_added = new Date();
					    element.date_added->set(buffer, "/");
					    ret_val = true;
                        break;
			    }
		    }
	    }
        return ret_val;
    }
};

extern string get_string( const string& prompt, int maxSize );
void gen_id( product& p );
string get_product_id( void );
bool is_valid_product_id( vector<product>& p, const string& id );

void do_prod_descript( vector<product>& v, product& p );
void get_prod_descript( product& p );
bool is_valid_descript( vector<product>& p, const string& descript );

void do_prod_quantity( product& p );
void get_prod_quantity( product& p );
bool is_valid_quantity( int quantity, const unsigned int min, const unsigned int max );

void do_prod_cost( product& p , unsigned int type);
void get_prod_cost( product& p, unsigned int type, const string& prompt );
bool is_valid_cost( double cost, double min, double max );

void do_prod_date_added( product& p );
void get_prod_date_added( product& p );

void create_product( vector<product>& p );
void update_product( vector<product>& p );
void show_product( const product& p );

#endif __PRODUCT_H__



product cpp
///////////////////////////////////////
/// \file product cpp
/// \brief Inventory Product Module
/// 
/// \author John 'Ghost' Wicks
/// \version 01.00.01
/// \date 2007-10-06
///
/// \section History
/// \brief Revision 0.99.0 03-Oct-2007 Initial release
// JWW 2007-10-16 v01.00.01 Eliminated transform calls with string
//                          since gnu fails to compile with them
///
///////////////////////////////////////

#include "product.h"

///////////////////////// 
/// \fn gen_id( product& p )
/// \brief Generate the Product's identification 
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void gen_id( product& p )
{
	string temp;
    typedef string::const_iterator SI;
    SI p1 = p.description.begin();

    while(p1 != p.description.end() )
    {
        switch( toupper(*p1) ){
            case 'A':
            case 'E':
            case 'I':
            case 'O':
            case 'U':
                break;
            default:
                if( isspace( *p1 ) )
                    temp += "_";
                else
                    temp += *p1;
                break;
        }        
         ++p1;
    }
    
    temp += "_";
	temp += p.date_added->get( YEAR );
    temp += "_";
    temp += p.date_added->get( MONTH );
    temp += "_";
    temp += p.date_added->get( DAY );
	p.id = temp;
}

///////////////////////// 
/// \fn get_product_id( void )
/// \brief Gets a product id from user
/// 
/// \b SideEffects : None\n 
/// \b Output : Prompt to enter product identifier
/// 
/// \param none
/// 
/// \return string with id number or decription entered
//////////////////////// 
string get_product_id( void )
{
	string ret_val;

	ret_val = get_string("Enter product ID or FULL item description to modify: ", 1024);
	
	return ret_val;
}

///////////////////////// 
/// \fn is_valid_product_id( vector<product>& p, const string& id )
/// \brief Checks to make sure id is a valid product
/// 
/// \b SideEffects : None\n 
/// \b Output : Error message if product not found 
/// 
/// \param c - Reference to vector of products
/// \param id - string id to check against product id or description
/// 
/// \return true if id or description is found in a product false otherwise
//////////////////////// 
bool is_valid_product_id( vector<product>& p, const string& id )
{
	bool ret_val = false;
	typedef vector<product>::const_iterator VCI;

	VCI f = find_if( p.begin(), p.end(), prod_eq(id));
	if(f != p.end() ){
		ret_val = true;
	}
	else{
		cerr << "Unable to locate Product with id OR description: "<< id << endl;
		cerr << "Please try again..." << endl << endl;
	}
	return ret_val;
}

///////////////////////// 
/// \fn do_prod_descript( product& p )
/// \brief Handles getting and checking the product description 
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void do_prod_descript( vector<product>& v, product& p )
{
	do
	{
		get_prod_descript( p );
	}while(!is_valid_descript( v, p.description ));
}

///////////////////////// 
/// \fn get_prod_descript( product& p )
/// \brief Gets the Products description 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter product description\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void get_prod_descript( product& p )
{
	p.description = get_string("Enter product's description: ", maxStringSize);
}

///////////////////////// 
/// \fn is_valid_descript( const string& description )
/// \brief Checks to see if product description is valid 
/// 
/// \b SideEffects: None\n 
/// \b Output: Error message if description is invalid\n
///
/// \param description - current product description to check validity of
/// 
/// \return ret_val - boolean true if description is valid and false otherwise 
///////////////////////// 
bool is_valid_descript( vector<product>& p, const string& description )
{
	bool ret_val = false;
	typedef vector<product>::const_iterator VCI;

	VCI f = find_if( p.begin(), p.end(), prod_eq(description));
	if(f == p.end() ){
		ret_val = true;
	}
	else{
		cerr << "That Product already exists in the database: "<< description << endl;
		cerr << "Please try again..." << endl << endl;
	}    

	return ret_val;
}

///////////////////////// 
/// \fn do_prod_quantity( product& p )
/// \brief Handles getting and checking the product quantity 
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void do_prod_quantity( product& p )
{
	do
	{
		get_prod_quantity( p );
	}while(!is_valid_quantity( p.quantity, (unsigned int)numeric_limits<unsigned int>::min(), (unsigned int)numeric_limits<unsigned int>::max() ));
}

///////////////////////// 
/// \fn get_prod_quantity( product& p )
/// \brief Gets the Products quantity 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter product quantity\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void get_prod_quantity( product& p )
{
	stringstream sTemp;
	
	sTemp << get_string("Enter product's quantity on hand: ", maxStringSize);
	sTemp >> p.quantity;
}

///////////////////////// 
/// \fn is_valid_quantity( int quantity, const unsigned int min, const unsigned int max )
/// \brief Checks to see if product quantity is valid 
/// 
/// \b SideEffects: None\n 
/// \b Output: Error message if quantity is invalid\n
///
/// \param quantity - current product quantity to check validity of
/// \param min - minimum acceptable value
/// \param max - maximum acceptable value
/// 
/// \return ret_val - boolean true if quantity is valid and false otherwise 
///////////////////////// 
bool is_valid_quantity( int quantity, const unsigned int min, const unsigned int max )
{
	bool ret_val = false;

	if( (quantity > (int)min) && ((unsigned int)quantity < max) ){
		ret_val = true;
	}
	else{
		cerr << "Error: Number entered must be between " << min << " and " << max << endl;
	}

	return ret_val;
}

///////////////////////// 
/// \fn do_prod_cost( product& p, unsigned int type )
/// \brief Handles getting and checking the product cost 
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void do_prod_cost( product& p, unsigned int type )
{
	if( type == 1 )
		do{
			get_prod_cost( p, type, "Enter product's wholesale price: " );
		}while(!is_valid_cost( p.wholesale, (double)numeric_limits<double>::min(), (double)numeric_limits<double>::max() ));
	else
		do{
			get_prod_cost( p, type, "Enter product's retail price: " );
		}while(!is_valid_cost( p.retail, p.wholesale, (double)numeric_limits<double>::max() ));

}

///////////////////////// 
/// \fn get_prod_cost( product& p )
/// \brief Gets the Products cost 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter product cost\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void get_prod_cost( product& p, unsigned int type, const string& prompt )
{
	stringstream sTmp;
	
	sTmp << get_string(prompt, maxStringSize);
	
	switch( type )
	{
		case 1:
				sTmp >> p.wholesale;
				break;
		default:
				sTmp >> p.retail;
				break;
	}
}

///////////////////////// 
/// \fn is_valid_cost( double cost, const double min, const double max )
/// \brief Checks to see if product cost is valid 
/// 
/// \b SideEffects: None\n 
/// \b Output: Error message if cost is invalid\n
///
/// \param cost - current product cost to check validity of
/// \param min - minimum allowable product cost
/// \param max - maximum allowable product cost
/// 
/// \return ret_val - boolean true if cost is valid and false otherwise 
///////////////////////// 
bool is_valid_cost( double cost, const double min, const double max )
{
	bool ret_val = false;
	unsigned long int ulInt = 0;
	long int slInt = 0;

	ulInt = (unsigned long int)(cost * 100);
	slInt = (long int)(cost * 100);

	if (ulInt == slInt && ulInt > (unsigned long int)(min*100) && (long int)ulInt < (max*100)){
		ret_val = true;
	}
	else
		cerr << "Cost should be greater than " << min << " and less than " << max << endl;

	return ret_val;
}

///////////////////////// 
/// \fn do_prod_date_added( product& p )
/// \brief Handles getting and checking the product date added 
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void do_prod_date_added( product& p )
{
	do{
		get_prod_date_added( p );	
	}while(!p.date_added->is_valid());
}

///////////////////////// 
/// \fn get_prod_date_added( product& p )
/// \brief Gets the Products date added 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter product date added\n
/// 
/// \param p - product structure for storage of data
/// \return none 
///////////////////////// 
void get_prod_date_added( product& p )
{
	string sDate;
	
	sDate = get_string("Enter the date item was added to inventory (MM/DD/YYYY): ", maxStringSize);
	p.date_added = new Date( sDate );
}

//////////////////////// 
/// \fn create_product( vector<product>& p )
/// \brief Gets new product data 
/// 
/// \b SideEffects: None\n 
/// \b Output: Prompts user to enter data for new product\n
/// 
/// \param p - vector of product structures for storage of data
/// \return none 
///////////////////////// 
void create_product( vector<product>& p )
{
	product* pProd = new( product );
	product& rfProd = *pProd;

	do_prod_descript( p, rfProd );
	do_prod_quantity( rfProd );
	do_prod_cost( rfProd, PRD_TYPE_WHOLESALE );
	do_prod_cost( rfProd, PRD_TYPE_RETAIL );
	do_prod_date_added( rfProd );
	gen_id( rfProd );
	p.push_back( *pProd );

	delete pProd;
}

///////////////////////// 
/// \fn show_product( const product& p )
/// \brief Shows product record
/// 
/// \b SideEffects: None\n 
/// \b Output: Displays information for the product\n
/// 
/// \param p - const product structure
/// \return none 
///////////////////////// 
void show_product( const product& p )
{
    cout << endl << endl;
	cout << "Your product id is: " << p.id << endl;
	cout << "Description: " << p.description << endl;
	cout << "Quantity on Hand: " << p.quantity << endl;
	cout << "Retail Cost: " << p.retail << endl;
	cout << "Wholesale Cost: " << p.wholesale << endl;
	cout << "Date Added: " << p.date_added->get() << endl << endl;
}

///////////////////////// 
/// \fn istream& operator>>( istream& is, product& element )
/// \brief Friend operator for extracting a tab delimited product
///  from the stream
/// 
/// \b SideEffects: None\n 
/// \b Output: None\n
/// 
/// \param is - input stream 
/// \param element - product structure to fill from stream
/// \return istream& is 
///////////////////////// 
std::istream& operator >>( istream& is, product& element )
{
    char buffer[maxStringSize];
    unsigned int fieldCnt = 0;

    while(is && fieldCnt < REC_FIELD_COUNT )
    {
	    is.getline(buffer,(streamsize)maxStringSize, '\t');
	    if(is)
	    {
		    switch( fieldCnt )
		    {
			    case 0: element.id = buffer; ++fieldCnt; break;
			    case 1: element.description = buffer; ++fieldCnt; break;
			    case 2: element.quantity = ioConv<int>(buffer); ++fieldCnt; break;
			    case 3: element.retail = ioConv<double>(buffer); ++fieldCnt; break;
			    case 4: element.wholesale = ioConv<double>(buffer); ++fieldCnt; break;
			    default:
                    element.date_added = new Date();
				    element.date_added->set(buffer, "/");
                    ++fieldCnt;
                    break;
		    }
	    }
    }
    return is;
}





Is This A Good Question/Topic? 0
  • +

Page 1 of 1