how to handle opencv error with a non-exception code base - c++

I have a non-exception grpc service code base which uses “error code return” strategy to deals with all kinds of errors(when an error happens(not all, only the unhandleable ones), it causes a chain reaction all the way to the service response call and response the client request call with an error code)
now I have to rely on some third party library (say openCV), and I'm wondering how to handle the potential errors that may caused by openCV apis.
code examples:
#include<opencv2/opencv/hpp>
int A(args)
{
...
int errCode = B(); //this is how i handle all my internal api call or other thrid party same error-style api calls, it returns an int error code and checked
if(errCode)
{
//log...;
//resouce release...
return errCode;
}
...
return 0; //0 means succ call
}
int B(args)
{
...
cv::OPENCV_API(cv::Mat, cv::Mat,...args). // how to handle this api call?
...
}
and here is my questions:
1.what will happen if something is wrong with the opencv api call, I searched opencv error handling, see that most of it throws exceptions, so the whole service will be aborted?
if I don't want to use try {...} catch {...} in my code, how should I work with exception-throw style third party code to avlid them from crash my service?
3.say the worst, I have to use try {...} catch {...}, how should it be woked out? is some thing like below will work?(means that my service won't abort or cratch):
//int A() remains same
int B(args)
{
...
try
{
...
cv::OPENCV_API(cv::Mat, cv::Mat,...args). // how to handle this api call?
...
}
catch
{
log...;
int errCode = -1000;
return errCode;
}
return 0;
}
TL;DR if I don't use throw-exception syntax in my own code, how do I utilize and error-handling with thrid-party code that applys throw-exception syntax?

Related

How to delegate an action to function return?

The problem
I have the following simple situation popping up all over the place. A large number of requests come to the device with a function signature like this:
Err execute( const ICommandContext &context,
const RoutineArguments &arguments,
RoutineResults &results)
There is essentially a request handling server that will call this execute the function for a variety of request types that have these signatures. We have 2 return paths in the case of an error.
The Err output type (consider it to be equivalent to an int) which is used to inform the server or system that something has gone wrong that is to do with the system, not the request. This is always sorted at the top of the function before the user request is dealt with.
RoutineResults provides a setStatus function that can be used to return failure information of the request to the client.
For this reason we have a lot of this type of code popping up:
// Failure due to request
Err error = someFunctionCall(clientInput);
if (!error.success()) {
results.setStatus(error); // Inform the client of the error
return SUCCESS; // Inform the system that we are all good
}
We have a particular request type that has around 15 parameters that come in and are sent off around the system. We would conceptually need 15 of this if error do set which seems wasteful. It is also prone to errors if we need to go through and change anything about how we return. How can we effectively delegate the setStatus and return to a short amount of code that only needs to happen once in the function?
A Macro Solution
A c system might solve this with a macro, something like:
#define M_InitTry Err error
#define M_Try(statement) if (!(error = statement).success()) { goto catch_lab; }
#define M_Catch catch_lab: if (!error.successs())
#define M_Return return error
Which would be used like this:
Err execute( const ICommandContext &context, ...) {
M_InitTry;
...
M_Try(someFunctionCall(clientInput));
M_Try(someFunctionCall(otherClientInput));
...
M_Catch {
// Other specific actions for dealing with the return.
results.setStatus(error);
error = SUCCESS;
}
M_Return;
}
This cleans the code nicely, but is not particularly nice with the goto. It will cause problems if defining variables that might be skipped by a goto.
A delegating solution
I was trying to think of a more C++ so I thought an RAII type delegate might help. Something like:
class DelegateToFunctionEnd {
typedef std::function<void(void)> EndFunction;
public:
DelegateToFunctionEnd(EndFunction endFunction) : callAtEnd(endFunction) { }
~DelegateToFunctionEnd() {
callAtEnd();
}
private:
EndFunction callAtEnd;
};
Pretty simple, it does a delegate of the action until the function return by implementing the action in the destructor. You might use it like this:
Err execute( const ICommandContext &context, ...) {
Err error;
DelegateToFunctionEnd del(std::bind(&RoutineResults::setStatus, &results, std::cref(error)));
error = someFunctionCall(clientInput));
if (error) return SUCCESS;
...
}
Live example.
This solution seems ok, but has several problems:
It is not as clear what is happening.
It is easier to make a mistake about setting error correctly.
You still need a large number of if statements to deal with the returns.
The ability to configure the terminating action is not great.
Dangerous if the user doesn't carefully consider the destruction order of items at function return.
A better solution?
This must be a problem that comes up often. Is there a general solution that provides a clean delegation of this set and returns type action?
I have some unfortunate restrictions below. Don't let these stop you from answering because it might be helpful for future people.
I am working on a c++03 limited system. We have boost, but no c++11.
Embedded system and we have silly rules about exceptions and memory allocation.
If error status codes are proving troublesome, you should consider using exceptions instead. That is, change the API of your functions
so they are guaranteed to have success as a post-condition
throw a suitable std::exception in the event of failure
It is impossible to "forget" to examine a status code if you do this. If you choose not to handle an error condition, the exception thrown by low-level code automatically percolates upwards. You only need to catch a low-level exception if
You need to do some manual roll-back or deallocation in the event of an error,
and RAII is impractical. In this case you would rethrow the expcetion.
You want to translate a low-level exception message or exception type into a high-level message, using a thrown) nested exception.
Maybe, you can write your statement as array, something like:
Err execute( const ICommandContext &context, ...)
{
const boost::function<Err()> functions[] = {
boost::bind(&someFunctionCall, std::ref(clientInput)),
boost::bind(&someFunctionCall, std::ref(otherClientInput)),
// ...
};
for (std::size_t i = 0; i != sizeof(functions) / sizeof(functions[0]); ++i) {
Err err = functions[i]();
if (!err.successs()) {
results.setStatus(err);
return SUCCESS;
}
}
return SUCCESS;
}
and if you do that several time with only different statements,
you might create
Err execute_functions(const ICommandContext &context, std::function<Err()> functions);
Maybe also provide other entry points as OnError depending of your needs.
Split the function.
The inner function returns an error code based on user input; the outer translates that to a client error, and only returns server side errors.
Inner function contains:
if(Err error = someFunctionCall(clientInput))
return error;
repeatedly. Outer has the relay to client error code, but only once.
Err just needs an operator bool. If it cannot have it, create a type that converts to/from Err and has an operator bool.
Can you add a method to error that does the check etc and return a bool.
if(!someFunctionCall(clientInput).handleSuccess(results))
{
return SUCCESS;
}

Properly terminating program. Using exceptions

Question:
Is using exceptions the proper way to terminate my program if all I want is to display an error message and close (accounting that I may be deep in the program)? Or can I just explicitly call something like exit(EXIT_FAILURE) instead?
What I'm Currently Doing:
I'm working on a game project and am trying to figure out the best way to terminate the program in the case of an error that calls for such an action. For example, in the case the textures can't be loaded I display an error message and terminate the program.
I'm currently doing this with exceptions like so:
int main()
{
Game game;
try
{
game.run();
}
catch (BadResolutionException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Resolution");
return 1;
}
catch (BadAssetException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Assets");
return 1;
}
catch (std::bad_alloc & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Memory");
return 1;
}
return 0;
}
All but bad_alloc are my own defined exceptions derived from runtime_error.
I don't need any manual resource cleanup and I'm using std::unique_ptr for any dynamic allocation. I just need to display the error message and close the program.
Research/Alternatives to Exceptions:
I've looked up a lot of posts on SO and other places and have seen others say anything from don't use exceptions, to use exceptions but your using them wrong. I've also looked up explicitly calling something like exit().
Using exit() sounds nice but I read it won't go back through the call stack up to main cleaning everything up (if I can find this again I'll post the link). Additionally, according to http://www.cplusplus.com/reference/cstdlib/exit/ this should not be used if multiple threads are active. I do expect to be creating a second thread for a short time at least once, and an error could occur in that thread.
Not using exceptions was mentioned in some replies here in relation to games https://gamedev.stackexchange.com/questions/103285/how-industy-games-handle-their-code-errors-and-exceptions
Use exceptions was discussed here: http://www.quora.com/Why-do-some-people-recommend-not-using-exception-handling-in-C++
There are a number of other sources I've read but those were the most recent I looked at.
Personal Conclusion:
Due to my limited experience of working with error handling and using exceptions, I'm not sure if I'm on the right track. I've chosen the route of using exceptions based on the code I posted above. If you agree that I should tackle those cases with exceptions, am I using it correctly?
It's generally considered good practice to let all exceptions propagate through to main. This is primarily because you can be sure the stack is properly unwound and all destructors are called (see this answer). I also think it's more organised to do things this way; you always known where your program will terminate (unless the program crashes). It's also facilitates more consistent error reporting (a point often neglected in exception handling; if you can't handle the exception, you should make sure your user knows exactly why). If you always start with this basic layout
int main(int argc, const char **argv)
{
try {
// do stuff
return EXIT_SUCCESS;
} catch (...) {
std::cerr << "Error: unknown exception" << std::endl;
return EXIT_FAILURE;
}
}
then you won't go far wrong. You can (and should) add specific catch statements for better error reporting.
Exceptions when multithreading
There are two basic ways of executing code asynchronously in C++11 using standard library features: std::async and std::thread.
First the simple one. std::async will return a std::future which will capture and store any uncaught exceptions thrown in the given function. Calling std::future::get on the future will cause any exceptions to propagate into the calling thread.
auto fut = std::async(std::launch::async, [] () { throw std::runtime_error {"oh dear"}; });
fut.get(); // fine, throws exception
On the other hand, if an exception in a std::thread object is uncaught then std::terminate will be called:
try {
std::thread t {[] () { throw std::runtime_error {"oh dear"};}};
t.join();
} catch(...) {
// only get here if std::thread constructor throws
}
One solution to this could be to pass a std::exception_ptr into the std::thread object which it can pass the exception to:
void foo(std::exception_ptr& eptr)
{
try {
throw std::runtime_error {"oh dear"};
} catch (...) {
eptr = std::current_exception();
}
}
void bar()
{
std::exception_ptr eptr {};
std::thread t {foo, std::ref(eptr)};
try {
// do stuff
} catch(...) {
t.join(); // t may also have thrown
throw;
}
t.join();
if (eptr) {
std::rethrow_exception(eptr);
}
}
Although a better way is to use std::package_task:
void foo()
{
throw std::runtime_error {"oh dear"};
}
void bar()
{
std::packaged_task<void()> task {foo};
auto fut = task.get_future();
std::thread t {std::move(task)};
t.join();
auto result = fut.get(); // throws here
}
But unless you have good reason to use std::thread, prefer std::async.
There's nothing wrong with catching unrecoverable errors and shutting down your program this way. In fact, it's how exceptions should be used. However, be careful not to cross the line of using exceptions to control the flow of your program in ordinary circumstances. They should always represent an error which cannot be gracefully handled at the level the error occurred.
Calling exit() would not unwind the stack from wherever you called it. If you want to exit cleanly, what you're already doing is ideal.
You have already accepted an answer, but I wanted to add something about this:
Can I just explicitly call something like exit() instead?
You can call exit, but (probably) shouldn't.
std::exit should be reserved for situations where you want to express "exit right now!", not simply "application has nothing left to do".
As an example, if you were to write a controller for a laser used in cancer treatments, your first priority in case something went wrong would be to shut down the laser and call std::exit - or possibly std::terminate (to ensure any side effects of a hanging, slow or crashing application do not kill a patient).
Similar to how exceptions should not be used for controlling application flow, exit should not be used to stop the application in normal conditions.
Is using exceptions the proper way to terminate my program if all I want is to display an error message and close (accounting that I may be deep in the program)?
Yes. This is the reason for using exceptions. An error occurred deep down in the code and something at a higher level will handle it. In your case, at the highest level.
There are arguments for/against exceptions vs. error codes, and this is a good read:
Exceptions or error codes
Can I just explicitly call something like exit() instead?
You can, but you may end up duplicating your logging code. Also, if in the future you decide that you want to handle an exception differently you will have to change all your exit calls. Imagine you wanted a different message, or to fall back on an alternative process.
Another similar question:
Correct usage of exit() in c++?
You also have a flaw in your approach as you don't handle all (C++) exceptions. You want something like this:
int main()
{
Game game;
try
{
game.run();
}
catch (BadResolutionException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Resolution");
return 1;
}
catch (BadAssetException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Assets");
return 1;
}
catch (std::bad_alloc & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Memory");
return 1;
}
catch (...)
{
// overload?
Notification::showErrorMessage("ERROR: Unhandled");
return 1;
}
return 0;
}
If you don't handle all* exceptions you could have the game terminate without telling you anything useful.
You can't handle ALL exceptions. See this link:
C++ catching all exceptions
From the documentation:
[[noreturn]] void exit (int status);
Terminate calling process
Terminates the process normally, performing the regular cleanup for terminating programs.
Normal program termination performs the following (in the same order):
Objects associated with the current thread with thread storage duration are destroyed (C++11 only).
Objects with static storage duration are destroyed (C++) and functions registered with atexit are called.
All C streams (open with functions in ) are closed (and flushed, if buffered), and all files created with tmpfile are removed.
Control is returned to the host environment.
Note that objects with automatic storage are not destroyed by calling exit (C++).
If status is zero or EXIT_SUCCESS, a successful termination status is returned to the host environment.
If status is EXIT_FAILURE, an unsuccessful termination status is returned to the host environment.
Otherwise, the status returned depends on the system and library implementation.
For a similar function that does not perform the cleanup described above, see quick_exit.

Can this use of C++ exceptions justified

I have a C++ API which throws exceptions in error conditions. Usually, the method I have seen in C++ APIs to notify errors is by special return codes and functions which returns last error string which can be checked if method returns an error code. This method has limitations, for example if you need to return an integer from a function and the whole integer value range is valid for return values so you can't return an error code.
Due to this, I choose to throw an exception in my API when an error occurs in a function.
Is this an acceptable usage of exceptions in C++?
Also, In some functions in my API (eg. authenticate()), I have two options.
return bool to indicate success.
return void and throw an exception if failed.
If first option is used, it is not consistent with other functions because they all throw exceptions. Also, it is difficult to indicate what is the error.
So is it ok to use second method in such functions too?
In following answer, it is mentioned that it is bad to use C++ exceptions for controlling program flow. Also, I have heard the same elsewhere too.
https://stackoverflow.com/a/1736162/1015678
Does my usage violates this? I cannot clearly identify whether I am using exceptions for controlling program flow here.
the method I have seen in C++ APIs to notify errors is by special return codes and functions which returns last error string which can be checked if method returns an error code.
Sometimes that's done for good reasons, but more often when you see that the C++ library wraps an older C library, has been written by someone more comfortable with C, written for client coders more comfortable with C, or is written for interoperability with C.
return an integer from a function and the whole integer value range is valid for return values so you can't return an error code.
Options include:
exceptions
returning with a wider type (e.g. getc() returns an int with -1 indicating EOF).
returning a success flag alongside the value, wrapped in a boost::optional, pair, tuple or struct
having at least one of the success flag and/or value owned by the caller and specified to the function as a non-const by-reference or by-pointer parameter
Is this an acceptable usage of exceptions in C++?
Sounds ok, but the art is in balancing the pros and cons and we don't know whether it's optimally convenient and robust for client code calling your functions. Understanding their expectations in key, which will partly be formed based on their overall C++ experience, but also from the rest of your API and any other APIs shipped alongside yours, and even from other APIs for other libraries they're likely to be using in the same apps etc..
Consider too whether the caller of a function is likely to want to handle the success or failure of that function in the context of the call, separately from other possible failures. For example, sometimes it's easier for client code to work with functions returning boolean success values:
if (fn(1) && !fn(2))
fn(3);
try
{
fn(1);
try
{
fn2();
}
catch (const ExpectedExceptionFromFn2Type&)
{
fn3();
}
}
catch (const PossibleExceptionFromFn1Type&)
{
// that's ok - we don't care...
}
But other times it can be easier with exceptions:
try
{
My_X x { get_X(99) };
if (x.is_happy(42))
x += next_prime_after(x.to_int() * 3);
}
catch (std::exception& e)
{
std::cerr << "oops\n";
}
...compared to...
bool success;
My_X x;
if (get_X(&x, 99)) {
if (x.is_valid() {
bool happy;
if (x.can_get_happy(&happy, 42) && happy) {
int my_int;
if (x.can_convert_to_int(&my_int)) {
if (!x.add(next_prime_after(x.to_int() * 3))) {
std::cerr << "blah blah\n";
return false;
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
} else { cerr / return false; }
(Exactly how bad it gets depends on whether functions support reporting an error, or can be trusted to always work. That's difficult too, because it something happens that makes it possible for a function to start failing (e.g. it starts using a data file that could potentially be missing), if client code didn't already accept and check an error code or catch exceptions, then that client code may need to be reworked once the potential for errors is recognised. That's less true for exceptions, which - when you're lucky - may propagate to some already-suitable client catch statement, but on the other hand it's a risky assuming so without at least eyeballing the client code.)
Once you've considered whatever you know about client usage, there may still be some doubt about which approach is best: often you can just pick something and stick to it throughout your API, but occasionally you may want to offer multiple versions of a function, e.g.:
bool can_authenticate();
void authenticate_or_throw();
...or...
enum Errors_By { Return_Value, Exception };
bool authenticate(Errors_By e) { ... if (e == Exception) throw ...; return ...; }
...or...
template <class Error_Policy>
struct System
{
bool authenticate() { ... Error_Policy::return_or_throw(...); }
...
}
Also, In some functions in my API (eg. authenticate()), I have two options.
As above, you have more than 2 options. Anyway, consistency is very important. It sounds like exceptions are appropriate.
mentioned that it is bad to use C++ exceptions for controlling program flow
That is precisely what exceptions do and all they can be used for, but I do understand what you mean. Ultimately, striking the right balance is an art that comes with having used a lot of other software, considering other libraries your clients will be using alongside yours etc.. That said, if something is an "error" in some sense, it's at least reasonable to consider exceptions.
For something like authenticate(), I'd expect you to return a bool if you were able to compute a true/false value for the authentication, and throw an exception if something prevented you from doing that. The comment about using exceptions for flow control is suggesting NOT doing something like:
try {
...
authenticate();
// rely on the exception to not execute the rest of the code.
...
} catch (...) { ... }
For instance, I can imagine an authenticate() method that relies on contacting some service, and if you can't communicate with that service for some reason, you don't know if the credentials are good or bad.
Then again, the other major rule of thumb for APIs is "be consistent". If the rest of the API relies on exceptions to serve as the false value in similar cases, use that, but to me, it's a little on the ugly side. I'd lean toward reserving exceptions for the exceptional case - i.e. rare, shouldn't ever happen during normal operations, cases.

Use of uncaught_exception to handle error conditions

I have following problem.
I have database connection that are recycled (put back into pool).
For example:
{
session sql(conn_str); // take connection from pool
sql.exec("insert into ...")
} // at the end of the scope return connection to pool
However in certain cases recycling may be wrong - for example disconnect, or some other significant error.
So I want to automatically prevent from the connection being recycled. I want to
implement following technique using std::uncaught_exception - so the exec() function
would detect exceptions and prevent recycling:
session::exec(...)
{
guard g(this)
real_exec(...);
}
Where guard:
class guard {
public:
guard(session *self) : self_(self) {}
~guard() {
if(std::uncaught_exception()) {
self->mark_as_connection_that_should_not_go_to_pool();
}
}
}
Now, I'm aware of http://www.gotw.ca/gotw/047.htm that does not recommend using
std::uncaught_exception on the other case I don't see any wrong with my code also,
the provides examples discussed.
Are there any possible problems with this code.
Note:
I want this change to be non-intrusive so that SQL backend would be able to throw and not check for every case if it is critical or not.
I don't want user to take any action about it so it would be transparent for him.
I don't see any advantage to your method over something more straightforward:
session::exec()
{
try
{
real_exec();
}
catch(...)
{
mark_as_connection_that_should_not_go_to_pool();
throw;
}
}
If the verboseness of this solution bothers you, I will note that they haven't ripped macros out of C++ yet. I wouldn't prefer this version as it masks the underlying code and is kind of ugly.
#define GUARD try {
#define ENDGUARD } catch(...) { mark_as_connection_that_should_not_go_to_pool(); throw; }
session::exec()
{
GUARD
real_exec();
ENDGUARD
}
Another possibility is to assume failure until success is achieved.
session::exec()
{
mark_as_connection_that_should_not_go_to_pool();
real_exec();
mark_as_connection_that_may_go_to_pool();
}
Finally to answer the question of whether uncaught_exception will work as you've outlined, I will quote from Microsoft's documentation of the function:
In particular, uncaught_exception will return true when called from a destructor that is being invoked during an exception unwind.
It appears to do exactly what you'd expect.

Dispatching exceptions in C++

How should exceptions be dispatched so that error handling and diagnostics can be handled in a centralized, user-friendly manner?
For example:
A DataHW class handles communication with some data acquisition hardware.
The DataHW class may throw exceptions based on a number of possible errors: intermittent signal, no signal, CRC failure, driver error. Each type of error gets its own exception class.
The DataHW class is called by a number of different pieces of code that do different types of acquisition and analysis.
The proper error handling strategy depends on the type of exception and the operation being attempted. (On intermittent signal, retry X times then tell the user; on a driver error, log an error and restart the driver; etc.) How should this error handling strategy be invoked?
Coding error recovery into each exception class: This would result in exception classes that are rather large and contain high-level UI and system management code. This seems bad.
Providing a separate catch block for each type of exception: Since the DataHW class is called from many different places, each catch block would have to be duplicated at each call site. This seems bad.
Using a single catch block that calls some ExceptionDispatch function with a giant RTTI-based switch statement: RTTI and switch usually indicates a failure to apply OO design, but this seems the least bad alternative.
Avoid duplicating the catch blocks at each call site by catching (...) and calling a shared handler function which rethrows and dispatches:
f()
{
try
{
// something
}
catch (...)
{
handle();
}
}
void handle()
{
try
{
throw;
}
catch (const Foo& e)
{
// handle Foo
}
catch (const Bar& e)
{
// handle Bar
}
// etc
}
An idea I keep running into is that exceptions should be caught by levels which can handle them. For example, a CRC error might be caught by the function that transmits the data, and upon catching this exception, it might try to retransmit, whereas a "no signal" exception might be caught in a higher level and drop or delay the whole operation.
But my guess is that most of these exceptions will be caught around the same function. It is a good idea to catch and handle them seperately (as in soln #2), but you say this causes a lot of duplicate code (leading to soln #3.)
My question is, if there is a lot of code to duplicate, why not make it into a function?
I'm thinking along the lines of...
void SendData(DataHW* data, Destination *dest)
{
try {
data->send(dest);
} catch (CRCError) {
//log error
//retransmit:
data->send(dest);
} catch (UnrecoverableError) {
throw GivingUp;
}
}
I guess it would be like your ExceptionDispatch() function, only instead of switching on the exception type, it would wrap the exception-generating call itself and catch the exceptions.
Of course, this function is overly simplified - you might need a whole wrapper class around DataHW; but my point is, it would be a good idea to have a centralized point around which all DataHW exceptions are handled - if the way different users of the class would handle them are similar.
Perhaps you could write a wrapper class for the DataHW class?
The wrapper would offer the same functionality as the DataHW class, but also contained the needed error handling code. Benefit is that you have the error handling code in a single place (DRY principle), and all errors would be handled uniformly. For example you can translate all low level I/O exceptions to higher level exceptions in the wrapper.
Basically preventing low level exceptions being showed to user.
As Butler Lampson said: All problems in computer science can be solved by another level of indirection
There are three ways i see to solve this.
Writing wrapper functions
Write a wrapper function for each function that can throw exceptions which would handle exceptions. That wrapper is then called by all the callers, instead of the original throwing function.
Using function objects
Another solution is to take a more generic approach and write one function that takes a function object and handles all exceptions. Here is some example:
class DataHW {
public:
template<typename Function>
bool executeAndHandle(Function f) {
for(int tries = 0; ; tries++) {
try {
f(this);
return true;
}
catch(CrcError & e) {
// handle crc error
}
catch(IntermittentSignalError & e) {
// handle intermittent signal
if(tries < 3) {
continue;
} else {
logError("Signal interruption after 3 tries.");
}
}
catch(DriverError & e) {
// restart
}
return false;
}
}
void sendData(char const *data, std::size_t len);
void readData(char *data, std::size_t len);
};
Now if you want to do something, you can just do it:
void doit() {
char buf[] = "hello world";
hw.executeAndHandle(boost::bind(&DataHW::sendData, _1, buf, sizeof buf));
}
Since you provide function objects, you can manage state too. Let's say sendData updates len so that it knows how much bytes were read. Then you can write function objects that read and write and maintain a count for how many characters are read so far.
The downside of this second approach is that you can't access result values of the throwing functions, since they are called from the function object wrappers. There is no easy way to get the result type of a function object binder. One workaround is to write a result function object that is called by executeAndHandle after the execution of the function object succeeded. But if we put too much work into this second approach just to make all the housekeeping work, it's not worth the results anymore.
Combining the two
There is a third option too. We can combine the two solutions (wrapper and function objects).
class DataHW {
public:
template<typename R, typename Function>
R executeAndHandle(Function f) {
for(int tries = 0; ; tries++) {
try {
return f(this);
}
catch(CrcError & e) {
// handle crc error
}
catch(IntermittentSignalError & e) {
// handle intermittent signal
if(tries < 3) {
continue;
} else {
logError("Signal interruption after 3 tries.");
}
}
catch(DriverError & e) {
// restart
}
// return a sensible default. for bool, that's false. for other integer
// types, it's zero.
return R();
}
}
T sendData(char const *data, std::size_t len) {
return executeAndHandle<T>(
boost::bind(&DataHW::doSendData, _1, data, len));
}
// say it returns something for this example
T doSendData(char const *data, std::size_t len);
T doReadData(char *data, std::size_t len);
};
The trick is the return f(); pattern. We can return even when f returns void. This eventually would be my favorite, since it allows both to keep handle code central at one place, but also allows special handling in the wrapper functions. You can decide whether it's better to split this up and make an own class that has that error handler function and the wrappers. Probably that would be a cleaner solution (i think of Separation of Concerns here. One is the basic DataHW functionality and one is the error handling).