3
\$\begingroup\$

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.

\$\endgroup\$
4
  • 3
    \$\begingroup\$ "I want to make my own custom exception handling" Why? \$\endgroup\$
    – Mast
    Jan 25 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
    Jan 25 at 18:35
  • 1
    \$\begingroup\$ You're definitely using a lot of overrides for it. \$\endgroup\$
    – Mast
    Jan 25 at 18:44
  • \$\begingroup\$ @Mast I am sorry, could you elaborate more on it ? \$\endgroup\$
    – Widok
    Jan 25 at 19:15
3
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you very much, for your time! When it comes to the enum names should I go for "FailedToInitBrakes" ? \$\endgroup\$
    – Widok
    Feb 7 at 16:05
  • 1
    \$\begingroup\$ Yes that enum seems fine. \$\endgroup\$
    – Edward
    Feb 7 at 18:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.