
On Tue, Dec 2, 2008 at 6:53 PM, Robert Kawulak <robert.kawulak@gmail.com> wrote:
You're right, this is not a valid usage of constrained. The rule is that the result of the constraint invocation for the value must always be identical as long as you don't access either of them as non-const. Here the value and the constraint are accessed as const only, yet the result of the constraint invocation may be different for two subsequent invocations. I guess this is another thing that should be stated explicitly in the docs? ;-)
Yes ;-) - I think the docs should be more specific on the requirements on the underlying type and constraint required to guarantee the guarantee (perhaps in a separate section that focuses on just the requirements and in detail). Hopefully, there is a concise way of stating the requirements. You might also want to consider the following cases: class unconstrainable1 { public: unconstrainable1() { s_last_constructed = this; } unconstrainable1 *last_constructed() const { return s_last_constructed; } private: static unconstrainable1 *s_last_constructed; some_state m_state; }; The above is a forced example, but the same principle (where the class provides non-const access to itself or its state in its constructor) applies in more realistic cases (like objects that register themselves in a registry). class unconstrainable2 { unconstrainable2(some_state &state) : m_state_ptr(&state) {} some_state &state() const { return *m_state_ptr; } private: // can't design constraints based on *m_state_ptr; some_state *m_state_ptr; } Perhaps a concise way to describe a requirement (addressing both these cases, as well as the const mutable problem) is to say that the constraint must depend only on what is mutable by expressions that require a non-const reference to the underlying object? Are there also requirements that the way in which the underlying object is CopyConstructable and/or Swappable maintain the constraint? (I know a lot of these might seem obvious, but it's good to have an accurate list of things that might go wrong when you're thinking about making a type constrainable, or something *is* going wrong and you're trying to figure out why)
Speaking of access to the underlying object in situations where you need non-const access to it... you could provide a member function that takes a unary Callable as a parameter, and calls the Callable with a copy of the underlying object as the argument. After the call returns, it assigns the (perhaps modified) value of the copy back to the underlying object (through the policy / checking the constraint). AFAICT, your guarantee is still never violated, and this would provide a really useful piece of functionality.
Maybe I've missed something, but this is not too different from what you can already do (having constrained x and callable f):
// copy the value, modify, assign x = f(x.value());
The difference is that here f is responsible for making the copy. Are there some other important factors that would justify adding the member you describe?
if f is void f(value_type &v); then you need: value_type temp = x.value(); f(temp); x = temp; If this is a frequent use case, I'd prefer to be able to write call_using_copy(f, x); or call_using_copy(&f, x); or something like that. I guess it doesn't have to be a member (although that would be ok too). In any case, this is not a big deal, as it can be added as a free function. I just looked at the code (it's very nice to look at!), and am trying to get a grasp on the policy design. At first I had some doubts about it, but am getting more and more convinced that you have the design right. This is what I understand: * the policy gets called iff there is a problem * a problem happens when the underlying object is constructed with an invalid value, in which case the policy gets called with that invalid value as both the first and second parameters * a problem happens when an invalid value wants to be assigned to the underlying object, in which case the policy gets called with the current (valid) value as the first argument, and the new (invalid) value as the second argument * the first argument must satisfy the constraint when/if the policy returns. OK, that seems pretty crisp to me. Is there anything else to it? Best, Stjepan