# Creating a number with a range

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

1. ### Joe Van DykGuest

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:stream& operator<<(std: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

Joe

Joe Van Dyk, Aug 11, 2006

2. ### blytkerchanGuest

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:stream& operator<<(std: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

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

3. ### Joe Van DykGuest

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:stream& operator<<(std: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

> 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
4. ### Joe Van DykGuest

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:stream& operator<<(std: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

>> 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
5. ### Jerry CoffinGuest

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
6. ### Markus SchoderGuest

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
7. ### Joe Van DykGuest

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
8. ### Jerry CoffinGuest

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