Boost logo

Boost :

Subject: [boost] [Core] BOOST_TRY/BOOST_CATCH
From: Chris Glover (c.d.glover_at_[hidden])
Date: 2016-02-21 15:15:22


Hi,

I ran into a problem when trying to use BOOST_TRY/BOOST_CATCH to add
no-exception support to property tree.

Here's an example of the original code;

              try { write_ini(stream, pt, flags); } catch (ini_parser_error
&e) { BOOST_PROPERTY_TREE_THROW(ini_parser_error( e.message(), filename,
e.line())); }

I converted this code to use the macros from core/no_exception_support.hpp

        *BOOST_TRY *{
            write_ini(stream, pt, flags);
        }
        *BOOST_CATCH *(ini_parser_error &e) {
            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                e.message(), filename, e.line()));
        }
        *BOOST_CATCH_END*

The issue is that now the exception, ini_parser_error& e, is not defined
when I compile with exceptions off.

I solved this by adding a local instance of ini_parser_error above the try
block

  *ini_parser_error e("", "", 0);*
        BOOST_TRY {
            write_ini(stream, pt, flags);
        }
        BOOST_CATCH (ini_parser_error &e) {
            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                e.message(), filename, e.line()));
        }
        BOOST_CATCH_END

This is a little gross, but it's not too bad and I was expecting the
variable to be optimized away. But it isn't -- instantiating that exception
on the stack is very clearly generating instructions on VS2015 and GCC 5.3.

While this is not a huge deal, I don't like it so one way I can think to
fix this is to conditionally compile out the catch block, but that defeats
the purpose of the macros in the first place.

        BOOST_TRY {
            write_ini(stream, pt, flags);
        }
        BOOST_CATCH (ini_parser_error &e) {
    *#ifdef BOOST_NO_EXCEPTIONS*
            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                e.message(), filename, e.line()));
    *#endif*
        }
        BOOST_CATCH_END

Then I thought maybe I can declval a fake 'e' in the else block by
redefining the macros as follows;

#ifndef BOOST_NO_EXCEPTIONS
    #define BOOST_TRY { try {
    #define BOOST_CATCH(type, var) } catch(type var) {
    #define BOOST_CATCH_END } }
#else
    #define BOOST_TRY { if(true) {
    #define BOOST_CATCH(type, var) } else if(false) { type var =
boost::declval<type>();
    #define BOOST_CATCH_END } }
#endif

This is working, but is possibly undefined behaviour. Does anyone have
thoughts on that? It also requires splitting up the type from the variable
name.

The next step to this would be to add a third parameter for some
constructor args so we can just create an exception object directly.
Something like:

#define BOOST_CATCH(exception_type, var, args) } else if(false) {
boost::decay<exception_type>::type var args;

So now the original code would look like this:

        BOOST_TRY {
            write_ini(stream, pt, flags);
        }
        BOOST_CATCH (ini_parser_error&, e, ("", "", 0)) {
            BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                e.message(), filename, e.line()));
        }
        BOOST_CATCH_END

This is starting to look a little nasty to me so I'm wondering if anyone
has any thoughts on this? Should I stop worrying about it and just use the
#ifdef? The reality is that I work in an environment without exceptions and
we hit these things from time to time so I'd like to fix up as many as
possible.

Thanks,

-- chris


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk