Boost logo

Boost :

Subject: Re: [boost] [review][constrained_value] Review of ConstrainedValueLibrary begins today
From: Stjepan Rajko (stjepan.rajko_at_[hidden])
Date: 2008-12-04 11:25:22


On Tue, Dec 2, 2008 at 6:53 PM, Robert Kawulak <robert.kawulak_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk