From: Rune Allnor on
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
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
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
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
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! ]