Problem with exceptions, templates and pure virtual functions

  • Thread starter Dmitry Prokoptsev
  • Start date
D

Dmitry Prokoptsev

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.
 
A

Alf P. Steinbach

* 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.
 
D

Dmitry Prokoptsev

Alf said:
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.

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.
 
A

amparikh

Dmitry said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top