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.
Related
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…
I am trying to create a custom exception class, throw it, and display the error message, but I am doing something wrong, causing the exception to not get thrown and the message to not get printed.
Here's the exception class:
class UnbalancedParenthesesException : public std::exception {
int line_number {0};
public:
UnbalancedParenthesesException(int line_number) :
line_number { line_number }
{}
virtual const char* what() const throw() {
std::string exception_message =
"Papentheses imbalance at line " + std::to_string(line_number) + "\n";
return exception_message.c_str();
}
};
I am trying totry/throw/catch as follows:
void handle_closed_paren(int line_number) {
try {
if (definitely_unbalanced()) {
throw UnbalancedParenthesesException(line_number);
}
} catch (const UnbalancedParenthesesException& e) {
std::out << e.what() << "\n";
}
There is nothing pertinent to this error in the console.
Thanks in advance.
Your what() method is creating a local std::string variable and then returning a pointer to its internal data, which will be left dangling when the std::string goes out of scope and is destroyed when what() exits.
You need to store your error message in a std::string that is a member of the class so it does not go out of scope prematurely. Fortunately, std::exception already has an internal std::string for that purpose. So, instead of formatting the error message in what() itself, you should format it in your derived constructor and pass it to the base class constructor, letting the base what() method return it as-is:
class UnbalancedParenthesesException : public std::exception
{
int mLineNumber;
public:
UnbalancedParenthesesException(int line_number) : std::exception("Parentheses imbalance at line " + std::to_string(line_number)), mLineNumber(line_number) {}
// optional, if the catcher needs access to the value
int lineNumber() const { return mLineNumber; }
};
Your program has undefined behaviour as you are returning the result of c_str() on a std::string that goes out of scope. Anything could happen.
Beyond that, if you're not seeing an exception then one was not thrown, probably because the result of definitely_unbalanced() is falsey.
Step through your program using your debugger.
I'm trying to understand the behavior of exceptions in c++.
I wrote the following code:
class A{
public:
A(){
};
~A(){
cout<<"hello";
};
};
int exceptionTest(){
throw "blablabla";
};
int main(){
A sd;
int test = exceptionTest();
return 0;
}
I've noticed that in this case the distructor gets called even though no one caught the exception.
If I change the "main" code to:
int main(){
A* sd = new A();
int test = exceptionTest();
return 0;
}
The distructor will not be called.
Can anyone please tell me what is the reason for the different behavior?
Thanks,
Li
The fact that you are throwing an exception is irrelevant here. In your first example, sd is an object that exists on the stack. When execution exits its scope, for whatever reason, it gets destroyed. In the second example, sd is a pointer to an object that was explicitly allocated using new. This object will not be destroyed until that pointer is passed to delete; since you never do so, your program is currently leaking it.
The standard has the following to say on the matter:
-9- If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementation-defined.
So your compiler performs stack unwinding (invoking destructors of locals), others may not. For example, with G++ or codepad.org, this program will not output "hello".
Dynamically allocated objects are not destroyed until you explicitly destroy them (with delete or such). In particular, if an exception occurs in the meantime, code may never reach the deallocation statement.
Local variable destructors are called automatically, as soon as the variable is out of scope.
Destructors are never called on pointers, so you must call it yourself.
I've noticed that in this case the distructor gets called even though no one caught the exception.
That's exactly what to expect.
This mechanism is a RAII consequence that makes you "sure" that resources will be freed even if there is an exception. For example :
class File
{
public:
File( const std::string filename ) : file_handler(file_open( filename )) { } // whatever the implementation
~File() { file_close(file_handler); }
private:
FileHandler file_handler;
};
void test(){ throw "This is a test"; }
int main()
{
File file("test.txt");
test();
return false;
}
You're assured that the file will be closed even with the throw. So if you use RAII to manage your resources.
That's because when the exception is thrown, until it get catch, it goes back in the call stack and if there is no catch the local objects are destroyed the way they would be if we got out of scope.
This is not really an answer, but I might clarify the behavior, in case of RAII mechanism, that I understood from the other answer and Mike's comments.
#include <iostream>
class Bar
{
public:
Bar() { std::cout << "Bar constructor" << std::endl; }
~Bar() { std::cout << "Bar destructor" << std::endl; }
};
void foo()
{
throw("Exception");
}
int main()
{
// Variation, add { to create a new scope
Bar bar;
foo();
// Variation : }
return 0;
}
Using g++, this code, where the exception is not catched will output the following:
Bar constructor
terminate called after throwing an instance of 'char const*'
Aborted
Meaning that g++ does not unwind the stack (or let go the variable out of scope, if I understand the "variant" correctly), so the destructor is not called.
However, if you catch the exception:
#include <iostream>
class Bar
{
public:
Bar() { std::cout << "Bar constructor" << std::endl; }
~Bar() { std::cout << "Bar destructor" << std::endl; }
};
void foo()
{
throw("Exception");
}
int main()
{
try
{
Bar bar;
foo();
}
catch (...)
{
// Nothing here
}
return 0;
}
then the output will be
Bar constructor
Bar destructor
and you recover the correct behavior.
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.
The C++ Standard, paragraph 15.1.4 sais:
The memory for the temporary copy of the exception being thrown is allocated in an unspecified way, except as noted in 3.7.3.1. The temporary persists as long as there is a handler being executed for that exception.
I'm wondering why this code crashes (I know that it's not best practice):
class magicException
{
private:
char* m_message;
public:
magicException(const char* message)
{
m_message = new char[strlen(message) + 1];
strcpy(m_message, message);
}
~magicException()
{
cout << "Destructor called." << endl;
delete[] m_message;
}
char* getMessage()
{
return m_message;
}
};
void someFunction()
{
throw magicException("Bang!");
}
int main(int argc, char * argv[])
{
try
{
someFunction();
}
catch (magicException& ex)
{
cout << ex.getMessage() << endl;
}
return 0;
}
Specifically, the destructor of the thrown magicException object gets called before the catch block. If I however add a copy constructor to my class:
magicException(const magicException& other)
{
cout << "Copy constructor called." << endl;
m_message = new char[strlen(other.m_message) + 1];
strcpy(m_message, other.m_message);
}
Then the code works, the destructor gets called at the expected place (the end of the catch block), but interestingly the copy constructor still doesn't get called. Is it optimized away by the compiler (Visual C++ 2008 with optimizations turned off), or am I missing something?
Specifically, the destructor of the
thrown magicException object gets
called before the catch block.
Yes, as your quote from the standard says, a copy is taken by the compiler, and the original (probably) discarded. Your problem is the lack of a copy constructor in your original code. However, a C++ compiler is allowed to remove (or add) copy constructor calls in all sorts of situations, including this one.