Reputation: 2594
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?
Upvotes: 3
Views: 988
Reputation: 85371
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) { ... }
.
Upvotes: 2
Reputation: 307
This answer is really just piecing together the things already mentioned by @Marek R and @churill.
In the comments you say you
std::exception
, or use std::exception::what()
.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.
Upvotes: 0
Reputation: 992
@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;
};
}
Upvotes: 0
Reputation: 11340
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
.
Upvotes: 2
Reputation: 37900
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;
};
}
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;
};
}
Upvotes: 4