Boost logo

Boost :

Subject: Re: [boost] [constrained_value] Constrained Value review results
From: Robert Kawulak (robert.kawulak_at_[hidden])
Date: 2010-10-28 22:28:31


> From: Stewart, Robert

> I suggest that your documentation present a list of the supported use cases and then develop them
> according to some measure of frequency of use or, perhaps more appropriate, by increasing complexity.

Thanks for the suggestions, I'll try to make the docs more clear.

> > > The _c suffix, borrowed from MPL, is good for specifying
> > > that the bounds are compile time constants.
> > > Adding that to "bounded" seems wrong because the bounded's
> > > value can still change, within the confines
> > > of the bounds. (The same is true of "static" and "ct" as
> > > suffixes or prefixes.)
> >
> > That's right, but so far I don't think we've found better names.
>
> None of the names is good and I haven't thought of one that would work well. That's why I've been
> suggesting alternative syntaxes that use bounded (or constrained) so that we don't need a name for
> what's defying a meaningful name.

Changing the syntax for bounded or not, I wouldn't want to give up providing something as convenient as bounded_int (or let it be
bounded_ct) just because we couldn't find a better name. Compare

    bounded<int, bound::int_c<0>, bound::int_c<10>>

to

    bounded_ct<int, 0, 10>

Moreover, consider this erroneous case:

    bounded<unsigned char, bound::int_c<-10>, bound::int_c<1000> >

A sufficiently smart compiler could warn in such case, but the warning would point to implementation details (comparison of unsigned
char and int in within_bounds class). This, however, will make it complain with clear indication of the error:

    bounded_ct<unsigned char, -10, 1000>

> > We could have the bounds specifiers and bounds inclusion specifiers:
> >
> > namespace bound {
> >
> > struct included; // bound always included in the interval
> > struct excluded; // bound always excluded from the interval
> > struct optional; // can be included/excluded at runtime
> >
> > template <typename Type, Type Value, typename
> > Inclusion = included>
> > struct c; // bound as a compile-time constant expression
> >
> > template <intmax_t Value, typename Inclusion = included>
> > struct int_c; // bound as a compile-time constant int
>
> Can/should this support optional? Giving the initial value using a compile time constant is not
> unreasonable, but is it wise to provide for that use case? Might it be better to require runtime
> initialization of runtime changeable bounds?

I'm not sure I understand you. What bound::optional means is that the bound can be "opened"/"closed" at runtime; that is, you may
switch between [0, 10] and (0, 10] independently to the fact whether the bound's value is fixed or mutable. I know "optional" may be
confusing at first encounter, but so far I haven't found a better name.

> > Being more explicit it is also unfortunately more verbose in
> > most cases, compared to the current syntax:
> >
> > bounded_int<int, -10, 10>
>
> This is implicitly inclusive of the bounds which restricts its use still further.

It doesn't restrict its use - you may change bounds inclusion by specifying subsequent template arguments. The defaults are to
include bounds, which is the more common use case.

> > bounded<int>
> > bounded<rational, quarter2type, half2type>
> > bounded<int, mpl::int_<0>, big_prime>
> > bounded<int, mpl::int_<0>, int, throw_exception<>,
> > mpl::false_, mpl::true_>
>
> The last of those isn't meaningful at a glance. What do the Booleans mean at the end? One has to
> count parameters and match them to the arguments. Clearly, one could created named Booleans --
> "included" and "excluded" for example

This is exactly what came to my mind just after I sent my reply. bounds::included, bounds::excluded and bounds::optional mentioned
above can be defined anyway, even for the old syntax. Why I haven't thought of this earlier... ;-)

So let's see it again. To express the following intervals ("?" means bound mutable at runtime):

    int [-10, 10]
    int [?, ?]
    rational [1/4, 1/2]
    big_int [0, big_prime]
    unsigned [42, ?)

The new way:

    bounded<int, bound::int_c<-10>, bound::int_c<10> >
    bounded<int, bound::of_type<int>, bound::of_type<int> >
    bounded<rational, bound::of_type<quarter2type>, bound::of_type<half2type> >
    bounded<big_int, bound::int_c<0>, bound::of_type<big_prime> >
    bounded<unsigned, bound::uint_c<42, bound::included>, bound::of_type<unsigned, bound::excluded> >

The current way:

    bounded<int, mpl::int_<-10>, mpl::int_<10> >
    bounded<int>
    bounded<rational, quarter2type, half2type>
    bounded<big_int, mpl::int_<0>, big_prime>
    bounded<unsigned, mpl::integral_c<unsigned, 0>, unsigned, throw_exception<>, bound::included, bound::excluded>

I have yet another idea, somehow merging the two syntaxes. It looks like the current syntax as long as you don't specify bounds
inclusion explicitly, in which case it would look similar to the new syntax:

    bounded<int, mpl::int_<-10>, mpl::int_<10> >
    bounded<int>
    bounded<rational, quarter2type, half2type>
    bounded<big_int, mpl::int_<0>, big_prime>
    bounded<unsigned, bound<mpl::integral_c<unsigned, 0>, included>, bound<unsigned, excluded> >

> I agree with that to a point. You already provide multiple ways of doing things, however:
> constrained, bounded, and bounded_int.

Yes, but those are slightly different things (each one is a special case of another), while providing two syntaxes for bounded would
really be two ways to do exactly the same.

> The right way to view that is to make the common easy and the
> less common possible.

Both the syntaxes are equivalent in terms of possibilities. The tradeoff is higher verbosity in most cases (the new syntax) vs.
obscurity in less common cases (the current syntax).

Best regards,
Robert


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