An integer type with range checking.

J

Jason Heyes

I would like to be able to extract an integer from a stream without having
to write a test when I want the integer within some range. Unfortunately
there is no range-checked integer type in the standard C++ library. Consider
the following function:

std::istream &read_range_checked_integer(std::istream &is, int
&result_value)
{
int value;
if (!(is >> value))
{
std::cout << "failed to extract integer" << std::endl;
return is;
}

if (!(value >= 0 && value < 64))
{
std::cout << "extracted integer is out of range" << std::endl;
is.setstate(std::ios_base::failbit);
return is;
}

result_value = value;
return is;
}

I want a type to help write read_range_checked_integer more succinctly. For
example:

std::istream &read_range_checked_integer(std::istream &is, int
&result_value)
{
range_checked_integer<0, 64> value;
if (!(is >> value))
{
std::cout << "failed to extract range_checked_integer<0, 64>" <<
std::endl;
return is;
}

result_value = value.get_int();
return is;
}

Can somebody help me find a type that behaves like range_checked_integer?
Thanks alot.
 
K

Karsten Baumgarten

Jason said:
I would like to be able to extract an integer from a stream without having
to write a test when I want the integer within some range. Unfortunately
there is no range-checked integer type in the standard C++ library. Consider
the following function:

std::istream &read_range_checked_integer(std::istream &is, int
&result_value)
{
int value;
if (!(is >> value))
{
std::cout << "failed to extract integer" << std::endl;
return is;
}

if (!(value >= 0 && value < 64))
{
std::cout << "extracted integer is out of range" << std::endl;
is.setstate(std::ios_base::failbit);
return is;
}

result_value = value;
return is;
}

I want a type to help write read_range_checked_integer more succinctly. For
example:

std::istream &read_range_checked_integer(std::istream &is, int
&result_value)
{
range_checked_integer<0, 64> value;
if (!(is >> value))
{
std::cout << "failed to extract range_checked_integer<0, 64>" <<
std::endl;
return is;
}

result_value = value.get_int();
return is;
}

Can somebody help me find a type that behaves like range_checked_integer?
Thanks alot.

Create a class for ranged integer values and overload the stream
operators. For example:


// rint.h

#ifndef _RINT_H_
#define _RINT_H_

#include <iostream>

class RInt
{
public:
RInt(int min, int max);
virtual ~RInt();

friend std::istream& operator >>(std::istream& s, RInt& i);
friend std::eek:stream& operator <<(std::eek:stream& s, const RInt& i);


private:
int value, min, max;
};


// rint.cpp

#include "rint.h"
#include <iostream>

using namespace std;

RInt::RInt(int min, int max) : value (min), min (min), max (max)
{
}

RInt::~RInt()
{
}

istream& operator >>(istream& s, RInt& i)
{
int tmp;
s >> tmp;

if (tmp < i.min || tmp > i.max)
s.clear(s.failbit);
else
i.value = tmp;

return s;
}

ostream& operator <<(ostream& s, const RInt& i)
{
s << i.value;
return s;
}

#endif //_RINT_H_
 
J

Jerry Coffin

Jason said:
I would like to be able to extract an integer from a stream without
having to write a test when I want the integer within some range.

This may be a bit more elaborate than you were thinking of, but it
should do the job:

// Partially tested code. Compiles and works to at least some degree,
// but may still harbor bugs.
#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;
}
};

The most obvious difference is that this passes the underlying type as
a template parameter instead of making it part of the name. This makes
ranges of various different types about equally easy to work with --
e.g. you can just about as easily do a bounded<double, 0.001, 0.1> as a
bounded<int, 0, 512> like you asked for.
 
J

Jason Heyes

Jerry Coffin said:
This may be a bit more elaborate than you were thinking of, but it
should do the job:

// Partially tested code. Compiles and works to at least some degree,
// but may still harbor bugs.
#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;
}
};

The most obvious difference is that this passes the underlying type as
a template parameter instead of making it part of the name. This makes
ranges of various different types about equally easy to work with --
e.g. you can just about as easily do a bounded<double, 0.001, 0.1> as a
bounded<int, 0, 512> like you asked for.

Looks good. You allow for implicit conversion between the bounded type and
type T. Why, then, do you need operator=(T const &v)?
 
K

Karl Heinz Buchegger

Jason said:
Looks good. You allow for implicit conversion between the bounded type and
type T. Why, then, do you need operator=(T const &v)?

Think of:
bounded< int, 1, 5 > a = 3;
bounded< int, 5, 10 > b = 8;
a = b;
 
J

Jerry Coffin

Jason Heyes wrote:

[ ... ]
Looks good. You allow for implicit conversion between the bounded
type and type T. Why, then, do you need operator=(T const &v)?

Instantiating a template over two different sets of parameters results
in two completely unrelated types. The copy constructor is ONLY used to
assign between two objects of exactly the same type.

As such, without this, assignment from one bounded type to a different
bounded type would involve two type conversions -- but only one can be
done implicitly, so all such assignments would require explicit type
casts (which I'd consider undesirable).
 
J

Jason Heyes

Jerry Coffin said:
Instantiating a template over two different sets of parameters results
in two completely unrelated types. The copy constructor is ONLY used to
assign between two objects of exactly the same type.

As such, without this, assignment from one bounded type to a different
bounded type would involve two type conversions -- but only one can be
done implicitly, so all such assignments would require explicit type
casts (which I'd consider undesirable).

Would you consider defining operator+ and operator- for bounded?
 
J

Jerry Coffin

Jason Heyes wrote:

[ ... ]
Would you consider defining operator+ and operator- for bounded?

I've considered going whole-hog and adding the full range of operators
to it, but so far I've never seen a benefit that justified the work.
OTOH, if somebody else sees it as a worthwhile way to spend their time,
they're certainly more than welcome to take the code and run with it.

In case that wasn't clear: the code is in the public domain, so anybody
can treat it as if they had written it themself, and use it in any way
they please, modify it as they see fit, etc. If I was going to control
it at all, the sole stipulation would be that it never be published as
"free software". I won't try to enforce that, but if you do it, don't
plan on ever being invited to any of my parties. Better yet, make sure
I never know about it. :)
 
J

Jason Heyes

Jerry Coffin said:
I've considered going whole-hog and adding the full range of operators
to it, but so far I've never seen a benefit that justified the work.

As long as I can easily read a bounded type from a stream, I'm happy. :)
Beyond that I don't see the need for a bounded type. For example, I wouldn't
try to replace ordinary ints with bounded ones. Would you?
 
J

Jerry Coffin

Jason Heyes wrote:

[ ... ]
As long as I can easily read a bounded type from a stream, I'm happy. :)
Beyond that I don't see the need for a bounded type. For example, I
wouldn't try to replace ordinary ints with bounded ones. Would you?

Yes, when it's important that the variable stay within certain bounds.
 
J

Jason Heyes

Jerry Coffin said:
Jason Heyes wrote:

[ ... ]
As long as I can easily read a bounded type from a stream, I'm happy. :)
Beyond that I don't see the need for a bounded type. For example, I
wouldn't try to replace ordinary ints with bounded ones. Would you?

Yes, when it's important that the variable stay within certain bounds.

Often you don't need a bounded type to keep a variable within bounds.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,201
Messages
2,571,048
Members
47,650
Latest member
IanTylor5

Latest Threads

Top