Boost logo

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