|
From: Rune Allnor on 17 Jun 2008 05:14 Hi all. I have this class to validate a string according to a specification contained in a string. I make a function object and overload operator() to emulate a free function: class validator1 { public: bool operator()(std::string); }; The problem is to identify breaches of the spec in the string, depending on the exact nature of the error. Suffice it to say that there are several types of possible errors, and some errors concern single characters while other errors concern groups of characters. So it is not obvious how to report problem identifiers back to the caller. At present, the validating method looks like this: bool validator1::operator()(std::string s) { for (size_t i = 0; i<s.size();++i) { if (error1(s[i])) {throw exception1a(i);} if (error2(s[i])) {throw exception1b(i);} } return true; } The routine checks for a number of errors, and throws an exception to indicate what type of error is encountered, and the offending character in the string. If no errors are encountered, the call returns 'true'. While this works, I am not very happy with it. First, there is the mixing of return mechanisms. A good result is indicated by return code while a bad result is returned by exception. Second, the different types of errors causes an elaborate hierarchy of exception types, which somehow becomes clumsy. There is the alternative to add error inspection methods in the class and throw 'simple' exceptions: class validator2 { size_t errorpos_; public: size_t getErrorPos() const; bool operator()(std::string); }; bool validator1::operator()(std::string s) { for (size_t i = 0; i<s.size();++i) { if (error1(s[i])) {throw exception2a();} if (error2(s[i])) {throw exception2b();} } return true; } In this case the type of the thrown exception identifies the cause of the error and the caller can use the inspector method getErrorPos() to identify the offending character. As far as I can tell, the choise of one method over the other is a matter of personal preference. Or is there some argument (stated above or which I might have missed) which would be considered decisive for (not) choosing any of the solutions? Or are there standard ways to handle these problems? I consider return codes and return Variant objects not to be relevant otions, as much as a matter of 'aestethics' as for any other reason. Rune. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Alberto Ganesh Barbati on 17 Jun 2008 11:57 Rune Allnor ha scritto: > > At present, the validating method looks like this: > > bool validator1::operator()(std::string s) > { > for (size_t i = 0; i<s.size();++i) > { > if (error1(s[i])) {throw exception1a(i);} > if (error2(s[i])) {throw exception1b(i);} > } > return true; > } > > The routine checks for a number of errors, and throws > an exception to indicate what type of error is encountered, > and the offending character in the string. If no errors > are encountered, the call returns 'true'. Why does it return a bool? If the function either returns true or throws, then you could as well make it return void. > While this works, I am not very happy with it. > First, there is the mixing of return mechanisms. > A good result is indicated by return code while > a bad result is returned by exception. If you make the function return void, you will get: * good result: return * bad result: exception which is what one would expect. > Second, the different types of errors causes > an elaborate hierarchy of exception types, which > somehow becomes clumsy. > > There is the alternative to add error inspection > methods in the class and throw 'simple' exceptions: > > class validator2 { > size_t errorpos_; > public: > size_t getErrorPos() const; > bool operator()(std::string); > }; > > bool validator1::operator()(std::string s) > { > for (size_t i = 0; i<s.size();++i) > { > if (error1(s[i])) {throw exception2a();} > if (error2(s[i])) {throw exception2b();} > } > return true; > } > > In this case the type of the thrown exception > identifies the cause of the error and the caller > can use the inspector method getErrorPos() > to identify the offending character. > > As far as I can tell, the choise of one method > over the other is a matter of personal preference. Storing the result of the validation in the validator class is a bad design IMHO. It requires that the validator object is accessible at the catch site, which may or may not be happen. Moreover, it makes the validator non-reentrant. > Or is there some argument (stated above or which > I might have missed) which would be considered > decisive for (not) choosing any of the solutions? > Or are there standard ways to handle these > problems? > > I consider return codes and return Variant objects > not to be relevant otions, as much as a matter of > 'aestethics' as for any other reason. Actually, I would steer away from exception in this case. From a philosophical point of view, a validation failure is not an error condition and so it shouldn't be modeled as an exception. One possible approach is to create a polymorphic hierarchy of validation failures which are instantiated when needed. For example: struct validation_failure { virtual ~validation_failure() {} virtual void report_to_user() = 0; }; struct validation_failure_a : validation_failure { // state and constructor here virtual void report_to_user(); }; struct validation_failure_b : validation_failure { // state and constructor here virtual void report_to_user(); }; std::auto_ptr<validation_failure> validator::operator()(std::string s) { for (size_t i = 0; i<s.size();++i) { if (error1(s[i])) { return std::auto_ptr<validation_failure>( new validation_failure_a(i)); } if (error2(s[i])) { return std::auto_ptr<validation_failure>( new validation_failure_b(i)); } } return std::auto_ptr<validation_failure>(); } At the call site, the code might be simply: if (std::auto_ptr<validation_failure> failure = val(str)) { failure->report_to_user(); } notice that the auto_ptr will take care of deleting the failure object if needed. If you don't like auto_ptr, you might use shared_ptr or any other pointer type. HTH, Ganesh -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Alberto Ganesh Barbati on 18 Jun 2008 10:26 Stefan Ram ha scritto: > Rune Allnor <allnor(a)tele.ntnu.no> writes: >> I have this class to validate a string according to a >> specification contained in a string. > > A class models an entity, a function models an activity. > To validate is an activity. So, this responsibilit can > be assigned to a function, not a class. > This is old school. There are several patterns where activities can be modeled with classes, in particular the Strategy pattern (http://en.wikipedia.org/wiki/Strategy_pattern) and the Command pattern (http://en.wikipedia.org/wiki/Command_pattern). UI validation tends to be implemented using the Strategy pattern, so I see nothing wrong with modeling validation "activities" as classes. Just my opinion, Ganesh -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Rune Allnor on 18 Jun 2008 10:40 On 18 Jun, 04:57, Alberto Ganesh Barbati <AlbertoBarb...(a)libero.it> wrote: .... > At the call site, the code might be simply: > > if (std::auto_ptr<validation_failure> failure = val(str)) > { > failure->report_to_user(); > } > > notice that the auto_ptr will take care of deleting the failure object > if needed. Just let me see if I understand why a function can return a pointer to a virtual base class: - The derived class is only instantiated on a failure condition. - The auto_ptr will see to that the destructor is called at end of scope. The virtual destructor ensures that the correct destructor is called. - The return value will be an invalid pointer if the input is valid, since no constructors of error reports will have been called. So, the task for today will be to see if I can combine this with the general scheme proposed in a different post (which I repeat below since the post where it was proposed apparently will be deleted from USENET archives within a few days.) Thanks for the help! Rune /////////////////////////////////////////////// So one might write a method such as report status( ::std::string const & string ) { ... } and an appropriate class report { ... } The usage might be, for example, report const string_status( status( string )); if( string_status.is_good() )... else ::std::cerr << string_status; -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Alberto Ganesh Barbati on 19 Jun 2008 07:03 Rune Allnor ha scritto: > On 18 Jun, 04:57, Alberto Ganesh Barbati <AlbertoBarb...(a)libero.it> > wrote: > ... >> At the call site, the code might be simply: >> >> if (std::auto_ptr<validation_failure> failure = val(str)) >> { >> failure->report_to_user(); >> } >> >> notice that the auto_ptr will take care of deleting the failure object >> if needed. > > Just let me see if I understand why a function can return > a pointer to a virtual base class: The base class is not virtual in this case. It's a regular base class with a virtual destructor, a completely different concept. virtual base classes are introduced with this syntax: class derived : public virtual base {...}; ^^^^^^^ > > - The derived class is only instantiated on a failure > condition. Correct > - The auto_ptr will see to that the destructor is called > at end of scope. The virtual destructor ensures that > the correct destructor is called. Correct > - The return value will be an invalid pointer if the > input is valid, since no constructors of error reports > will have been called. Correct. However, on success the pointer will be null and, strictly speaking, a null pointer is not "invalid": it's a valid pointer that points to no object. HTH, Ganesh -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
|
Next
|
Last
Pages: 1 2 3 Prev: std::terminate() and std::unexpected() Next: static variables in g++ |