Boost logo

Boost :

From: Preston A. Elder (prez_at_[hidden])
Date: 2005-01-28 22:10:21


On Fri, 28 Jan 2005 16:09:28 +0300, Vladimir Prus wrote:

> This is indeed cannot be conveniently done in the current version. You can
> create a subclass of 'value_semantics', say,
> "ip_address_value_semantics_t", then create a function
> "ip_address_value_semantics", and use it instead of "value". But passing
> just the validator function is more convenient, so I intend to implement
> this functionality.
Why does your vector validator not share a common ancestor with your
non-vector validator?

It does a lexical_cast<T> inside it - when it would much more sense to use
a structure somewhat similar to the following:

// Implemented as a class so we can partially specialize it later on
template<class T>
class validate_internal
{
public:
    template<class charT>
    T operator()(const std::basic_string<charT> &s)
    {
        try
        {
            return boost::lexical_cast<T>(s);
        }
        catch (const bad_lexical_cast &)
        {
            throw invalid_option_value(s);
        }
    }
};

template<class T, class charT>
T validate_internal()
{
}

template<class T, class charT>
validate(boost::any &v,
         const std::vector<std::basic_string<charT> >& xs)
         T *, long)
{
    validators::check_first_occurance(v);
    std::basic_string<charT> s(validators::get_single_string(xs));
    static validate_internal<T> validator;
    v = validator(s);
}

template<class T, class charT>
validate(boost::any &v,
         const std::vector<std::basic_string<charT> >& xs)
         std::vector<T> *, long)
{
    if (v.empty())
        v = boost::any(std::vector<T>());
    std::vector<T> *tv = boost::any_cast<std::vector<T> >(&v);
    assert(tv);
    static validate_internal<T> validator;
    for (unsigned int i = 0; i < xs.size(); ++i)
        tv->push_back(validator(xs[i]));
}

This way, if I need my own validator for a specific type, I just need to
make the validate_internal specialization for my type, and it will work
stand-alone OR with a vector.

I have this exact issue, where I need to validate a custom type that needs
more than a lexical_cast, so my validate_internal (for my 'duration'
class) for that would look like:

template<>
class validate_internal<duration>
{
public:
    template<class charT>
    duration operator()(const std::basic_string<charT> &s)
    {
        try
        {
            date_duration date(not_a_date_time);
            time_duration time(not_a_date_time);
            StringToDuration(s, date, time);
            return duration(date, time);
        }
        catch (const invalid_duration_format &)
        {
            throw invalid_option_value(s);
        }
    }
};

This conversion would be used then whether I used value<duration>() or
value<std::vector<duration> >(). Which is what I'm pretty sure most
people want :)

It also makes it easier for people writing custom parsers, since they can
call the standard validation functions too. Notice, btw, how I made the
instantiation of the validator static - this should save some cycles
(not too many, but some) - by only ever creating one instance of the
validator for each type in each function calling it - as opposed to
creating an instance of the validator each time it is needed :)

As an aside, I know it would be easy, but is there any reason you did not
include the validators for std::set, std::multiset, std::list, std::deque,
std::queue and std::stack (the other 'big' single-storage containers)?
They would be very easy to do, except the first two use 'insert', the
second two use 'push_back' and the third two use 'push' to add an entry to
the container, respectively :)

Next - what is the use of the fourth argument to the validate() function?

Finally, as a feature request, I'd LOVE to have a way to be able to have
something like:
mykey.1.host = some_host
mykey.1.port = some_port
mykey.1.password = some_pass
mykey.1.priority = some_prio
mykey.2.host = some_host
mykey.2.port = some_port
mykey.2.password = some_password
mykey.2.priority = some_prio

Without having to define every possible 'middle' portion (1, 2, etc).
Even if this only allowed sequential numbers as the 'middle' portion.
Right now, one of my data items is defined as:

mykey = some_host1 some_port1 some_pass1 some_prio1
mykey = some_host2 some_port2 some_pass2 some_prio2

Which is kind of ugly, when it would make much more sense to do:
[mykey.1]
host = some_host1
port = some_port1
password = some_password1
priority = some_priority1

and so on.

Anyway, let me know :)

-- 
PreZ :)
Founder. The Neuromancy Society (http://www.neuromancy.net)

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk