I am learning C++ from the Stroustrup book "Programming Principles and Practice". I have been doing the exercises in each chapter. I have gotten stuck on chapter 6. I understand the concepts of classes, constructors, functions, and 'throw and catch' error handling, but seemingly not well enough to actually write a program with these elements that compiles and runs. I have written a program for exercise 10 in chapter 6 that runs almost correctly.

It is not a pretty program and the error exception for non-integer inputs causes the program to crash with a message relating to error exception #3 instead of my intended #4. I don't know how to put all the error exceptions into the same class. Thank you in advance for your help!

N.B. You will have to change the header line if you want to run this program. The header in the program is the one Stroustrup instructs us to download from his site

#include "../../std_lib_facilities.h"

class myexception1: public exception    // Classes for handling errors.
{
  virtual const char* what() const throw()
  {
    return "The set and subset sizes must be greater than 0.\n";
  }
} myex1;

class myexception2: public exception
{
  virtual const char* what() const throw()
  {
    return "The set size must be greater than the subset size.\n";
  }
} myex2;

class myexception3: public exception
{
  virtual const char* what() const throw()
  {
    return "Either a 'c' for combination or a 'p' for permutation must be indicated.\n";
  }
} myex3;

class myexception4: public exception
{
  virtual const char* what() const throw()
  {
    return "The set and subset sizes must be integers.\n";
  }
} myex4;

int factorial (int x)     //  Factorial function using the recursive method.
{
	int f;
	if (x == 0) {
		f = 1;
	} else {
		f = x * factorial(x-1);
	}
	return (f);
}

int permu (int n, int r)     // Ordered arrangement calculation (permuatation) for subset of size 'r' out of a set of size 'n'. 
{
	int pm;
	pm = factorial(n) / factorial(n-r);
	return pm;
}

int combi (int n, int r)    // Unordered arrangement calculation (combination) for subset of size 'r' out of a set of size 'n'.
{
	int cb;
	cb = permu(n,r) / factorial(r);
	return cb;
}

int main () 
try {
	int c = 0;
	int d = 0;
	char z = ' ';
	int result = 0;

	cout <<"Enter the set size which must be greater than zero, the subset size, \nand the type of computation.\n";
	cout <<"The set size must be greater the subset size.  \nEnter 'c' for combination or 'p' for permutation.\n";
	cout <<"Separate your entries by a space.\n";

	cin >> c >> d >> z;

	if (c <= 0 || d < 0) throw myex1;         // Negative size error.
	if (c < d) throw myex2;                   // Set size less than subet size error.
	if (z != 'c' && z != 'p') throw myex3;    // Choice other than combination or permutation error.
	if (c%1 != 0 || d%1 != 0) throw myex4;    // Non-integer error.

    if (z == 'p') {
		result = permu(c,d);
        cout << "The number of permutations is " << result <<".\n";
	}

	if (z == 'c') {
	    result = combi(c,d);
        cout << "The number of combinations is " << result <<".\n";
	}

    keep_window_open();
    return 0;

} catch(exception& e) {
	cerr << e.what() << endl;
    
	keep_window_open();
    return 1;

} catch(...) {
	cerr <<"Something else went wrong.\n";
    
	keep_window_open();
    return 2;
}

.

dusktreader commented: I am impressed by your ambition to self-teach c++. Nice code +1

IIRC, the a subsequent chapter of the book deals with completing this program. You would learn there about most of these issues (input and output, modulus operations, recovering from errors and so on), and be asked to refine the first draft of the program. So, perhaps the intent is to let you write some code right now, which may not be the final form of the program that you would write.

So, if the following does not make too much sense, don't bother too much right now. One thing that you might want to start do immediately is: consider giving somewhat more intuitive names for your varables. eg.

int set_sz = 0;
int subset_sz = 0;
char command = ' ';
int result = 0;

> I don't know how to put all the error exceptions into the same class.

Each exception indicates a different kind of error, and errors are discriminated based on the type of the exception. Putting everything into the same class may not be what you want to do. However, you could have a common base class from which all your exception types are derived. eg.

class program_error : public exception // base class for exceptions.
{
    virtual const char* what() const throw()
    {
        return "A program specific error.\n" ;
    }
} ;

class subsetset_size_too_large_error : public program_error
{
    virtual const char* what() const throw()
    {
        return "The set size must be greater than the subset size.\n" ;
    }
} ;

class non_int_input_error : public program_error
{
    virtual const char* what() const throw()
    {
        return "The set and subset sizes must be integers.\n" ;
    }
} ;

// etc

You could then catch all in a single catch with a const reference to the base class.

try
{
    // ....
};
catch( const program_error& e )
{
    std::cerr << e.what() << '\n' ;
    // ...
}

(I presume that you have already learned the basics of inheritance. If not you could have a single class with an enum member indicating the kind of error, and then switch on the enum in virtual const char* what() const throw() ).

Prefer catching exceptions by const reference, unless your intent is to modify the caught object.

The exception objects need not be pre-created; even if you create them before hand, what is thrown would be a new object which is a copy of the original. Just throw them this way: if ( c < d ) throw subsetset_size_too_large_error() ; > if (c%1 != 0 || d%1 != 0) throw myex4; // Non-integer error. You would never be able to catch a non-integer set size error this way; c and d are of type int! Instead you need to do something like:

if( std::cin >> set_sz >> subset_sz )
{
    // user has entered integer values for set_sz and subset_sz
    std::cin >> command ;
    // proceed to check sizes, command etc
    // if ok, compute and print result
}
else
{
    // attempt to read two integers from stdin failed
    throw non_int_input_error() ;
}

Thank you very much for your suggestions, Vijayan. I hadn't thought that having different classes for different exceptions was an appropriate formulation.

This morning I woke up realizing why my 4th exception was crashing the program. I am using the modulo operator with a double when it can only be used with ints. I need a different test for an int.

I haven't yet learned about inheritance and enumerators. I have taken a look at chapter 7 and found what I saw very intimidating! I have noticed that Stroustrup builds on earlier programs in later exercises. Several exercises in chapters 6 and 7 ask the reader to modify programs from exercises in chapter 4.

>>I am using the modulo operator with a double when it can only be used with ints.

Although you don't need it here, if you do need in the future, the modulo function for double is fmod().

Thank you very much for your suggestions, Vijayan. I hadn't thought that having different classes for different exceptions was an appropriate formulation.

I respectfully disagree. I believe that different exception types should have different class definitions. This paradigm is used extensively in many C++ libraries. I do agree that you should probably create your own base exception class and then derive from that your different sorts of exceptions specific to your implementation.

This part troubles me:

catch(exception& e) { /*code*/ }
catch(...) { /*code*/ }

It seems to me that the second catch statement is unreachable. If I remember correctly, the exception class is the most fundamental base class for all C++ exceptions. If I am correct, then all exceptions will be caught in the first catch. If you implement your own base exception class, say MathException , then you could have the first catch block look for only MathException instances. I use this sort of thing often. It allows you to catch exceptions that are caused in your code, and the generic exception catch will report if something unanticipated happens.

By the way, your code looks very good. Very nice work in self-education.

@dusktreader,

Thank you for the kind words!

The examples I saw regarding throw and catch exceptions only had one type of exception so there was only one class. That is why I didn't realize that having separate classes for each type of exception was the proper thing to do. Thanks to you and Vijayan, I have learned otherwise.

@mike,

Your suggestion works! I had to change the variable type to double, but using fmod yields the correct exception. Here is the new code I used. There is a warning about conversion from double to int since the functions yield ints but that is easily fixed.

try {
	double c = 0;
	double d = 0;
	char z = ' ';
	int result = 0;

	cout <<"Enter the set size which must be greater than zero, the subset size, \nand the type of computation.\n";
	cout <<"The set size must be greater the subset size.  \nEnter 'c' for combination or 'p' for permutation.\n";
	cout <<"Separate your entries by a space.\n";

	cin >> c >> d >> z;

	if (c <= 0 || d < 0) throw myex1;         // Negative size error.
	if (c < d) throw myex2;                   // Set size less than subet size error.
	if (z != 'c' && z != 'p') throw myex3;    // Choice other than combination or permutation error.
	if (fmod(c,1) != 0 || fmod(d,1) != 0) throw myex4;    // Non-integer error.

The comments so far have addressed the areas I needed to learn about. Technically, the thread could be considered solved. I would like to see if other posters make useful suggestions.

Be a part of the DaniWeb community

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