Problem with exceptions, templates and pure virtual functions

Discussion in 'C++' started by Dmitry Prokoptsev, Jul 8, 2006.

  1. Hello,

    I need to write a class for exceptions which can be thrown, caught,
    stored and thrown once again. I have written the following code:

    --- begin ---
    #include <string>

    class Exception {
    public:
    virtual ~Exception() throw() {}
    virtual const char* what() const throw() = 0;
    virtual void raise() = 0;
    };

    class RuntimeError: public Exception {
    public:
    RuntimeError(const std::string& what): what_(what) {}
    const char* what() const throw() { return what_.c_str(); }
    void raise() { throw *this; }
    private:
    std::string what_;
    };

    template<class Self, class Base> class ExceptionImpl: public Base {
    public:
    typedef ExceptionImpl<Self, Base> Impl;

    void raise() { throw *this; }
    template<class A> ExceptionImpl(A a): Base(a) {}
    // ExceptionImpl(const Self& self): Base(self) {}
    };

    class AbstractError: public RuntimeError {
    public:
    AbstractError(const std::string& what): RuntimeError(what) {}
    virtual void foo() = 0;
    };

    class ConcreteError: public ExceptionImpl<ConcreteError, AbstractError>
    {
    public:
    ConcreteError(const std::string& what): Impl(what) {}
    void foo() {}
    };

    int main()
    {
    ConcreteError c("foo");
    return 0;
    }
    --- end ---

    ....and received those strange compiler messages (Microsoft Visual C++
    2005):

    --- begin ---
    Compiling...
    exceptions.cpp
    c:\src\temp\exceptions\exceptions.cpp : warning C4717:
    'ConcreteError::ConcreteError' : recursive on all control paths,
    function will cause runtime stack overflow
    Linking...
    exceptions.obj : error LNK2019: unresolved external symbol "public:
    __thiscall ExceptionImpl<class ConcreteError,class
    AbstractError>::ExceptionImpl<class ConcreteError,class
    AbstractError>(class ExceptionImpl<class ConcreteError,class
    AbstractError> const &)"
    (??0?$ExceptionImpl@VConcreteError@@VAbstractError@@@@QAE@ABV0@@Z)
    referenced in function "public: virtual void __thiscall
    ExceptionImpl<class ConcreteError,class AbstractError>::raise(void)"
    (?raise@?$ExceptionImpl@VConcreteError@@VAbstractError@@@@UAEXXZ)
    exceptions.obj : error LNK2001: unresolved external symbol "public:
    virtual void * __thiscall ExceptionImpl<class ConcreteError,class
    AbstractError>::`scalar deleting destructor'(unsigned int)"
    (??_G?$ExceptionImpl@VConcreteError@@VAbstractError@@@@UAEPAXI@Z)
    exceptions.obj : error LNK2001: unresolved external symbol "public:
    virtual void * __thiscall ExceptionImpl<class ConcreteError,class
    AbstractError>::`vector deleting destructor'(unsigned int)"
    (??_E?$ExceptionImpl@VConcreteError@@VAbstractError@@@@UAEPAXI@Z)
    C:\src\temp\exceptions\Debug\exceptions.exe : fatal error LNK1120: 3
    unresolved externals
    --- end ---

    If I uncomment the commented constructor in ExceptionImpl,
    ConcreteError::ConcreteError() stops to be recursive, but what compiler
    wants from me when it complains about absence of `scalar deleting
    destructor'?

    Linker messages disappear if pure virtual function AbstractError::foo()
    is removed or replaced with non-pure or non-virtual, or if body of
    function ExceptionImpl::raise() is replaced with empty one.

    gcc3 seem to handle this without any warnings, so what's wrong with
    this code?

    Thanks in advance.
     
    Dmitry Prokoptsev, Jul 8, 2006
    #1
    1. Advertising

  2. * Dmitry Prokoptsev:
    >
    > template<class Self, class Base> class ExceptionImpl: public Base {
    > public:
    > typedef ExceptionImpl<Self, Base> Impl;
    >
    > void raise() { throw *this; }
    > template<class A> ExceptionImpl(A a): Base(a) {}
    > // ExceptionImpl(const Self& self): Base(self) {}
    > };
    >
    > If I uncomment the commented constructor in ExceptionImpl,
    > ConcreteError::ConcreteError() stops to be recursive,


    The main /technical/ problem here seems to lie in 'throw *this', which
    invokes the ExceptionImpl copy constructor (instantiation of
    ExceptionImpl). In the case where Base is abstract and causes
    ExceptionImpl to be abstract, you're not permitted to instantiate
    ExceptionImpl. You can't instantiate an abstract class.

    More generally, I don't think this design is good.

    Start by deriving from std::runtime_error, and forget the template
    stuff; at least make a non-template version work first.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jul 8, 2006
    #2
    1. Advertising

  3. Alf P. Steinbach wrote:

    > The main /technical/ problem here seems to lie in 'throw *this', which
    > invokes the ExceptionImpl copy constructor (instantiation of
    > ExceptionImpl). In the case where Base is abstract and causes
    > ExceptionImpl to be abstract, you're not permitted to instantiate
    > ExceptionImpl. You can't instantiate an abstract class.


    Ok, but replacing this line with "throw static_cast<Self&>(*this)"
    didn't help.

    > More generally, I don't think this design is good.


    Maybe... then could you please give any advices about doing all this
    stuff? I just want each exception class to provide functions
    "duplicate()" and "raise()" without writing these functions for each
    class by hand.

    > Start by deriving from std::runtime_error, and forget the template
    > stuff; at least make a non-template version work first.


    Of course, in real life all exceptions will be derived from
    std::runtime_error, but in code mentioned above that inheritance was
    removed to minimize dependencies.
     
    Dmitry Prokoptsev, Jul 8, 2006
    #3
  4. Dmitry Prokoptsev

    Guest

    Dmitry Prokoptsev wrote:
    > Alf P. Steinbach wrote:
    >
    > > The main /technical/ problem here seems to lie in 'throw *this', which
    > > invokes the ExceptionImpl copy constructor (instantiation of
    > > ExceptionImpl). In the case where Base is abstract and causes
    > > ExceptionImpl to be abstract, you're not permitted to instantiate
    > > ExceptionImpl. You can't instantiate an abstract class.

    >
    > Ok, but replacing this line with "throw static_cast<Self&>(*this)"
    > didn't help.


    that's because that would lead to recursive instantiations.
    What I would do is provide a partial specialization of ExceptionImpl
    for AbtrsactError and provide a defauly implemnatation of Foo() and
    since you are overriding it in ConcreteError any way, that would work
    for you.

    Though I have to agree with Alf that this is not the best of designs.

    >
    > > More generally, I don't think this design is good.

    >
    > Maybe... then could you please give any advices about doing all this
    > stuff? I just want each exception class to provide functions
    > "duplicate()" and "raise()" without writing these functions for each
    > class by hand.
    >
    > > Start by deriving from std::runtime_error, and forget the template
    > > stuff; at least make a non-template version work first.

    >
    > Of course, in real life all exceptions will be derived from
    > std::runtime_error, but in code mentioned above that inheritance was
    > removed to minimize dependencies.
     
    , Jul 8, 2006
    #4
    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. IK
    Replies:
    2
    Views:
    612
    hemraj
    Jul 23, 2004
  2. Todd Aspeotis
    Replies:
    3
    Views:
    470
    Kanenas
    May 30, 2005
  3. Replies:
    5
    Views:
    457
    mlimber
    Oct 12, 2005
  4. John Goche
    Replies:
    10
    Views:
    758
    Marcus Kwok
    Dec 8, 2006
  5. a
    Replies:
    7
    Views:
    364
    dasjotre
    Jun 28, 2007
Loading...

Share This Page