|
Boost : |
Subject: Re: [boost] D-style scope guards?
From: Martin Christiansson (martin.christiansson_at_[hidden])
Date: 2010-09-19 04:40:16
19 sep 2010 kl. 08:59 skrev Ulrich Eckhardt:
>
> Martin Christiansson wrote:
>> GUARDED_SCOPE_SUCCESS() will only run if the function is exited with
>> wrapper for return keyword (GUARDED_SCOPE_RETURN). The wrapper must
>> be used since it updates the hidden guard variables. Using plain
>> "return" will behave as if exception was thrown.
>
> Having to use a big and ugly macro is too intrusive, signaling failure when
> returning normally is just wrong.
>
I've been thinking about a better solution. I need to call a lot of C-functions that use return value and errno for error reporting. Writing a lot of code around each call is a bit clumsy so I tried to create a guard macro instead that puts all relevant data into an exception if the "C function" returned an error.
Now it looks like this example with two small silly C-functions:
extern "C" {
int foo(const char* s) {
return strlen(s) - 12;
}
const char* bar(int n) {
return n != 42 ? NULL : "Hello World!";
}
}
int main()
{
using boost::lambda::_1;
int rc;
const char* ptr;
try {
rc = RETVALGUARD(_1 < 0, foo, "Hello World!");
cout << "rc = " << rc << endl;
ptr = RETVALGUARD(_1 == ((char*) NULL), bar, 42);
cout << "ptr = " << ptr << endl;
} catch (std::exception& e) {
cout << (&e)->what() << endl;
}
return 0;
}
boost::bind is used when calling the function with it arguments. The return value is evaluated with the conditional expression where _1 is the return value of the C-function. An exception containing return value, errno, function name, file name and line will be thrown if the expression evaluates to true. Now I get a what() that reports: " Exception thrown in retval_guard.cpp at line 65 due to invalid return value from call to foo(): return value = -1, errno = Invalid argument"
The macro looks like this:
template<typename E, typename T>
T conditional_throw_legacy_exception(E cond, T rc, const char* file_name,
uint32_t line, const char* func_name)
{
if(cond(rc)) {
throw legacy_exception<T>(rc, file_name, line, func_name);
}
return rc;
}
#define RETVALGUARD(COND, FUNC, ...) \
conditional_throw_legacy_exception(COND, \
boost::bind(FUNC, __VA_ARGS__)(), \
static_cast<const char*>(__FILE__), \
__LINE__, \
static_cast<const char*>(# FUNC))
Still need some code cleanup, but I think it will be quicker to translate from C-style return values to a "legacy exception" than writing the code manually for each function call. It will be cleaner than allowing for return to report error and a very low effort to add the macro guard around all C api calls.
/Martin
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk