Exception propagation

Discussion in 'C++' started by ittium, Dec 10, 2011.

  1. ittium

    ittium Guest

    Group,

    I have a situation where some of the class/non-class methodsmake many
    levels of function calls, some of the function in this call hierarchy
    may throw exception, what is a good idea

    #1 inner function rethrow (by empty throw), such that exception is
    propagated back to the top level function.

    #2inner function does not throw (by empty throw), this exception will be
    by **default** propagated back to the top level routine (assuming
    intermediate routines are not catching the exception).

    #3 When it is a good idea that function(inner) receiving the exception
    or some intermediate function catch it and from there error event is
    propagated back through some error code.

    thanks
    Ittium
    ittium, Dec 10, 2011
    #1
    1. Advertising

  2. ittium

    Ian Collins Guest

    On 12/10/11 09:28 PM, ittium wrote:
    > Group,
    >
    > I have a situation where some of the class/non-class methodsmake many
    > levels of function calls, some of the function in this call hierarchy
    > may throw exception, what is a good idea
    >
    > #1 inner function rethrow (by empty throw), such that exception is
    > propagated back to the top level function.
    >
    > #2inner function does not throw (by empty throw), this exception will be
    > by **default** propagated back to the top level routine (assuming
    > intermediate routines are not catching the exception).
    >
    > #3 When it is a good idea that function(inner) receiving the exception
    > or some intermediate function catch it and from there error event is
    > propagated back through some error code.



    The first place where it makes sense to catch the exception catches it
    and performs the appropriate recovery actions. if there isn't an
    appropriate place, catch it in main, report and exit.

    --
    Ian Collins
    Ian Collins, Dec 10, 2011
    #2
    1. Advertising

  3. ittium

    Goran Guest

    On Dec 10, 9:28 am, ittium <> wrote:
    > Group,
    >
    > I have a situation where some of the  class/non-class methodsmake many
    > levels of function calls, some of the function in this call hierarchy
    > may throw exception, what is a good idea
    >
    > #1 inner function rethrow (by empty throw), such that exception is
    > propagated back to the top level function.
    >
    > #2inner function does not throw (by empty throw), this exception will be
    > by **default** propagated back to the top level routine (assuming
    > intermediate routines are not catching the exception).
    >
    > #3 When it is a good idea that function(inner) receiving the exception
    > or some intermediate function catch it and from there error event is
    > propagated back through some error code.


    In C++ code, there's no good reason to ever STOP exception propagation
    AND replace it with return code, except e.g. in main. Or perhaps when
    you are writing a module that should be exposed to a language that
    can't receive C++ exceptions.

    Further, there are RARE reasons to stop exception propagation. Off the
    top of my head, some of those rare ones are:

    * main() - you seldom want want terminate() to kick in, which is what
    will happen if exception escapes main()
    * for similar reason, any top-level thread function
    * to re-throw existing exception, but with further info about the
    error (e.g. "bad_alloc happened when fryding the swoorf"). This is
    because lowest-level exceptions have no further info about the context
    in which they happened, and that is useful, and C++ has no call stack
    in exception objects ;-).
    * there's a loop whose whole body can fail, but next iteration still
    should happen, usually after reporting the error. Example: a server-
    kind-of-a-program, who has a client serving loop, and serving one
    client request failed.

    You seem to be trying to make a rule of thumb here, but without the
    context. That is seldom a good idea. It's better to ask "what should I
    do" from a given context.

    Goran.
    Goran, Dec 10, 2011
    #3
  4. ittium

    Christopher Guest

    On Dec 10, 12:29 pm, Goran <> wrote:
    > On Dec 10, 9:28 am, ittium <> wrote:
    >
    >
    >
    >
    >
    > > Group,

    >
    > > I have a situation where some of the  class/non-class methodsmake many
    > > levels of function calls, some of the function in this call hierarchy
    > > may throw exception, what is a good idea

    >
    > > #1 inner function rethrow (by empty throw), such that exception is
    > > propagated back to the top level function.

    >
    > > #2inner function does not throw (by empty throw), this exception will be
    > > by **default** propagated back to the top level routine (assuming
    > > intermediate routines are not catching the exception).

    >
    > > #3 When it is a good idea that function(inner) receiving the exception
    > > or some intermediate function catch it and from there error event is
    > > propagated back through some error code.

    >
    > In C++ code, there's no good reason to ever STOP exception propagation
    > AND replace it with return code, except e.g. in main. Or perhaps when
    > you are writing a module that should be exposed to a language that
    > can't receive C++ exceptions.
    >
    > Further, there are RARE reasons to stop exception propagation. Off the
    > top of my head, some of those rare ones are:
    >
    > * main() - you seldom want want terminate() to kick in, which is what
    > will happen if exception escapes main()
    > * for similar reason, any top-level thread function
    > * to re-throw existing exception, but with further info about the
    > error (e.g. "bad_alloc happened when fryding the swoorf"). This is
    > because lowest-level exceptions have no further info about the context
    > in which they happened, and that is useful, and C++ has no call stack
    > in exception objects ;-).
    > * there's a loop whose whole body can fail, but next iteration still
    > should happen, usually after reporting the error. Example: a server-
    > kind-of-a-program, who has a client serving loop, and serving one
    > client request failed.
    >
    > You seem to be trying to make a rule of thumb here, but without the
    > context. That is seldom a good idea. It's better to ask "what should I
    > do" from a given context.
    >
    > Goran.- Hide quoted text -
    >
    > - Show quoted text -



    Catching an exception at the top level function of a thread is not so
    rare in my opinion and very often overlooked.
    I've actually made it a point to examine thread entry points now, and
    make sure everything is caught and handled in some way.
    Christopher, Dec 11, 2011
    #4
  5. ittium

    Ebenezer Guest

    On Dec 10, 12:29 pm, Goran <> wrote:
    >
    > In C++ code, there's no good reason to ever STOP exception propagation
    > AND replace it with return code, except e.g. in main. Or perhaps when
    > you are writing a module that should be exposed to a language that
    > can't receive C++ exceptions.
    >
    > Further, there are RARE reasons to stop exception propagation. Off the
    > top of my head, some of those rare ones are:
    >
    > * main() - you seldom want want terminate() to kick in, which is what
    > will happen if exception escapes main()
    > * for similar reason, any top-level thread function
    > * to re-throw existing exception, but with further info about the
    > error (e.g. "bad_alloc happened when fryding the swoorf"). This is
    > because lowest-level exceptions have no further info about the context
    > in which they happened, and that is useful, and C++ has no call stack
    > in exception objects ;-).
    > * there's a loop whose whole body can fail, but next iteration still
    > should happen, usually after reporting the error. Example: a server-
    > kind-of-a-program, who has a client serving loop, and serving one
    > client request failed.
    >


    Well that last one is hardly rare. Most programs today are
    servers, I think.


    I stop the propagation in the following for performance
    reasons:

    cmw_request
    getRequest (uint8_t byteOrder
    , sock_type sock)
    {
    static ReceiveBuffer<SameFormat> localbuf(4096);
    #ifdef ENDIAN_BIG
    static ReceiveBuffer<LeastSignificantFirst> otherlocalbuf(4096);
    #else
    static ReceiveBuffer<MostSignificantFirst> otherlocalbuf(4096);
    #endif

    uint8_t clientFormat;
    Read(sock, &clientFormat, 1);
    try {
    if (clientFormat == byteOrder) {
    localbuf.sock_ = sock;
    while (!localbuf.GotPacket()) ;
    return cmw_request(localbuf);
    } else {
    otherlocalbuf.sock_ = sock;
    while (!otherlocalbuf.GotPacket()) ;
    return cmw_request(otherlocalbuf);
    }
    } catch :):std::exception const& ex) {
    clientFormat == byteOrder ? localbuf.Reset() :
    otherlocalbuf.Reset();
    throw;
    }
    }

    The exception catching allows me to make some variables
    static that would otherwise be allocating and releasing
    memory from the heap each time the function is called.
    It is kind of experimental, but so far so good.

    The above function is from this file --
    http://webEbenezer.net/misc/cmwAmbassador.cc.


    Brian Wood
    Ebenezer Enterprises
    http://webEbenezer.net
    Ebenezer, Dec 12, 2011
    #5
  6. ittium

    Goran Guest

    On Dec 12, 6:44 am, Ebenezer <> wrote:
    > On Dec 10, 12:29 pm, Goran <> wrote:
    >
    >
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > > In C++ code, there's no good reason to ever STOP exception propagation
    > > AND replace it with return code, except e.g. in main. Or perhaps when
    > > you are writing a module that should be exposed to a language that
    > > can't receive C++ exceptions.

    >
    > > Further, there are RARE reasons to stop exception propagation. Off the
    > > top of my head, some of those rare ones are:

    >
    > > * main() - you seldom want want terminate() to kick in, which is what
    > > will happen if exception escapes main()
    > > * for similar reason, any top-level thread function
    > > * to re-throw existing exception, but with further info about the
    > > error (e.g. "bad_alloc happened when fryding the swoorf"). This is
    > > because lowest-level exceptions have no further info about the context
    > > in which they happened, and that is useful, and C++ has no call stack
    > > in exception objects ;-).
    > > * there's a loop whose whole body can fail, but next iteration still
    > > should happen, usually after reporting the error. Example: a server-
    > > kind-of-a-program, who has a client serving loop, and serving one
    > > client request failed.

    >
    > Well that last one is hardly rare.  Most programs today are
    > servers, I think.
    >
    > I stop the propagation in the following for performance
    > reasons:
    >
    > cmw_request
    > getRequest (uint8_t byteOrder
    >             , sock_type sock)
    > {
    >   static ReceiveBuffer<SameFormat> localbuf(4096);
    > #ifdef ENDIAN_BIG
    >   static ReceiveBuffer<LeastSignificantFirst> otherlocalbuf(4096);
    > #else
    >   static ReceiveBuffer<MostSignificantFirst> otherlocalbuf(4096);
    > #endif
    >
    >   uint8_t clientFormat;
    >   Read(sock, &clientFormat, 1);
    >   try {
    >     if (clientFormat == byteOrder) {
    >       localbuf.sock_ = sock;
    >       while (!localbuf.GotPacket()) ;
    >       return cmw_request(localbuf);
    >     } else {
    >       otherlocalbuf.sock_ = sock;
    >       while (!otherlocalbuf.GotPacket()) ;
    >       return cmw_request(otherlocalbuf);
    >     }
    >   } catch :):std::exception const& ex) {
    >     clientFormat == byteOrder ? localbuf.Reset() :
    > otherlocalbuf.Reset();
    >     throw;
    >   }
    >
    > }


    I don't think you should count this as "stopping propagation", as you-
    re-throw.

    You should further consider using Alexandrescu/Martinean's excellent
    scope guard for such things. That would give you e.g.

    cmw_request
    getRequest (uint8_t byteOrder
    , sock_type sock)
    {
    static ReceiveBuffer<SameFormatlocalbuf(4096);
    #ifdef ENDIAN_BIG
    static ReceiveBuffer<LeastSignificantFirstotherlocalbuf(4096);
    #else
    static ReceiveBuffer<MostSignificantFirstotherlocalbuf(4096);
    #endif

    uint8_t clientFormat;
    Read(sock, &clientFormat, 1);
    ON_BLOCK_EXIT_OBJ(byteOrder ? localbuf : otherlocalbuf,
    &buffer_class::Reset());
    if (clientFormat == byteOrder) {
    localbuf.sock_ = sock;
    while (!localbuf.GotPacket()) ;
    return cmw_request(localbuf);
    } else {
    otherlocalbuf.sock_ = sock;
    while (!otherlocalbuf.GotPacket()) ;
    return cmw_request(otherlocalbuf);
    }

    +1 for Paavo's remark. Use RAII, that gets you more time for good
    things in life ;-).

    Goran.
    Goran, Dec 12, 2011
    #6
  7. ittium

    Ebenezer Guest

    On Dec 12, 12:24 am, Paavo Helde <> wrote:
    > Ebenezer <> wrote in news:3937da31-10c3-4eea-9105-
    >
    > > The exception catching allows me to make some variables
    > > static that would otherwise be allocating and releasing
    > > memory from the heap each time the function is called.
    > > It is kind of experimental, but so far so good.

    >
    > This is just lack of RAII. Why don't you just put a Reset() call in the
    > destructor of ReceiveBuffer?
    >


    I think you may have a point(s) in the other thread and I'll
    have to do some digging on that later. So it seems easier to
    reply here.
    I'm intentionally avoiding RAII here in order
    to reuse resources. I don't think calling Reset in the dtor
    would help. (I can't think of other places where I avoid
    RAII.)
    Ebenezer, Dec 12, 2011
    #7
  8. ittium

    Goran Guest

    On Dec 12, 11:50 am, Ebenezer <> wrote:
    > On Dec 12, 12:24 am, Paavo Helde <> wrote:
    >
    > > Ebenezer <> wrote in news:3937da31-10c3-4eea-9105-

    >
    > > > The exception catching allows me to make some variables
    > > > static that would otherwise be allocating and releasing
    > > > memory from the heap each time the function is called.
    > > > It is kind of experimental, but so far so good.

    >
    > > This is just lack of RAII. Why don't you just put a Reset() call in the
    > > destructor of ReceiveBuffer?

    >
    > I think you may have a point(s) in the other thread and I'll
    > have to do some digging on that later.  So it seems easier to
    > reply here.
    > I'm intentionally avoiding RAII here in order
    > to reuse resources.  I don't think calling Reset in the dtor
    > would help.  (I can't think of other places where I avoid
    > RAII.)


    Perhaps Paavo was a bit too quick with his suggestion... You don't
    __need__ to destroy ReceiveBuffer. You can merely have an object whose
    destruction calls Reset() on the buffer (Reset() should be a no-fail
    operation, which seems a good guess). So you have best of both world:
    resource reuse that you hold dear, and application of RAII for simpler
    code.

    That said, it looks like you could get your resource reuse by changing
    ReceiveBuffer to receive the buffer it operates on from the outside.
    (I am guessing that the expensive resource is said buffer that is on
    the heap, and that Reset is dirt-cheap otherwise.)

    Note: function you've shown isn't thread-safe due to the use of the
    static. I don't know if it matters, but if it does, you should look
    for thread-local storage on your platform.

    Goran.
    Goran, Dec 12, 2011
    #8
  9. ittium

    Ebenezer Guest

    On Dec 12, 6:06 am, Goran <> wrote:
    > On Dec 12, 11:50 am, Ebenezer <> wrote:
    >
    >
    >
    >
    >
    > > On Dec 12, 12:24 am, Paavo Helde <> wrote:

    >
    > > > Ebenezer <> wrote in news:3937da31-10c3-4eea-9105-

    >
    > > > > The exception catching allows me to make some variables
    > > > > static that would otherwise be allocating and releasing
    > > > > memory from the heap each time the function is called.
    > > > > It is kind of experimental, but so far so good.

    >
    > > > This is just lack of RAII. Why don't you just put a Reset() call in the
    > > > destructor of ReceiveBuffer?

    >
    > > I think you may have a point(s) in the other thread and I'll
    > > have to do some digging on that later.  So it seems easier to
    > > reply here.
    > > I'm intentionally avoiding RAII here in order
    > > to reuse resources.  I don't think calling Reset in the dtor
    > > would help.  (I can't think of other places where I avoid
    > > RAII.)

    >
    > Perhaps Paavo was a bit too quick with his suggestion... You don't
    > __need__ to destroy ReceiveBuffer. You can merely have an object whose
    > destruction calls Reset() on the buffer (Reset() should be a no-fail
    > operation, which seems a good guess). So you have best of both world:
    > resource reuse that you hold dear, and application of RAII for simpler
    > code.
    >
    > That said, it looks like you could get your resource reuse by changing
    > ReceiveBuffer to receive the buffer it operates on from the outside.
    > (I am guessing that the expensive resource is said buffer that is on
    > the heap, and that Reset is dirt-cheap otherwise.)


    That's correct. Thanks for the reminder about scope guard.
    I have to think about that and your arguments here further.

    >
    > Note: function you've shown isn't thread-safe due to the use of the
    > static. I don't know if it matters, but if it does, you should look
    > for thread-local storage on your platform.
    >


    Yes, I'm aware of that. The program is currently single-threaded.
    I've thought about the possibility of makings it use two threads.
    (It is the middle tier of a 3-tier system.) One thread would handle
    requests from the front tier and the other thread would handle
    responses from the back tier. I don't think changing the program in
    that way would be a problem as far as the statics in this function.
    Ebenezer, Dec 12, 2011
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Oleg
    Replies:
    1
    Views:
    714
    Francisco Rodriguez
    Feb 18, 2004
  2. Replies:
    5
    Views:
    387
    E.J. Pitt
    Aug 26, 2005
  3. Brendon Costa
    Replies:
    0
    Views:
    760
    Brendon Costa
    Nov 3, 2007
  4. Replies:
    3
    Views:
    402
    Juha Nieminen
    Jun 18, 2009
  5. James Harris

    Exception propagation in C

    James Harris, Dec 29, 2011, in forum: C Programming
    Replies:
    63
    Views:
    1,506
    Joe keane
    Feb 17, 2012
Loading...

Share This Page