Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed Exception copy constructor, expanded what() #755

Merged
merged 1 commit into from
Nov 18, 2019
Merged

Conversation

dkeeney
Copy link

@dkeeney dkeeney commented Nov 17, 2019

While fixing the copy constructor on the Exception object I expended the what function to also include the source file and line number with the error message.

I added a unit test in htm/types/ExceptionTest.cpp

@dkeeney
Copy link
Author

dkeeney commented Nov 17, 2019

Fix for issue #753

@dkeeney
Copy link
Author

dkeeney commented Nov 17, 2019

Note: there is a field in the Exception object for the stackTrace which is not implemented. It is a cool idea but hard to implement in all platforms/compilers. Lets leave that unimplemented for now.

Copy link
Member

@breznak breznak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this after me, Dave 👍
But I think there's still a new issue introduced, see below.

Exception(filename_, lineno_, copy.getMessage(), stackTrace_);

Exception(const Exception &copy) : std::runtime_error("") {
filename_ = copy.filename_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for fixing this!

@@ -158,6 +163,8 @@ class Exception : public std::runtime_error { //TODO rename to NTAException to b
UInt32 lineno_;
mutable std::string message_; //mutable bcs modified in getMessage which is used in what() but that needs be const
std::string stackTrace_;
mutable std::string what_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need what_ to be a class member? it's not part of the constructors, and only used by what()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The what() function is an override of the std::exception so we cannot change its signature. It returns a char*. This must point to something that will have a scope that last at least as long as the Exception class instance. If it is a local variable it goes away before it can be returned. So that is why I made it a class member.

@@ -103,7 +104,11 @@ class Exception : public std::runtime_error { //TODO rename to NTAException to b
* @retval [const Byte *] the exception message
*/
virtual const char *what() const noexcept override {
return getMessage();
try {
what_ = "Exception: " + Path::getBasename(filename_) + "(" + std::to_string(lineno_) + ") message: " + message_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you extend this for the logic in getMessage()? Otherwise, this won't properly work with streaming operators<<, I think.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When 'what_' is computed the stream no longer exists. Let me explain...

When an exception is thrown, it is copied. Streams cannot be copied. So in the copy constructor, getMessage( ) is called. This will combine the streamed fields into message_ string and clear the stream. The new instance of Exception will have a new instance of the stringstream ss_ which will be empty. It is this copy of Exception that is referenced in the try/catch.

So, when "what_" is computed all of the streamed info is already in the message_. I could call getMessage( ) but it would just return message_.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an exception is thrown, it is copied. Streams cannot be copied. So in the copy constructor, getMessage( ) is called.

thanks for this explanation! 👍

NTA_THROW << "This msg";
} catch (const Exception &e) {
EXPECT_STREQ(e.getMessage(), "This msg");
EXPECT_STREQ(e.what(), "Exception: ExceptionTest.cpp(52) message: This msg");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok here, but try e.what() before the e.getMessage(), see my comment above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order will make no difference because e.getMessage( ) was already called once in the copy constructor so message_ already contains the streamed data as explained above.

Oh, and I did try both ways to confirm.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just discovered a situation where the copy would not occur.
If the Exception object in the try block ends up on the stack in the same place in the catch block then the copy does not occur.

This happens in the other two test cases which I had not tried until this morning. If the NTA_THROW macro is used, it is inside an 'if' block so its stack location changed thus it forces the copy, but not in the other two test cases.

So, you are right, I still need the call to getMessage( ) when creating what_.

You already merged so I will include that change in my next PR which is about ready to be pushed.

@breznak breznak merged commit 288dceb into master Nov 18, 2019
@breznak breznak deleted the Exception_copy branch November 18, 2019 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants