Boost version: 1.61, GCC version 6.2.0, C++14
Exceptions inside a boost::async() call seem to lose their type, while it works correctly with std::async. Is there a fundamental difference between std::async and boost::async I'm missing?
The following code works (using std::async):
#include <iostream>
#include <stdexcept>
#include <future>
using namespace std;
struct MyException : std::exception {
const char* what() const noexcept override { return "message"; }
};
int main() {
try {
auto future = std::async(std::launch::async, [] { throw MyException(); });
future.get();
} catch (const MyException& e) {
cout << "MyException caught. Message: " << e.what() << endl;
} catch (const std::exception& e) {
cout << "std::exception caught. Message: " << e.what() << endl;
}
}
and outputs
$ g++ test.cpp -std=c++14 -lpthread -lboost_thread -lboost_system && ./a.out
MyException caught. Message: message
However, the following code (it's the same, just using boost::async instead of std::async) does not work as expected:
#include <iostream>
#include <stdexcept>
#include <boost/thread/future.hpp>
using namespace std;
struct MyException : std::exception {
const char* what() const noexcept override { return "message"; }
};
int main() {
try {
auto future = boost::async(boost::launch::async, [] { throw MyException(); });
future.get();
} catch (const MyException& e) {
cout << "MyException caught. Message: " << e.what() << endl;
} catch (const std::exception& e) {
cout << "std::exception caught. Message: " << e.what() << endl;
}
}
It outputs:
$ g++ test.cpp -std=c++14 -lpthread -lboost_thread -lboost_system && ./a.out
std::exception caught. Message: std::exception
It also doesn't work for boost::future::then continuations:
#include <iostream>
#include <stdexcept>
#define BOOST_THREAD_VERSION 4
#include <boost/thread/future.hpp>
using namespace std;
struct MyException : std::exception {
const char* what() const noexcept override { return "message"; }
};
int main() {
try {
auto future = boost::make_ready_future(3).then([](boost::future<int>) -> int { throw MyException(); });
future.get();
} catch (const MyException& e) {
cout << "MyException caught. Message: " << e.what() << endl;
} catch (const std::exception& e) {
cout << "std::exception caught. Message: " << e.what() << endl;
}
}
outputs:
$ g++ test.cpp -std=c++14 -lpthread -lboost_thread -lboost_system && ./a.out
std::exception caught. Message: std::exception
So the concrete type of the exception seems to be lost when crossing the thread boundary. However, it doesn't seem to be connected to threading, because according to my tests, the same is true for std::launch::deferred (which works fine) and boost::launch::deferred (which loses the exception type).
Related
Today I met a weird behaviour in catching exceptions in C++, could anyone clarify it to me? Code snippe
#include <iostream>
#include <string>
#include <exception>
int main() {
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) {
std::cerr << "std::invalid_argument" << std::endl;
} catch (const std::out_of_range&) {
std::cerr << "std::out_of_range" << std::endl;
} catch (const std::exception&) {
std::cerr << "Caught by ancestor" << std::endl;
} catch (...) {
auto ptr = std::current_exception();
auto type = __cxxabiv1::__cxa_current_exception_type();
std::cerr << type->name() << std::endl;
std::cerr << "..." << std::endl;
}
return 0;
}
Writes to output
St16invalid_argument
...
Environment details
C++ 14, disabled RTTI
Clang 13.1.6 arm64-apple-darwin-21.6.0
macOS Monterey 12.6
I expect exception to be caught on the very first catch block
Upd. Even simplest catch doesn't work for me on the environment
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) { // not caught
std::cerr << "std::invalid_argument" << std::endl;
}
// Example program
#include <iostream>
#include <string>
#include <stdexcept>
void log(const std::exception& e) {
try {
throw e;
}
catch (const std::logic_error& e1) {
std::cout << "logic_error: " << e1.what() << std::endl; // How to get logic_error back once caught?
}
catch (const std::exception& e1) {
std::cout << "exception: " << e1.what() << std::endl;
}
}
int main()
{
try {
throw std::logic_error("sth wrong");
}
catch (const std::exception& e) {
log(e);
}
}
I don't want to add more catch clauses is because I want to have a central place to log detailed exception message, it could be very different across different exceptions.
Is there a way to narrow down the std::exception to derived exception in the catch clause?
You can rethrow the original exception in your log method to preserve its original state. This way everything should work as you would expect it.
Modified log method looks like this:
void log(const std::exception& e) {
try {
throw;
}
catch (const std::logic_error& e1) {
std::cout << "logic_error: " << e1.what() << std::endl;
}
catch (const std::exception& e1) {
std::cout << "exception: " << e1.what() << std::endl;
}
}
See throw expression for more details on how throw e and throw differs in this example.
std::exception::what() is virtual. So you don't need to get back the original type to write the logs. You just have to ensure that what() is redefined for your custom exceptions that inherits std::exception.
This may become:
void log(const std::exception & e)
{
std::cout << e.what() << std::endl;
}
You might use std::rethrow_exception:
void log(std::exception_ptr eptr) {
try {
if (eptr) {
std::rethrow_exception(eptr);
}
}
catch (const std::logic_error& e1) {
std::cout << "logic_error: " << e1.what() << std::endl;
}
catch (const std::exception& e1) {
std::cout << "exception: " << e1.what() << std::endl;
}
}
int main()
{
try {
throw std::logic_error("sth wrong");
}
catch (const std::exception&) { // or even catch (...)
log(std::current_exception());
}
}
You don't have to use only base exceptions provided by the standard library, you can make your own exception hierarchy with the methods you need.
#include <exception>
#include <string>
#include <iostream>
class MyBaseException: public std::exception
{
public:
virtual std::string output() const = 0;
/* Define methods you need here */
};
class MySpecificException : public MyBaseException
{
public:
const char* what() const noexcept override {return "error"; }
std::string output() const override { return "Specific debug output that I want"; }
};
int main(){
try{
throw MySpecificException{};
}catch(const MyBaseException& e){
std::cout << e.output() << '\n';
}catch(const std::exception& e) {
std::cout << e.what() << '\n';
}
}
You can use run time type information to get the type of the execption:
#include <iostream>
#include <string>
#include <stdexcept>
#include <typeinfo>
#if defined(__clang__) || defined(__GNUC__)
#include <cxxabi.h>
#endif
std::string getTypename(const std::exception& e)
{
#if defined(__clang__) || defined(__GNUC__)
// .name() returns a mangled name on gcc and clang
int status;
return abi::__cxa_demangle(typeid( e ).name(), 0, 0, &status);
#else
// visual studio returns a human readable name
return typeid( e ).name();
#endif
}
void log(const std::exception& e) {
std::cout << getTypename( e ) << ": " << e.what() << std::endl;
}
int main()
{
try {
throw std::logic_error("sth wrong");
}
catch (const std::exception& e) {
log(e);
}
}
The getTypename() method may not work on all platforms and you might need to adjust it to your needs. You might want to use boost::core::demangle rather than calling the gcc demangle API directly.
I'm working on a fairly lengthy program, and after running fine for awhile, suddenly I'm getting:
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr
Being new to exception handling, I did some research and found that I would likely get more information by adding the following to my main function:
int main(int argc, char **argv){
try{
//stuff
}
catch(exception const &exc){
cerr << "Caught exception: " << exc.what() << endl;
}
}
The result of this is the following output:
Caught exception: basic_string::substr
This isn't any more useful than the default output; it doesn't tell me anything about the line triggering the core dump (there are many substr calls in my program), the data the substr is attempting to process, etc. Is there a method for displaying information such as this in C++, or is my only option to use a debugger such as gdb?
There are a few ways.
As you said, a debugger - but that won't help you once the code is in production.
Nested exceptions and function try blocks. e.g.:
#include <exception>
#include <stdexcept>
#include <iostream>
#include <sstream>
#include <iomanip>
void bar(std::string& s, int i)
try
{
s.at(i) = 'A';
}
catch(...)
{
std::ostringstream ss;
ss << "error in bar(" << std::quoted(s) << ", " << i << ")";
std::throw_with_nested(std::runtime_error(ss.str()));
}
void foo(std::string& s)
try
{
bar(s, 6);
}
catch(...)
{
std::ostringstream ss;
ss << "error in foo(" << std::quoted(s) << ")";
std::throw_with_nested(std::runtime_error(ss.str()));
}
void stuff()
try
{
std::string s;
foo(s);
}
catch(...)
{
std::throw_with_nested(std::runtime_error("error in stuff()"));
}
void print_exception(std::ostream& os, const std::exception& e, int level = 0)
{
os << std::string(level, ' ') << "exception: " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
} catch(const std::exception& e) {
print_exception(os, e, level+1);
} catch(...) {}
}
int main()
{
try{
stuff();
}
catch(std::exception& e)
{
print_exception(std::cerr, e);
return 127;
}
return 0;
}
sample output:
exception: error in stuff()
exception: error in foo("")
exception: error in bar("", 6)
exception: basic_string::at: __n (which is 6) >= this->size() (which is 0)
You could use boost::stacktrace in place of the above nested exception handling.
http://coliru.stacked-crooked.com/a/f21bd35632a0a036
Why does this program
#include <clang-c/Index.h>
#include <exception>
#include <iostream>
int main() {
try {
throw std::exception("threw");
} catch (const std::exception& e) {
std::cout << e.what() << "\n";
}
CXIndex idx = clang_createIndex(0, 0);
clang_disposeIndex(idx);
return 0;
}
behave as expected, but this one
#include <clang-c/Index.h>
#include <exception>
#include <iostream>
int main() {
CXIndex idx = clang_createIndex(0, 0);
clang_disposeIndex(idx);
try {
throw std::exception("threw");
} catch (const std::exception& e) {
std::cout << e.what() << "\n";
}
return 0;
}
crashes?
More generally, what could a function do that causes subsequent exceptions to not get caught?
Using: visual c++ 10.0 (tried the different /EH flags), clang 3.4 (built with the same)
I have a program throwing an exception that is caught on some configurations (Suse Linux, g++ version 4.4.1) as expected but is obviously not caught on another, here: SunOS 5.10, g++ version 3.3.2. Following is the implementation of my exception class:
CException.hpp:
#ifndef _CEXCEPTION_HPP
#define _CEXCEPTION_HPP
#include <string>
#include <sstream>
#include <exception>
#include <stdlib.h>
#include <iostream>
class CException : public std::exception {
public:
CException();
CException(const std::string& error_msg);
CException( const std::stringstream& error_msg );
CException( const std::ostringstream& error_msg );
virtual ~CException() throw();
const char* what() const throw();
static void myTerminate()
{
std::cout << "unhandled CException" << std::endl;
exit(1);
};
private:
std::string m_error_msg;
};
CException.cpp:
#include "CException.hpp"
#include <string>
#include <sstream>
CException::CException()
{
std::set_terminate(myTerminate);
m_error_msg = "default exception";
}
CException::CException(const std::string& error_msg)
{
std::set_terminate(myTerminate);
m_error_msg = error_msg;
}
CException::CException(const std::stringstream& error_msg)
{
std::set_terminate(myTerminate);
m_error_msg = error_msg.str();
}
CException::CException(const std::ostringstream& error_msg)
{
std::set_terminate(myTerminate);
m_error_msg = error_msg.str();
}
CException::~CException() throw()
{
}
const char* CException::what() const throw()
{
return m_error_msg.c_str();
}
#endif /* _CEXCEPTION_HPP */
Unfortunately, I've not been able to create a simple program to reproduce the issue, but I will try to outline the code.
The exception is thrown in a function foo() in some file Auxiliary.cpp:
std::ostringstream errmsg;
//...
errmsg << "Error occured.";
throw CException( errmsg );
Function foo() is used in the main program:
#include Auxiliary.hpp
//...
int main( int argc, char** argv )
{
try {
//...
foo();
} catch ( CException e ) {
std::cout << "Caught CException" << std::endl;
std::cout << "This is the error: " << e.what( ) << std::endl;
} catch ( std::exception& e ) {
std::cout << "std exception: " << e.what( ) << std::endl;
} catch ( ... ) {
std::cout << "unknown exception: " << std::endl;
}
I can see that the exception isn't caught as the program exits with printing unhandled CException which is defined by myTerminate().
I've tried the -fexceptions option to the GNU compiler without success. The compiler options are actually the same on both systems.
At the moment I just can't figure out what the problem is. Any ideas are appreciated. Thanks!
I've found out that the problem is caused by the use of a Fortran95 compiler. It is used as a linker when building the program on a Sun machine, on other machines g++ is used. I have no idea what exactly the problem is but I think I'll just switch to g++ on the Sun machines as well.