Throwing non-const temporaries by reference - c++

Is there any problem with throwing an object constructed on the stack in a try-block by non-const reference, catching it and modifying it, then throwing it by reference to another catch block?
Below is a short example of what I'm refering to.
struct EC {
EC(string msg) { what = msg; }
string where;
string what;
void app(string& t) { where += t; }
string get() { return what; }
};
try {
try {
try {
EC error("Test");
throw error;
}
catch (EC& e) {
e.app("1");
throw e;
}
}
catch (EC& e) {
e.app("2");
throw e;
}
}
catch (EC& e) {
e.app("3");
cout << e.where << endl;
cout << e.get() << endl;
}
Is it possible that this could cause e.what to contain junk, but e.where to remain intact? For example:
e.where is "123"
e.get() returns a lot of garbage data, until it happens to hit a null byte.

There's no such thing as "throwing by reference". It is simply impossible. There's no syntax for that. Every time you try to "throw a reference", a copy of the referenced object is actually thrown. Needless to say, there are no attempts to throw by reference in your code.
It is possible to catch a previously thrown exception by reference (even by a non-const one) and modify the temporary exception object through it. It will work. In fact, you can re-throw the now-modified existing exception object instead of creating a new one. I.e. you can just do
throw;
instead of
throw e;
in your catch clauses and still get the correctly behaving code, i.e. the original object (with modifications) will continue its flight throgh the handler hierarchy.
However, your code is ill-formed at the
e.app("1");
call (and other calls to app) since the parameter is non-const reference. Change the app declaration to either
void app(const string& t) { where += t; } // <- either this
void app(string t) { where += t; } // <- or this
for it to compile.
Otherwise, you code should work fine. You are not supposed to get any garbage from get(). If you do, it must be either a problem with your compiler or with your code that you don't show.

Related

Why does an exception thrown in a constructor fully enclosed in try-catch seem to be rethrown?

Considering this silly looking try-catch chain:
try {
try {
try {
try {
throw "Huh";
} catch(...) {
std::cout << "what1\n";
}
} catch(...) {
std::cout << "what2\n";
}
} catch(...) {
std::cout << "what3\n";
}
} catch(...) {
std::cout << "what4\n";
}
its output will surely be (and is) what1, because it will be caught by the closest matching catch. So far so good.
However, when I try to create a constructor for a class that tries to initialise a member via member initialiser list (which will result in an exception being raised) like so:
int might_throw(int arg) {
if (arg < 0) throw std::logic_error("que");
return arg;
}
struct foo {
int member_;
explicit foo(int arg) try : member_(might_throw(arg)) {
} catch (const std::exception& ex) { std::cout << "caught1\n"; }
};
int main() {
try {
auto f = foo(-5);
} catch (...) { std::cout << "caught2\n"; }
}
The output of the program is now:
caught1
caught2
Why is the exception being rethrown here (I assume that it is, otherwise why would two catches fire?)? Is this mandated by the standard or is it a compiler bug? I am using GCC 10.2.0 (Rev9, Built by MSYS2 project).
cppreference has this to say about a function-try-block (which is what we have here):
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw.
So there we have it. Your exception is automatically rethrown when the catch on the constructor's member initialization list exits. I guess the logic is that your constructor is deemed to have failed so (after the exception handler in the constructor performs any cleanup, perhaps) the exception is automatically propagated to the caller.
While the other answer gives a great official explanation, there is also a really intuitive way to see why things have to behave this way: Consider the alternative.
I've replaced the int with a string to make the issue obvious, but the same principle applies with arithmetic types as well.
std::string might_throw(const std::string& arg) {
if (arg.length() < 10) throw std::logic_error("que");
return arg;
}
struct foo {
std::string member_;
explicit foo(const std::string& arg) try : member_(might_throw(arg)) {
} catch (const std::exception& ex) { std::cout << "caught1\n"; }
};
int main() {
try {
auto f = foo("HI");
std::cout << f.member_ << "\n"; // <--- HERE
} catch (...) { std::cout << "caught2\n"; }
}
What would be supposed to happen if the exception did not propagate?
Not only did arg never make it to member, but the string's constructor never got invoked at all. It's not even default constructed. Its internal state is completely undefined. So the program would be simply broken.
It's important that the exception propagates in such a way to avoid messes like this.
To pre-empt the question: Remember that the reason initializer lists are a thing in the first place is so that member variables can be initialized directly without having their default constructor invoked beforehand.

Throwing bad_exception when calling current_exception()

Link https://en.cppreference.com/w/cpp/error/current_exception provides the following description of current_exception():
If called during exception handling (typically, in a catch clause), captures the current exception object and creates an std::exception_ptr that holds either a copy or a reference to that exception object (depending on the implementation).
...
If the implementation of this function requires copying the captured exception object and its copy constructor throws an exception, the returned pointer will hold a reference to the exception thrown. If the copy constructor of the thrown exception object also throws, the returned pointer may hold a reference to an instance of std::bad_exception to break the endless loop.
I am trying to find out if the implementation of current_exception() in GCC7 copies captured exception object, or just returns the reference to an already existing object. So far, I think that GCC implements the second case. I've tried to check it by executing the following code:
class my_copy_exception : public exception
{
public:
my_copy_exception () : exception () {}
my_copy_exception (const my_copy_exception& other) :
exception(other)
{
throw my_copy_exception();
}
const char* what () const throw() {return "my_copy_exception";}
};
int main()
{
try
{
throw my_copy_exception();
}
catch (const exception& e)
{
cout << e.what() << endl;
exception_ptr eptr = current_exception();
try
{
rethrow_exception(eptr);
}
catch(const std::exception& en)
{
cout << en.what() << endl;
exception_ptr eptrn = current_exception();
cout << (eptr == eptrn) << endl;
}
}
}
It produces the following output:
my_copy_exception
my_copy_exception
1
Whether it is possible to claim that there is no copying of the exception object? If not, how to make current_exception() throw bad_exception?
The good thing about open source software like GCC 7 is that, rather than attempt to reverse engineer what it probably is doing, you can simply go and look at the source code to see exactly what it is doing.
In the case of GCC 7.4, the implementation of std::current_exception() can be found in libstdc++, more specifically, in libsupc++/eh_ptr.cc line 177:
std::exception_ptr
std::current_exception() noexcept
{
__cxa_eh_globals *globals = __cxa_get_globals ();
__cxa_exception *header = globals->caughtExceptions;
if (!header)
return std::exception_ptr();
// Since foreign exceptions can't be counted, we can't return them.
if (!__is_gxx_exception_class (header->unwindHeader.exception_class))
return std::exception_ptr();
return std::exception_ptr(
__get_object_from_ambiguous_exception (header));
}
The fist couple of lines here just fetch the currently active exception. If there is no active exception or the active exception did not come from this C++ runtime, then it returns an empty exception_ptr (see here, here, here, here, here, here, and here for the details on how these checks operate). If there is an active exception that did come from the C++ runtime, it then gets itself a pointer to the active exception object and constructs an exception_ptr. The exception_ptr constructor it uses simply increments the reference counter of the exception object.
Thus, it would seem that libstdc++ exceptions are reference-counted and a copy is never made in the libstdc++ implementation of std::current_exception() for GCC 7, which is in accordance with the requirements of the specification and seems to match your observations…

Is this use of c_str with exception undefined behavior?

I've seen several similar snippets of code that looked like this:
struct MyExcept : std::exception {
explicit MyExcept(const char* m) noexcept : message{m} {}
const char* what() const noexcept override {
return message;
}
const char* message;
};
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept{error.c_str()};
}
int main() {
try {
foo();
} catch (const MyExcept& e) {
// Is this okay?
std::cout << e.message << std::endl;
}
}
In the line following the comment Is this okay?, we read the c-style string that was allocated in the foo function using std::string. Since the string is destructed with stack unwinding, is this undefined behavior?
If it's indeed undefined behavior, what if we replace the main function with this one?
int main() {
foo();
}
Since there is no catch, the compiler is not forced to unwind the stack, and yet output the result of what() in the console and abort the program. So is it still undefined behavior?
Yes, that's undefined behavior. You are working with a dangling pointer.
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept{error.c_str()};
} // << error goes out of scope here and so does the pointer returned
// from c_str()
Since there is no catch, the compiler is not forced to unwind the stack, and yet output the result of what() in the console and abort the program. So is it still undefined behavior?
Since the default implementation will use std::terminate and in turn calling std::abort() this may be still undefined behavior because most of the standard handler implementations will try to dereference what().
You can install your own handlers though to avoid that.
Your first snippet has undefined behavior. [exception.ctor]/1:
As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this section, called stack unwinding.
Here, the destructor or error is called, causing the c_str() to become a dangling pointer. Later dereferencing it, when you use std::cout for instance, is undefined behavior.
Your second snippet is perfectly fine. There is no reason why it would be undefined behavior. You never actually call what, or do anything else that might result in undefined behavior. The only thing not defined by the Standard is if stack unwinding happens or not, [except.terminate]/2:
In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std​::​terminate() is called.
As others stated, the code has undefined behavior, since the pointer assigned to message is left dangling.
std::runtime_error already provides a solution to this issue. Call its constructor that takes a std::string as input, and don't override what() at all:
struct MyExcept : std::runtime_error {
explicit MyExcept(const std::string & m) noexcept : std::runtime_error(m) {}
};
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept(error);
}
int main() {
try {
foo();
}
catch (const MyExcept& e) {
std::cout << e.what() << std::endl;
}
}
std::runtime_error has an internal std::string whose data what() returns by default, thus avoiding the dangling issue.

How to forward a thrown expression from one catch block to another?

I am writing a program in C++ which handles errors via throwing strings (either std::string or const char*), like so :
if (/* failure condition 1 */)
throw std::string("Error 1 : ...") + std::to_string(foo);
if (/* failure condition 2 */)
throw "Error 2 : ...";
Then I would catch them this way :
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
std::cerr << ":( " << e << std::endl;
} catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
I would like to avoid writing the same line twice, by forwarding the string from the first catch block to the second one :
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
throw e.c_str(); // Does not work
} catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
However this code won't work, the thrown char const* simply won't be catched.
Is there a way to do this?
Thanks :)
No, you cannot do this.
[C++14: 15.1/1]: When an exception is thrown, control is transferred to the nearest handler with a matching type (15.3); “nearest” means the handler for which the compound-statement or ctor-initializer following the try keyword was most recently entered by the thread of control and not yet exited.
You would have to nest entire try/catch pairs:
try {
try {
thisIsLikelyToThrowErrors();
}
catch (const std::string& e) {
throw e.c_str(); // Does not work
}
}
catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
Ew!
But now you have another problem, in that the std::string e dies after you've thrown its C-string buffer (it'd stay alive if you re-threw it). Probably try to avoid this in general, eh?
Throw actual exceptions, instead.
You can handle the common setup in an independent function externally, calling it from your catches clauses.
In your example, you can do:
inline void HandleStringyException(const char* excStr)
{
//do something
}
Then:
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
HandleStringyException(e.c_str());
} catch (const char* e) {
HandleStringyException(e);
}
P.S. It should be mentioned that exceptions are better to be handled polymorphically, and in order to do so, the objects thrown should be chosen wisely. As mentioned by others, throwing objects like strings and pointers is a bad practice, and generally you'd like to throw objects derived from std::exception. Sometimes, however, the throwing code is not under your control and you have to make the best of it. I think that's a different discussion.
There is a simple solution to your actual problem:
Throw only std::string or only const char*.
Or even better throw something derived from std::exception

C++ get description of an exception caught in catch(...) block

can I get description of an exception caught by
catch(...)
block? something like .what() of std::exception.
There is one trick you might be able to use:
catch(...) {
handle_exception();
}
void handle_exception() {
try {
throw;
} catch (const std::exception &e) {
std::cout << e.what() << "\n";
} catch (const int i) {
std::cout << i << "\n";
} catch (const long l) {
std::cout << l << "\n";
} catch (const char *p) {
std::cout << p << "\n";
} catch (...) {
std::cout << "nope, sorry, I really have no clue what that is\n";
}
}
and so on, for as many different types as you think might be thrown. If you really know nothing about what might be thrown then even that second-to-last one is wrong, because somebody might throw a char* that doesn't point to a nul-terminated string.
It's generally a bad idea to throw anything that isn't a std::exception or derived class. The reason std::exception exists is to allow everybody to throw and catch objects that they can do something useful with. In a toy program where you just want to get out of there and can't even be bothered to include a standard header, OK, maybe throw an int or a string literal. I don't think I'd make that part of a formal interface. Any exceptions you throw are part of your formal interface, even if you somehow forgot to document them.
That block might catch an int, or a const char*, or anything. How can the compiler possibly know how to describe something when it knows nothing about it? If you want to get information off an exception, you must know the type.
If you know you only throw std::exception or subclasses, try
catch(std::exception& e) {...e.what()... }
Otherwise, as DeadMG wrote, since you can throw (almost) everything, you cannot assume anything about what you caught.
Normally catch(...) should only be used as the last defense when using badly written or documented external libraries. So you would use an hierarchy
catch(my::specialException& e) {
// I know what happened and can handle it
... handle special case
}
catch(my::otherSpecialException& e) {
// I know what happened and can handle it
... handle other special case
}
catch(std::exception& e) {
//I can at least do something with it
logger.out(e.what());
}
catch(...) {
// something happened that should not have
logger.out("oops");
}
Since C++11 you can capture the current exception with a pointer:
std::exception_ptr p; // default initialization is to nullptr
try {
throw 7;
}
catch(...)
{
p = std::current_exception();
}
This behaves like a smart pointer; so long as there is at least one pointer pointing to the exception object it is not destroyed.
Later (maybe even in a different function) you can take action in a similar way to the current top answer:
try {
if ( p )
std::rethrow_exception(p);
}
catch(int x)
{
}
catch(std::exception &y)
{
}
How we have our exceptions implemented is that, we have our own Exception classes, that are all derived from std::exception..
Our exceptions will contain Exception message, Function name, File name and line where exceptions are generated. These are all useful not just to show the Messages but also can be used for logging which helps to diagnose the Exception quite easily. So, we get the entire information about the Exceptions generated.
Remember exceptions are for us to get information about what went wrong. So, every bit of information helps in this regard..
Quoting bobah
#include <iostream>
#include <exception>
#include <typeinfo>
#include <stdexcept>
int main()
{
try {
throw ...; // throw something
}
catch(...)
{
std::exception_ptr p = std::current_exception();
std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
}
return 1;
}