You are here:

C++/Void functions and the switch operation (logic errors)

Advertisement


Question
QUESTION: Hey Ralph,
Hope you're doing well. I wanted to ask you something pertaining to a C++ code I'd recently written. Its actually supposed to be a personal finance system. Here's the breakdown in a nutshell:
-- user gets a menu asking for what he wants to do.
-- his options are... (update his income, update his expenses <fixed or variable>, get totals, and others). I've not attempted going into others as I plan to keep some extra features there only, so we'll get to that once the rest is clear.

Good news so far for me is, despite being so horrible at c++ in the past, I got the code to work and the black window actually showed up after 2 weeks of staying up late at nights and reading line by line through online sources and books figuring out what's wrong with the code.

However, I have one basic problem in my code that's vexing me greatly because the compiler is not picking it up for me, it seems to be a logic error. As such I've no idea what I'm supposed to do. Since the code is really long I'll write some pseudo-code here:

#include iostream and fstream, along with using namespace std
- declare 5 functions (all void). they are like this: main, submenu 1, submenu 2, etc. the last one is for other options (currently  only holds my name) , so technically i've only worked on 4 things.

-int main ()
---> all this does is run all the functions

thing is, at the end of every menu I've added a line where the user can decide if he wants to go back to the main menu or not. that's where it goes wrong since my compiler ignores the user input! It goes something like:

cout << "do you wish to go back (1 for yes and 2 for no)"<<endl;
cin >> ans1;

switch (ans1)
{
case 1:
    mainmenu();
break;

case 2:
    submenu1();
}     
      
};

but the compiler refuses to take me to main should i press (1). It will re-run the same function (Since, this function is about 2 things: paycheck income and interest). Once it takes in two values it again ignores what I want it to do and will run off to the next submenu!

If I start the program at submenu 2, for instance, it will refuse any input and the instant I enter something, it will run back to the first submenu. I checked out the link you've posted in your instructions and I'm looking for answers but its pretty much what I've seen all over the web and I can't find the answer to anything. I do hope you've understood my problem and lead me the right way. Sorry I wrote too much before posting in my code. I just didn't think it would be a good thing to slam a wall of code text in your face expecting you to figure out exactly what's wrong. I appreciate all the help/advice you're willing to give. Once again I'd like to remind you that the last function (designer) has nothing in it except my name (and maybe yours later!), I plan to figure out the rest before I attempt to add anything in there.
____________________________________________________________

#include <iostream>
#include <fstream>
void mainmenu();
void submenu1();
void submenu2();
void submenu3();
using namespace std;



#include <iostream>
#include <fstream>
void mainmenu();
void submenu1();
void submenu2();
void submenu3();
void designer();
using namespace std;



int main ()
{
 mainmenu();
 submenu1();
 submenu2();
 submenu3();  
 designer();
   system ("pause");
   return 0;

}

void mainmenu()
{
 int first_user_input;
 cout <<" Welcome to personal financial record keeping system " << endl;
 cout <<"***********************"<< endl;
 cout <<"* 1. Update Income    *"<< endl;
 cout <<"* 2. Update Expenses  *"<< endl;
 cout <<"* 3. Calculate totals *"<< endl;
 cout <<"* 4. Other options    *"<< endl;
 cout <<"***********************"<< endl;
 cout <<"Please enter the number for the option of interest" <<endl;
 cin >> first_user_input;
 switch (first_user_input)
 {case 1:
 submenu1();
 break;
 case 2:
 submenu2();
 break;
 case 3:
 submenu3();
 break;
 case 4:
 designer();}
};




void submenu1()
{system ("cls");
 int update_income_choice;
 double paycheck;
 double interest;
 char ans1;
  ofstream income;
  income.open ("Income.txt", ios::in);
  
 cout << "You have chosen to update Income entries" << endl;
 cout << "Please enter the number for the option you wish to modify" << endl;
 cout << "*******************" << endl;
 cout << "*  1. Paychecks   *" << endl;
 cout << "*  2. Interest    *" << endl;
 cout << "*******************" << endl;
 cin >> update_income_choice;

     if (update_income_choice == 1)
     {
        cout << "Please enter the ammount (in RM) of the paycheck" << endl;
        cin >> paycheck;
        income << "Paycheck: " << paycheck << endl;
     }
     else if (update_income_choice == 2)
     {
        cout << "Please enter the ammount (in RM) of the Interest" << endl;
        cin >> interest;   
        income << "Income: " << interest << endl;
     }
cout << "do you wish to go back (1 for yes and 2 for no)"<<endl;
cin >> ans1;
switch (ans1)
{case 1:
mainmenu();
break;
case 2:
submenu1();}     
      
};  

void submenu2()
 {system ("cls");
 int update_expenses_choice;
 char ans2;
 cout << "You have chosen to update Expenses entries" << endl;
 cout << "Please enter the number for the option you wish to modify" << endl;
 cout << "**************************" << endl;
 cout << "*  1. Fixed Expenses     *" << endl;
 cout << "*  2. Variable Expenses  *" << endl;  
 cout << "**************************" << endl;
 cin >> update_expenses_choice;
 
switch (update_expenses_choice)
{      case 1:
     {int fixed_expenses_choice;
      double car_insurance_ammount;
      double cell_phone_charges_ammount;
      double student_loan_ammount;
      double rent_ammount;
      double others_ammount;
      
      ofstream fixed_expenses;
      fixed_expenses.open ("Fixed Expenses.txt", ios::in);
      
         cout << "You have chosen to update Fixed Expenses" << endl;
         cout << "Which Fixed Expense would you like to add?" << endl;
         cout << "%%%%%%%%%%%%%%%%%%%%%" << endl;
         cout << "%  1. Car Insurance %" << endl;
         cout << "%  2. Cell Phone    %" << endl;
         cout << "%  3. Student Loans %" << endl;
         cout << "%  4. Rent          %" << endl;
         cout << "%  5. Others        %" << endl;
         cout << "%%%%%%%%%%%%%%%%%%%%%" << endl;
         
         cin >>  fixed_expenses_choice;
         switch (fixed_expenses_choice)
         {case 1:
         {cout << "Enter Car Insurance Expense ammount" << endl;
         cin >> car_insurance_ammount;         
         fixed_expenses << "Car Insurance: " << car_insurance_ammount << endl;
         }break;
         case 2:
         { cout << "Enter Cell Phone Expenses ammount" << endl;
         cin >> cell_phone_charges_ammount;
         fixed_expenses << "Cell Phone: " << cell_phone_charges_ammount << endl;       
         }break;
         case 3:
         {cout << "Enter Student Loans ammount" << endl;
         cin >> student_loan_ammount;
         fixed_expenses << "Student Loan: " << student_loan_ammount << endl;
         }break;
         case 4:
         {cout << "Enter Rent ammount" << endl;
         cin >> rent_ammount;
         fixed_expenses << "Rent: " << rent_ammount << endl;
         }break;
         case 5:
         {cout << "Enter ammount of the other expenses: " << endl;
         cin >> others_ammount;
         fixed_expenses << "Others: " << others_ammount << endl;
         }break;
     cout << "do you wish to go back (1 for yes and 2 for no)"<<endl;
    cin >> ans2;
    switch (ans2)
    {case 1:
     mainmenu();
     break;
     case 2:
     submenu2();}
     }}
     break;
     
 
     case 2:
     {int variable_expenses_choice;
     double food_expenses_ammount;
     double book_expenses_ammount;
     double variable_others_ammount;
     
     ofstream variable_expenses;
     variable_expenses.open ("Variable Expenses.txt", ios::in);
     
         cout << "You have chosen to update Variable Expenses" << endl;
         cout << "Which Variable Expense would you like to add?" << endl;
         cout << "##############" << endl;
         cout << "# 1. Food    #" << endl;
         cout << "# 2. Books   #" << endl;
         cout << "# 3. Others  #" << endl;
         cout << "##############" << endl;
         cin >> variable_expenses_choice;
         switch (variable_expenses_choice)
         {  case 1:
         {
         cout << "Enter Food Expenses Ammount: " << endl;
         cin >> food_expenses_ammount;
         variable_expenses << "Food: " << food_expenses_ammount << endl;
         }
         break;
         
         case 2:
         {
         cout << "Enter Book Expenses Ammount: " << endl;
         cin >> book_expenses_ammount;
         variable_expenses << "Books: " << book_expenses_ammount << endl;   
         }
         break;
         
         case 3:
         {
         cout << "Enter ammount of other expenses: " << endl;
         cin >> variable_others_ammount;
         variable_expenses << "Others: " << variable_others_ammount << endl;
         
         }
         break;     
     }  
cout << "do you wish to go back (1 for yes and 2 for no)"<<endl;
      cin >> ans2;
      switch (ans2)
      {case 1:
      mainmenu();
      break;
      case 2:
      submenu2();    
     }

}
}};

void submenu3()
  {system ("cls");
  int totals_choice;
  char ans3;
  cout << "You have chosen to Calculate Totals" << endl;
  cout << "********************************" << endl;
  cout << "*  1. Total Income          *" << endl;
  cout << "*  2. Total Fixed Expenses     *" << endl;
  cout << "*  3. Total Vairable Expenses  *" << endl;
  cout << "*  4. Total Expenses          *" << endl;
  cout << "*  5. Disposable Income        *" << endl;
  cout << "*  6. Entire financial report  *" << endl;
  cout << "********************************" << endl;
  cout << "Please enter the number for the option you wish to view" << endl;
  cin >> totals_choice;
  // your codes  here;
   
cout << "Do you wish to go back (1 for yes and 2 for no)"<<endl;
cin >> ans3;
switch (ans3)
{case 1:
mainmenu();
break;
case 2:
submenu3();
break;}
};






void designer()
{ cout << "#####################################"<<endl;
 cout << "#  JOEY          #"<<endl;
 cout << "#  JOEY          #"<<endl;
 cout << "#####################################"<<endl;
char ans4;  
cout << "do you wish to go back (1 for yes and 2 for no)"<<endl;
cin >> ans4;
switch (ans4)
{case 1:
mainmenu();
break;
case 2:
designer();}
};

ANSWER: Well a quick look at the code reveals that you are reading in a char then using it like an int:

   char ans4;  // ans4 is a char

   // Reads values as a character thus reads '1' or '2' not 1 or 2
   cin >> ans4;

   switch (ans4)
   {
   case 1:   // case for character value 1 not character value '1'


In C++ chars are tricky beasts when read (or written). They do not get the usual conversion to an integer, digit by digit, as the other integer types do, they read one character in the character set in use on the system in question. Thus although a character can typically handle small integer values from 0 to 255 (if char is unsigned) or -128 to +127 (if char is signed), reading one as above reads a single character, thus entering:

   100

When asked for ans4 will only read the '1' and will not convert it to the value 1 but leave it as the value of the character that represents a 1 symbol in the character set in use. On a PC this is likely to be ASCII (or an extended variation of it) and in this encoding the digit symbols are assigned the hexadecimal values starting at 0x30 (for 0), 0x31 (for 1) up to 0x39 (for 9), which are the value 48, 49, ..., 57 in decimal.

A similar problem occurs if you are using a char to hold a small integer value and wish it printed out:

   char ans4(50);

   cout << ans4 << endl;

Assuming an ASCII compatible character set is in use then the above will print 2 on a line not 50, as 2 is the symbol represented in ASCII by the character value 50. To actually output 50 we would have to convert the char to an int:

   cout << int(ans4) << endl;

(a short int or long int would also work).


So either change your ansN types to be int rather than char:

   int ans4;

or change your case statements to use characters instead of raw integer values:

   switch (ans4)
   {
   case '1':   // case for character value 1 not character value '1'

   // etc...

The latter would be a good choice because then you could use y for yes and n for no:

   char ans4;  

   cout << "do you wish to go back (y for yes and n for no)"<<endl;

   cin >> ans4;

   switch (ans4)
   {
   case 'y':
       mainmenu();
       break;

   case 'n':
       designer();
   }

We can even cater for Y or y for yes and n or N for no:

   switch (ans4)
   {
   case 'y':
   case 'Y':
       mainmenu();
       break;

   case 'n':
   case 'N':
       designer();
   }

Now your code is fragile. What happens if the user enters something other than y, Y, n or N? Well none of the options handled by the switch statements will apply. Thus you should trap this condition and handle it somehow using a default label in the switch statement:

   switch (ans4)
   {
   case 'y':
   case 'Y':
       mainmenu();
       break;

   case 'n':
   case 'N':
       designer();
       break;   // break now required !!!

   default:
       // handle erroneous input from user
       break;// not required but add it anyway!
   }

The obvious way to handle bad input is to display an error message and loop around and ask the user to enter their choice again:

   for (;;)  // C/C++ idiom for (loop) forever
   {
       char ans4;  

       cout << "do you wish to go back (y for yes and n for no)"
         << endl;

       cin >> ans4;

       switch (ans4)
       {
       case 'y':
       case 'Y':
         mainmenu();
         break;

       case 'n':
       case 'N':
         designer();
         break;   // break now required !!!

       default:
         cout << "\nPlease enter y or n\n";
         break; // not required but add it anyway!
       }
   } // end for-ever for loop

The above works because if you enter a correct value the program will route off elsewhere - back to mainmenu or back to designer.

Except that in the long run it does not work that well. This is because you are nesting calls to your functions ever deeper and deeper, even calling them recursively (means re-entering a function before returning from previous call(s) to the same function). While this is an allowed and powerful technique it is almost certainly _not_ what you wanted to do here. Each call requires more stack space (i.e. memory). Eventually your program will run out of stack space (OK, so on a modern PC this might take quite some time), and will most likely crash.

What you should do is loop around each function until the user requests not to, at which point you return to the previous function call, the one that called the function in the first place:

   void designer()
   {
       for (;;)  // C/C++ idiom for (loop) forever
       {
         cout << "#####################################"<<endl;
         cout << "#  JOEY          #"<<endl;
         cout << "#  JOEY          #"<<endl;
         cout << "#####################################"<<endl;

         bool bad(true);
         while ( bad )
         {
         char ans4;  

         cout << "do you wish to go back "
         "(y for yes and n for no)"
         << endl;

         cin >> ans4;

         switch (ans4)
         {
         case 'y':
         case 'Y':
         return;  // return to caller
         break;

         case 'n':
         case 'N':
         bad = false; // input OK, set bad false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( bad )

       } // end for-ever for loop
   } // end of function designer

Now the for-ever loop encompasses the whole of the designer function. This extra loop mean we have to modify the check for y/n loop. I now use a while loop that loops until a bad flag is set false to indicate a good input. This only needs to be done for the n case because if the user wishes to return to the main menu this is exactly what we do - return from the call to designer to where ever we were called from, effectively exiting the while ( bad ) loop in the process. In fact the code returns to the point just after the call was made, i.e. from:

 case 4:
     designer();
 // designer will return to here in mainmenu.
 }
}

Of course in the call from main it will return to main:

   int main ()
   {
     mainmenu();
     submenu1();
     submenu2();
     submenu3();  
     designer();

   // designer will return here when called from main

       system ("pause");
       return 0;

   }

So I would suggest that the calls to submenus and designer from main are not required, and that mainmenu needs to use a loop similar to designer. The loop could most easily be exited by adding a case in the option selection switch for all done, say something suitably out of range of other possible option values like 999:

   void mainmenu()
   {
       int first_user_input;

       cout <<" Welcome to personal financial record keeping system "
         << endl;

       for (;;)
       {
         cout <<"***********************"<< endl;

         cout <<"* 1. Update Income    *"<< endl;

       // ...


         cout <<"* 4. Other options    *"<< endl;
         cout <<"*999 Quit          *"<< endl;

         cout <<"***********************"<< endl;

         switch (first_user_input)
         {
         case 1:
         submenu1();
         break;

         case 4:
         designer();
         break;      // break now needed!!!

         case 999:
         return;  // to main
         }
       }
   }

The above shows the basic idea. I'll leave it to you to add the checking to ensure the user enters only permitted option values, as this would be very much similar to the case in the designer function I showed previously. You will of course have to modify the other functions in a similar fashion; again I shall leave this up to you as you now have a template to work from. If you do not then the

   return;  // to main

Will of course not always do so!

Note that I have added break statements after all case and default clauses. It is usually best to do so in case extra code is added after - as in the case of case 999: and adding the default: labels to error trapping. I do not bother if the code returns as this is a break of a different sort - a return from the whole function prevents the flow of control dropping through to later cases as well. This is trying to make your code easier to maintain - as in the future when applying a quick change adding additional cases you might forget to check the previous cases to ensure they have required break/return or similar, thus leading to you having an interesting time debugging your code to discover why things are not going to plan!

Also note that I left the terminating brace of switch statements on a separate line. This is also a nod to future proofing - it is easier to add additional code before this style of termination than the one you were using where the terminating brace was at the end of the line of the last statement in the switch.

Note also that I have removed some of the blank lines between lines of your code _ I do not know if you actually added them all in or it is an artefact of posting through AllExpert!

Code is best viewed (as always) using a mono-spaced font like Courier. And that the code above came straight out of my head and has not been tested at all (not even compiled!). So be wary of typos and other mistakes. I apologise of course for any such problems you find but hope you can still work out what I intended to show.

Finally you seem to think it is the compiler that executes your code. It does not. C++ compilers generally produce native code for the platform you are using. They do use a bit of support code and of course C and C++ make extensive use of library functionality. In fact many compilers have options that would allow you to see the native assembler code representing the machine instructions they produce.





---------- FOLLOW-UP ----------

QUESTION: Ralph! I can't thank you enough for the earlier help you gave me. I modified my code and re-wrote all my functions, (never knew about the infinite loop), and its all great now! Now I would to get a little more advice from you, if you don't mind giving it. This time, pertaining to files. I don't have a problem making files (I think) but I'm slightly confused on the part where I need the file to send information to my screen, for the totals function ...

In Income, Fixed Expenses, and Variable Expenses, im sending information to a file. However in Totals, I need to do some calculations. For example, let's say the user gives option 1 to me... which is total income. total income is actually paycheck + interest. How can I display this on the screen?

I'm just looking for the right way to calculate stuff from one file and then show the output on my screen. If you could please provide me with one example of a calculation, I think I could easily work out the rest of the function. The function goes something like this:


void submenu4 ()
{
    for (;;)
    {
        system ("cls");
        int totals_choice;
        
        
         cout << "You have chosen to Calculate Totals" << endl;
         cout << "********************************" << endl;
         cout << "*  1. Total Income          *" << endl;
         cout << "*  2. Total Fixed Expenses     *" << endl;
         cout << "*  3. Total Vairable Expenses  *" << endl;
         cout << "*  4. Total Expenses          *" << endl;
         cout << "*  5. Disposable Income        *" << endl;
         cout << "*  6. Entire financial report  *" << endl;
         cout << "********************************" << endl;
         cout << "Please enter the number for the option you wish to view" << endl;
         cin >> totals_choice;

//rest unwritten, except the bool part you gave me earlier on, so i need to know how to go on about with the calculations.

the complete code is as follows:
________________________________________________________
#include <iostream>
#include <fstream>
void mainmenu();
void submenu1();
void submenu2();
void submenu3();
void submenu4();
void designer();
using namespace std;



int main ()
{
 mainmenu();
 submenu1();
 submenu2();
 submenu3();
 submenu4();  
 designer();
 
 
 system ("pause");
 return 0;

}

void mainmenu()
  {
      system ("cls");
      int first_user_input;

      cout <<" Welcome to personal financial record keeping system "<< endl;

      
      
         cout <<"***********************************"<< endl;

         cout <<"* 1. Update Income          *"<< endl;

         cout <<"* 2. Update Expenses  -- Fixed    *"<< endl;
         
         cout <<"* 3. Update Expenses  -- Variable *"<< endl;
 
         cout <<"* 4. Total          *"<< endl;
         
         cout <<"* 5. Others          *"<< endl;
         
         cout <<"*999 Quit          *"<< endl;

         cout <<"***********************************"<< endl;
         
         cout << "Enter option of choice" << endl;
         cin >> first_user_input;
         switch (first_user_input)
         {
         case 1:
         submenu1();
         break;
         case 2:
         submenu2();
         break;
         case 3:
         submenu3();
         break;
         case 4:
         submenu4();
         break;
         case 5:
         designer();       
         
         case 999:
         return;  // to main
         }
      
  };

void submenu1()
{
     for (;;)  // C/C++ idiom for (loop) forever
      {
 system ("cls");
 int update_income_choice;
 double paycheck;
 double interest;

 ofstream income;
 income.open ("Income.txt", ios::in);
  
 cout << "You have chosen to update Income entries" << endl;
 cout << "Please enter the number for the option you wish to modify" << endl;
 cout << "*******************" << endl;
 cout << "*  1. Paychecks   *" << endl;
 cout << "*  2. Interest    *" << endl;
 cout << "*******************" << endl;
 cin >> update_income_choice;

     if (update_income_choice == 1)
     {
        cout << "Please enter the ammount (in RM) of the paycheck" << endl;
        cin >> paycheck;
        income << "Paycheck: " << paycheck << endl;
     }
     else if (update_income_choice == 2)
     {
        cout << "Please enter the ammount (in RM) of the Interest" << endl;
        cin >> interest;   
        income << "Income: " << interest << endl;
     }
     
     bool tcp(true);
         while ( tcp )
         {
         char ans1;  

         cout << "do you wish to go back to main?"
         "(y for yes and n for no)"
         << endl;

         cin >> ans1;

         switch (ans1)
         {
         case 'y':
         case 'Y':
         mainmenu();  // return to caller
         break;

         case 'n':
         case 'N':
         tcp = false; // input OK, set tcp false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( tcp )

      } // end for-ever for loop
  }; // end of function

void submenu2 ()
{
    for (;;)
    {
        system ("cls");
      int fixed_expenses_choice;
      double car_insurance_ammount;
      double cell_phone_charges_ammount;
      double student_loan_ammount;
      double rent_ammount;
      double others_ammount;
      
      ofstream fixed_expenses;
      fixed_expenses.open ("Fixed Expenses.txt", ios::in);
      
         cout << "You have chosen to update Fixed Expenses" << endl;
         cout << "Which Fixed Expense would you like to add?" << endl;
         cout << "%%%%%%%%%%%%%%%%%%%%%" << endl;
         cout << "%  1. Car Insurance %" << endl;
         cout << "%  2. Cell Phone    %" << endl;
         cout << "%  3. Student Loans %" << endl;
         cout << "%  4. Rent          %" << endl;
         cout << "%  5. Others        %" << endl;
         cout << "%%%%%%%%%%%%%%%%%%%%%" << endl;
         cout << "Please enter the number for the option you wish to modify" << endl;
         cin >>  fixed_expenses_choice;
         if (fixed_expenses_choice == 1 )
         {
         cout << "Enter Car Insurance Expense ammount" << endl;
         cin >> car_insurance_ammount;         
         fixed_expenses << "Car Insurance: " << car_insurance_ammount << endl;
         }   
         else if (fixed_expenses_choice == 2)
         {
         cout << "Enter Cell Phone Expenses ammount" << endl;
         cin >> cell_phone_charges_ammount;
         fixed_expenses << "Cell Phone: " << cell_phone_charges_ammount << endl;       
         }
         else if (fixed_expenses_choice == 3)
         {
         cout << "Enter Student Loans ammount" << endl;
         cin >> student_loan_ammount;
         fixed_expenses << "Student Loan: " << student_loan_ammount << endl;
         }
         else if (fixed_expenses_choice == 4)
         {
         cout << "Enter Rent ammount" << endl;
         cin >> rent_ammount;
         fixed_expenses << "Rent: " << rent_ammount << endl;
         }
         else if (fixed_expenses_choice ==5)
         {
         cout << "Enter ammount of the other expenses: " << endl;
         cin >> others_ammount;
         fixed_expenses << "Others: " << others_ammount << endl;
         }
    bool tcp(true);
         while ( tcp )
         {
         char ans2;  

         cout << "do you wish to go back to main?"
         "(y for yes and n for no)"
         << endl;

         cin >> ans2;

         switch (ans2)
         {
         case 'y':
         case 'Y':
         mainmenu();  // return to caller
         break;

         case 'n':
         case 'N':
         tcp = false; // input OK, set tcp false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( tcp )

      } // end for-ever for loop
};


void submenu3 ()
{
    for (;;)
    {
    system ("cls");
    int variable_expenses_choice;
     double food_expenses_ammount;
     double book_expenses_ammount;
     double variable_others_ammount;
     
     ofstream variable_expenses;
     variable_expenses.open ("Variable Expenses.txt", ios::in);
     
         cout << "You have chosen to update Variable Expenses" << endl;
         cout << "Which Variable Expense would you like to add?" << endl;
         cout << "##############" << endl;
         cout << "# 1. Food    #" << endl;
         cout << "# 2. Books   #" << endl;
         cout << "# 3. Others  #" << endl;
         cout << "##############" << endl;
         cout << "Please enter the number for the option you wish to modify" << endl;
         cin >> variable_expenses_choice;
         if (variable_expenses_choice ==1)
         {  
         
         cout << "Enter Food Expenses Ammount: " << endl;
         cin >> food_expenses_ammount;
         variable_expenses << "Food: " << food_expenses_ammount << endl;
         }
         
         
         else if (variable_expenses_choice ==2)
         {
         cout << "Enter Book Expenses Ammount: " << endl;
         cin >> book_expenses_ammount;
         variable_expenses << "Books: " << book_expenses_ammount << endl;   
         }
         
         
         else if (variable_expenses_choice ==3)
         {
         cout << "Enter ammount of other expenses: " << endl;
         cin >> variable_others_ammount;
         variable_expenses << "Others: " << variable_others_ammount << endl;
         
         }
         
     
   bool tcp(true);
         while ( tcp )
         {
         char ans3;  

         cout << "do you wish to go back to main?"
         "(y for yes and n for no)"
         << endl;

         cin >> ans3;

         switch (ans3)
         {
         case 'y':
         case 'Y':
         mainmenu();  // return to caller
         break;

         case 'n':
         case 'N':
         tcp = false; // input OK, set tcp false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( tcp )

      } // end for-ever for loop
   
};  

void submenu4 ()
{
    for (;;)
    {
        system ("cls");
        int totals_choice;
        
        
         cout << "You have chosen to Calculate Totals" << endl;
         cout << "********************************" << endl;
         cout << "*  1. Total Income          *" << endl;
         cout << "*  2. Total Fixed Expenses     *" << endl;
         cout << "*  3. Total Vairable Expenses  *" << endl;
         cout << "*  4. Total Expenses          *" << endl;
         cout << "*  5. Disposable Income        *" << endl;
         cout << "*  6. Entire financial report  *" << endl;
         cout << "********************************" << endl;
         cout << "Please enter the number for the option you wish to view" << endl;
         cin >> totals_choice;
        
        
         bool tcp(true);
         while ( tcp )
         {
         char ans4;  

         cout << "do you wish to go back "
         "(y for yes and n for no)"
         << endl;

         cin >> ans4;

         switch (ans4)
         {
         case 'y':
         case 'Y':
         mainmenu();  // return to caller
         break;

         case 'n':
         case 'N':
         tcp = false; // input OK, set tcp false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( tcp )

      } // end for-ever for loop
   
};  

void designer ()
  {
      for (;;)  // C/C++ idiom for (loop) forever
      {
         cout << "#########"<<endl;
         cout << "#  Joe  #"<<endl;
         cout << "#  Joe  #"<<endl;
         cout << "#########"<<endl;

         bool tcp(true);
         while ( tcp )
         {
         char ans5;  

         cout << "do you wish to go back "
         "(y for yes and n for no)"
         << endl;

         cin >> ans5;

         switch (ans5)
         {
         case 'y':
         case 'Y':
         mainmenu();  // return to caller
         break;

         case 'n':
         case 'N':
         tcp = false; // input OK, set bad false
         // to quit while loop
         break;   // break now required !!!

         default:
         cout << "\nPlease enter y or n\n";
         break;// not required but add it anyway!
         } // end of switch
         } // end of while ( bad )

      } // end for-ever for loop
  }; // end of function designer


Answer
You do not get the file to do anything – it is a chunk of data.

Rather you read the data in the file and perform operations on that data when it is read into memory.

Then presumably once you have the result of the calculation you know how to display it:

   cout << "The total income value is: " << total_income << '\n';

If you had the values in memory already as simple objects then you would have no problem I am guessing:

double paycheck = 2345.98;
double interest = paycheck * 0.043; // 4.3 % interest
double total_income = paycheck + interest;

Note: the above is a very simplified calculation for interest for example purposes only.

So your problem comes down to 2 parts for each calculation:

   1/ performing the calculation
   2/ obtaining the data for the calculation

The two are somewhat related in that you have to perform the calculation using the basic values you have available in the file.

Hence in my simple example above if the file contained both the pay check and interest values then the calculation for interest could be left out and you could use the values directly to calculate the total income value.

If however you had only the pay check value and the interest rate data (maybe from a different source – e.g. another file) then you would have to obtain the interest rate data and pay check data and then apply this data to obtain the interest value and finally produce the total income value.

So how do you obtain data from a file? By using the inverse of writing the data to file! That is you open the file for reading, and extract data from the stream rather than inserting it into the stream:

    ifstream income;
    income.open("Income.txt"); // ifstream assumes ios::in

    std::string fieldname;
    double      paycheck(0.0);
    double      interest(0.0);

    int const NumberOfIncomeFields(2);

    for ( int i(0); i != NumberOfIncomeFields; ++i )
    {
         income >> fieldname;
         if (fieldname == "Paycheck:")
       {
         income >> paycheck;
         income >> fieldname;
         if (fieldname != "Income:")
         {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
         }
         income >> interest;
       }
        else if (fieldname == "Income:")
       {
         income >> interest;
         income >> fieldname;
         if (fieldname != "Paycheck:")
         {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
         }
         income >> paycheck;
       }
       else
       {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
       }

This reads the first paycheck, interest pair from the income file. I had trouble quickly assessing your code so am not sure exactly how your files are laid out (or guess how they should be laid out), but it seemed to me that you are supposed to write pay check and income values together for each entry. The code you have seems at a quick glance to allow for all sorts of laxity – you can fill the file with just pay check or just interest values for example and the pay check and interest values need not be written in the same order each time. Also you are using "magic" values for the prefixing keyword that identify what the value following is – they are quoted each time you wish to use them – note I use each keyword twice in the code extract above. A better solution is to place such magic values into constants. If they apply to the whole file (i.e. across functions) then place at the top of the file after #includes but before the first function:

   char const * KeywordIncomePaycheck = "Paycheck:";
   char const * KeywordIncomeInterest = "Income:";
   int const NumberOfIncomeFields(2);

Otherwise just define them in the function they apply to.

This makes it much, much easier to change the actual values used.
For example the interest values in your income file are currently tagged with the keyword "Income:" whereas I suspect you meant "Interest:". If your code used the KeywordIncomeInterest variable instead of literal text each time then you only have to change the actual value in one place:

   char const * KeywordIncomePaycheck = "Paycheck:";
   char const * KeywordIncomeInterest = "Interest:";
   int const NumberOfIncomeFields(2);


   // ...

    ifstream income;
    income.open("Income.txt"); // ifstream assumes ios::in

    std::string fieldname;
    double      paycheck(0.0);
    double      interest(0.0);

    for ( int i(0);  < NumberOfIncomeFields; ++i )
    {
         income >> fieldname;
         if (fieldname == KeywordIncomePaycheck)
       {
         income >> paycheck;
         income >> fieldname;
         if (fieldname != KeywordIncomeInterest)
         {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
         }
         income >> interest;
       }
        else if (fieldname == KeywordIncomeInterest)
       {
         income >> interest;
         income >> fieldname;
         if (fieldname != KeywordIncomePaycheck)
         {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
         }
         income >> paycheck;
       }
       else
       {
         cerr << "Error! Income file not in expected format!"
         << endl;
         return;
       }

And then re-build your program and all the uses of KeywordIncomeInterest will now refer to the text "Interest:". You will of course need to delete or overwrite any existing data file containing the old value!

Of course you can simplify things by not writing data to file in inconsistent ways. You should collect a paycheck, interest pair and then write both to the income file at the _same_ time:

   income << paycheck << ' ' << interest << endl;

Now the file consists of lines of two values arranged in pay check, interest order:

   2345.98 100.89
   1234.65 53.09
       .
       .
       .

You can now read them in using much more simple code:

   income >> paycheck;
   income >> interest;

Or even:


   income >> paycheck >> interest;

   
Note that I have left out (as you seem to have done) all error checking on the streams. This is stupid. In my case it is because I wish not to clutter up examples with too much error checking code. In your actual program I suggest you check the state of at least the file streams after each operation:

   income << paycheck << ' ' << interest << endl;

   if (!income) { /* handle error */ }

There are a set of flags used to indicate what state a steam is in: good, bad, fail, eof (end of file), with an equivalent set of functions: good() returns true if stream is OK, fail() returns true if the stream is in a failed state (usually a formatting error such as reading a string when a number expected) or a bad (unrecoverable error) state; bad() returns true if the stream is in the bad state; eof() returns true if the stream is at the end of the file – note you have to actually have tried to read past the end for this to be detected!!! The stream itself can be directly tested for good or not good state, as above. Additionally for file streams we have is_open() which returns true if the stream is open, so should be used after open to ensure open was successful. For more on streams and stream state please refer to a good C++ library reference such as "The C++ Standard Library A Tutorial and Reference" by Nicolai M. Josuttis. There are some references online: See for example http://www.cplusplus.com/ and http://www.digilife.be/quickreferences/PT.htm, however I cannot vouch for the completeness or correctness of these articles.



I note you specify ios::in to your output file streams but never as far as I can tell do you read and write to these files in these functions. I suspect therefore you added this to prevent your data being overwritten (maybe – not sure) or you thought you may have to read data to locate values to update. If it is the second reason then all well and good. If it is the first then you can open an existing file for update such that it is not truncated (emptied) when you re-open it by specifying the open mode as ios::out | ios::app. The file will be positioned at the end of existing data ready for further data to be appended. If you wish to overwrite existing data then you would need to position the file to say, the start. You can do this using the seekp (seek for putting – i.e. writing) function:

   income.open("Income.txt", ios::out | ios::app);

   if ( !income.is_open() )
   {
   // handle unable to open file error
   }

   income.seekp( 0 ); // seek to beginning for writing.

There is an equivalent seekg for seeking for getting (reading) data. To find out where a file stream is currently positioned you can use the tell functions tellp and tellg. Again refer to a C++ IOSteams reference for more details (I am using the Josuttis book here to ensure I get things right, chapter 13 covers IOStreams).

So in short:

-   Data files are for storing data not being nice to read by people. (Ok so some files might be designed to be nicely formatted for humans to read – these are often called reports though!!).
-   Therefore you can leave off complexities such as keywords, tagging etc. for simple data sets as you have here.
-   This can make reading the data much, much easier.
-   You will of course have to modify how the data is collected and written.
-   You should avoid "magic" numbers and strings in your code. They complicate maintenance and can obscure meaning.
-   You should check the state of streams, especially file streams and formatted input operations as they can fail.
-   Checking for end of file is useful for terminating a loop that is operating on all records in a file. However be aware that you will need to check after each read for end of file to prevent using bad data between the failing read and say a check in the loop exit logic.
-   Once you have the raw input values from file you perform calculations to obtain the required results.
-   You write out the results with any additional formatting – helpful text prompts, etc. from within the program not within the data file. OK so maybe in a complex program such formatting data may also in a file and allows some customisation by the user, but let’s not get carried away here – I think that is well beyond the scope of your assignment!!!

Hope this help.

Finally: Please do not post your whole program to me in future it is getting out of hand. Please take the time to reduce the code posted to the absolute minimum required to demonstrate your point. The code should still be compilable if possible, even if I have to paste the code into a wrapper function. Because of the length of your code I have not bothered to try to compile it and have had trouble navigating around it to see what you are doing! Please help me reduce the amount of time I have to spend answering you – I think everyone else here has just had dinner and I suspect mine is now cold and nasty!!!  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.