Creating a number with a range

Discussion in 'C++' started by Joe Van Dyk, Aug 11, 2006.

  1. Joe Van Dyk

    Joe Van Dyk Guest

    Hi,

    Say I have:
    class Latitude
    {
    friend bool operator==(const Latitude& lhs, const Latitude& rhs);
    friend bool operator<(const Latitude& lhs, const Latitude& rhs);
    friend std::eek:stream& operator<<(std::eek:stream& os, const Latitude& l);

    public:
    explicit Latitude(double latitude);
    double latitude() const { return latitude_; }

    private:
    double latitude_;
    };


    So, it's essentially a wrapper around a double. The constructor enforces
    that the latitude can't be less than -90 or greater than 90. Comparison
    operators do the comparison of the double.

    Now, I've got to do the same with Longitude and Altitude. They're all
    kinda the same, except that Longitude can range from -180 to 180, and
    Altitude doesn't really have a limit (I guess its the limit on what a
    double can hold).

    How can I consolidate this logic? Templates? Inheritance? Or am I going
    about this completely wrong?

    Joe
    Joe Van Dyk, Aug 11, 2006
    #1
    1. Advertising

  2. Joe Van Dyk

    blytkerchan Guest

    Joe Van Dyk wrote:
    > Hi,
    >
    > Say I have:
    > class Latitude
    > {
    > friend bool operator==(const Latitude& lhs, const Latitude& rhs);
    > friend bool operator<(const Latitude& lhs, const Latitude& rhs);
    > friend std::eek:stream& operator<<(std::eek:stream& os, const Latitude& l);
    >
    > public:
    > explicit Latitude(double latitude);
    > double latitude() const { return latitude_; }
    >
    > private:
    > double latitude_;
    > };
    >
    >
    > So, it's essentially a wrapper around a double. The constructor enforces
    > that the latitude can't be less than -90 or greater than 90. Comparison
    > operators do the comparison of the double.
    >
    > Now, I've got to do the same with Longitude and Altitude. They're all
    > kinda the same, except that Longitude can range from -180 to 180, and
    > Altitude doesn't really have a limit (I guess its the limit on what a
    > double can hold).
    >
    > How can I consolidate this logic? Templates? Inheritance? Or am I going
    > about this completely wrong?

    One more or less elegant way to approach this might be to use a policy
    for validation:

    -- BEGIN EXAMPLE CODE --
    // DerangedDouble.h
    #include <stdexcept>

    template < class ValidationPolicy >
    class DerangedDouble
    {
    public :
    DerangedDouble( double d )
    : d_(d)
    {
    if (!ValidationPolicy::validate(d))
    throw std::range_error("value out of range!");
    else
    { /* all is well */ }
    }

    private :
    double d_;
    };

    // ValidationPolicies.h
    struct LatitudeValidation
    {
    static bool validate(double d)
    {
    return d < 90 && d > -90;
    }
    };

    struct LongitudeValidation
    {
    static bool validate(double d)
    {
    return d >= -180 && d <= 180;
    }
    };

    struct AltitudeValidation
    {
    static bool validate(double d)
    {
    return d >= 0;
    }
    };

    // test.cpp
    #include "DerangedDouble.h"
    #include "ValidationPolicies.h"

    typedef DerangedDouble< LongitudeValidation > Longitude;
    typedef DerangedDouble< LatitudeValidation > Latitude;
    typedef DerangedDouble< AltitudeValidation > Altitude;

    int main()
    {
    Longitude longitude(0);
    Latitude latitude(0);
    Altitude altitude(0);

    return 0;
    }
    --- END EXAMPLE CODE ---
    The fun with that is that you can easily make another class, say
    "Temperature" with a TemperatureValidation policy.
    Have a look at Andrei Alexandrescu's book "Modern C++ Design" for more
    extensive information on policies :)

    rlc
    blytkerchan, Aug 11, 2006
    #2
    1. Advertising

  3. Joe Van Dyk

    Joe Van Dyk Guest

    On Thu, 10 Aug 2006 20:54:42 -0700, blytkerchan wrote:

    > Joe Van Dyk wrote:
    >> Hi,
    >>
    >> Say I have:
    >> class Latitude
    >> {
    >> friend bool operator==(const Latitude& lhs, const Latitude& rhs);
    >> friend bool operator<(const Latitude& lhs, const Latitude& rhs);
    >> friend std::eek:stream& operator<<(std::eek:stream& os, const Latitude& l);
    >>
    >> public:
    >> explicit Latitude(double latitude);
    >> double latitude() const { return latitude_; }
    >>
    >> private:
    >> double latitude_;
    >> };
    >>
    >>
    >> So, it's essentially a wrapper around a double. The constructor enforces
    >> that the latitude can't be less than -90 or greater than 90. Comparison
    >> operators do the comparison of the double.
    >>
    >> Now, I've got to do the same with Longitude and Altitude. They're all
    >> kinda the same, except that Longitude can range from -180 to 180, and
    >> Altitude doesn't really have a limit (I guess its the limit on what a
    >> double can hold).
    >>
    >> How can I consolidate this logic? Templates? Inheritance? Or am I going
    >> about this completely wrong?

    > One more or less elegant way to approach this might be to use a policy
    > for validation:
    >
    > -- BEGIN EXAMPLE CODE --
    > // DerangedDouble.h
    > #include <stdexcept>
    >
    > template < class ValidationPolicy >
    > class DerangedDouble
    > {
    > public :
    > DerangedDouble( double d )
    > : d_(d)
    > {
    > if (!ValidationPolicy::validate(d))
    > throw std::range_error("value out of range!");
    > else
    > { /* all is well */ }
    > }
    >
    > private :
    > double d_;
    > };
    >
    > // ValidationPolicies.h
    > struct LatitudeValidation
    > {
    > static bool validate(double d)
    > {
    > return d < 90 && d > -90;
    > }
    > };
    >
    > struct LongitudeValidation
    > {
    > static bool validate(double d)
    > {
    > return d >= -180 && d <= 180;
    > }
    > };
    >
    > struct AltitudeValidation
    > {
    > static bool validate(double d)
    > {
    > return d >= 0;
    > }
    > };
    >
    > // test.cpp
    > #include "DerangedDouble.h"
    > #include "ValidationPolicies.h"
    >
    > typedef DerangedDouble< LongitudeValidation > Longitude;
    > typedef DerangedDouble< LatitudeValidation > Latitude;
    > typedef DerangedDouble< AltitudeValidation > Altitude;
    >
    > int main()
    > {
    > Longitude longitude(0);
    > Latitude latitude(0);
    > Altitude altitude(0);
    >
    > return 0;
    > }
    > --- END EXAMPLE CODE ---
    > The fun with that is that you can easily make another class, say
    > "Temperature" with a TemperatureValidation policy.
    > Have a look at Andrei Alexandrescu's book "Modern C++ Design" for more
    > extensive information on policies :)
    >
    > rlc


    Thanks! I was thinking something along those lines. Why is
    AltitudeValidation::validate static?

    Joe
    Joe Van Dyk, Aug 11, 2006
    #3
  4. Joe Van Dyk

    Joe Van Dyk Guest

    On Fri, 11 Aug 2006 14:09:36 +0000, Joe Van Dyk wrote:

    > On Thu, 10 Aug 2006 20:54:42 -0700, blytkerchan wrote:
    >
    >> Joe Van Dyk wrote:
    >>> Hi,
    >>>
    >>> Say I have:
    >>> class Latitude
    >>> {
    >>> friend bool operator==(const Latitude& lhs, const Latitude& rhs);
    >>> friend bool operator<(const Latitude& lhs, const Latitude& rhs);
    >>> friend std::eek:stream& operator<<(std::eek:stream& os, const Latitude& l);
    >>>
    >>> public:
    >>> explicit Latitude(double latitude);
    >>> double latitude() const { return latitude_; }
    >>>
    >>> private:
    >>> double latitude_;
    >>> };
    >>>
    >>>
    >>> So, it's essentially a wrapper around a double. The constructor enforces
    >>> that the latitude can't be less than -90 or greater than 90. Comparison
    >>> operators do the comparison of the double.
    >>>
    >>> Now, I've got to do the same with Longitude and Altitude. They're all
    >>> kinda the same, except that Longitude can range from -180 to 180, and
    >>> Altitude doesn't really have a limit (I guess its the limit on what a
    >>> double can hold).
    >>>
    >>> How can I consolidate this logic? Templates? Inheritance? Or am I going
    >>> about this completely wrong?

    >> One more or less elegant way to approach this might be to use a policy
    >> for validation:
    >>
    >> -- BEGIN EXAMPLE CODE --
    >> // DerangedDouble.h
    >> #include <stdexcept>
    >>
    >> template < class ValidationPolicy >
    >> class DerangedDouble
    >> {
    >> public :
    >> DerangedDouble( double d )
    >> : d_(d)
    >> {
    >> if (!ValidationPolicy::validate(d))
    >> throw std::range_error("value out of range!");
    >> else
    >> { /* all is well */ }
    >> }
    >>
    >> private :
    >> double d_;
    >> };
    >>
    >> // ValidationPolicies.h
    >> struct LatitudeValidation
    >> {
    >> static bool validate(double d)
    >> {
    >> return d < 90 && d > -90;
    >> }
    >> };
    >>
    >> struct LongitudeValidation
    >> {
    >> static bool validate(double d)
    >> {
    >> return d >= -180 && d <= 180;
    >> }
    >> };
    >>
    >> struct AltitudeValidation
    >> {
    >> static bool validate(double d)
    >> {
    >> return d >= 0;
    >> }
    >> };
    >>
    >> // test.cpp
    >> #include "DerangedDouble.h"
    >> #include "ValidationPolicies.h"
    >>
    >> typedef DerangedDouble< LongitudeValidation > Longitude;
    >> typedef DerangedDouble< LatitudeValidation > Latitude;
    >> typedef DerangedDouble< AltitudeValidation > Altitude;
    >>
    >> int main()
    >> {
    >> Longitude longitude(0);
    >> Latitude latitude(0);
    >> Altitude altitude(0);
    >>
    >> return 0;
    >> }
    >> --- END EXAMPLE CODE ---
    >> The fun with that is that you can easily make another class, say
    >> "Temperature" with a TemperatureValidation policy.
    >> Have a look at Andrei Alexandrescu's book "Modern C++ Design" for more
    >> extensive information on policies :)
    >>
    >> rlc

    >
    > Thanks! I was thinking something along those lines. Why is
    > AltitudeValidation::validate static?
    >
    > Joe


    Oh, nevermind, I get it. That make it a class function, or whatever the
    correct terminology is.

    template<class ValidationPolicy>
    class BaseNumber
    {
    public:
    explicit BaseNumber(double val)
    : value_(0)
    {
    if (!ValidationPolicy::validate(val))
    {
    throw std::range_error(std::string("Needs to be between "
    + ValidationPolicy::min + " and "
    + ValidationPolicy::max));
    }
    value_ = val;
    }

    double value() const { return value_; }

    private:
    double value_;
    };

    struct BaseValidation
    {
    static double max, min;
    static bool validate(double val)
    {
    return val >= min && val <= max;
    }
    };

    struct AltitudeValidation : public BaseValidation
    {
    AltitudeValidation() { max = 10000000; min = 0; }
    };
    typedef BaseNumber<AltitudeValidation> Altitude;


    It's not working, since I can't access max and min inside
    BaseValidation::validate.

    Is something like the above possible? Or am I completely going in the
    wrong direction?

    Joe
    Joe Van Dyk, Aug 11, 2006
    #4
  5. Joe Van Dyk

    Jerry Coffin Guest

    In article <>,
    says...

    [ ... ]

    > So, it's essentially a wrapper around a double. The constructor enforces
    > that the latitude can't be less than -90 or greater than 90. Comparison
    > operators do the comparison of the double.


    You've gotten (at least) one suggestion already, but IMO, using a policy
    to do nothing more than specify a range is overkill. I've posted this a
    few times before, but I guess once more won't hurt too much:

    #include <exception>
    #include <iostream>
    #include <functional>

    template <class T, T lower, T upper, class less=std::less<T> >
    class bounded {
    T val;

    static bool check(T const &value) {
    return less()(value, lower) || less()(upper, value);
    }

    public:
    bounded() : val(lower) { }

    bounded(T const &v) {
    if (check(v))
    throw std::domain_error("out of range");
    val = v;
    }

    bounded(bounded const &init) : val(init.v) {}

    bounded &operator=(T const &v) {
    if (check(v))
    throw std::domain_error("Out of Range");
    val = v;
    return *this;
    }

    operator T() const { return val; }

    friend std::istream &operator>>(std::istream &is, bounded &b) {
    T temp;
    is >> temp;

    if (check(temp))
    is.setstate(std::ios::failbit);
    b.val = temp;
    return is;
    }
    };

    It might make sense to use a policy template, but the policy would be to
    govern what happens when you attempt to assign an out-of-range value. As
    it stands right now, this throws an exception, but for different
    circumstances, it might be reasoanble to reduce the new value modulo the
    range (or upper value of the range), or simply fail the assignment,
    leaving the variable unaltered. I've never had a need for that, however,
    so I've never gotten beyond the point of mentioning it in a post or two.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Aug 11, 2006
    #5
  6. Jerry Coffin wrote:

    > In article <>,
    > says...
    >
    > [ ... ]
    >
    >> So, it's essentially a wrapper around a double. The constructor enforces
    >> that the latitude can't be less than -90 or greater than 90. Comparison
    >> operators do the comparison of the double.

    >
    > You've gotten (at least) one suggestion already, but IMO, using a policy
    > to do nothing more than specify a range is overkill. I've posted this a
    > few times before, but I guess once more won't hurt too much:
    >
    > #include <exception>
    > #include <iostream>
    > #include <functional>
    >
    > template <class T, T lower, T upper, class less=std::less<T> >
    > class bounded {


    But T would need to be an integral type here making it useless
    for the OP.
    Markus Schoder, Aug 11, 2006
    #6
  7. Joe Van Dyk

    Joe Van Dyk Guest

    On Fri, 11 Aug 2006 20:21:45 +0200, Markus Schoder wrote:

    > Jerry Coffin wrote:
    >
    >> In article <>,
    >> says...
    >>
    >> [ ... ]
    >>
    >>> So, it's essentially a wrapper around a double. The constructor enforces
    >>> that the latitude can't be less than -90 or greater than 90. Comparison
    >>> operators do the comparison of the double.

    >>
    >> You've gotten (at least) one suggestion already, but IMO, using a policy
    >> to do nothing more than specify a range is overkill. I've posted this a
    >> few times before, but I guess once more won't hurt too much:
    >>
    >> #include <exception>
    >> #include <iostream>
    >> #include <functional>
    >>
    >> template <class T, T lower, T upper, class less=std::less<T> >
    >> class bounded {

    >
    > But T would need to be an integral type here making it useless
    > for the OP.


    As T can't be a floating point type, right? Is that going to be fixed in
    an upcoming standard?
    Joe Van Dyk, Aug 11, 2006
    #7
  8. Joe Van Dyk

    Jerry Coffin Guest

    In article <44dccabb$0$10151$-online.net>,
    says...

    [ ... ]

    > But T would need to be an integral type here making it useless
    > for the OP.


    Sorry -- I'd meant to mention that (at least IMO) most things that deal
    with hard boundaries probably shouldn't be using floating point in the
    first place. Floating point and "hard" are almost mutually exclusive
    concepts. A floating point number usually needs to be viewed as a rather
    soft, imprecise kind of thing, so if you're dealing with hard limits,
    chances are you shouldn't be using a floating point number.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Aug 14, 2006
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    46
    Views:
    949
    Antoon Pardon
    Jul 25, 2006
  2. Lambda
    Replies:
    2
    Views:
    381
    James Kanze
    Jul 16, 2008
  3. Tomoyuki Kosimizu

    Range does not take an Range object.

    Tomoyuki Kosimizu, Nov 25, 2003, in forum: Ruby
    Replies:
    3
    Views:
    138
    Tomoyuki Kosimizu
    Nov 27, 2003
  4. David Bird
    Replies:
    1
    Views:
    195
    Tiago Macedo
    Jun 23, 2008
  5. Joey Zhou
    Replies:
    5
    Views:
    230
    Joey Zhou
    Apr 15, 2011
Loading...

Share This Page