Skip to content

Instantly share code, notes, and snippets.

@yak1ex
Created May 31, 2011 14:52
Show Gist options
  • Save yak1ex/1000636 to your computer and use it in GitHub Desktop.
Save yak1ex/1000636 to your computer and use it in GitHub Desktop.
Fusion adapter for variadic std::tuple
#ifndef YAK_FUSION_STD_TUPLE_ADAPTER_HPP
#define YAK_FUSION_STD_TUPLE_ADAPTER_HPP
#include <boost/config.hpp>
#ifdef BOOST_NO_VARIADIC_TEMPLATES
#error Need to enable variadic templates
#endif
#include <cstddef>
#include <tuple>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/int.hpp>
#include <boost/fusion/include/tag_of_fwd.hpp>
#include <boost/fusion/include/iterator_base.hpp>
///////////////////////////////////////////////////////////////////////
//
// Workarond
//
namespace yak { namespace util {
// Currently, cv specialization is missing in libstdc++.
template<std::size_t, typename> struct tuple_element;
template<std::size_t I, typename ... Types>
struct tuple_element<I, std::tuple<Types...>> : std::tuple_element<I, std::tuple<Types...>> {};
template<std::size_t I, typename T>
struct tuple_element<I, const T>
{
typedef typename std::add_const<typename yak::util::tuple_element<I, T>::type>::type type;
};
template<std::size_t I, typename T>
struct tuple_element<I, volatile T>
{
typedef typename std::add_volatile<typename yak::util::tuple_element<I, T>::type>::type type;
};
template<std::size_t I, typename T>
struct tuple_element<I, const volatile T>
{
typedef typename std::add_cv<typename yak::util::tuple_element<I, T>::type>::type type;
};
// Currently, cv specialization is missing in libstdc++.
// Currently, integral_constant is not used in libstdc++.
template<typename> struct tuple_size;
template<typename ... Types>
struct tuple_size<std::tuple<Types...>> : boost::mpl::int_<sizeof...(Types)> {};
template<typename T>
struct tuple_size<const T> : tuple_size<T> {};
template<typename T>
struct tuple_size<volatile T> : tuple_size<T> {};
template<typename T>
struct tuple_size<const volatile T> : tuple_size<T> {};
namespace detail {
template<typename T>
struct tuple_access_type
{
typedef const T& const_type;
typedef volatile T& volatile_type;
typedef const volatile T& cv_type;
typedef T& non_cv_type;
};
template<typename T>
struct tuple_access_type<T&>
{
typedef T& const_type;
typedef T& volatile_type;
typedef T& cv_type;
typedef T& non_cv_type;
};
template<typename T>
struct tuple_access_type<T&&>
{
typedef T&& const_type;
typedef T&& volatile_type;
typedef T&& cv_type;
typedef T&& non_cv_type;
};
}
}}
///////////////////////////////////////////////////////////////////////
//
// Minimum adapter for std::tuple to fusion sequence
//
namespace boost { namespace fusion {
struct random_access_traversal_tag;
}}
namespace yak { namespace fusion {
struct std_tuple_tag;
struct std_tuple_iterator_tag;
template<typename Struct, int Pos>
struct std_tuple_iterator : boost::fusion::iterator_base<yak::fusion::std_tuple_iterator<Struct, Pos>>
{
static_assert(Pos >= 0 && Pos <= yak::util::tuple_size<Struct>::value, "invalid iterator range");
typedef Struct struct_type;
typedef boost::fusion::random_access_traversal_tag category;
typedef std::integral_constant<int, Pos> index;
std_tuple_iterator(Struct &str) : struct_(str) {}
Struct& struct_;
};
}}
namespace boost { namespace fusion {
namespace traits {
template<typename ... Args>
struct tag_of<std::tuple<Args...>>
{
typedef yak::fusion::std_tuple_tag type;
};
template<typename ... Args>
struct tag_of<const std::tuple<Args...>>
{
typedef yak::fusion::std_tuple_tag type;
};
template<typename ... Args>
struct tag_of<volatile std::tuple<Args...>>
{
typedef yak::fusion::std_tuple_tag type;
};
template<typename ... Args>
struct tag_of<const volatile std::tuple<Args...>>
{
typedef yak::fusion::std_tuple_tag type;
};
template<typename Struct, int Pos>
struct tag_of<yak::fusion::std_tuple_iterator<Struct, Pos>>
{
typedef yak::fusion::std_tuple_iterator_tag type;
};
}
namespace extension {
template<typename> struct value_of_impl;
template<>
struct value_of_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator> struct apply;
template<typename Struct, int Pos>
struct apply<yak::fusion::std_tuple_iterator<Struct, Pos>>
{
typedef typename std::remove_cv<
typename yak::util::tuple_element<Pos, Struct>::type
>::type type;
};
};
template<typename> struct deref_impl;
template<>
struct deref_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator>
struct apply;
template<typename Struct, int Pos>
struct apply<yak::fusion::std_tuple_iterator<Struct, Pos>>
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<Pos, Struct>::type
>::non_cv_type type;
static type call(const yak::fusion::std_tuple_iterator<Struct, Pos> &it)
{
return std::get<Pos>(it.struct_);
}
};
template<typename Struct, int Pos>
struct apply<const yak::fusion::std_tuple_iterator<Struct, Pos>>
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<Pos, Struct>::type
>::const_type type;
static type call(const yak::fusion::std_tuple_iterator<Struct, Pos> &it)
{
return std::get<Pos>(it.struct_);
}
};
};
template<typename> struct next_impl;
template<>
struct next_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator>
struct apply
{
typedef typename Iterator::struct_type struct_type;
typedef typename Iterator::index index;
typedef yak::fusion::std_tuple_iterator<struct_type, index::value + 1> type;
static type call(const Iterator &it) { return type(it.struct_); }
};
};
template<typename> struct prior_impl;
template<>
struct prior_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator>
struct apply
{
typedef typename Iterator::struct_type struct_type;
typedef typename Iterator::index index;
typedef yak::fusion::std_tuple_iterator<struct_type, index::value - 1> type;
static type call(const Iterator &it) { return type(it.struct_); }
};
};
template<typename> struct distance_impl;
template<>
struct distance_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator1, typename Iterator2>
struct apply
{
typedef typename Iterator1::index index1;
typedef typename Iterator2::index index2;
typedef int type;
static type call(const Iterator1&, const Iterator2&) { return index2::value - index1::value; }
};
};
template<typename> struct advance_impl;
template<>
struct advance_impl<yak::fusion::std_tuple_iterator_tag>
{
template<typename Iterator, typename N>
struct apply
{
typedef typename Iterator::struct_type struct_type;
typedef typename Iterator::index index;
typedef yak::fusion::std_tuple_iterator<struct_type, index::value + N::value> type;
static type call(const Iterator &it) { return type(it.struct_); }
};
};
template<typename> struct is_sequence_impl;
template<>
struct is_sequence_impl<yak::fusion::std_tuple_tag>
{
template<typename> struct apply : boost::mpl::true_ {};
};
template<typename> struct begin_impl;
template<>
struct begin_impl<yak::fusion::std_tuple_tag>
{
template<typename Sequence>
struct apply
{
typedef yak::fusion::std_tuple_iterator<Sequence, 0> type;
static type call(Sequence &seq) { return type(seq); }
};
};
template<typename> struct end_impl;
template<>
struct end_impl<yak::fusion::std_tuple_tag>
{
template<typename Sequence>
struct apply
{
typedef yak::fusion::std_tuple_iterator<Sequence, yak::util::tuple_size<Sequence>::value> type;
static type call(Sequence &seq) { return type(seq); }
};
};
template<typename> struct size_impl;
template<>
struct size_impl<yak::fusion::std_tuple_tag>
{
template<typename Sequence>
struct apply : yak::util::tuple_size<Sequence> {};
};
template<typename> struct at_impl;
template<>
struct at_impl<yak::fusion::std_tuple_tag>
{
template<typename Sequence, typename N>
struct apply
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<N::value, Sequence>::type
>::non_cv_type type;
static type call(Sequence &seq) { return std::get<N::value>(seq); }
};
template<typename Sequence, typename N>
struct apply<const Sequence, N>
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<N::value, const Sequence>::type
>::const_type type;
static type call(const Sequence &seq) { return std::get<N::value>(seq); }
};
template<typename Sequence, typename N>
struct apply<volatile Sequence, N>
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<N::value, volatile Sequence>::type
>::volatile_type type;
static type call(volatile Sequence &seq) { return std::get<N::value>(seq); }
};
template<typename Sequence, typename N>
struct apply<const volatile Sequence, N>
{
typedef typename yak::util::detail::tuple_access_type<
typename yak::util::tuple_element<N::value, const volatile Sequence>::type
>::cv_type type;
static type call(const volatile Sequence &seq) { return std::get<N::value>(seq); }
};
};
template<typename> struct value_at_impl;
template<>
struct value_at_impl<yak::fusion::std_tuple_tag>
{
template<typename Sequence, typename N>
struct apply : yak::util::tuple_element<N::value, Sequence> {};
};
}
}}
#endif
// You need to compile this file without -std=c++0x option
#define BOOST_AUTO_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <boost/config.hpp>
#ifndef BOOST_NO_VARIADIC_TEMPLATES
#include "std_tuple_adapter.hpp"
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/iterator.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/add_const.hpp>
#include <boost/type_traits/add_volatile.hpp>
#include <boost/type_traits/add_cv.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/test/auto_unit_test.hpp>
///////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(test_empty)
{
typedef std::tuple<> Seq;
Seq seq;
// Checking valid expressions
// Forward Sequence
typedef boost::fusion::result_of::begin<Seq>::type begin_type;
begin_type begin = boost::fusion::begin(seq);
typedef boost::fusion::result_of::end<Seq>::type end_type;
end_type end = boost::fusion::end(seq);
boost::fusion::result_of::size<Seq>::type size = boost::fusion::size(seq);
boost::fusion::result_of::empty<Seq>::type empty = boost::fusion::empty(seq);
// boost::fusion::result_of::front<Seq>::type front = boost::fusion::front(seq);
BOOST_CHECK(begin == end);
BOOST_CHECK((boost::is_same<boost::fusion::result_of::size<Seq>::type, boost::mpl::int_<0>>::value));
BOOST_CHECK(size == 0);
BOOST_CHECK((boost::is_same<boost::fusion::result_of::empty<Seq>::type, boost::mpl::bool_<true>>::value));
BOOST_CHECK(empty == true);
// BOOST_CHECK(front == ...);
// front = ...
// BOOST_CHECK(front == ...);
// BOOST_CHECK(boost::fusion::front(seq) == ...);
// next
BOOST_MPL_ASSERT((boost::fusion::result_of::equal_to<begin_type, end_type>));
// neq
// advance_c<>
// advance<>
// deref
// *
boost::fusion::result_of::distance<begin_type, end_type>::type distance = boost::fusion::distance(begin, end);
BOOST_CHECK(begin == end);
BOOST_CHECK(distance == 0);
// Bidirectional Sequence
// boost::fusion::result_of::back<Seq>::type back = boost::fusion::back(seq);
// BOOST_CHECK(back == ...);
// back = ...
// BOOST_CHECK(back == ...);
// BOOST_CHECK(boost::fusion::back(seq) == ...);
// prior
// advance_c<> with negative
// advance<> with negative
// Random Access Sequence
// boost::fusion::result_of::at<Seq, 0>::type at0 = boost::fusion::at<0>(seq);
// BOOST_CHECK(at0 == ...);
// at0 = ...
// BOOST_CHECK(at0 == ...);
// BOOST_CHECK(boost::fusion::at<0>(seq) == ...);
}
BOOST_AUTO_TEST_CASE(test_scalar1)
{
typedef std::tuple<int> Seq;
const int init = 5, change = 3;
Seq seq(init);
// Checking valid expressions
// Forward Sequence
typedef boost::fusion::result_of::begin<Seq>::type begin_type;
begin_type begin = boost::fusion::begin(seq);
typedef boost::fusion::result_of::end<Seq>::type end_type;
end_type end = boost::fusion::end(seq);
boost::fusion::result_of::size<Seq>::type size = boost::fusion::size(seq);
boost::fusion::result_of::empty<Seq>::type empty = boost::fusion::empty(seq);
boost::fusion::result_of::front<Seq>::type front = boost::fusion::front(seq);
BOOST_CHECK(begin != end);
BOOST_CHECK((boost::is_same<boost::fusion::result_of::size<Seq>::type, boost::mpl::int_<1>>::value));
BOOST_CHECK(size == 1);
BOOST_CHECK((boost::is_same<boost::fusion::result_of::empty<Seq>::type, boost::mpl::bool_<false>>::value));
BOOST_CHECK(empty == false);
BOOST_CHECK(front == init);
BOOST_CHECK(&front == &std::get<0>(seq));
front = change;
BOOST_CHECK(front == change);
BOOST_CHECK(boost::fusion::front(seq) == change);
front = init;
typedef boost::fusion::result_of::next<begin_type>::type next_type;
next_type next = boost::fusion::next(begin);
BOOST_CHECK(next == end);
BOOST_MPL_ASSERT_NOT((boost::fusion::result_of::equal_to<begin_type, end_type>));
BOOST_MPL_ASSERT((boost::fusion::result_of::equal_to<next_type, end_type>));
BOOST_MPL_ASSERT_NOT((boost::fusion::result_of::equal_to<begin_type, next_type>));
BOOST_MPL_ASSERT((boost::is_same<
boost::fusion::result_of::advance_c<begin_type, 1>::type,
next_type>));
BOOST_CHECK(boost::fusion::advance_c<1>(begin) == next);
BOOST_MPL_ASSERT((boost::is_same<
boost::fusion::result_of::advance<begin_type, boost::mpl::int_<1>>::type,
next_type>));
BOOST_CHECK(boost::fusion::advance<boost::mpl::int_<1>>(begin) == next);
BOOST_CHECK(boost::fusion::deref(begin) == init);
BOOST_CHECK(&boost::fusion::deref(begin) == &std::get<0>(seq));
BOOST_CHECK(*begin == boost::fusion::deref(begin));
BOOST_CHECK(&*begin == &boost::fusion::deref(begin));
boost::fusion::result_of::distance<begin_type, end_type>::type distance = boost::fusion::distance(begin, end);
BOOST_CHECK(distance == 1);
BOOST_CHECK(boost::fusion::distance(begin, boost::fusion::next(begin)) == 1);
BOOST_CHECK(boost::fusion::distance(begin, boost::fusion::advance_c<1>(begin)) == 1);
BOOST_CHECK(boost::fusion::distance(begin, boost::fusion::advance<boost::mpl::int_<1>>(begin)) == 1);
// Bidirectional Sequence
boost::fusion::result_of::back<Seq>::type back = boost::fusion::back(seq);
BOOST_CHECK(back == init);
BOOST_CHECK(&back == &std::get<0>(seq));
back = change;
BOOST_CHECK(back == change);
BOOST_CHECK(boost::fusion::back(seq) == change);
back = init;
typedef boost::fusion::result_of::prior<end_type>::type prior_type;
prior_type prior = boost::fusion::prior(end);
BOOST_CHECK(begin == prior);
BOOST_MPL_ASSERT((boost::fusion::result_of::equal_to<begin_type, prior_type>));
BOOST_MPL_ASSERT_NOT((boost::fusion::result_of::equal_to<prior_type, end_type>));
BOOST_CHECK(boost::fusion::prior(boost::fusion::next(begin)) == begin);
BOOST_CHECK(boost::fusion::next(boost::fusion::prior(end)) == end);
BOOST_MPL_ASSERT((boost::is_same<
boost::fusion::result_of::advance_c<end_type, -1>::type,
prior_type>));
BOOST_CHECK(boost::fusion::advance_c<-1>(end) == prior);
BOOST_MPL_ASSERT((boost::is_same<
boost::fusion::result_of::advance<end_type, boost::mpl::int_<-1>>::type,
prior_type>));
BOOST_CHECK(boost::fusion::advance<boost::mpl::int_<-1>>(end) == prior);
boost::fusion::result_of::distance<prior_type, end_type>::type distance2 = boost::fusion::distance(prior, end);
BOOST_CHECK(distance2 == 1);
BOOST_CHECK(boost::fusion::distance(boost::fusion::prior(end), end) == 1);
BOOST_CHECK(boost::fusion::distance(boost::fusion::advance_c<-1>(end), end) == 1);
BOOST_CHECK(boost::fusion::distance(boost::fusion::advance<boost::mpl::int_<-1>>(end), end) == 1);
// Random Access Sequence
boost::fusion::result_of::at_c<Seq, 0>::type at0 = boost::fusion::at_c<0>(seq);
BOOST_MPL_ASSERT((boost::is_same<boost::fusion::result_of::at_c<Seq, 0>::type, int&>));
BOOST_MPL_ASSERT((boost::is_same<boost::fusion::result_of::value_at_c<Seq, 0>::type, int>));
BOOST_CHECK(at0 == init);
BOOST_CHECK(&at0 == &std::get<0>(seq));
at0 = change;
BOOST_CHECK(at0 == change);
BOOST_CHECK(boost::fusion::at_c<0>(seq) == change);
at0 = init;
}
typedef boost::mpl::vector<
boost::mpl::quote1<boost::mpl::identity>,
boost::mpl::quote1<boost::add_const>,
boost::mpl::quote1<boost::add_volatile>,
boost::mpl::quote1<boost::add_cv>
> trans_types;
typedef boost::mpl::vector<
std::string,
const std::string,
volatile std::string,
const volatile std::string
> base_types;
struct make_tuple
{
template<typename T>
struct apply
{
typedef std::tuple<T, T*, T&, T&&> type;
};
};
typedef boost::mpl::transform<
base_types,
make_tuple
>::type tuple_types;
struct inner_fold
{
template<typename T1, typename T2>
struct apply
{
typedef typename boost::mpl::fold<
trans_types,
T1,
boost::mpl::push_back<boost::mpl::_1, boost::mpl::pair<boost::mpl::_2, T2>>
>::type type;
};
};
typedef boost::mpl::fold<
tuple_types,
boost::mpl::vector<>,
inner_fold
>::type target_types;
template<typename T>
struct tuple_elems
{
template<typename Arg>
struct apply
{
typedef typename yak::util::tuple_element<Arg::value, T>::type type;
};
};
template<typename Trans>
struct tuple_access_type
{
template<typename T>
struct apply
{
typedef typename std::conditional<
std::is_reference<T>::value,
T,
typename boost::mpl::apply<Trans, T>::type &
>::type type;
};
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_type, T, target_types)
{
typedef typename T::first trans_type;
typedef typename T::second tuple_type;
typedef typename boost::mpl::apply<trans_type, tuple_type>::type target_type;
typedef boost::mpl::range_c<int, 0, yak::util::tuple_size<target_type>::value> range_c;
typedef typename boost::mpl::copy<range_c, boost::mpl::back_inserter<boost::mpl::vector<>>>::type range;
typedef typename boost::mpl::transform<range, tuple_elems<tuple_type>>::type raw_arg_types;
typedef typename boost::mpl::transform<range, tuple_elems<target_type>>::type adjust_arg_types;
typedef typename boost::mpl::transform<raw_arg_types, tuple_access_type<boost::mpl::protect<boost::mpl::bind<trans_type, boost::mpl::_1>>>>::type access_types;
typedef typename boost::mpl::transform<range, boost::fusion::result_of::value_at<target_type, boost::mpl::_1>>::type value_at_types;
BOOST_MPL_ASSERT((boost::is_same<value_at_types, adjust_arg_types>));
typedef typename boost::mpl::transform<range, boost::fusion::result_of::at<target_type, boost::mpl::_1>>::type at_types;
BOOST_MPL_ASSERT((boost::is_same<at_types, access_types>));
typedef typename boost::mpl::transform<
range,
boost::fusion::result_of::deref<
boost::fusion::result_of::advance<
typename boost::fusion::result_of::begin<target_type>::type,
boost::mpl::_1
>
>
>::type deref_types;
BOOST_MPL_ASSERT((boost::is_same<deref_types, access_types>));
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment