2

For my current project I am regularly defining custom exceptions that I want to catch.

However, I am struggling with deciding what the best structure is for my code regarding the definition of these exceptions.

Initially, I just defined the exceptions nested inside my classes that throw them, like so:

class Foo {
   struct CustomException : public std::runtime_error {
   }
   void TrySomething() {
     throw CustomException();
   }
};

However, now I am using template classes, I cannot access this exception without providing a template argument. That is, I cannot do:

template class <T>
class Foo {
   struct CustomException : public std::runtime_error {
   }
   void TrySomething() {
     throw CustomException();
   }
};

void SomeFunction() {
  try {
    Foo foo;
    foo.TrySomething();
  }
  catch (const Foo::CustomException& exc) {
  }
}

Because Foo without a template argument is not a class. I'm trying to figure out what the cleanest solution to this is (with the option of reworking the rest of the project to follow the chosen standard).

Options that come in mind:

  • Make an interface for Foo and put the exception class in there (this seems a bit excessive, making an interface just for this reason)
  • Put the exception in a namespace I have made for Foo specifically
  • Put the exception 'free' around Foo (but I am guessing in a project namespace, which I don't have for my project right now, but I have read is good practise anyway)
  • Put all exceptions in a single file, in a namespace or struct
  • Make one 'Exceptions' namespace, and extend this whenever I want to add one (in the class file that throws the exception)
Charlee
  • 23
  • 4

2 Answers2

0

If your exception genuinely is inextricably linked to the template class, but is independent of a particular instantiation of your template, then using a pattern like

struct FooBase
{
   struct FooBaseException : public std::exception
   {
   };
};

template class <T> 
struct Foo : FooBase
{
};

is one way. Empty base class optimisations will apply, and conceptually at least you've introduced some relationship between the template instantiations.

Going forward, you might find that common functions appear in the base class.

This sort of pattern was common in the 1990s where templates could cause code bloat, but I see a use for it in this particular instance too.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Thanks!Would you say it is uncommon to put exceptions as nested to the class that 'throws' them? – Charlee Jun 27 '20 at 16:36
0

I don't really get why for you need to do template for your exceptions. I believe that the code will satisfy all your needs. You can either create enums for frequently used exceptions or rise single custom exceptions.

    enum class ErrorCode {
      kErrorOne,
    };
    
    // Helper function to get an explanatory string for error
    inline std::string GetErrorString(ErrorCode error_code) {
      switch (error_code) {
        case ErrorCode::kErrorOne:
          return "Some error";
      }
      return {};
    }

    class CustomException : public std::runtime_error {
     public:
      explicit CustomException(ErrorCode error_code) : std::runtime_error{GetErrorString(error_code)} {}

  CustomException(const std::string& what)
      : std::runtime_error{what} {}    
    };

Also, you can take a look here, to a pretty similar question

sty4
  • 16
  • 3
  • No the question is not about having an error class with a template argument, but a nested error class in the class throwing the error. – Charlee Jun 27 '20 at 16:35