
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