Many Boost.Filesystem functions and methods (and the pattern also exists in Boost.Asio) will throw a boost::system::error_code in case of error, and sometimes it is not as obvious as you might expect, case in point:
1
2 3 4 5 6 |
boost :: filesystem :: path p ;
if(boost::filesystem::exists(p)) |
This code throws an exception if for some reason the boost::filesystem::exists triggers an error (if the system calls fails, which can happen when the file system is corrupt, a permission is missing…).
In other words, the function returns true if it correctly assessed that the file exists, false if it correctly assessed that the file doesn’t exist and will throw an exception if it cannot.
Two catches here:
In quasardb, when we use that kind of API we prefer to use the version that takes the error code as a parameter:
1
2 3 4 5 6 7 8 9 10 11 12 |
boot :: system :: error_code ec ;
boost :: filesystem :: path p ; if(boost::filesystem::exist(p, ec)) // file exists, do something // eventually test for ec depending on the context |
Exceptions in C++ is a hot topic but our design decision is to use exceptions as little as possible as they often obfuscate important error paths. We have many critical moments in the software (for example when sending a reply on the network or writing to the disk) where all error paths must be extremely clear in the code to make sure we don’t get in a situation where we incorrectly recover from an error.
The problem with passing an error code as a reference is that it is not immediate that the value is updated by the function.
This is why in our own code we prefer a more "modern" approach:
1
2 3 4 |
std :: error_code ec ;
bool result = false ; std::tie(result, ec)= qdb::some_function(); |
When reading the code above it is very easy to understand what is going on and thanks to copy elision this comes at no performance cost.