Exceptions in Inheritance

0 votes
asked Apr 21 by Max Chance (120 points)
I'm having trouble figuring out how to add the exceptions into my assignment.

"Take your Avatar class and add exception handling by defining classes that inherit exception.

Every attribute should have a decided range and set that range."

#pragma once

#include <iostream>

using namespace std;

class Avatar

{

private:

int strength;

int speed;

public:

Avatar();

~Avatar();

//mutators to change values

void setStrength(int);

void setSpeed(int);

//accessor functions

int getStrength() const;

int getSpeed() const;

void display();

};

#include "Avatar.h"

Avatar::Avatar()

{

}

Avatar::~Avatar()

{

}

void Avatar::setStrength(int strength)

{

this->strength = strength;

}

void Avatar::setSpeed(int speed)

{

this->speed = speed;

}

int Avatar::getStrength() const

{

return strength;

}

int Avatar::getSpeed() const

{

return speed;

}

void Avatar::display()

{

cout << "Strength is: " << strength << "\n";

cout << "Speed is: " << speed << "\n";

}

#include "Avatar.h"

int main()

{

//values for avatar 1

Avatar avatar1;

avatar1.setStrength(10);

avatar1.setSpeed(15);

avatar1.display();

//values for avatar 2

Avatar avatar2;

cout << "Avatar 2 values are: " << endl;

avatar2.setStrength(8);

avatar2.setSpeed(20);

avatar2.display();

return 0;

}

2 Answers

0 votes
answered Jul 14 by xDELLx (2,430 points)
#include <iostream>
#include <exception>
#include <typeinfo>

using namespace std;
//runtime_error
class runtime_value_error : public  runtime_error //Base class of custom exception
{
    private :
        int __value;
        char * __print;
    public:
        runtime_value_error ( int value , char * to_print=nullptr):runtime_error("")
               {
            __value = value;
            __print = to_print;
               }
        const char* what() const noexcept
        { return __print==nullptr ? "Attempting to set illegal value \n" :__print; ;}
};

class speed_value_error : public runtime_value_error{ //specialised exception for error in speed
    public:
    speed_value_error(int value =0, char * to_print="Illegal speed value error\n"):
        runtime_value_error(value,to_print)
        {}
};

class strength_value_error : public runtime_value_error{ //specialised exception for error in strength
    public:
    strength_value_error(int value =0, char * to_print="Illegal strength value error\n"):
        runtime_value_error(value,to_print)
        {}    
};

class Avatar
{
    private:
        int strength;
        int speed;
    public:
        Avatar();
        ~Avatar();
        //mutators to change values
        void setStrength(int) throw (runtime_error);
        void setSpeed(int) throw (runtime_error);
        //accessor functions
        int getStrength() const;
        int getSpeed() const;
        void display() throw (runtime_value_error);
};
//#include "Avatar.h"
Avatar::Avatar()
{
    strength=-1;
    speed=-1;
}
Avatar::~Avatar()
{
}
void Avatar::setStrength(int strength) throw (runtime_error)
{
    if (strength < 0 ){
      runtime_value_error x(strength,"-ve value supplied\n") ; //parametrized message  
    // runtime_error x("ABC");
    this->strength = -1;
      throw x;
    }
    this->strength = strength;
}
void Avatar::setSpeed(int speed) throw (runtime_error)
{
    if (speed < 0 ) {
        throw runtime_value_error(0); //im-too-lazy-to-type-now exception ;)
    }
    this->speed = speed;
}
int Avatar::getStrength() const
{
    return strength;
}
int Avatar::getSpeed() const
{
    return speed;
}
void Avatar::display() throw (runtime_value_error)
{
    if (strength < 0 && speed < 0){ //If object is not initialised ,throw the error
        throw runtime_value_error(0,"Object not initialized\n");
    }
    if (strength < 0){ //throwing data specific exceptions
        throw strength_value_error();
    }
    if (speed < 0){ //throwing data specific exceptions
        throw speed_value_error();

    }

//All data is valid , display the data

    cout << "Strength is: " << strength << "\n";
    cout << "Speed is: " << speed << "\n";
}
//#include "Avatar.h"
int main()
{
    //values for avatar 1
    Avatar avatar1;
    try{
        avatar1.setStrength(-10);
    }catch (runtime_error &r){
        cout <<"Error setting strength for Av1 "<<r.what();
    }catch (... ) {
        cout <<"Unknown Exception caught"<<endl;
    }
    avatar1.setSpeed(15);
    cout << "DETAILS OF AVATAR 1 Below\n";
    try{
    avatar1.display();
    }catch(speed_value_error &x){
        cout << x.what();
        cout << "Displaying only strength  :" <<avatar1.getStrength();
    }catch(strength_value_error &x){
        cout << x.what();
        cout << "Displaying only speed :" <<avatar1.getSpeed();
    }catch(runtime_value_error &x){
        cout << x.what();
    }
    //values for avatar 2
    Avatar avatar2;
    cout << "\nAvatar 2 values are: " << endl;
    avatar2.setStrength(8);
    avatar2.setSpeed(20);
    avatar2.display();
    return 0;
}
0 votes
answered Jul 15 by Peter Minarik (7,160 points)
edited Jul 17 by Peter Minarik

Improved solution

I tried to improve xDELx's solution and came up with this. You can see the whole project here.

Checked.hpp

This is the core of the project. This does the validation of the values and throwing of exceptions if needed.

A few important notes here:

  1. I wanted this class to seamlessly stand in for numeric types. Hence the usage of template argument, cast operator and assignment operator overloading.
  2. Because of the usage of templates, the class cannot be separated to header (.h) and source (.cpp) file, everything has to be kept in a single file (hence the extension .hpp).
  3. I didn't want to have the (somewhat) clumsy get/set operators, so made sure when this class is used, it can be changed, but cannot be reassigned. For this, the assignment and move assignment operators got removed.
  4. _min and _max do not need to changed, so they are marked constant.
  5. set() and get() are kept, but they are not necessary if someone wants to see fewer functions, their functionality can be moved into the overloaded operators.
#pragma once

#include "OutOfBoundsException.hpp"
#include "ValueNotSetException.hpp"
#include "ValueTooLargeException.hpp"

template<typename T> class Checked
{
private:
    const T _min;
    const T _max;
    T _actual;
    bool _isValid;
    
public:
    Checked(T actual, T min, T max) :
        _min(min),
        _max(max)
    {
        if (min > max)
            throw ValueTooLargeException(min, max, "min cannot be larger than max.");

        set(actual);
    }
    
    Checked & operator=(const Checked & other) = delete; // Do not allow assignment
    
    Checked & operator=(Checked && other) = delete; // Do not allow move assignment
    
    bool isValid() const { return _isValid; }
    
    T getMin() const { return _min; }
    
    T getMax() const { return _max; }
    
    T get() const { return _isValid ? _actual : throw ValueNotSetException("no valid value have been provided."); }
    
    void set(T value)
    {
        if (_min <= value && value <= _max)
        {
            _actual = value;
            _isValid = true;
        }
        else
        {
            throw OutOfBoundsException(value, _min, _max, "trying to set an invalid value.");
            _isValid = false;
        }
    }
    
    Checked & operator=(T value)
    {
        set(value);
        return *this;
    }
    
    operator T() const { return get(); }
};

Avatar

After introduction of the Checked class, the Avatar becomes really simple. One just needs to define the attributes and the Checked class takes care of their behaviour.

Avatar.h

#pragma once

#include "Checked.h"

class Avatar
{
private:
    const std::string _name;
    static std::string visualiseAttribute(int value, int min, int max);

public:
    Checked<int> strength;
    Checked<int> speed;
    Checked<int> agility;

    Avatar(std::string name, int strength, int speed, int agility);
    void display();
};

Avatar.cpp

#include "Avatar.h"

#include <iostream>
#include <sstream>

Avatar::Avatar(std::string name, int strength, int speed, int agility) :
    _name(std::move(name)),
    strength(strength, 1, 100),
    speed(speed, 1, 100),
    agility(agility, 1, 100)
{
}

void Avatar::display()
{
    std::cout << "Avatar: " << _name << std::endl;
    std::cout << "\tStrength: " << strength << ' ' << visualiseAttribute(strength, 1, 100) << std::endl;
    std::cout << "\tSpeed:    " << speed << ' ' << visualiseAttribute(speed, 1, 100) << std::endl;
    std::cout << "\tAgility:  " << agility << ' ' << visualiseAttribute(agility, 1, 100) << std::endl;
}

std::string Avatar::visualiseAttribute(int value, int min, int max)
{
    std::stringstream ss;
    for (int i = min; i <= max; i++)
        ss << (i <= value ? '+' : ' ');

    return ss.str();
}

The Exceptions

They are pretty much what one would expect. Inherit from existing one and add some extra info.

OutOfBoundsException.hpp

#pragma once

#include <stdexcept>
#include <sstream>

template<typename T> class OutOfBoundsException : public std::out_of_range
{
private:
    static std::string createMessage(T actual, T min, T max, const std::string & message = "")
    {
        std::stringstream ss;
        ss << actual << " is out of bounds [" << min << ", " << max << "]: " << message;
        return ss.str();
    }

public:
    OutOfBoundsException(T actual, T min, T max, const std::string & message = "") :
        std::out_of_range(createMessage(actual, min, max, message))
    {
    }
};

ValueTooLargeException.hpp

#pragma once

#include <stdexcept>
#include <sstream>

template<typename T> class ValueTooLargeException : public std::out_of_range
{
private:
    static std::string createMessage(T actual, T max, const std::string & message = "")
    {
        std::stringstream ss;
        ss << actual << " cannot exceed " << max << "]: " << message;
        return ss.str();
    }

public:
    ValueTooLargeException(T actual, T max, const std::string & message = "") :
        std::out_of_range(createMessage(actual, max, message))
    {
    }
};

ValueNotSetException.hpp

#pragma once

#include <stdexcept>

class ValueNotSetException : public std::runtime_error
{
public:
    ValueNotSetException(const std::string & message = "") :
        std::runtime_error(message)
    {
    }
};

Test The Application

One can test the behaviour with some simple code in the main, such as:

#include "Avatar.h"

#include <iostream>

int main()
{
    try
    {
        Avatar avatar1("Avatar 1", 10, 15, 17);
        //avatar1.setStrength(0); // Out of bounds
        //avatar1.setSpeed(101); // Out of bounds
        //avatar1.agility = Checked<int>(16, 15, 17); // Cannot use deleted function
        avatar1.display();

        Avatar avatar2("Avatar 2", 5, -3, 8); // Out of bounds in constructor
        avatar2.display();
    }
    catch (OutOfBoundsException<int> & outOfBoundsException)
    {
        std::cerr << "Exception caught: " << outOfBoundsException.what() << std::endl;
    }

    return 0;
}
Welcome to OnlineGDB Q&A, where you can ask questions related to programming and OnlineGDB IDE and and receive answers from other members of the community.
...