Search code examples
c++boostboost-fusion

Boost Fusion transform type manipulation and as_vector


I am trying to get the point of using Fusion and am stumped with this simple example:

#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/transform.hpp>

template< typename T >
struct S {
    typedef T type;
};

struct S_f {
    template< typename T >
    struct result {
        typedef typename T::type type;
    };
};

int main () {
    using namespace boost;
    typedef fusion::vector<S<int>> from_type;
    BOOST_MPL_ASSERT((fusion::traits::is_sequence< fusion::vector< int > > ));

    typedef fusion::result_of::transform< from_type, S_f >::type to_type;
    BOOST_MPL_ASSERT((fusion::traits::is_sequence< to_type > ));

    typedef fusion::result_of::as_vector< to_type >::type value_type; // error
}

The asserts pass but the typedef for value_type fails with the error below. I could not fund any discrepancy between the code and the docs and no remedy elsewhere on stackoverflow or boost mailing lists.

AFAICT the code is correct: the result of applying the transform metafunction is a transform_view and a transform_view is a sequence, as shown by the passing asserts. Yet, the application of as_vector metafunction on the transform_view fails. What gives?!

Any help is appreciated. I am NOT interested in mixing mpl in. I know I can take a detour through MPL and a few fusion questions on SO that ask about type manipulation have answers that advocate MPL. According to the docs, I wouldn't need MPL.

clang++ -std=c++1z -c t.cpp
In file included from main.cpp:4:
In file included from /usr/local/include/boost/fusion/include/transform.hpp:11:
In file included from /usr/local/include/boost/fusion/algorithm/transformation/transform.hpp:11:
In file included from /usr/local/include/boost/fusion/view/transform_view/transform_view.hpp:15:
In file included from /usr/local/include/boost/fusion/view/transform_view/transform_view_iterator.hpp:18:
/usr/local/include/boost/fusion/view/transform_view/detail/value_of_impl.hpp:37:74: error: no type named 'type' in 'boost::mpl::apply<boost::fusion::detail::apply_transform_result<S_f>, S<int>, mpl_::na, mpl_::na, mpl_::na, mpl_::na>'
                typedef typename mpl::apply<transform_type, value_type>::type type;
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/include/boost/fusion/iterator/value_of.hpp:52:15: note: in instantiation of template class 'boost::fusion::extension::value_of_impl<boost::fusion::transform_view_iterator_tag>::apply<boost::fusion::transform_view_iterator<boost::fusion::vector_iterator<boost::fusion::vector<S<int>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_>, 0>, S_f> >' requested here
            : extension::value_of_impl<typename detail::tag_of<Iterator>::type>::
              ^
/usr/local/include/boost/fusion/container/vector/detail/cpp03/preprocessed/as_vector10.hpp:19:49: note: in instantiation of template class 'boost::fusion::result_of::value_of<boost::fusion::transform_view_iterator<boost::fusion::vector_iterator<boost::fusion::vector<S<int>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_>, 0>, S_f> >' requested here
            typedef typename fusion::result_of::value_of<I0>::type T0;
                                                ^
/usr/local/include/boost/fusion/container/vector/convert.hpp:26:17: note: in instantiation of template class 'boost::fusion::detail::barrier::as_vector<1>::apply<boost::fusion::transform_view_iterator<boost::fusion::vector_iterator<boost::fusion::vector<S<int>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_>, 0>, S_f> >' requested here
                template apply<typename result_of::begin<Sequence>::type>::type
                ^
main.cpp:26:32: note: in instantiation of template class 'boost::fusion::result_of::as_vector<boost::fusion::transform_view<boost::fusion::vector<S<int>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_>, S_f, boost::fusion::void_> >' requested here
    typedef fusion::result_of::as_vector< to_type >::type value_type; // error
                               ^
1 error generated.

Solution

  • The problem with template metaprogramming is that when you fail to meet the preconditions of a metafunction, you get lots of meaningless errors. The requirement on transform is that F is a unary Polymorphic Function Object. The explanation of what that is in the documentation is a little weak, but you can tell from the examples: this is an object that you can call with arguments. That is, result_of<F(T)>::type needs to be well-formed.

    What you're passing to transform is:

    struct S_f {
        template< typename T >
        struct result {
            typedef typename T::type type;
        };
    };
    

    That is not a Polymorphic Function Object. Nor is it a metafunction class. This is not something that either Boost.Fusion nor Boost.MPL would be able to understand. Where this is particularly confusing is that the transform<> metafunction is lazy - so it looks like you did that part correctly. It's only in as_vector<> that the transformation is actually applied, so it looks like that's where the point of failure is.

    To convert it to a Polymorphic Function Object, just change the nested result class template into a call operator:

    struct S_f {
        template< typename T >
        typename T::type operator()(T );
    };
    

    No definition necessary, since you're not actually calling it. With that fix, your code compiles.