
On Mon, Dec 8, 2008 at 9:35 AM, Robert Kawulak <robert.kawulak@gmail.com> wrote:
From: Stjepan Rajko
I am inclined to think that the implementation you have now (minus the asserts and the nomenclature) covers all of the uses fairly well. Existence of in-between use cases (conditionally monitored values, e.g., log whenever the temperature is over 35 degrees Celsius) leads me to believe that neither of {monitored value, conditional value} should be implemented in terms of the other. I think they should be implemented under a common abstraction, and I think your implementation implements that abstraction (again, minus the asserts and the nomenclature).
I think the current implementation is not the most suitable one for monitored values. The asignment operator is:
if( constraint()(v) ) _value() = v; else error_handler()(_value(), v, _constraint());
So calling the monitor (error_handler) excludes assignment of the value, unless the monitor performs the assignment by itself. This is a bit clumsy -- e.g., having the case with logging the temperature if it exceeds a treshold, monitor would not only have to log, but also to assign the value.
I see implementation of monitored values' assignment a bit different:
if( _monitor(_value, new_value) ) // monitor decides whether the value should be assigned _value = new_value; // but does not perform the assignment by itself
Then, conditionally-monitored values extend this by defining the following monitor callback:
if( _condition(new_value) ) // no need to invoke the monitor return true; else // invoke the monitor return _monitor(old_value, new_value, _condition);
And finally, constrained value would be a conditionally-monitored value, where the condition is the constraint and the inner monitor callback is the error policy.
Then you have a different mechanism for monitored vs. conditionally monitored values. Instead, you could have a wrapper for the policy that does the assignment for you. purely_monitored_value<T, action> expands to: what_is_now_called_constrained<T, always_false, always_assign<action> >
For now, I would be plenty happy if you just took out the asserts, or made them optional (with defaulting to asserts, if you wish). That way I can at least start experimenting with your library in a monitored_value context, and let you know how it goes (I have use cases for this).
By saying about making the invariant asserts optional you mean something like wrapping them in a conditional compilation macro (like BOOST_CONSTRAINED_VALUE_NO_INVARIANT_ASSERTS) to be able to turn them off globally?
That would be fine.
I still can't convince myself to the idea of separating invariant from the test. IMO guarantee of the invariant is a strong point of the library. Giving up the guarantee not only may make the library more complicated for the users, but it may also lower the value of the library as a debugging device (since there will be less checks for coherence of the given set of policies, more opportunities to make a bug).
You do *not* give up the invariant. You are just saying that the test and the invariant are not the same thing. You still always guarantee the invariant. Not only that, but allowing wiggle room between the test and invariant allows you to guarantee invariants where before you couldn't. Furthermore, I think that the case where the test and invariant are the same thing is very important, and should take a prominent place in the library documentation. For example: take what you have in the documentation right now that depends on test <==> invariant, and call it "Perfectly Constrained Values" or "Verifiably/testably Constrained Values" or something. Then briefly discuss the test ==> invariant case as "look, you can do this too, just understand that the test doesn't test for the invariant exactly". If you think about it, you are already separating the test from the invariant in your advanced examples. Think about the object that uses the library to keep track of it's min/max. The test checks for whether you have crossed the previous min/max. Sure, you could say the invariant is the same: "the object is between the min and max present in the constraint object". But really, what kind of guarantee is this? If I need to look at the constraint to figure out what I'm being guaranteed, I might as well look at the value itself and see where it stands. I would consider this as "no invariant". There, you already have docs for this case :-) Best, Stjepan