I want to make my own custom exception handling and I am curious if I am going the right path, maybe some of you could suggest to me how I could improve it? Or maybe I have went the wrong direction? (Also it would be amazing to know how I could make this piece of code more of a modern C++.)
Here is a simple namespace with classes, which inherits from std::error_category
and std::system_error
.
#include <string>
#include <system_error>
#include <iostream>
namespace CustomException {
class ErrorC : public std::error_category {
using Base = std::error_category;
public:
char const *name() const noexcept
override { return "App error"; }
std::error_condition default_error_condition(int const code) const noexcept
override {
(void) code;
return {};
}
bool equivalent(int const code, std::error_condition const &condition) const noexcept
override {
(void) code;
(void) condition;
return false;
}
bool equivalent(std::error_code const &code, int const condition) const noexcept
override { return Base::equivalent(code, condition); }
std::string message(int const condition) const
override { return "An application error occurred, code = " + std::to_string(condition); }
constexpr ErrorC() : Base{} {}
};
auto app_error_category()
-> ErrorC const & {
static ErrorC the_instance;
return the_instance;
}
enum class BreakErrorCode {
FAILED_INIT_BREAKS = 1,
FAILED_TO_LINK_BREAKS = 2,
};
class BreakError : public std::system_error {
using Base = std::system_error;
public:
BreakErrorCode appErrorCode() const {
return static_cast<BreakErrorCode>(code().value());
}
explicit BreakError(const BreakErrorCode code)
: Base{(int) code, app_error_category()} {
}
BreakError(const BreakErrorCode code, const std::string &description)
: Base{(int) code, app_error_category(), description} {
}
};
enum class EngineErrorCode {
FAILED_INIT_ENGINE = 101,
MOTHER_BOARD_FAILED_LINKING = 102,
};
class EngineError : public std::system_error {
using Base = std::system_error;
public:
EngineErrorCode appErrorCode() const {
return static_cast<EngineErrorCode>(code().value());
}
EngineError(const EngineErrorCode code)
: Base{(int) code, app_error_category()} {
}
EngineError(const EngineErrorCode code, const std::string &description)
: Base{(int) code, app_error_category(), description} {
}
};
enum class ControlErrorCode {
CONTROL_GIMBAL_FAILED = 301,
OBTAIN_CONTROL_AUTHORITY_FAILED = 302,
};
}
Main.cpp, which invokes methods throwing exceptions from previously declared namespace.
using namespace CustomException;
void initEngine() {
throw EngineError(EngineErrorCode::FAILED_INIT_ENGINE, "Failed init engine");
}
void initBreaks() {
throw BreakError(BreakErrorCode::FAILED_INIT_BREAKS, "Failed init breaks");
}
int main() {
try {
initEngine();
initBreaks();
}
catch (const BreakError &error) {
std::cout << error.what() << std::endl;
}
catch (const EngineError &error) {
std::cout << error.what() << std::endl;
}
catch (...) {
std::cout << "Caught something unexpected error" << std::endl;
}
return 0;
}
I would appreciate very much any kind of comments / help.
-
3\$\begingroup\$ "I want to make my own custom exception handling" Why? \$\endgroup\$Mast– Mast ♦2021年01月25日 18:21:23 +00:00Commented Jan 25, 2021 at 18:21
-
\$\begingroup\$ Hey Mast, I probably should rephrase myself, I want to extend system_error with my own classes, which inherits from it, for better readability and re-usability. Am I thinking wrongly here ? \$\endgroup\$Widok– Widok2021年01月25日 18:35:44 +00:00Commented Jan 25, 2021 at 18:35
-
1\$\begingroup\$ You're definitely using a lot of overrides for it. \$\endgroup\$Mast– Mast ♦2021年01月25日 18:44:22 +00:00Commented Jan 25, 2021 at 18:44
-
\$\begingroup\$ @Mast I am sorry, could you elaborate more on it ? \$\endgroup\$Widok– Widok2021年01月25日 19:15:03 +00:00Commented Jan 25, 2021 at 19:15
1 Answer 1
Here are some things that may help you improve your code.
Separate interface from implementation
The interface goes into a header file and the implementation (that is, everything that actually emits bytes including all functions and data) should be in a separate .cpp
file. The reason is that you might have multiple source files including the .h
file but only one instance of the corresponding .cpp
file. In other words, split your existing CustomException
namespace into into a .h
file and a .cpp
file.
Correctly override functions
The code currently contains this as part of class ErrorC
:
bool equivalent(int const code, std::error_condition const &condition) const noexcept
override {
(void) code;
(void) condition;
return false;
}
This is not right for several reasons. First, the standard says that this should return true
if the error is equivalent, but this does not do that. Instead, I'd write:
return default_error_condition(code) == condition;
Better still, omit it, since this is not a pure virtual method in std::error_category
.
Don't create pointless classes
The CustomException
class is doing very little here and I would strongly recommend omitting it and just deriving directly from std::error_category
. See C.120 for further guidance.
Don't use all caps for enum name
ALL CAPS have been traditionally used for macros. To avoid misleading the reader, don't use them if it's not a macro. See ES.9.
Fix the spelling
If the machine has "breaks" it is broken. If it has "brakes" it has the ability to slow down. These homonyms are easily confused, but it's important to make sure you don't have spelling errors, especially in interface code, so that you don't confuse users of your code.
Consider combining error codes
It appears that you are intending to have non-overlapping and unique error codes for each type of error, but that is not assured because the errors are in three different enum
classes. I'd suggest combining them.
Reconsider your classes
If the error codes were combined, as suggested above, then the differentiation among them would be by the error code. Having different classes doing exactly the same thing makes little sense to me. I'd suggest a single error class derived from std::error_category
would be sufficient.
-
\$\begingroup\$ Thank you very much, for your time! When it comes to the enum names should I go for "FailedToInitBrakes" ? \$\endgroup\$Widok– Widok2021年02月07日 16:05:39 +00:00Commented Feb 7, 2021 at 16:05
-
1\$\begingroup\$ Yes that
enum
seems fine. \$\endgroup\$Edward– Edward2021年02月07日 18:53:31 +00:00Commented Feb 7, 2021 at 18:53