// Programming Assignment 5 Example Solution
// CS 176, Summer 2001
#include <fstream>
#include <cstdlib>
#include <string>
#include <cassert>
// According to the problem, there can be at most 25 cheeses in the warehouse
// inventory. Declaring a named constant avoids the dreaded magic number.
static const int max_inventory = 25;
// Each item in the inventory has four fields.
struct inventory_item {
string cheese; // The name of the cheese.
int max; // The maximum quantity at hand.
int current; // The current quantity at hand.
int min; // The minimum quantity at hand.
};
// Reading the problem statement, it seems it can be solved if we can do four
// things:
//
// 1 Read in the inventory.
static int read_inventory(string, inventory_item[]);
// 2 Read in the daily sales and adjust the inventory.
static void process_orders(string, inventory_item[], int);
// 3 Write the inventory to a file.
static void make_new_inventory_file(string, inventory_item[], int);
// 4 Write the reorder file.
static void make_reorders_file(string, inventory_item[], int);
static void process_inventory(void) {
inventory_item inventory[max_inventory];
const int inventory_size = read_inventory("inventory.dat", inventory);
process_orders("sales.dat", inventory, inventory_size);
make_new_inventory_file("new-inventory.dat", inventory, inventory_size);
make_reorders_file("reorders.dat", inventory, inventory_size);
}
static int read_inventory(
string inventory_filename, inventory_item inventory[max_inventory]) {
// Read the inventory information in the file named inventory_filename; store
// the inventory information in the array inventory. Return the number of
// inventory items read and stored in invetory.
ifstream inventory_file(inventory_filename.c_str());
if (!inventory_file) {
cerr << "Can't open inventory file \"" << inventory_filename << "\".\n";
exit(EXIT_FAILURE);
}
int items = 0;
while (items < max_inventory) {
// Read an inventory item. This code belongs in a separate procedure, but
// we don't yet know enough c++ to correctly pass around file streams
// between procedures via parameters (and, of course, we recoil at the
// thought of using globals to pass values between procedures). Also,
// bailing out at the first error is a bit draconian, but again we don't
// know enough c++ (and we're not going to learn enough c++ in this class)
// to sensibly handle I-O errors, and dealing with I-O errors is hard in
// any event.
inventory_item item;
if (!(inventory_file >> item.max)) {
if (inventory_file.eof())
break;
cerr << "Error while reading maximum inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!(inventory_file >> item.current)) {
cerr << "Error while reading current inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!(inventory_file >> item.min)) {
cerr << "Error while reading minimum inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!getline(inventory_file, item.cheese)) {
cerr << "Error while reading cheese name.\n";
exit(EXIT_FAILURE);
}
inventory[items] = item;
items++;
}
inventory_file.close();
return items;
}
static int find_cheese(
const inventory_item in[], int inventory_size, string c) {
// Return the index of the cheese c in the inventory in or -1 if c isn't in
// the inventory.
assert((0 <= inventory_size) && (inventory_size <= max_inventory));
for (int i = 0; i < max_inventory; i++)
if (in[i].cheese == c)
return i;
return -1;
}
static void process_orders(
string sales_filename, inventory_item inventory[], int inventory_size) {
ifstream sales_file(sales_filename.c_str());
if (!sales_file) {
cerr << "Can't open the sales file \"" << sales_filename << "\".\n";
exit(EXIT_FAILURE);
}
while (true) {
// Read the next sales item from the file. What was written about reading
// inventory items in read_inventory() also applies here.
struct sales_item {
string store; // The store making the sale.
string cheese; // The name of the cheese sold.
int quantity; // The amount of cheese sold.
} s;
if (!(sales_file >> s.store)) {
if (sales_file.eof())
break;
cerr << "Error while reading store identifier.\n";
exit(EXIT_FAILURE);
}
if (!(sales_file >> s.quantity)) {
cerr << "Error while reading cheese sales quantity.\n";
exit(EXIT_FAILURE);
}
if (!getline(sales_file, s.cheese)) {
cerr << "Error while reading cheese name.\n";
exit(EXIT_FAILURE);
}
// Find the inventory entry for the cheese sold.
const int i = find_cheese(inventory, inventory_size, s.cheese);
if (i < 0) {
cerr << "Unknown cheese \"" << s.cheese << "\" in sales file.\n";
exit(EXIT_FAILURE);
}
// Adjust the quantity on hand, remembering the amount can't go below 0.
if ((inventory[i].current -= s.quantity) < 0)
inventory[i].current = 0;
}
sales_file.close();
}
static void make_new_inventory_file(
string inventory_filename, inventory_item inventory[], int inventory_size) {
// Write out the inventory_size items in inventory to the file having the
// name inventory_filename.
ofstream outs(inventory_filename.c_str());
if (!outs) {
cerr << "Can't open new inventory file \"" << inventory_filename
<< "\".\n";
exit(EXIT_FAILURE);
}
while (--inventory_size >= 0) {
outs << inventory[inventory_size].max << " "
<< inventory[inventory_size].current << " "
<< inventory[inventory_size].min << " "
<< inventory[inventory_size].cheese << "\n";
if (!outs.good()) {
cerr << "Error during inventory item write.\n";
exit(EXIT_FAILURE);
}
}
outs.close();
if (!outs.good()) {
cerr << "Error closing new inventory file.\n";
exit(EXIT_FAILURE);
}
}
static void make_reorders_file(
string reorder_filename, inventory_item inventory[], int inventory_size) {
// Write out the reorder items to the file with the name given by
// reorder_filename based on the state of the inventory_size items in
// inventory.
ofstream outs(reorder_filename.c_str());
if (!outs) {
cerr << "Can't open new inventory file \"" << reorder_filename
<< "\".\n";
exit(EXIT_FAILURE);
}
while (--inventory_size >= 0) {
inventory_item item = inventory[inventory_size];
if (item.current < item.min) {
outs << item.max - item.current<< " " << item.cheese << "\n";
if (!outs.good()) {
cerr << "Error during reorder item write.\n";
exit(EXIT_FAILURE);
}
}
}
// Check for close errors on open-for-write files because errors could
// indicate a loss of data.
outs.close();
if (!outs.good()) {
cerr << "Error closing reorders file.\n";
exit(EXIT_FAILURE);
}
}
// This solution works, but it isn't a good solution because a lot of the
// support code for inventories is out there hanging in the breeze, making
// inventories difficult to understand and complicated to use. There might be
// some advantage to encapsulating inventories in an object.
class Inventory {
public:
// Create a new inventory by reading the inventory description in the
// file named f.
Inventory(string f);
// Adjust the inventory based on the sales stored in the file named f.
void process_sales(string f);
// Write the inventory to the file named f.
void write_inventory(string f);
// Write the inventory reorders to the file named f.
void write_reorders(string f);
private:
// Here's an advantage to using classes: the support code needed to
// implement an inventory can be hidden in the class's private section.
// Clients using the Inventory class can ignore this code, and Inventory
// class implementors have the freedom to change the code as needed.
static const int max_inventory = 25;
struct inventory_item {
string cheese; // The name of the cheese.
int max; // The maximum quantity at hand.
int current; // The current quantity at hand.
int min; // The minimum quantity at hand.
};
inventory_item inventory[max_inventory];
int inventory_size;
int find_cheese(string);
};
// And here's the payoff: simpler parameter lists (because the extra code
// needed to implement an inventory is kept in the class and doesn't need to be
// passed around as parameters) leading to a simpler interface for inventory
// processing.
static void process_inventory_class(void) {
Inventory inventory("inventory.dat");
inventory.process_sales("sales.dat");
inventory.write_inventory("new-inventory.dat");
inventory.write_reorders("reorders.dat");
}
// The code that follows is essentially the same as the previous code, so it
// will be shortened by dropping the comments.
Inventory::Inventory(string inventory_filename) {
ifstream inventory_file(inventory_filename.c_str());
if (!inventory_file) {
cerr << "Can't open inventory file \"" << inventory_filename << "\".\n";
exit(EXIT_FAILURE);
}
inventory_size = 0;
while (inventory_size < max_inventory) {
inventory_item item;
if (!(inventory_file >> item.max)) {
if (inventory_file.eof())
break;
cerr << "Error while reading maximum inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!(inventory_file >> item.current)) {
cerr << "Error while reading current inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!(inventory_file >> item.min)) {
cerr << "Error while reading minimum inventory quantity.\n";
exit(EXIT_FAILURE);
}
if (!getline(inventory_file, item.cheese)) {
cerr << "Error while reading cheese name.\n";
exit(EXIT_FAILURE);
}
inventory[inventory_size] = item;
inventory_size++;
}
inventory_file.close();
}
void Inventory::process_sales(string sales_filename) {
ifstream sales_file(sales_filename.c_str());
if (!sales_file) {
cerr << "Can't open the sales file \"" << sales_filename << "\".\n";
exit(EXIT_FAILURE);
}
while (true) {
struct sales_item {
string store; // The store making the sale.
string cheese; // The name of the cheese sold.
int quantity; // The amount of cheese sold.
} s;
if (!(sales_file >> s.store)) {
if (sales_file.eof())
break;
cerr << "Error while reading store identifier.\n";
exit(EXIT_FAILURE);
}
if (!(sales_file >> s.quantity)) {
cerr << "Error while reading cheese sales quantity.\n";
exit(EXIT_FAILURE);
}
if (!getline(sales_file, s.cheese)) {
cerr << "Error while reading cheese name.\n";
exit(EXIT_FAILURE);
}
const int i = find_cheese(s.cheese);
if (i < 0) {
cerr << "Unknown cheese \"" << s.cheese << "\" in sales file.\n";
exit(EXIT_FAILURE);
}
if ((inventory[i].current -= s.quantity) < 0)
inventory[i].current = 0;
}
sales_file.close();
}
void Inventory::write_inventory(string inventory_filename) {
ofstream outs(inventory_filename.c_str());
if (!outs) {
cerr << "Can't open inventory file \"" << inventory_filename
<< "\".\n";
exit(EXIT_FAILURE);
}
for (int i = 0; i < inventory_size; i++) {
outs << inventory[i].max << " "
<< inventory[i].current << " "
<< inventory[i].min << " "
<< inventory[i].cheese << "\n";
if (!outs.good()) {
cerr << "Error during inventory item write.\n";
exit(EXIT_FAILURE);
}
}
outs.close();
if (!outs.good()) {
cerr << "Error closing inventory file.\n";
exit(EXIT_FAILURE);
}
}
void Inventory::write_reorders(string reorder_filename) {
ofstream outs(reorder_filename.c_str());
if (!outs) {
cerr << "Can't open the reorder file \"" << reorder_filename
<< "\".\n";
exit(EXIT_FAILURE);
}
for (int i = 0; i < inventory_size; i++) {
inventory_item item = inventory[i];
if (item.current < item.min) {
outs << item.max - item.current<< " " << item.cheese << "\n";
if (!outs.good()) {
cerr << "Error during reorder item write.\n";
exit(EXIT_FAILURE);
}
}
}
outs.close();
if (!outs.good()) {
cerr << "Error closing reorders file.\n";
exit(EXIT_FAILURE);
}
}
int Inventory::find_cheese(string c) {
// Return the index of the cheese c in the inventory in or -1 if c isn't in
// the inventory.
assert((0 <= inventory_size) && (inventory_size <= max_inventory));
for (int i = 0; i < max_inventory; i++)
if (inventory[i].cheese == c)
return i;
return -1;
}
int main() {
process_inventory();
}
syntax highlighted by Code2HTML, v. 0.9