0

If I want to provide a substituteable ErrorHandler class which can be used to provide different styles of handling (e.g. rethrow, log and rethrow, swallow, call some function, etc), then I can do something like the following:

ErrorHandler.h (this is long and should probably be read only after the problem, below)

template<typename Target, typename F, typename... Args>
using IsReturnType = std::is_same<std::result_of_t<F(Args...)>, Target>;

template<typename ReturnType, typename Function, typename... Args>
using EnableIfReturnType = typename std::enable_if<IsReturnType<ReturnType, Function, Args...>::value, ReturnType>::type;

class ErrorHandler
{
public:
    virtual void handleException(std::exception& e);
    void Throw(std::exception& e);

    // Templated Try functions
};

// Return type is void
template<typename F, typename... Ts>
typename EnableIfReturnType<void, F, Ts...>
ErrorHandler::Try(F&& function, Ts&&... args) {
    try {
        std::forward<F>(function)(std::forward<Ts>(args)...);
    }
    catch (std::exception& e) {
        handleException(e);
    }
}

// Return type is short
template<typename F, typename... Ts>
typename EnableIfReturnType<short, F, Ts...>
ErrorHandler::Try(F&& function, Ts&&... args) {
    short result = 0;
    try {
        result = std::forward<F>(function)(std::forward<Ts>(args)...);
        if (result != 0) {
            std::string msg = "getmessagehereErr";
            throw std::runtime_error(msg);
        }
    }
    catch (std::exception& e) {
        handleException(e);
    }
    return result;
}

// Return type is other
template<typename F, typename... Ts>
typename std::enable_if<!((IsReturnType<short, F, Ts...>::value) || (IsReturnType<void, F, Ts...>::value)), std::result_of_t<F(Ts...)>>::type
ErrorHandler::Try(F&& function, Ts&&... args) {
    using R = std::result_of_t<F(Ts...)>;
    R result;
    try {
        result = std::forward<F>(function)(std::forward<Ts>(args)...);
    }
    catch (std::exception& e) {
        handleException(e);
    }
    return result;
}

SomeImplementation.cpp

extern ErrorHandler *eh;
short result = eh->Try(Function, Arg1, Arg2); 
// where Function(Arg1, Arg2) returns a short, although 
// a different return type could be provided for a different function via the templates

This allows me to wrap function calls with Try and have the ErrorHandler pointed to be *eh (which could be a child of ErrorHandler, with different handleException(std::exception &e)).

Real question starts here

However, if I want to throw an error from a function that I know doesn't result in an exception, I can't do this:

int result = nonThrowingFunction();
if (result) { throw std::exception("Message"); } 

because that won't be passed to the ErrorHandler *eh.

I can't directly call eh->handleException(..), because the default implementation is {throw;}, and this won't work properly if the exception it is passed hasn't already been thrown.

I can call Throw(e) with

Throw(std::exception &e) {
    try {
        throw e;
    }
    catch(std::exception &ex) {
        handleException(ex);
    }
}

but this slices the exception, meaning all exceptions thrown in this way become whatever type I specify for Throw. I can't template Throw, because I can't get a pointer to a template function (in the general case).

Is there some good solution here, or should I write out an overloaded Throw for each exception type? (And if that's the sensible solution, is there some preprocessor magic that can write the same function for a list of different arguments?)

Edited to try and clarify the point of the question, and guide readers away from the somewhat bulky template<...> Try(...) function(s).

chrisb2244
  • 2,940
  • 22
  • 44
  • But I have to `throw explicitExceptionObject;` first - `throw;` will only allow me to pass an existing exception to the next handler, right? – chrisb2244 Nov 16 '17 at 13:00
  • What's wrong with `result = eh->Try(&nonThrowingFunction);` ? – Jarod42 Nov 16 '17 at 13:01
  • [std::exception_ptr](http://en.cppreference.com/w/cpp/error/exception_ptr) might interest you. – Jarod42 Nov 16 '17 at 13:07
  • @Jarod42, I think (hope!) that's fine in the `short` case, which explicitly handles non-zero return values. In fact, my example there wasn't ideal, because I can't currently imagine a way to know from a function that doesn't return an error code or an exception that it failed (at least in a generic way). The use case I'm currently looking at is something like `if(!validInput) { eh->Throw(std::runtime_error("Bad input leads to bad output")); }` or similar... – chrisb2244 Nov 16 '17 at 13:08
  • I think with exception_ptr I need to have already thrown an exception somewhere. It would allow me to do as in the example at the link you give, but then I need some 5 lines for an exception, in the middle of code that in principle shouldn't be throwing exceptions. One line is a bit less intrusive (especially if I use it a few times in different places). – chrisb2244 Nov 16 '17 at 13:10
  • You might pass an "`ResultValidator`": `result = f(...); if (!resultValidator(result)) {throw std::runtime_error("Bad result");})` ? – Jarod42 Nov 16 '17 at 13:14
  • @Jarod42 I think that a ResultValidator-style function of an ErrorHandler class would probably work, but it would remove the possibility of having the caller specify the exception type, at which point slicing would be basically the same. – chrisb2244 Nov 16 '17 at 14:46
  • I have difficulty to see/understand your problem. Do you want to change `Try` ? Can you provide a problematic code with expected usage. – Jarod42 Nov 16 '17 at 14:55
  • `Try` is a templated wrapper for other functions - my only minor frustration with it is that I have to change `func(a,b)` to `Try(func, a, b)` rather than `Try(func(a,b))`, but that's not the aim of this question (and is presumably also impossible, since `Try(func(a,b))` will be parsed as `Try(result of func(a,b))`... Here, I'm looking at ways to explicitly instruct ErrorHandler to throw a new exception of a type specified by the caller with some message, i.e. instantiate a new exception inheriting from `std::exception` and then throw it via `handleException(std::exception &e)`. – chrisb2244 Nov 16 '17 at 15:02
  • As an example usage, considering the answers already given, we could consider `bool isValid = ; if (!isValid) { eh->Throw(std::runtime_exception("Unable to parse the input correctly")); }` or `eh->Throw(std::invalid_argument("The input arguments were nonsense!"));` – chrisb2244 Nov 16 '17 at 15:10
  • 1
    Possible syntax for `Try([&](){return func(a, c);});`. Your may provide "`ExceptionThrower`" (similar to factory, but which throw :) ).: `eh->Throw([](){ throw std::invalid_argument("The input arguments were nonsense!"); })` or template: `eh->Throw("The input arguments were nonsense!");` – Jarod42 Nov 16 '17 at 15:13

2 Answers2

0

Using a template to handle any exception type should work:

template<typename ExceptionType>
void Throw(ExceptionType &e) {
    try {
        throw e;
    }
    catch(ExceptionType &ex) {    
        handleException(ex);
    }
}
owacoder
  • 4,815
  • 20
  • 47
0

Linking to explicitly instantiated templates works via pointer.

As a result, the following is valid (or at least, compileable and unit-test-passing) code.

ErrorHandler.h

class ErrorHandler {
...
template<typename E>
void Throw(E& e);
...
}

ErrorHandler.cpp

template<typename E>
void ErrorHandler::Throw(E& e)
{
    try {
        throw e;
    }
    catch (E&e) {
        handleException(e);
    }
}

template void ErrorHandler::Throw<std::exception>(std::exception&);
template void ErrorHandler::Throw<std::runtime_error>(std::runtime_error&);
template void ErrorHandler::Throw<std::invalid_argument>(std::invalid_argument&);

and reduces the burden to add a new exception that won't be sliced down to adding an additional line to the cpp file, which isn't included anywhere else, saving compilation times.

This also won't compile if the result would slice the exception, since then there will be a linking error.

chrisb2244
  • 2,940
  • 22
  • 44