Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why doesn't my simple custom exception's what() return any text?

30 views
Skip to first unread message

Mark

unread,
Oct 12, 2022, 5:06:48 AM10/12/22
to
I have this tiny eg:
```cpp
// g++ -o t t.cpp && ./t
#include <exception>
#include <iostream>

using namespace std;

class Error : public std::exception {
public:
Error(const char* message) : message_(message_) {}
const char* what() const throw() { return message_; }
private:
char* message_;
};

class Value {
public:
virtual ~Value() {}
virtual size_t size() const {
throw Error("scalars don't have a size");
}
};

int main() {
auto v = new Value();
try {
cout << "t " << v->size() << endl;
} catch (Error& err) {
cout << "error:" << err.what() << endl;
return EXIT_FAILURE;
}
}
```
When I build & run it the output is:
```
t error:
```
with no following newline.

What am I doing wrong?

Ralf Fassel

unread,
Oct 12, 2022, 5:17:10 AM10/12/22
to
* Mark <m.n.sum...@googlemail.com>
| class Error : public std::exception {
| public:
| Error(const char* message) : message_(message_) {}

Look closely at what you're initializing message_ with.

R'

Mark

unread,
Oct 12, 2022, 5:19:41 AM10/12/22
to
Thank you! (I also had to change the private member to `const char * message_`.)

Ralf Fassel

unread,
Oct 12, 2022, 5:27:04 AM10/12/22
to
* Mark <m.n.sum...@googlemail.com>
Given the const-to-non-const assignment in the CTOR, I was wondering why
this did compile w/o error.
Took me a little while until I spotted it, too ;-)

R'

David Brown

unread,
Oct 12, 2022, 5:57:30 AM10/12/22
to
On 12/10/2022 11:06, Mark wrote:
> I have this tiny eg:
> ```cpp
> // g++ -o t t.cpp && ./t
<snip>
>
> What am I doing wrong?

You forgot the critical compiler flags:

g++ -Wall -Wextra -O2 -o t t.cpp

Running your compiler without warnings enabled is like driving without
seatbelts and airbags - you might be lucky, but there might be a mess
that is hard to identify. Running it without optimisation is like
driving when stuck in first gear. C++ is not designed to be used
without optimising compilers. Use at least -O1 to inline all the "no
code generation" functions that abound in C++, and to enable analysis
for static error checking.

Compiling the Error class with -Wall -Wextra gives

<source>: In constructor 'Error::Error(const char*)':
<source>:5:5: warning: 'Error::message_' is initialized with itself
[-Winit-self]
5 | Error(const char* message) : message_(message_) {}
| ^~~~~
<source>:5:23: warning: unused parameter 'message' [-Wunused-parameter]
5 | Error(const char* message) : message_(message_) {}
| ~~~~~~~~~~~~^~~~~~~
Compiler returned: 0


The tools are there to help, and would have given you the answer
immediately. (But then we'd have missed this Usenet thread!)

Mark

unread,
Oct 12, 2022, 6:32:16 AM10/12/22
to
On Wednesday, October 12, 2022 at 10:57:30 AM UTC+1, David Brown wrote:
> On 12/10/2022 11:06, Mark wrote:
> > I have this tiny eg:
> > ```cpp
> > // g++ -o t t.cpp && ./t
> <snip>
> >
> > What am I doing wrong?
> You forgot the critical compiler flags:
>
> g++ -Wall -Wextra -O2 -o t t.cpp
>
[snip]

Good point. I'm actually using scons so I'll add those now.

Thanks!

daniel...@gmail.com

unread,
Oct 12, 2022, 9:36:45 AM10/12/22
to
On Wednesday, October 12, 2022 at 5:19:41 AM UTC-4, Mark wrote:
> On Wednesday, October 12, 2022 at 10:17:10 AM UTC+1, Ralf Fassel wrote:
> > * Mark <m.n.sum...@googlemail.com>
> > | class Error : public std::exception {
> > | public:
> > | Error(const char* message) : message_(message_) {}
> > Look closely at what you're initializing message_ with.
> >
> Thank you! (I also had to change the private member to `const char * message_`.)

I would suggest instead you use

class Error : public std::runtime_error {
public:
Error(const char* message) : std::runtime_error(message) {}
}

std::runtime_error makes a copy of the message rather than
holding a pointer to it (usually in an internal ref counted string
because copying a std::runtime_error is not allowed to throw.)

Daniel

Mark

unread,
Oct 12, 2022, 11:31:39 AM10/12/22
to
[snip]
> class Error : public std::runtime_error {
> public:
> Error(const char* message) : std::runtime_error(message) {}
> }
>
> std::runtime_error makes a copy of the message rather than
> holding a pointer to it (usually in an internal ref counted string
> because copying a std::runtime_error is not allowed to throw.)
>
> Daniel

Why is that better? Most message strings will be constants "something happened", with some being created dynamically "something happened to " + obj.

Paavo Helde

unread,
Oct 12, 2022, 2:27:10 PM10/12/22
to
Right, and your solution won't work for the dynamically created
messages. Regardless however rare they are, it still won't work. So you
would need to add a second mechanism for handling them, and having two
mechanisms instead of one, without any justification, would be
definitely worse.

In case you come from C or otherwise have strong premature optimization
desires, rest assured that the exception throwing mechanism itself is
typically hundreds of times slower than an error message copy, so there
is no point to try to save on that.


Mark

unread,
Oct 13, 2022, 2:23:47 AM10/13/22
to
Thanks. I've changed to that. It didn't compile at first but then I discovered that I had to change the include from <exception> to <stdexcept>.
0 new messages