In our apps we're using a shared inhouse library which provides filesystem functions. All the functions are noexcept.
In several apps i found that similar or identical error return translations are implemented again and again [which are then reported on a console or some windows app] and was wondering
- if it might be a good design to provide default error strings which are transported with exceptions as part of a additional function in the library
- or if each of these functions should be wrapped in the respective application because the error strings might need application specific context (in this case they don't in their current implementation)
- or if i should stick with the existing function and reimplement the translation on every invocation (which is how it's implemented currently)
- or generally what's the best way to approach this.
The code is windows only and not planned to be ported to other platforms. The existing functions in the library can't be changed but it's possible to add new functions.
Here's the code: (Live Demo)
The new function createDirectory_CanThrow
is part of the library and provides default translations of the error codes.
#include <cassert>
#include <iostream>
#include <string_view>
#include <windows.h>
namespace myorg::filesystem {
// cannot change this enum
enum class CreateDirectoryResult {
Ok = 0,
See_GetLastError, // windows specific; use ::GetLastError() to get the reason
CollidesWithAFileHavingTheSameName,
};
// cannot change this method
CreateDirectoryResult createDirectory(std::string_view directory) noexcept {
// ...
return CreateDirectoryResult::Ok;
}
// is this good design?
// reimplement it per app?
// repeat it on every invocation and don't create a wrapper?
void createDirectory_CanThrow(std::string_view directory) {
switch (createDirectory(directory))
{
default:
assert(false);
[[fallthrough]];
case CreateDirectoryResult::Ok:
break;
case CreateDirectoryResult::CollidesWithAFileHavingTheSameName:
throw std::runtime_error("Cannot create the directory because its name collides with a file with the same name.");
case CreateDirectoryResult::See_GetLastError: {
LPVOID buf;
::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, (LPCVOID)::GetLastError(), 0, 0, (LPSTR)&buf, 0, NULL);
std::string msg = (char const*)buf;
::LocalFree(buf);
throw std::runtime_error(msg);
}
}
}
}
int main()
{
try
{
myorg::filesystem::createDirectory_CanThrow("C:\\test");
}
catch (std::runtime_error const& e)
{
std::cout << "Error creating directory: " << e.what() << "\n";
}
}