Перестановки списка типов Использование boost:: mpl
Я пытаюсь создать список, содержащий перестановки данного списка типов.
Приведенный ниже код, кажется, функционирует, хотя и без предполагаемого результата, когда я использую указанный список вместо создания нового списка, удаляя его с фактического ввода. Об этом свидетельствует разница между перестановкой_helper и broken_helper ниже.
Кто-нибудь знает, почему mpl::remove
не работает должным образом в этом случае?
#include <boost/mpl/list.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/joint_view.hpp>
#include <boost/mpl/remove.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/equal.hpp>
namespace mpl = boost::mpl;
struct test_type1 {};
struct test_type2 {};
struct test_type3 {};
template< typename T >
struct permutations;
template <typename value>
struct permutations<mpl::list1< value > >: mpl::list1<mpl::list1< value > > {};
template< typename value, typename T>
struct permutation_helper:
mpl::transform< typename permutations<
mpl::list1<test_type3> >::type,
mpl::push_front< mpl::_1, value> > { };
template< typename value, typename T>
struct broken_helper:
mpl::transform< typename permutations<
mpl::remove<T, value> >::type,
mpl::push_front< mpl::_1, value> > { };
template< typename T >
struct permutations:
mpl::fold< T,
mpl::list0<>,
mpl::joint_view< mpl::_1,
broken_helper<mpl::_2, T > > > { };
typedef mpl::list2<test_type1, test_type2> typelist;
typedef permutations<typelist>::type perms;
int main() {
BOOST_MPL_ASSERT(( mpl::equal< perms, typelist > ));
return 0;
}
Я использовал assert для определения того, что возвращается из функции, typelist не является ожидаемым результатом. Это сообщение, которое assert возвращает для broken_helper:
testcase.cpp: In function ‘int main()’:
testcase.cpp:45: error: no matching function for call to ‘assertion_failed(mpl_::failed************ boost::mpl::equal<boost::mpl::joint_view<boost::mpl::joint_view<boost::mpl::list0<mpl_::na>, boost::mpl::l_end>, boost::mpl::l_end>, boost::mpl::list2<test_type1, test_type2>, boost::is_same<mpl_::arg<-0x00000000000000001>, mpl_::arg<-0x00000000000000001> > >::************)’
Вывод с использованием permutation_helper является фактическим списком:
testcase.cpp: In function ‘int main()’:
testcase.cpp:45: error: no matching function for call to ‘assertion_failed(mpl_::failed************ boost::mpl::equal<boost::mpl::list2<test_type1, test_type2>, boost::mpl::joint_view<boost::mpl::joint_view<boost::mpl::list0<mpl_::na>, boost::mpl::l_item<mpl_::long_<1l>, boost::mpl::l_item<mpl_::long_<2l>, test_type1, boost::mpl::list1<test_type3> >, boost::mpl::l_end> >, boost::mpl::l_item<mpl_::long_<1l>, boost::mpl::l_item<mpl_::long_<2l>, test_type2, boost::mpl::list1<test_type3> >, boost::mpl::l_end> >, boost::is_same<mpl_::arg<-0x00000000000000001>, mpl_::arg<-0x00000000000000001> > >::************)’
Ответы
Ответ 1
mpl::remove
работает правильно. Проблема заключается в вашем шаблоне для перестановок одиночных списков: он ловит только те типы mpl::list
s, в то время как результат удаления имеет другой тип последовательности.
Другими словами, результат mpl::remove
равен mpl::equal
в одиночном списке, но не std::is_same
:
#include <boost/mpl/list.hpp>
#include <boost/mpl/remove.hpp>
#include <boost/mpl/equal.hpp>
namespace mpl = boost::mpl;
struct test_type1 {};
struct test_type2 {};
typedef mpl::list2<test_type1, test_type2> typelist;
typedef mpl::remove<typelist, test_type1>::type t;
typedef mpl::list1<test_type2> t_corr;
static_assert(mpl::equal<t,t_corr>::value, "t equals t_corr");
// the following will fail:
// static_assert(std::is_same<t,t_corr>::value, "t same type as t_corr");
int main() {
return 0;
}
Вы можете исправить эту проблему, специализировав свой шаблон для списка одиночных элементов, не основанный на точном типе mpl::list
, но для свойства длиной 1:
#include <boost/mpl/list.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/joint_view.hpp>
#include <boost/mpl/remove.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/next.hpp>
namespace mpl = boost::mpl;
struct test_type1 {};
struct test_type2 {};
struct test_type3 {};
template< typename T, typename _ENABLE=void >
struct permutations;
template <typename T>
struct permutations<T,
typename std::enable_if<mpl::size<T>::value==1>::type>
{
typedef typename mpl::list1<T> type;
};
template< typename value, typename T>
struct permutation_helper:
mpl::transform< typename permutations<
mpl::list1<test_type3> >::type,
mpl::push_front< mpl::_1, value> > { };
template< typename value, typename T>
struct broken_helper:
mpl::transform< typename permutations<
typename mpl::remove<T, value>::type >::type,
mpl::push_front< mpl::_1, value> > { };
template< typename T >
struct permutations<T,
typename std::enable_if<(mpl::size<T>::value>1)>::type>:
mpl::fold< T,
mpl::list0<>,
mpl::joint_view< mpl::_1,
broken_helper<mpl::_2, T > > > { };
typedef mpl::list2<test_type1, test_type2> typelist;
typedef permutations<typelist>::type perms;
typedef mpl::list<mpl::list<test_type1, test_type2>,
mpl::list<test_type2, test_type1> > perms_corr;
int main() {
static_assert(mpl::size<perms>::value == 2, "perms has correct size");
static_assert(mpl::equal<mpl::front<perms>::type,
mpl::front<perms_corr>::type>::value, "perms has correct front");
typedef mpl::next<mpl::begin<perms>::type>::type perms_2nd;
typedef mpl::next<mpl::begin<perms_corr>::type>::type perms_corr_2nd;
static_assert(mpl::equal<perms_2nd, perms_corr_2nd>::value, "perms has correct 2nd element");
return 0;
}
Кстати,
static_assert(mpl::equal<perms, perms_corr>::value, "perms correct");
завершится с ошибкой по тем же причинам.
Ответ 2
В качестве дополнения к ответу, который опубликовал Ларс:
Данный алгоритм перестановок, использующий joint_view, похоже, не работает в списке с размером больше двух. Я заменил его на mpl:: copy в front_inserter, и алгоритм работал отлично.
#include <boost/mpl/copy.hpp>
#include <boost/mpl/front_inserter.hpp>
template< typename T >
struct permutations<T, typename std::enable_if<(mpl::size<T>::value>1)>::type> :
mpl::fold< T,
mpl::list0<>,
mpl::copy< broken_helper< mpl::_2, T >,
mpl::front_inserter< mpl::_1 > > > { };
Ответ 3
Вы пытались поместить код как принадлежащий к соответствующим пространствам имен библиотеки?
namespace boost
{
namespace mpl
{
// code
}
}
Это может показаться глупым, но поскольку я уже решил аналогичную проблему (но не использовал MPL!).