HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

A class to encapsulate a pair of ints with range-checking

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withrangecheckingclassencapsulatepairints

Problem

The goal is to have a helper class to wrap the concept of a resolution safely,
encapsulating the range check into the class, since otherwise it can get lost by
a client not so cautious.

I think this is all the context that's required, but an additional bit of info that may be helpful is that this looks a bit like what Rust does, where you can build restricted types out of the more common ones. Then again, I don't know Rust, and the above is a bit of a gossip that I've heard, so it may be wrong.

Technically it's just a pair of two unsigned integers of some kind (normal or
long etc.), but the type is similar for both, of course. I want to enforce this
as well, so that no one uses two different types by accident, thus instead of
using std::pair I use Eigen::Matrix. Eigen is
alright since it's a hard dependency of the project and I'm sure it won't go
away.

Version one (I think it's suboptimal for the reasons given below):

Header:

#ifndef RESOLUTION_H
#define RESOLUTION_H

#include 

class Resolution {
public:
    Resolution(unsigned int _x, unsigned int _y);
    static Resolution min();
    static Resolution max();
    inline unsigned int x() const { return res[0]; }
    inline unsigned int y() const { return res[1]; }
protected:
    Eigen::Matrix res;
};

#endif


Impl:

#include 

#include "resolution.h"

Resolution::Resolution(unsigned int _x, unsigned int _y) {
    assert(_x  min().x() && _y > min().y());
    res[0] = _x; res[1] = _y;
}

Resolution Resolution::min() { return Resolution{600, 480}; }
Resolution Resolution::max() { return Resolution{3840, 2160}; }


Bad bad because unsigned int is hardcoded so many times and can get lost if
someone tries to change it later. Attempt 2, fixing it:

Header:

```
#ifndef RESOLUTION_H
#define RESOLUTION_H

#include

class Resolution {
public:
typedef unsigned int impl_type;
public:
Resolution(impl_type _x, impl_type _y);
static Resolution min();
static Resolution max();
inline i

Solution

I'd start by separating concerns. To do that, I'd design this as two types. One just restricts an arithmetic type to a range. The other holds a pair of those to form a coordinate. Restricting an arithmetic type to a range might look something like this:

template 
class bounded { 
    T val;

    void assure_range(T v) {
        if (v < lower || upper <= v)
            throw std::range_error("Value out of range");
    }

public:
    bounded(bounded const &o) : val(o.val) {}

    bounded &operator=(T v) { 
        assure_range(v);
        val = v;
        return *this;
    }

    bounded(T const &v=T()) {
        assure_range(v);
        val = v;
    }

    operator T() { return val; }
};


Then a struct to hold a couple of those is trivial:

template
struct coordinate {
    bounded x;
    bounded y;
};


This would be instantiated something like this:

coordinate r{1024, 768};

Code Snippets

template <class T, T lower, T upper>
class bounded { 
    T val;

    void assure_range(T v) {
        if (v < lower || upper <= v)
            throw std::range_error("Value out of range");
    }

public:
    bounded(bounded const &o) : val(o.val) {}

    bounded &operator=(T v) { 
        assure_range(v);
        val = v;
        return *this;
    }

    bounded(T const &v=T()) {
        assure_range(v);
        val = v;
    }

    operator T() { return val; }
};
template<class T, T min_x, T max_x, T min_y, T max_y>
struct coordinate {
    bounded<T, min_x, max_x> x;
    bounded<T, min_y, max_y> y;
};
coordinate<unsigned, 640, 3840, 480, 2160> r{1024, 768};

Context

StackExchange Code Review Q#124404, answer score: 3

Revisions (0)

No revisions yet.