::GetLastError()
errno
, perror()
glGetError()
Only an error code - must always be looked up
auto handle = ::CreateFileMapping(file, NULL, PAGE_READWRITE,
0, 0, 0);
if (!handle) {
auto error = ::GetLastError();
std::cout << "Creating file mapping: " << error << std::endl;
return;
}
auto memory = ::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
if (!memory) {
auto error = ::GetLastError();
std::cout << "Error mapping view: " << error << std::endl;
}
Even worse if we don't have a nice special value for failure,
like nullptr
for pointers.
HANDLE handle;
auto has_error = ::CreateFileMapping(file, NULL, PAGE_READWRITE,
0, 0, 0, &handle);
if (has_error) {
auto error = ::GetLastError();
std::cout << "Creating file mapping: " << error << std::endl;
return;
}
POINTER memory;
has_error = ::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0,
&memory);
if (has_error) {
auto error = ::GetLastError();
std::cout << "Error mapping view: " << error << std::endl;
}
auto handle = ::CreateFileMapping(file, NULL, PAGE_READWRITE,
0, 0, 0);
/*
if (!handle) {
auto error = ::GetLastError();
std::cout << "Creating file mapping: " << error << std::endl;
return;
}
*/
auto memory = ::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
/*
if (!memory) {
auto error = ::GetLastError();
std::cout << "Error mapping view: " << error << std::endl;
}
*/
auto handle = ::CreateFileMapping(file, NULL, PAGE_READWRITE,
0, 0, 0);
auto memory = ::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
if (!memory) {
auto error = ::GetLastError();
std::cout << "Error mapping view: " << error << std::endl;
}
Which error is that?
glGetError()
auto handle = ::CreateFileMapping(file, NULL, PAGE_READWRITE,
0, 0, 0);
auto memory = ::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
if (!memory) {
auto error = ::GetLastError();
std::cout << "Error mapping view: " << error << std::endl;
}
Which error is that?
::GetLastError()
and errno
are thread-local -
they have a separate value for each thread in the processHRESULT hr = ::DoSomething();
if (FAILED(hr)) {
}
Exception is an anomalous event requiring special processing, outside of the normal flow.
The process of responding to the occurance of exceptions.
Exceptions allow error handling to happen in a separate flow, outside of the main flow of the program.
Allow us to focus on the main flow of the program.
float sqrt(float x) {
// C++ allows throwing any type of value
if (x < 0) throw 42;
// ...
}
try { // try block
float input;
std::cin >> input;
std::cout << sqrt(input) << std::endl;
}
catch (int x) { // catch clause
std::cout << "Please enter a non-negative number" <<
std::endl;
}
catch (std::exception& e) {
}
What happens when an exception is thrown in C++?
std::terminate()
is calledAppropriate catch clause
Appropriate catch clause
std::exception
hierarchystd::exception
is the base class of all standard C++
exceptions
std::exception
class exception {
public:
const char* what() const {
// returns an explanatory string
}
};
std::exception
class exception {
public:
const char* what() const {
// returns an explanatory string
}
};
std::exception
hierarchystd::bad_alloc
std::bad_cast
std::bad_exception
std::logic_error
std::runtime_error
Custom exceptions should be derived from std::exception
catch (std::exception e) {
// e just got "sliced"
// the derived object got cut off
}
catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
std::terminate
std::terminate
calls the currently installed
std::terminate_handler
. The default
std::terminate_handler
calls std::abort
The process of destroying the stack frames until the appropriate catch clause is found.
During this process all destructors of automatic variables are executed. This allows for freeing any resources.
Throwing an exception during stack unwinding is going to
call std::terminate()
. This is the reason to consider
destructors that throw exceptions a bad practice.
std::terminate
is called when ...std::exit
orstd::at_quick_exit
throws an exceptionstd::unexcepted
is executedstd::thread
std::thread
is destroyed or assigned tostd::terminate()
is calledA set of guidelines that class library implementers and clients use when reasoning about exception handling safety in C++ programs.
Operations are guaranteed to succeed. All exceptions are handled internally and are not visible to the client.
Operations can fail, but failed operations have no side effects, as if they didn't attempt to run at all.
Operations can fail, failed operations might have side effects, but no invariants are broken.
Oh, well, ...
Follow the begin transaction/commit/rollback pattern
// m_End points the last element of the vector
void vector::push_back(T& x) {
if (size() < capacity()) {
new (m_End + 1) T(x);
// if it throws, m_End will not be moved
++m_End;
// commit the push
} else {
std::vector<T> newvector;
newvector.reserve((size() + 1) * 2);
newvector.assign(begin(), end());
newvector.push_back(x);
using std::swap;
// commit the push_back
swap(*this, newvector);
}
}
std::error_code
boost::system
void create_directory(const std::string& name, std::error_code&
error);
std::error_code error;
create_directory("path", error);
if (error)
{
if (error == std::errc:file_exists) {
}
}
std::error_code
is the platform-specific errorstd::errc::*
are platform-agnostic error conditionserror_code
http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html
std::optional
C++
14C++
17boost::optional
Allows to express that there is no such value.
sqrt(-42); ///?
template <typename T>
class optional {
T& get();
T* get_ptr();
const T& get_value_or(const T& default) const;
T& operator*();
T* operator->();
explicit operator bool();
};
Having only an error (with a callstack if we are lucky) often is not enough.
Logging is essential for understanding what is going on with the program.
C++
library for logging.At the end there may be crashes in the application