Boost logo

Boost :

Subject: Re: [boost] [constrained_value] Constrained Value review results
From: Robert Kawulak (robert.kawulak_at_[hidden])
Date: 2010-10-16 21:29:57


> From: Stewart, Robert
> Given that each bound can be found in the four quadrants independent of the other, your bounded_int
> class template is restrictive.

Of course it is, because it is only meant to be a shortcut in one special and very common case. In all other cases "bounded" is the
one to use. Its default template parameters have been designed to simplify the usage in the other common use cases.

> Don't you need an easier way to supply and configure the bounds so
> each can be placed into any of the four quadrants independently?

Is the current way too complicated after looking at the examples in the docs?

> bounded simply arranges for the bounds to be of the value type so they can be altered at runtime,
> right?

By default, yes, but not in general. They may be of any type, see the example with bounded-length string, and depending on the type
used they can or can't be altered at runtime.

> bounded_int requires compile time constants for both ranges and cannot be changed at runtime.
> The key characteristic of the latter, to me, is that the bounds are fixed at compile time; they cannot
> be changed at runtime. However, I get that bounded can also be configured with types that generate an
> initial value at runtime that cannot be changed thereafter (so they are, effectively of type T const
> rather than T. Therefore, runtime mutability is not the entire picture for bounded_int.

Yes, this is what I tried to explain. ;-) The key functionality of "bounded_int" is making it easier to specify fixed bounds that
can be expressed with a compile-time constant expression. Not ANY fixed bounds.

> 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.

> In your example with quarter2type, you show:
>
> bounded<rational, quarter2type, half2type>
>
> Perhaps the best thing for what you're calling bounded_int is this;
>
> bounded<int, bound_c<0>, bound_c<10> >
>
> It isn't as succinct as this:
>
> bounded_int<int, 0, 10>

Rather:

    bounded<int, bound_c<int, 0>, bound_c<int, 10> >

...which is even less succint and redundant - in the bound_c template you would have to specify the type as the first parameter to
know what is the type of the second one. Another option is to have something like:

    template<intmax_t Value> bounded_c;

Which has two small issues:

- it is usable only for integer constant expressions (no pointers),
- it is not usable if one wants to use uintmax_t value too big to be expressed with intmax_t.

The issues could be overcame by defining several versions of the template, like:

    bound_c<typename Type, Type Value>
    bound_int_c<intmax_t Value>
    bound_uint_c<uintmax_t Value>

Which may make things more complicated.

> but it does eliminate the troubling naming problem and, I think, reduces confusion. Consider:
>
> bounded<int, bound_c<0>, big_prime>

Which currently looks like:

    bounded<int, mpl::int_<0>, big_prime>

I can't see a big difference. :P

In general, the idea looks interesting and also consistent with the idea of Vicente Botet to specify bounds inclusion like this:

    bounded<int, open_c<0>, close_c<100> >

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

        template <uintmax_t Value, typename Inclusion = included>
        struct uint_c; // bound as a compile-time constant uint

        template <typename Type, typename Inclusion = included>
        struct of_type; // bound mutable at runtime OR constant expressed with type

    }

This would allow to write:

    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<int, bound::int_c<0>, bound::of_type<big_prime> >
    bounded<int, bound::int_c<0, bound::included>, bound::of_type<int, bound::excluded> >

Being more explicit it is also unfortunately more verbose in most cases, compared to the current syntax:

    bounded_int<int, -10, 10>
    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_>

Implementation of the proposed syntax is relatively easy. However, it has also its weaknesses and maybe should rather be an
alternative to than replacement of the current syntax (it could be just another template-typedef for bounded, just as bounded_int
is). But then having two possible syntaxes to do exactly the same things sounds like a bad idea...

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