views:

388

answers:

6

I've been looking for an example that shows how to implement constraints in C++ (or a boost library that lets me do this easily), but without much luck. The best I could come up with off the top of my head is:

#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>

template<typename T>
class constrained
{
    public:
     constrained(boost::function<bool (T)> constraint, T defaultValue, T value = defaultValue)
     {
      ASSERT(constraint(defaultValue));
      ASSERT(constraint(value));

      this->value = value;
      this->defaultValue = defaultValue;   
      this->constraint = constraint;      
     }

     void operator=(const T &assignedValue)
     {
      if(constraint(assignedValue))
       value = assignedValue;  
     } 

    private:
     T value;
     T defaultValue;
     boost::function<bool (T)> constraint;
};

int main(int argc, char* argv[])
{
    constrained<int> foo(boost::lambda::_1 > 0 && boost::lambda::_1 < 100, 5, 10);

    foo = 20; // works
    foo = -20; // fails

    return 0;
}

Of course there's probably some more functionality you'd want from a constraint class. This is just an idea for a starting point.

Anyway, the problem I see is that I have to overload all operators that T defines in order to make it really behave like a T, and there is no way for me to find out what those are. Now, I don't actually need constraints for that many different types, so I could just leave out the template and hard code them. Still, I'm wondering if there's a general (or at least more succint/elegant) solution or if there's anything seriously wrong with my approach.

+1  A: 

I might just be confused, but if you are facing parameters that must not violate specific constraints, wouldn't it be easiest to create a class for them, checking for constraints in constructors and assignment operators?

DevSolar
I could do that of course, but I have lots of different parameters with lots of different constraints, so the result would be a lot of brittle boilerplate code. In the above solution, all the logic is in a single variable declaration. I'd say that's better.
drby
+3  A: 

Looks good as for tiny example. But be sure to implement all the operators and handle somehow wrong values.

foo = 100; // works
++foo; // should throw an exception or perform an assert

Use boost operators to help you with operators overload.

And probably it would be good to have an option as a template parameter: either exception or assertion.

I'd use such class. It is always better to have an index parameter that auto check vector range and do assertion.

void foo( VectorIndex i );
Mykola Golubyev
"It is always better to have an index parameter that auto check vector range and do assertion." - how about at()?
DevSolar
@DevSolar: a) at() throws an exception; b) such VectorIndex can be implemented for any legacy Array in the project.
Mykola Golubyev
+2  A: 

I agree with Mykola Golubyev that boost operators would help.

You should define all the operators that you require for all the types you are using.

If any of the types you are using don't support the operator (for example the operator++()), then code that calls this method will not compile but all other usages will.

If you want to use different implementations for different types then use template specialisation.

iain
+2  A: 

You don't need to overload all operators as others have suggested, though this is the approach that offers maximum control because expressions involving objects of type constrained<T> will remain of this type.

The alternative is to only overload the mutating operators (=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, pre and post ++, pre and post --) and provide a user-defined conversion to T:

template<typename T>
class constrained {
    ... // As before, plus overloads for all mutating operators
public:
    operator T() const {
        return value;
    }
};

This way, any expression involving a constrained<T> object (e.g. x + y where x is int and y is constrained<int>) will be an rvalue of type T, which is usually more convenient and efficient. No safety is lost, because you don't need to control the value of any expression involving a constrained<T> object -- you only need to check the constraints at a time when a T becomes a constrained<T>, namely in constrained<T>'s constructor and in any of the mutating operators.

j_random_hacker
+2  A: 

Boost.Constrained_Value may be of interest to you. It was reviewed last December, but it is not in the latest Boost release. IIRC, the review was mostly positive, but the decision is still pending.

Éric Malenfant
A: 

Boost actually had such a library under discussion (I don't know what became of it). I've also written my own version of such a type, with slightly different behaviour (less flexible, but simpler). I've blogged an admittedly somewhat biased comparison here: Constrained vs. restricted value types

Edit: apparently Eric knows better what happened to boost's implementation.