3

I am using the C library libusb. libusb methods return libusb_error enum values to signify errors. I want these methods to throw exceptions instead.

Each libusb error has a description that can be retrieved through the libusb_strerror() function. I want this to be what the what() method of thrown exceptions return.

My first attempt involved a wrapper function that throws a std::runtime_error constructed with the error description:

libusb_error wrapper(libusb_error error) { if(error >= 0) return error; throw std::runtime_error(libusb_strerror(error)); } 

But using that approach means I cannot catch libusb errors only nor specific libusb errors.

I then made a templated subclass of std::exception for libusb errors:

template<libusb_error error> class LIBUSBError : public std::exception { using std::exception::exception; virtual const char* what() const throw() { return libusb_strerror(error); } }; 

That approach allows me to catch specific libusb errors. However, template parameters have to be constant expressions, requiring me to use multiple statements for each enum value in my wrapper function:

// * * * libusb_error wrapper(libusb_error error) { if(error >= 0) return error; if(error == LIBUSB_ERROR_IO) { throw LIBUSBError<LIBUSB_ERROR_IO>(); } if(error == LIBUSB_ERROR_ACCESS) { throw LIBUSBError<LIBUSB_ERROR_ACCESS>(); } // ... etc } // * * * try { wrapper(libusb_open(device, &handle)); } catch(LIBUSBError<LIBUSB_ERROR_ACCESS> e) { std::cerr << e; continue; } // * * * 

How can I convert return values of the libusb methods into C++ exceptions without requiring me to write out each enum value?

11
  • you could create a map storing appropriate exception depending on enum value Commented Sep 9, 2017 at 12:14
  • 2
    Do you need separate catch block for each type of error? If not, it would be sensible to write a ctor in LIBUSBError which will accept one argument (enum value) and construct std::exception from it accordingly. Commented Sep 9, 2017 at 12:15
  • 1
    If you still want separate class per each error, check this answer on how to generate factory which produces them: stackoverflow.com/questions/29400314/… Commented Sep 9, 2017 at 12:16
  • How many errors are there, and what is their range? Commented Sep 9, 2017 at 12:16
  • @myaut Yes, I need a separate catch block for each error. Commented Sep 9, 2017 at 12:20

4 Answers 4

2

I think there is no way to convert between runtime values (in variables) to compile-time values (template parameters) other than switch/case, if, etc.

If your errors are in some range, say, 0 to 100 you can do the following with templates:

template<int taError> class MyException : public std::exception { using std::exception::exception; virtual const char* what() const throw() override { printf("It's %d\n", taError); return std::exception::what(); } }; template<int taError> void selectThrow(int myErr) { if (myErr == taError) { throw MyException<taError>(); } return selectThrow<taError - 1>(myErr); } template<> void selectThrow<0>(int) { } 

Then you use it like:

int a = rand(); // replace with the function call that may fail if (a != 0) { // This check is important to let success scenario proceed quickly selectThrow <100>(a); } 

You can make a function to avoid writing if in every place:

void checkThrow(int errCode) { if(errCode != 0) { selectThrow<100>(errCode); // If nothing has been thrown, you can raise "unknown error" exception here } } 

But if the range for your errors is too large for template metaprogramming, then one option is variadic templates with explicitly specifying each error you handle.

template<typename... taOthers> void throwVariadic(int) { /* throw "unexpected error" exception here */ } template<int taCur, int... taOthers> void throwVariadic(int errCode) { if (taCur == errCode) { throw MyException<taCur>(); } return throwVariadic<taOthers...>(errCode); } void checkError(int errCode) { if(errCode == 0) return; throwVariadic<1, 3, 7, 15, 25>(errCode); } 

In the above example you need to replace <1, 3, 7, 15, 25> with actual error names you have: <LIBUSB_ERROR_IO, LIBUSB_ERROR_ACCESS /*, ...*/>

Finally, there is an option to resort to switch/case plus macros (the latter let you avoid excessive typing).

// Short for Handle LibUsb Error #define HLUE(var) case var: throw LIBUSBError<var>(); switch(error) { default: /* Throw "unknown error" exception here */ case 0: return 0; HLUE(LIBUSB_ERROR_IO) HLUE(LIBUSB_ERROR_ACCESS) /* etc for other codes */ } #undef HLUE 
Sign up to request clarification or add additional context in comments.

1 Comment

This answer contains some information about possible performance of the first piece of code. The link was originally posted by @myaut as a comment to my question.
2

The usual approach is to write your own exception class, derived from the appropriate std exception:

class libusb_exception : public std::runtime_error { public: libusb_exception(libusb_error error) : runtime_error(libusb_strerror(error)) {} }; 

Now you can throw objects of that type:

if (error < 0) throw libusb_exception(error); 

You could also store the error value in the exception object.

More generally, look at the design of the system error facility in the C++ standard library. It's intended to be a general model for handling system-generated errors, which typically show up as numeric values.

1 Comment

This approach allows me to catch libusb errors only but I want to be able to distinguish between the different libusb errors preferably directly in the the catch clause. This way of subclassing is much cleaner though.
1

I think you can at least partially solve your problem by introducing another level of indirection. Let's start with libusb_exception proposed by @Pete Becker:

class libusb_exception : public std::runtime_error { public: libusb_exception(libusb_error error) : runtime_error(libusb_strerror(error)) {} }; 

Now, let's build template to signify specific libusb exceptions, based on the above class:

template<libusb_error error> class specific_libusb_exception : public libusb_exception { specific_libusb_exception() : libusb_exception(error) { } }; 

Now, let's introduce a function which evaluates if libusb error requires handling and how specific it is:

void assure_libusb(libusb_error error) { switch(error) { case LIBUSB_SUCCESS: return; case LIBUSB_ERROR_IO: // needs specific handling throw specific_libusb_exception<LIBUSB_ERROR_IO>(); case LIBUSB_ERROR_OVERFLOW: // also needs specific handling throw specific_libusb_exception<LIBUSB_ERROR_OVERFLOW>(); default: // the rest is handled by generic libusb error handler throw libusb_exception(error); } } 

Now you call libusb this way:

assure_libusb(libusb_function_to_call(...)); 

letting handler up the stack, or

try { assure_libusb(libusb_function_to_call(...)); } catch(specific_libusb_exception<LIBUSB_ERROR_IO> &e) { : } catch(...) { throw; } 

3 Comments

This still requires me to write out each enum value. Using a switch makes it slightly shorter though.
You can only write those enums which you have to specifically handle. And one way, or another, you will have to do it. Even if you employ template metaprogramming, sooner or later you will see in the code both the conditions (be it if, switch or something else) and the enum values. Additionally putting all of the enums in one place (like the assure_libusb function above) makes code easier to maintain - any new error code needs to be added only there.
The first part of Serge's answer doesn't use all the enum values, only the minimum and maximum.
0

I meant something more like this:

#include <exception> #include <tuple> #include <type_traits> #include <map> enum libusb_error { LIBUSB_SUCCESS, LIBUSB_ERROR_IO, LIBUSB_ERROR_INVALID_PARAM, LIBUSB_ERROR_ACCESS, LIBUSB_ERROR_NO_DEVICE, LIBUSB_ERROR_NOT_FOUND, LIBUSB_ERROR_BUSY, LIBUSB_ERROR_TIMEOUT, LIBUSB_ERROR_OVERFLOW, LIBUSB_ERROR_PIPE, LIBUSB_ERROR_INTERRUPTED, LIBUSB_ERROR_NO_MEM, LIBUSB_ERROR_NOT_SUPPORTED, LIBUSB_ERROR_OTHER }; const char *libusb_strerror(libusb_error error) { if (LIBUSB_ERROR_IO) return "LIBUSB_ERROR_IO"; if (LIBUSB_ERROR_INVALID_PARAM) return "LIBUSB_ERROR_INVALID_PARAM"; } class generic_error { public: virtual const char* what() const throw() = 0; virtual void do_throw() const = 0; }; template<libusb_error error> class LIBUSBError : public generic_error { public: virtual const char* what() const throw() { return libusb_strerror(error); } virtual void do_throw() const { throw *this; } }; template <class SupportedErrors = std::tuple<std::integral_constant<libusb_error, LIBUSB_ERROR_IO>, std::integral_constant<libusb_error, LIBUSB_ERROR_INVALID_PARAM>>> struct ErrorMap; template <libusb_error... Es> struct ErrorMap<std::tuple<std::integral_constant<libusb_error, Es>...>>{ static std::map<libusb_error, generic_error*> em; }; template <libusb_error... Es> std::map<libusb_error, generic_error*> ErrorMap<std::tuple<std::integral_constant<libusb_error, Es>...>>::em = {{Es, new LIBUSBError<Es>{}}...}; libusb_error known_at_runtime() { return LIBUSB_ERROR_IO; } int main() { try { ErrorMap<>::em[known_at_runtime()]->do_throw(); } catch (LIBUSBError<LIBUSB_ERROR_IO>&) { } } 

[live demo]

PS. sorry I believe that at wandbox there is no way to include libusb which would make the code a bit clearer

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.