Opened 15 years ago

Last modified 15 years ago

#1396 new Bugs

wrong result_of invocation around transform_view

Reported by: Shunsuke Sogame <pstade.mb@…> Owned by: Joel de Guzman
Milestone: Boost 1.36.0 Component: fusion
Version: Boost Development Trunk Severity: Problem
Keywords: Cc:

Description

See the following snippet. For some reason, boost::result_of< ::identity(int) > is invoked in as_vector, which means that the argument is rvalue. It should be boost::result_of< ::identity(int &) >.

#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/transform_view.hpp>
#include <boost/fusion/include/vector.hpp>


struct identity
{
    template<class FunCall>
    struct result;

    template<class Fun>
    struct result<Fun(int&)>
    {
        typedef int& type;
    };

    int& operator()(int& i) const
    {
        return i;
    }
};


int main()
{
    typedef boost::fusion::vector<int, int> from_t;
    from_t from;
    boost::fusion::transform_view<from_t, ::identity> v(from, ::identity());

    boost::fusion::as_vector(v); // doesn't compile.
}

Attachments (2)

transform.cpp (1.0 KB ) - added by anonymous 15 years ago.
value_of_transform_view_defect.cpp (8.5 KB ) - added by Shunsuke Sogame <pstade.mb@…> 15 years ago.
A defect report of this problem

Download all attachments as: .zip

Change History (10)

comment:1 by anonymous, 15 years ago

There needs to be a specialization for both Fun(int&) and Fun(int):

    struct identity
    {
        template <typename FunCall>
        struct result;

        template <typename Fun>
        struct result<Fun(int&)>
        {
            typedef int& type;
        };

        template <typename Fun>
        struct result<Fun(int)>
        {
            typedef int& type;
        };

        int& operator()(int& i) const
        {
            return i;
        }
    };

or better yet:

    struct identity
    {
        template <typename FunCall>
        struct result;

        template <typename Fun, typename T>
        struct result<Fun(T)>
        {
            typedef typename boost::add_reference<T>::type type;
        };

        template <typename T>
        T& operator()(T& i) const
        {
            return i;
        }
    };

This deserves some explanation. Here's why:

Your initial vector is:

    vector<int, int>

The underlying types are int and int (hint: not int&). Now, when as_vector tries to compute the resulting vector, it calls value_of to know the exact type of the elements in the input sequence. value_of strips the unnecessary reference that deref may potentially add. It just so happens that what you return is a reference, but that's irrelevant. The important thing is that the input sequence is:

    int, int

then your identity transform is applied calling

     result<Fun(int)>

hence, the result:

    int&, int&

Now...

We haven't started yet. We merely computed the desired result for as_vector. Now the fun begins, the actual conversion starts. The input sequence is walked by an iterator. Here, we *deref* the iterator --which returns an int&. Now, it's obvious why deref returns a reference -- to avoid copies. But then, transform_iterator::deref is also called with this reference parameter, which ultimately calls the identity transform:

     result<Fun(int&)>

Why doesn't STL iterators have this? It does! It's the value_type. Now if only std::vector<T&> is allowed, then the both the value_type and the reference_type would be T&.

Aha! now this is starting to sound like a FAQ :-)

by anonymous, 15 years ago

Attachment: transform.cpp added

comment:2 by anonymous, 15 years ago

Here's a more generic identity transform that works for mpl sequences too, FWIW (attached)

by Shunsuke Sogame <pstade.mb@…>, 15 years ago

A defect report of this problem

in reply to:  2 comment:3 by Shunsuke Sogame <pstade.mb@…>, 15 years ago

Replying to anonymous:

Here's a more generic identity transform that works for mpl sequences too, FWIW (attached)

BTW, according to language lawyers, this identity isn't allowed under TR1, because it is not consistent with decltype.

comment:4 by anonymous, 15 years ago

IMO, you are wrong. It is correct. The example you have in the boost list thread certainly is ill formed. The example I have here is not.

I've followed the thread (http://tinyurl.com/298abt) but what you missed is the template. The T in Fun(T) is generic and can accommodate references too. This(int) does not.

If you want to avoid dangling references, you can write it as:

struct identity
{
    template<class FunCall>
    struct result;

    template <class Fun, class T>
    struct result<Fun(T&)>
    {
        typedef T& type;
    };
    
    template <class Fun, class T>
    struct result<Fun(T const&)>
    {
        typedef T type;
    };

    template <class T>
    T& operator()(T& val) const
    {
        return val;
    }

    template <class T>
    T operator()(T const& val) const
    {
        return val;
    }
};

in reply to:  4 comment:5 by Shunsuke Sogame <pstade.mb@…>, 15 years ago

Replying to anonymous:

IMO, you are wrong. It is correct. The example you have in the boost list thread certainly is ill formed. The example I have here is not.

Ah, right. Unless identity in transform.cpp takes a rvalue, it is ok. Sorry for my confusing.

comment:6 by anonymous, 15 years ago

:-) Anyway, I am really convinced that your proposal to add the MetafunctionClass ValueOf for value_of implementation is the best way to go. It will be added :-)

comment:7 by Joel de Guzman, 15 years ago

Ooops. Didn't log in. The last two "anonymous" posts are mine (Joel)

in reply to:  6 comment:8 by Shunsuke Sogame <pstade.mb@…>, 15 years ago

Replying to anonymous:

:-) Anyway, I am really convinced that your proposal to add the MetafunctionClass ValueOf for value_of implementation is the best way to go. It will be added :-)

Please consider it carefully. I'm sometimes wrong :-) Thanks to your patience for my broken english, I have a temporary(cheating) workaround for this problem. Therefore, Fusion professionals have plenty of time to consider.

Regards,

Note: See TracTickets for help on using tickets.