Requirements for exception classes

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Kevin Goodsell

    Requirements for exception classes

    If I'm writing a class that will be used as an exception, what kinds of
    things do I need to watch out for? For example, is it necessary to make
    sure that the members of the class don't throw?

    Suppose my class has one or more std::string members. These could throw
    bad_alloc on construction or copy. Is this dangerous? Here's an example:

    #include <string>

    class SimpleException
    {
    std::string text;
    public:
    SimpleException (const std::string &s) : text(s) {}
    const std::string &GetText() { return text; }
    };

    int main()
    {
    try {
    throw SimpleException ("BORKED!"); /* what if constructor throws? */
    }
    catch (SimpleExceptio n e) { /* what if copying throws? */
    // ...
    }

    return 0;
    }

    Thanks.

    -Kevin
    --
    My email address is valid, but changes periodically.
    To contact me please use the address from a recent posting.

  • Alexander Terekhov

    #2
    Re: Requirements for exception classes


    Kevin Goodsell wrote:[color=blue]
    >
    > If I'm writing a class that will be used as an exception, what kinds of
    > things do I need to watch out for? For example, is it necessary to make
    > sure that the members of the class don't throw?
    >
    > Suppose my class has one or more std::string members.[/color]

    Bad idea.
    [color=blue]
    > These could throw bad_alloc on construction or copy.[/color]

    Write your own, immutable, ref-counted (or something like that) thing.
    Beside that, you don't really need what() in any sensible application.
    [color=blue]
    > Is this dangerous?[/color]

    Yes. Make your constructors no-throw.

    regards,
    alexander.

    Comment

    • Gianni Mariani

      #3
      Re: Requirements for exception classes

      Kevin Goodsell wrote:[color=blue]
      > If I'm writing a class that will be used as an exception, what kinds of
      > things do I need to watch out for? For example, is it necessary to make
      > sure that the members of the class don't throw?[/color]

      An exception that throws is probably a double exception and innevitably
      an abort.
      [color=blue]
      >
      > Suppose my class has one or more std::string members. These could throw
      > bad_alloc on construction or copy. Is this dangerous? Here's an example:[/color]

      Well sure if you catch bad_alloc and throw a std::string, almost
      inevitably a Murphy's law candidate.
      [color=blue]
      >
      > #include <string>
      >
      > class SimpleException
      > {
      > std::string text;
      > public:
      > SimpleException (const std::string &s) : text(s) {}
      > const std::string &GetText() { return text; }
      > };
      >
      > int main()
      > {
      > try {
      > throw SimpleException ("BORKED!"); /* what if constructor throws? */
      > }
      > catch (SimpleExceptio n e) { /* what if copying throws? */[/color]


      I read once that catching references was a better way because it could
      deal with polymorphic exceptions.


      catch (const SimpleException & e) {


      Since I rarely use exceptions, I'd have to do more research but it is
      somthing you could consider.
      [color=blue]
      > // ...
      > }
      >
      > return 0;
      > }
      >[/color]

      Comment

      • Chris Dams

        #4
        Re: Requirements for exception classes

        Gianni Mariani <gi2nospam@mari ani.ws> writes:
        [color=blue][color=green]
        >> If I'm writing a class that will be used as an exception, what kinds of
        >> things do I need to watch out for? For example, is it necessary to make
        >> sure that the members of the class don't throw?[/color][/color]
        [color=blue]
        >An exception that throws is probably a double exception and innevitably
        >an abort.[/color]

        Not in g++ 3.2.3 at least (don't know what the standard says). I tried:

        #include<iostre am>

        using namespace std;

        class ding
        { public:
        ding(){cout << "ding constr." << endl; throw 5;}
        };

        class test
        { public:
        test(){cout << "test constr." << endl; throw ding();}
        };

        int main()
        { try
        { test t;
        } catch(int)
        { cout << "cought int" << endl;
        } catch(ding)
        { cout << "cought ding" << endl;
        }
        return 0;
        }

        output:

        test constr.
        ding constr.
        cought int

        Bye,
        Chris Dams

        Comment

        • Kevin Goodsell

          #5
          Re: Requirements for exception classes

          Chris Dams wrote:
          [color=blue]
          > Gianni Mariani <gi2nospam@mari ani.ws> writes:
          >
          >[color=green][color=darkred]
          >>>If I'm writing a class that will be used as an exception, what kinds of
          >>>things do I need to watch out for? For example, is it necessary to make
          >>>sure that the members of the class don't throw?[/color][/color]
          >
          >[color=green]
          >>An exception that throws is probably a double exception and innevitably
          >>an abort.[/color]
          >
          >
          > Not in g++ 3.2.3 at least (don't know what the standard says). I tried:
          >[/color]

          <code snipped>

          This is a good example of one of my "theories" that I am hoping to
          either prove or disprove. In the case of the initial construction of the
          exception object (at the 'throw' expression), no exception has been
          thrown yet. So if that constructor throws, I *think* the exception from
          the constructor should propagate instead of the original exception.
          Therefore it would be OK to throw from that constructor.

          As for copying of the exception object, I suspect a throw in that case
          would be bad. But the stuff I found in TC++PLS basically only said that
          an exception thrown *from a destructor* that is invoked as a result of
          "stack unwinding" during an exception causes terminate() to be called.

          I've been looking for more information, but haven't found anything good yet.

          -Kevin
          --
          My email address is valid, but changes periodically.
          To contact me please use the address from a recent posting.

          Comment

          • Gianni Mariani

            #6
            Re: Requirements for exception classes

            Chris Dams wrote:[color=blue]
            > Gianni Mariani <gi2nospam@mari ani.ws> writes:
            >
            >[color=green][color=darkred]
            >>>If I'm writing a class that will be used as an exception, what kinds of
            >>>things do I need to watch out for? For example, is it necessary to make
            >>>sure that the members of the class don't throw?[/color][/color]
            >
            >[color=green]
            >>An exception that throws is probably a double exception and innevitably
            >>an abort.[/color]
            >
            >
            > Not in g++ 3.2.3 at least (don't know what the standard says). I tried:
            >
            > #include<iostre am>
            >
            > using namespace std;
            >
            > class ding
            > { public:
            > ding(){cout << "ding constr." << endl; throw 5;}
            > };
            >
            > class test
            > { public:
            > test(){cout << "test constr." << endl; throw ding();}
            > };
            >
            > int main()
            > { try
            > { test t;
            > } catch(int)
            > { cout << "cought int" << endl;
            > } catch(ding)
            > { cout << "cought ding" << endl;
            > }
            > return 0;
            > }
            >
            > output:
            >
            > test constr.
            > ding constr.
            > cought int
            >[/color]

            This same code dumps on gcc 3.3.1

            test constr.
            ding constr.
            Abort (core dumped)


            #0 0x42029241 in kill () from /lib/i686/libc.so.6
            #1 0x4202902a in raise () from /lib/i686/libc.so.6
            #2 0x4202a7d2 in abort () from /lib/i686/libc.so.6
            #3 0x4009b65c in __cxxabiv1::__t erminate(void (*)())
            (handler=0x4202 a664 <abort>) at
            .../../../../gcc-3.3.1/libstdc++-v3/libsupc++/eh_terminate.cc :47
            #4 0x4009b55e in __gxx_personali ty_v0 (version=1, actions=6,
            exception_class =51381379722543 86944, ue_header=0x0, context=0xbffff 5e0)
            at ../../../../gcc-3.3.1/libstdc++-v3/libsupc++/eh_personality. cc:434
            #5 0x40107cd4 in _Unwind_RaiseEx ception_Phase2 (exc=0x8049fc8,
            context=0xbffff 5e0) at ../../gcc-3.3.1/gcc/unwind.inc:57
            #6 0x40107e0b in _Unwind_RaiseEx ception (exc=0x8049fc8) at
            .../../gcc-3.3.1/gcc/unwind.inc:127
            #7 0x4009b7cc in __cxa_throw (obj=0x8049fc8, tinfo=0x400c3e4 4,
            dest=0x400c3e44 <globals_static >) at
            .../../../../gcc-3.3.1/libstdc++-v3/libsupc++/eh_throw.cc:75
            #8 0x08048b78 in ding (this=0x8049f90 ) at zzz.cpp:8
            #9 0x08048afd in test (this=0xbffff75 f) at zzz.cpp:13
            #10 0x08048922 in main () at zzz.cpp:18

            Comment

            • Kevin Goodsell

              #7
              Re: Requirements for exception classes

              Gianni Mariani wrote:
              [color=blue]
              > Chris Dams wrote:[color=green]
              >>
              >>
              >>
              >> Not in g++ 3.2.3 at least (don't know what the standard says). I tried:
              >>
              >> #include<iostre am>
              >>
              >> using namespace std;
              >>
              >> class ding
              >> { public:
              >> ding(){cout << "ding constr." << endl; throw 5;}
              >> };
              >>
              >> class test
              >> { public:
              >> test(){cout << "test constr." << endl; throw ding();}
              >> };
              >>
              >> int main()
              >> { try
              >> { test t;
              >> } catch(int)
              >> { cout << "cought int" << endl;
              >> } catch(ding)
              >> { cout << "cought ding" << endl;
              >> }
              >> return 0;
              >> }
              >>
              >> output:
              >>
              >> test constr.
              >> ding constr.
              >> cought int
              >>[/color]
              >
              > This same code dumps on gcc 3.3.1
              >
              > test constr.
              > ding constr.
              > Abort (core dumped)[/color]

              Then either gcc 3.3.1 is wrong or I'm not correctly interpreting the
              standard.

              15.5.1 The terminate() function [except.terminat e]

              1 In the following situations exception handling must be abandoned for
              less subtle error handling techniques:

              --when the exception handling mechanism, after completing evaluation
              of the expression to be thrown but before the exception is caught
              (_except.throw_ ), calls a user function that exits via an uncaught
              exception,[134]

              <other cases snipped>

              2 In such cases,
              void terminate();
              is called (_lib.exception .terminate_). In the situation where no
              matching handler is found, it is implementation-defined whether or not
              the stack is unwound before terminate() is called. In all other situ-
              ations, the stack shall not be unwound before terminate() is called.
              An implementation is not permitted to finish stack unwinding prema-
              turely based on a determination that the unwind process will eventu-
              ally cause a call to terminate().

              Footnote 134) reads:

              134) For example, if the object being thrown is of a class with a copy
              constructor, terminate() will be called if that copy constructor exits
              with an exception during a throw.

              The key part being "*after* completing evaluation of the expression to
              be thrown but *before* the exception is caught".

              -Kevin
              --
              My email address is valid, but changes periodically.
              To contact me please use the address from a recent posting.

              Comment

              • Gianni Mariani

                #8
                Re: Requirements for exception classes

                Kevin Goodsell wrote:[color=blue]
                > Gianni Mariani wrote:
                >[color=green]
                >> Chris Dams wrote:
                >>[/color][/color]

                ....nippity snip
                [color=blue][color=green][color=darkred]
                >>>
                >>> output:
                >>>
                >>> test constr.
                >>> ding constr.
                >>> cought int
                >>>[/color]
                >>
                >> This same code dumps on gcc 3.3.1
                >>
                >> test constr.
                >> ding constr.
                >> Abort (core dumped)[/color]
                >
                >
                > Then either gcc 3.3.1 is wrong or I'm not correctly interpreting the
                > standard.
                >[/color]

                .... standard snipped
                [color=blue]
                > The key part being "*after* completing evaluation of the expression to
                > be thrown but *before* the exception is caught".[/color]

                I think I read the standard in a way that makes gcc 3.3.1 correct.

                i.e. The expression IS evaluated - it threw an exception so we're
                talking *after* and no exception is ever caught in this code so we're
                talking *before* and the doc says teminate should be called.

                What do you think ?


                Comment

                • Kevin Goodsell

                  #9
                  Re: Requirements for exception classes

                  Gianni Mariani wrote:
                  [color=blue]
                  > Kevin Goodsell wrote:
                  >[color=green]
                  >>
                  >>
                  >> Then either gcc 3.3.1 is wrong or I'm not correctly interpreting the
                  >> standard.
                  >>[/color]
                  >
                  > ... standard snipped
                  >[color=green]
                  >> The key part being "*after* completing evaluation of the expression to
                  >> be thrown but *before* the exception is caught".[/color]
                  >
                  >
                  > I think I read the standard in a way that makes gcc 3.3.1 correct.
                  >
                  > i.e. The expression IS evaluated - it threw an exception so we're
                  > talking *after* and no exception is ever caught in this code so we're
                  > talking *before* and the doc says teminate should be called.
                  >
                  > What do you think ?[/color]

                  I'm not following your logic. The expression to be thrown is the thing
                  that appears after 'throw'. In the case of

                  throw Foo();

                  The expression to be thrown is Foo(), and its evaluation is not complete
                  until the constructor finishes. Are you saying that it is completed by
                  throwing its own exception? I would have to disagree with that.

                  I looked through GCC's bugzilla and the DRs from WG21's web site to see
                  if I could find an explanation for GCC's handling of that code, but I
                  didn't find anything. I think it may be a bug that either hasn't been
                  reported or that I overlooked.

                  -Kevin
                  --
                  My email address is valid, but changes periodically.
                  To contact me please use the address from a recent posting.

                  Comment

                  • Gianni Mariani

                    #10
                    Re: Requirements for exception classes

                    Kevin Goodsell wrote:[color=blue]
                    > Gianni Mariani wrote:
                    >[color=green]
                    >> Kevin Goodsell wrote:
                    >>[color=darkred]
                    >>>
                    >>>
                    >>> Then either gcc 3.3.1 is wrong or I'm not correctly interpreting the
                    >>> standard.
                    >>>[/color]
                    >>
                    >> ... standard snipped
                    >>[color=darkred]
                    >>> The key part being "*after* completing evaluation of the expression
                    >>> to be thrown but *before* the exception is caught".[/color]
                    >>
                    >>
                    >>
                    >> I think I read the standard in a way that makes gcc 3.3.1 correct.
                    >>
                    >> i.e. The expression IS evaluated - it threw an exception so we're
                    >> talking *after* and no exception is ever caught in this code so we're
                    >> talking *before* and the doc says teminate should be called.
                    >>
                    >> What do you think ?[/color]
                    >
                    >
                    > I'm not following your logic. The expression to be thrown is the thing
                    > that appears after 'throw'. In the case of
                    >
                    > throw Foo();
                    >
                    > The expression to be thrown is Foo(), and its evaluation is not complete
                    > until the constructor finishes. Are you saying that it is completed by
                    > throwing its own exception? I would have to disagree with that.
                    >
                    > I looked through GCC's bugzilla and the DRs from WG21's web site to see
                    > if I could find an explanation for GCC's handling of that code, but I
                    > didn't find anything. I think it may be a bug that either hasn't been
                    > reported or that I overlooked.[/color]


                    It all depends on what "complete" means in this context.

                    a) Complete could mean that execution of the expression has ended. (if
                    it failed or not may not be relevant) In the sense that no further
                    expression processing needs to be made.

                    b) Complete in the sense that there were no exceptions in computing the
                    expression and the expression computation successfully finished.

                    I tend to believe that it is a). However, since there is ambiguity and
                    a standard with unintentional ambiguities is an error, I think this is a
                    candidate for a defect report.

                    I bags you write one since you brought it up ! :-)

                    Comment

                    Working...