3

I'm trying to learn how to use custom C++ exceptions with template arguments. This is a dummy program that I'm trying to compile unsuccessfully:

#include <string> #include <iostream> template <class T> class MyException : public std::exception { public: T error; MyException(T err) { error = err; }; }; int main(void) { try { std::string err = "String error"; throw MyException<std::string>(err); return 0; } catch (MyException e) { std::cout << e << std::endl; return 1; }; } 

This is the error I get:

<source>: In function 'int main()': <source>:18:9: error: invalid use of template-name 'MyException' without an argument list 18 | catch (MyException e) { | ^~~~~~~~~~~ <source>:18:9: note: class template argument deduction is only available with '-std=c++17' or '-std=gnu++17' <source>:5:7: note: 'template<class T> class MyException' declared here 5 | class MyException : public std::exception { | ^~~~~~~~~~~ <source>:19:22: error: 'e' was not declared in this scope 19 | std::cout << e << std::endl; | ^ 

Could you please help me to fix it for C++11?

0

5 Answers 5

4

You can't catch any template! You can catch only specific type so only specific instance of template. So your code should look like this (quick fix):

#include <string> #include <iostream> template <class T> class MyException : public std::exception { public: T error; MyException(T err) { error = err; }; }; int main(void) { try { std::string err = "String error"; throw MyException<std::string>(err); return 0; } catch (const MyException<std::string>& e) { std::cout << e.what() << std::endl; return 1; }; } 

https://godbolt.org/z/P4czdP

If you need some way to catch all exceptions for this template, you need extra layer of inheritance to introduce common unique parent type for this template:

#include <string> #include <iostream> class MyCommonException : public std::exception {}; template <class T> class MyException : public MyCommonException { public: T error; MyException(T err) { error = err; }; }; int main(void) { try { std::string err = "String error"; throw MyException<std::string>(err); return 0; } catch (const MyCommonException& e) { std::cout << e.what() << std::endl; return 1; }; } 

https://godbolt.org/z/xz8r5a

Sign up to request clarification or add additional context in comments.

3 Comments

This almost looks like the solution I needed. Thanks. But I would want to print "String error", what should I change to get it?
Actually, I'd want to be able to use error, which is a generic object, inside the catch. I suppose that it's not possible.
It is possible. You can add some virtual method in MyCommonException which is able to print info.
2

c++ doesn't have a "template catch", you can only catch actual classes like

catch (MyException<string> e) { ... } 

Luckily std::exception::what() is virtual, so you could catch std::exception by const reference and print the result of what().

catch (std::exception const &e) { std::cout << e.what() << std::endl; return 1; }; 

And override what() in MyException to create the right error message there:

template <class T> class MyException : public std::exception { public: T error; MyException(T err) { error = err; }; const char* what() const noexcept override { // apply logic to create error message } }; 

This way you can catch anything that inherits from std::exception.

4 Comments

Will overloading the operator << for MyException be a bad idea to print the custom messages?
This looks very interesting, but in a not dummy program I would like to do different things if the exception is std::exception or if it is MyException.
@Medicalphysicist In this case you can use another layer of inheritance as seen in Mareks answer :)
@ThePhilomath I don't think it would be useful as you wouldn't be able to use this overload if you catch a std::exception.
2

Instead of answering directly, I will attempt to address the underlying confusion about C++ templates to hopefully prevent more of these issues from arising down the road.

Roughly speaking, the syntax template <...> class MyException ... {}; does NOT introduce anything "tangible" (a type or a value) into the program. There is no type MyException. The compiler instantiates a class from the template each time it encounters a specific syntax MyException<...>. But each instance of MyException<...> is unique for each combination of the template arguments; MyException<A> is distinct from MyException<B>, they might as well be called Bla and Pft - there is nothing in common between them.

So how to make MyException<A> and MyException<B> act in a common way? Well, just like you'd do with two unrelated classes - use inheritance and polymorphism.

// define some common base that is NOT a template class MyException : public std::exception { public: // put any common API's here ... virtual std::string commonWork() = 0; virtual ~MyException() {} }; // now make each template inherit from the common base ... template <class T> class MyExceptionImpl : public MyException { public: T error; MyExceptionImpl(T err) { error = err; } std::string commonWork() override { return ""; } }; 

Now you can catch (MyException const& e) { ... }.

Comments

0

@Marek has already explained that we can't catch template in C++. For your custom messages you can print them like:

#include <string> #include <iostream> template <class T> class MyException : public std::exception { public: T error; MyException(T err) { error = err; }; template<typename U> friend std::ostream& operator <<(std::ostream & , const MyException<U>&); }; template <typename T> std::ostream& operator<<( std::ostream& o, const MyException<T>& e) { return o<<e.error; } int main(void) { try { std::string err = "String error"; throw MyException<std::string>(err); return 0; } catch (const MyException<std::string>& e) { std::cout << e << std::endl; return 1; }; } 

1 Comment

Not sure whether it's a bad idea or not to overload operator << here.
0

This answer is really just piecing together the things already mentioned by @Marek R and @churill.

In the comments you say you

  1. do not want to catch std::exception, or use std::exception::what().
  2. want to print the error field specifically.

This is kind of just re-implementing the what functionality using a custom virtual function, but you could do it like this:

#include <string> #include <iostream> class MyBaseException : public std::exception { public: virtual std::ostream& intoStream(std::ostream& stream) const = 0; }; std::ostream& operator<<(std::ostream& stream, const MyBaseException& e) { return e.intoStream(stream); } template <typename T> class MyException : public MyBaseException { public: T error; MyException(T err) { error = err; }; std::ostream& intoStream(std::ostream& stream) const override { return stream << error; } }; int main(void) { try { std::string err = "String error"; throw MyException<std::string>(err); return 0; } catch (const MyBaseException& e) { std::cout << e << std::endl; return 1; }; } 

Since only class methods can be virtual, if you want to use the << operator, you need a separate virtual function, that can access the error field.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.