![]() |
Problem with exceptions, templates and pure virtual functions
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@@VAbstractE rror@@@@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@@VAbstractErro r@@@@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@@VAbstractErro r@@@@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. |
Re: Problem with exceptions, templates and pure virtual functions
* 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? |
Re: Problem with exceptions, templates and pure virtual functions
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. |
Re: Problem with exceptions, templates and pure virtual functions
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. |
| All times are GMT. The time now is 12:59 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.