Boost logo

Boost :

From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2003-01-02 05:56:10


Jaap Suter wrote:
> Hi,

Hi Jaap,

> I've written some MPL meta-functions that might be useful. But first a
> question though...
>
> Is it possible that (under certain conditions) the following line:
>
> mpl::int_c<
> mpl::minus<
> mpl::int_c< 0 >,
> mpl::int_c< 3 >
> >::type::value
> >
>
> has a different type than this one:
>
> mpl::minus<
> mpl::int_c< 0 >,
> mpl::int_c< 3 >
> >::type

Uhm, in fact, these are always different:

    typedef mpl::int_c<
                mpl::minus<
                    mpl::int_c< 0 >,
                    mpl::int_c< 3 >
>::type::value
> t1;

    typedef mpl::minus<
            mpl::int_c< 0 >,
            mpl::int_c< 3 >
>::type t2;
        
    BOOST_STATIC_ASSERT((!boost::is_same<t1,t2>::value));
    BOOST_STATIC_ASSERT((boost::is_same<
          t2
        , mpl::integral_c<int,-3>
>::value));

The motivation is that in ideal C++ word that has typedef templates all of
the following assertions would be true,

    int_c<N> === integral_c<int,N>
    bool_c<C> === integral_c<bool,C>
    size_t_c<N> === integral_c<std::size_t,N>

so the issue will go away by itself; another reason, of course, is that the
current semantics was the simplest to implement ;).

That's not to say that it should stay this way. Ideally, to support true
mixed-type arithmetic, e.g. 'plus< rational<1,10>, int_c<5> >', the current
MPL primitives such as 'plus', 'minus', 'multiply', etc. need a major
re-write, to become something along these lines:

    template<
          typename N1
        , typename N2
>
    struct plus
    {
        typedef typename promotion_traits<N1,N2>::type U_;
        typedef typename apply2<
              plus_traits<typename U_::tag>
            , typename construct<U_,N1>::type
            , typename construct<U_,N2>::type
>::type type;
    };

    template<>
    struct plus_traits<integral_c_tag>
    {
        template< typename N1, typename N2 > struct apply
            : integral_c<
                  typename N1::value_type
                , (N1::value + N2::value)
>
        {
        };
    };

If we had this infrastructure in place, making 'minus< int_c<0>, int_c<3>
>::type === int_c<-3>' would be trivial.

Figuring out a reasonable way to specify the promotion rules is probably the
hardest part there...

> And now on to the good stuff...
>
> If you go to http://jaap.flipcode.com/boost/ you will find a
> file called 'bit.zip'. In it are four files called:
>
> bit_and.hpp
> bit_or.hpp
> bit_xor.hpp
> bit_shift_right.hpp
>
> I am using all of these in my own code currently, and they
> work fine there.

Wonderful!

> Considering it's mostly copy paste work from the other
> meta-functions, it shouldn't present any difficulties. I didn't
> need the bit_shift_left meta function, so I haven't written
> that one yet. Once discussions has settled, I'll be glad to add
> that one too.
>
> Open issues:
>
> 1. I don't like the 'bit_' prefix, but there are already
> logical 'and' and 'or' operators and corresponding files. One
> option is to use 'bitwise' instead, but I liked the shortness of
> 'bit'.

IMO 'bit' is perfectly OK - in fact, the standard alternative tokens for &
and | are called 'bitand' and 'bitor' correspondingly. What I am kind of
ambivalent about is where to put the underscore, in the middle ('bit_and')
or at the end ('bitand_'). I _think_ I prefer the latter, because, if
applied consistently, it makes everything easier to remember:

    run-time compile-time

    void void_
    sizeof sizeof_
    &&, and and_
    ||, or or_
    !, not not_
    &, bitand bitand_
    |, bitor bitor_

BTW, I think 'bit_shift_right' should be named simply 'shift_right'; after
all, may be someone would like to "overload" it :).

>
> 2, On my PC I've placed these files in the boost/mpl/bit
> directory, just like boost/mpl/arithmetic and boost/mpl/logical
> have their own directories.
> Obviously, once dust has settled we need to make a 'bit.hpp'
> that includes all the bit_* files, and place it in the MPL root.

Actually, I was planning on bringing the content of "arithmetic", "logical",
and "comparison" directories to "boost/mpl" root (still preserving the
corresponding composite headers). In that light, I would suggest putting the
new headers directly into the root directory as well.

> 3. The bit_xor and bit_or functions are really simple. The
> bit_and function however presented a problem for the default values
> of parameters 3 to 5. I'm using 0xFFFFFFFF right now, which happens
> to work on my (32 bit) platform, but obviously this is not a good
> solution. Any suggestions are welcome.

That's somewhat a part of the larger issue - on one hand, having arithmetic
metafunctions accept more than two arguments definitely simplifies complex
expressions:

    plus< N, M, int_c<10> >
    
vs.
    plus< plus< N, M>, int_c<10> >

On the other, the current way to implement it exposes a few problems;
default arguments is one of them, but it's relatively easy to solve; a more
serious one will be on our way as soon as we try to implement the
architecture I've sketched at the beginning of the message. To solve these
issues, I am inclining toward something like the following:

    template<
          typename N1
        , typename N2
        , typename N3 = void_
        , typename N4 = void_
        , typename N5 = void_
>
    struct plus
        : plus< typename plus< typename plus<
            typename plus<N1,N2>::type,N3 >::type,N4>::type, N5>
    {
    };

    template<
          typename N1
        , typename N2
        , typename N3
        , typename N4
>
    struct plus<N1,N2,N3,N4,void_>
        : plus< typename plus< typename plus<
            N1,N2>::type, N3>::type, N4>
    {
    };

    // ...
    
    template<
          typename N1
        , typename N2
>
    struct plus<N1,N2,void_,void_,void_>
    {
        // implementation
    };

So you can go this way, or just make everything take two operands only, and
we'll take care of it later.

>
> 4. The interface for the bit_shift_right (and left) function should
> be decided. Right now, the bit_shift_right_c function takes two types,
> and two constants. However, I am not very knowledgable on what
> operator >> and << are allowed to do according to the standard

5.8 [expr.shift]? :)

> (For example, can the righthand operand be negative,

"The behavior is undefined if the right operand is negative, or greater than
or equal to the length in bits of the promoted left operand."

> when is a logical, bitwise or arithmetic shift performed, what if the
lefthand operand
> is negative, etc. etc.).

Negative left-hand operands are allowed. We shouldn't worry about anything
else since we just redirect to the built-in operators that do the right
thing :).

> The bit_shift_right (non _c) function just
> takes two parameters as usual, and using AUX_TYPEOF their types are
> passed to the bit_shift_right_c function (as with all the other
> meta-functions).
>

Looks good to me!

Aleksey


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