From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe
On 03/18/2013 08:30 AM, Andy Jost wrote:
Maybe looking at some code would help clarify the answers to these questions. I have about 300 lines partially implementing an adaptor for std::set using Boost.TypeErasure, plus a list of questions and problems I encountered. Is that too large to put into an email for this list? If so, where can I drop it?
That shouldn't be too large.
Ok, code is at the end; questions first. I realize many of these questions may be answered in the documentation. Linux 2.6.9-89.ELlargesmp x86_64 g++ (GCC) 4.5.2 1) Certain functions require a downcast, of sorts. The overload of std::set<T>::insert taking an iterator as hint is an example. Not just any iterator, but one with exactly the right type is needed. Maybe this is what type_erasure::is_same is for, but I haven't looked into it yet. 2) I start getting errors when the mpl::vector of requirements exceeds 20 elements: "error: wrong number of template arguments (21, should be 20)." As a blind guess, I tried including boost/mpl/vector/vector30.hpp but it doesn't help. I know there's a good chance the answer is in the MPL docs somewhere, but I haven't had a chance to go deeper. 3) I could not implement swap. If item 1 can be resolved, then this could work in the same way. Still, it seems that two any<> objects of the same type should be swappable (though, it is not obvious how that would be expressed). I couldn't find a way to do it. 4) When members are overloaded based on a constant this, I could only use the const-qualified version. So, I can add a requirement for set<T>::clear, but for set<T>::begin, the return value must be specified as const_iterator. 5) BOOST_TYPE_ERASURE_MEMBER cannot be used in a SEQ_FOR_EACH loop, because it uses SEQ_FOR_EACH internally. It would be handy to have a way to declare several members when the namespace path and prefix (e.g., has_) doesn't change, either by passing the z parameter or using another macro. 6) I could never get the bidirectional_iterator concept from TypeErasure working. I didn't find an example. I ended up writing out the requirements myself. Also, that required the use of relaxed and I didn't understand why. #define BOOST_TYPE_ERASURE_STL_MEMBER(key,name,arity) \ BOOST_TYPE_ERASURE_MEMBER((boost)(type_erasure)(has_##key),name,arity); \ /**/ /* std::allocator */ BOOST_TYPE_ERASURE_STL_MEMBER(address,address,1) BOOST_TYPE_ERASURE_STL_MEMBER(allocate1,allocate,1) BOOST_TYPE_ERASURE_STL_MEMBER(allocate2,allocate,2) BOOST_TYPE_ERASURE_STL_MEMBER(construct,construct,2) BOOST_TYPE_ERASURE_STL_MEMBER(deallocate,deallocate,2) BOOST_TYPE_ERASURE_STL_MEMBER(destroy,destroy,1) /* Iterators */ BOOST_TYPE_ERASURE_STL_MEMBER(begin,begin,0) BOOST_TYPE_ERASURE_STL_MEMBER(end,end,0) BOOST_TYPE_ERASURE_STL_MEMBER(rbegin,rbegin,0) BOOST_TYPE_ERASURE_STL_MEMBER(rend,rend,0) /* Capacity */ BOOST_TYPE_ERASURE_STL_MEMBER(empty,empty,0) BOOST_TYPE_ERASURE_STL_MEMBER(max_size,max_size,0) BOOST_TYPE_ERASURE_STL_MEMBER(size,size,0) /* Modifiers */ BOOST_TYPE_ERASURE_STL_MEMBER(clear,clear,0) BOOST_TYPE_ERASURE_STL_MEMBER(erase1,erase,1) BOOST_TYPE_ERASURE_STL_MEMBER(erase2,erase,2) BOOST_TYPE_ERASURE_STL_MEMBER(insert1,insert,1) BOOST_TYPE_ERASURE_STL_MEMBER(insert2,insert,2) BOOST_TYPE_ERASURE_STL_MEMBER(swap,swap,1) /* Observers */ BOOST_TYPE_ERASURE_STL_MEMBER(key_comp,key_comp,0) BOOST_TYPE_ERASURE_STL_MEMBER(value_comp,value_comp,0) /* Operations */ BOOST_TYPE_ERASURE_STL_MEMBER(count,count,1) BOOST_TYPE_ERASURE_STL_MEMBER(equal_range,equal_range,1) BOOST_TYPE_ERASURE_STL_MEMBER(find,find,1) BOOST_TYPE_ERASURE_STL_MEMBER(lower_bound,lower_bound,1) BOOST_TYPE_ERASURE_STL_MEMBER(upper_bound,upper_bound,1) /* Allocator */ BOOST_TYPE_ERASURE_STL_MEMBER(get_allocator,get_allocator,0) #define BOOST_TYPE_ERASURE_FWD_TYPEDEFS(z,base,name) typedef base::name name; #define BOOST_TYPE_ERASURE_STL_TYPEDEFS \ (key_type)(key_compare)(value_compare)(allocator_type) \ (iterator)(const_iterator)(reverse_iterator)(const_reverse_iterator) \ BOOST_TYPE_ERASURE_ALLOCATOR_TYPEDEFS \ /**/ #define BOOST_TYPE_ERASURE_ALLOCATOR_TYPEDEFS \ (value_type)(reference)(const_reference)(pointer)(const_pointer) \ (difference_type)(size_type) \ /**/ namespace boost { namespace type_erasure { template<typename T> struct allocator_typedefs { typedef T value_type; typedef T* pointer; typedef T& reference; typedef const T* const_pointer; typedef const T& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; }; template<typename T, typename Defs = allocator_typedefs<T> > struct allocator_requirements : mpl::vector< copy_constructible<> , assignable<> , has_address<typename Defs::pointer(typename Defs::reference)> , has_address< typename Defs::const_pointer(typename Defs::const_reference) > , has_allocate1<T*(typename Defs::size_type)> , has_allocate2<T*(typename Defs::size_type,void const *)> , has_deallocate<void(typename Defs::pointer,typename Defs::size_type)> , has_max_size<typename Defs::size_type()> , has_construct< void(typename Defs::pointer,typename Defs::const_reference) > , has_destroy<void(typename Defs::pointer)> > {}; template<typename T> struct any_allocator : any<allocator_requirements<T> > { BOOST_PP_SEQ_FOR_EACH( BOOST_TYPE_ERASURE_FWD_TYPEDEFS , typename allocator_typedefs<T> , BOOST_TYPE_ERASURE_ALLOCATOR_TYPEDEFS ) template<typename Allocator> any_allocator(Allocator const & allocator) : any<allocator_requirements<T>, _self>(allocator) {} }; template<typename T> struct compare_requirements : mpl::vector< copy_constructible<> , assignable<> , callable<bool(T const &, T const &)> > {}; template<typename T> struct any_compare { typedef any<compare_requirements<T> > type; }; template<typename T, typename Reference> struct input_iterator_requirements : mpl::vector< relaxed , constructible<_self()> , copy_constructible<> , assignable<> , incrementable<> , dereferenceable<Reference> , equality_comparable<> > {}; template<typename T, typename Reference = T const &> struct any_input_iterator { typedef any<input_iterator_requirements<T,Reference> > type; }; template<typename T, typename Reference> struct bidirectional_iterator_requirements // : bidirectional_iterator<T, Reference, Difference> : mpl::vector< relaxed , constructible<_self()> , copy_constructible<> , assignable<> , incrementable<> , decrementable<> , dereferenceable<Reference> , equality_comparable<> > {}; template<typename T, typename Reference = T&> struct any_bidirectional_iterator { typedef any<bidirectional_iterator_requirements<T,Reference> > type; }; template<typename T, typename Allocator = any_allocator<T> > struct any_set_typedefs { typedef T value_type; typedef T key_type; typedef typename any_compare<T>::type key_compare; typedef typename any_compare<T>::type value_compare; typedef Allocator allocator_type; typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; typedef typename allocator_type::pointer pointer; typedef typename allocator_type::const_pointer const_pointer; typedef typename allocator_type::difference_type difference_type; typedef typename allocator_type::size_type size_type; typedef typename any_bidirectional_iterator<value_type,reference>::type iterator; typedef typename any_bidirectional_iterator<value_type,const_reference>::type const_iterator; typedef iterator reverse_iterator; typedef const_iterator const_reverse_iterator; }; template<typename T, typename Allocator , typename Defs = any_set_typedefs<T,Allocator> > struct set_requirements : mpl::vector< constructible<_self()> , copy_constructible<> , assignable<> /* Iterators */ , has_begin<typename Defs::const_iterator()> , has_end<typename Defs::const_iterator()> , has_rbegin<typename Defs::const_reverse_iterator()> , has_rend<typename Defs::const_reverse_iterator()> /* Capacity */ , has_empty<bool()> , has_max_size<typename Defs::size_type()> , has_size<typename Defs::size_type()> /* Modifiers */ , has_clear<void()> , has_insert1< std::pair<typename Defs::const_iterator,bool>( typename Defs::value_type const & ) > // The next version of insert does not compile. // , has_insert2< // typename Defs::const_iterator( // typename Defs::const_iterator // , typename Defs::value_type const & // ) // > , has_insert2< void( typename any_input_iterator<T>::type , typename any_input_iterator<T>::type ) > /* Observers */ , has_key_comp<typename Defs::key_compare()> , has_value_comp<typename Defs::value_compare()> /* Operations */ , has_count<typename Defs::size_type(typename Defs::const_reference)> , has_equal_range< std::pair< typename Defs::const_iterator , typename Defs::const_iterator >(typename Defs::value_type const &) > , has_find< typename Defs::const_iterator(typename Defs::value_type const &) > , has_lower_bound< typename Defs::const_iterator(typename Defs::value_type const &) > , has_upper_bound< typename Defs::const_iterator(typename Defs::value_type const &) > /* Allocator */ // , has_get_allocator<typename Defs::allocator_type()> > {}; template<typename T, typename Allocator = any_allocator<T> > struct any_set_adaptor : any<set_requirements<T,Allocator>, _self &> { // Declare typedefs. typedef any_set_typedefs<T,Allocator> _typedefs; BOOST_PP_SEQ_FOR_EACH( BOOST_TYPE_ERASURE_FWD_TYPEDEFS , typename _typedefs , BOOST_TYPE_ERASURE_STL_TYPEDEFS ) template<typename Set> any_set_adaptor(Set & set) : any<set_requirements<T,Allocator>, _self &>(set) {} #if 0 // It appears there is no swap for type_erasure::any, either as a member or // assocaited function. void swap(any_set_adaptor & arg) { typedef any<set_requirements<T,Allocator>, _self &> base_type; swap( static_cast<base_type &>(*this), static_cast<base_type &>(arg) ); } #endif }; }} #undef BOOST_TYPE_ERASURE_FWD_TYPEDEFS #undef BOOST_TYPE_ERASURE_STL_TYPEDEFS #undef BOOST_TYPE_ERASURE_ALLOCATOR_TYPEDEFS #undef BOOST_TYPE_ERASURE_STL_MEMBER // main.cpp typedef boost::type_erasure::any_set_adaptor<int> set_type; void my_algorithm(set_type const & set) { std::set<int> foo(set.begin(), set.end()); set_type::iterator it; std::cout << "7 in set? " << bool(set.count(7)) << std::endl; std::cout << "empty? " << set.empty() << std::endl; std::cout << "is 3<4? " << set.key_comp()(3,4) << std::endl; std::cout << "nameof(reference) = " << typeid(set_type::reference).name() << std::endl; std::cout << sizeof(set_type) << std::endl; } int main() { any_allocator<int> a = std::allocator<int>(); int x; std::cout << a.address(x) << std::endl; std::set<int> set0; set0.insert(4); my_algorithm(set0); return 0; }