Hi, I've been programming as a hobby for a couple of years. I learned from online courses and documentation and I just came across something I don't even know how to explain, I think maybe that's because I'm doing it wrong...
I'm writing a library to handle a deck of cards, with a class "deck" and a class "card". I have a function which transfers some cards* from a vector (pile) to another one (hand), and when the pile doesn't contain enough cards, I throw an exception, so I can catch it and decide what I do then (depending on the game, it could end or I could shuffle the cards again).
It would be simpler return an int instead of throwing it (don't know why I didn't) but I feel like that's not the right way either. How would you do this?
1. return an int value, 1 on success, -1 on failure, -x on specific failure. Typical libraries have a function that you can pass the last error into to get a message back for the user or for yourself. You can make a global enum that has error codes.
2. return a struct that includes a value and a description if there is an error.
3. use a "state" object which you pass into the function that includes information about successes or failures.
4. use an event system (nice for multithreaded programs) that sends a specific message back to the main program with the negative response from the function.
Heck there are probably more options involving exceptions like you're using. My motto in programming is don't judge folks for how they program if it works, even if it pains me when taking over legacy code..
Everyone has a different style, just stay consistent and you'll be fine :)
The only thing you should ever throw is a class derived from std::exception. I know the language allows you to throw other things, but you generally shouldn't.
Deriving classes is good because:
1) It allows you to do a catch() based on a specific type of exception.
2) std::exception is bundled with a what() function which provides a textual explanation of what went wrong.
So the exception basically is your struct that has the error code with text.
Nice, that's almost what I ended up doing. I made a class derived from std::exception and implemented the what() function. I didn't include <stdexcept> though and it works... Should I?
The only thing you should ever throw is a class derived from std::exception. I know the language allows you to throw other things, but you generally shouldn't.
I made a class derived from std::exception and implemented the what() function.
You shouldn't have to re-implement the what() function. You can just use the parent class's version:
1 2 3 4 5 6 7 8
class MyException : public std::exception
{
public:
MyException(const std::string& msg) : exception(msg) {}
// no need to re-implement what() here... it will work fine without it. As long as you
// forward the string 'msg' to the std::exception ctor as I do above.
};
I didn't include <stdexcept> though and it works... Should I?
<stdexcept> defines several common exception types like runtime_error, underflow_error, etc. If you're not using those, you don't need to include that header. I just used it in my example because I was using underflow_error.
> I didn't include <stdexcept> though and it works... Should I?
To use std::exception, you need to #include <exception>
(Or include one of the headers that includes <exception>)
1 2 3 4
> class MyException : public std::exception
> {
> public:
> MyException(const std::string& msg) : std::exception(msg) {}
This won't compile. std::exception does not have a constructor that accepts a std::string
Ideally an exception class derived directly from std::exception should override what() - the (default constructed) base class object's what() would return an empty string. http://en.cppreference.com/w/cpp/error/exception/exception
Favour inheriting the program's exception classes from either std::logic_error or std::runtime_error or from a class derived from one of them.
Virtual inheritance means that when a class derives from your class, your class is no longer responsible for calling the constructor of the class you virtually inherited from; the deriving class is. This way you don't need to duplicate constructors all the way through the class hierarchy.
As an added benefit, it enables proper multiple inheritance (which is pretty controversial, by the way).
In the case of exceptions, this means you don't need to channel the message through each level of inheritance - the most derived class can call the base exception's constructor directly, and the rest of the ctor calls are to default ctors.
> Favour inheriting from exception base classes virtually
>> Is that so I can catch(std::exception) and what() will still work?
Yes, yes.
We want exception derived classes to be catchable by handlers for exception base classes.
When multiple inheritance is involved, inheriting virtually avoids the ambiguity in conversion from a derived class to a base class. (This ambiguity would result in an exception of a derived class type not being caught by the handler for the exception of the base class type.)
This small program illustrates the difference between the two:
clang++ -std=c++14 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors main.cpp -lsupc++ && ./a.out
this (non-virtual inheritance from exception base class) is bad:
caught some unknown error
------------------------------
this (virtual inheritance from exception base class) is what we want:
caught std::exception what: 'unable to access network => failed to open file'