AMDG On 03/24/2014 11:46 AM, Samuel Christie wrote:
Interesting. That looks useful, but at the same time I think I can actually get away without erasing _value. The actual type would be the erased type we were working on earlier, so I should be able to just stick it in explicitly.
How would you erase insert() though? Its return type seems fairly complicated. I tried using std::pair<te::any<te::forward_iterator<> >, bool>, but I get "invalid initialization of reference of type 'int&' from expression of type 'const int'" for the simple test case.
This is probably because unordered_set doesn't provide mutable iterators. You'll need to set the reference type of the iterator. Also if you create an independent any type for the iterator, you probably won't be able to do much with it, and you might as well return void. The iterator should really be another associated type, but that creates another problem. Handling composite return types like std::pair is painful. The library doesn't handle it because I don't have a good way to deal with it generically. The basic problem is that on the inside you have std::pair<std::unordered_set<int>::iterator, bool> and on the outside you have std::pair<any<requirements, _iter>, bool> and I need to convert from one to the other. Of course, it's fairly easy to do this for std::pair, but imagine instead trying to handle this for some arbitrary template with an unknown set of accessors and an unknown set of constructors. I'd need to have some kind of traits template that could be specialized to handle the conversion for specific types. If this were all, it would be manageable, but it gets worse. The library needs to convert from T (the stored type) to any<Concept, Placeholder>. The interface boundary inside the library doesn't know anything about T (this is the critical requirement for any kind of type erasure). It also doesn't know anything about Concept (This is necessary to allow composition of concept requirements). As a result the actual type passed through the dispatcher cannot be any<Concept, Placeholder> nor can it be T. It has to be void* (or something equivalent). So, given a template X<P>, which is used as the return type of an erased function, the actual conversions that we need are X<T> -> X<void*> -> X<any<C, P> >. This is now a problem, because there's absolutely no guarantee that X<void*> is a legal type. I don't have a good solution for this in the general case. It is possible to handle insert like this: typedef any<copy_constructible<> > internal_type; template<class C, class T, class I> struct has_insert { static std::pair<internal_type, bool> apply(C& c, const T& t) { // implicit conversion of std::pair should work, // but we need to make sure that we use the // specified iterator type regardless of what // c.insert actually returns. std::pair<I, bool> result = c.insert(t); return result; } }; namespace boost { namespace type_erasure { template<class C, class T, class I, class Base> struct concept_interface<has_insert<C, T, I>, Base, C> : Base { std::pair<typename rebind_any<Base, I>::type, bool> insert(typename as_param<Base, const T&>::type arg) { std::pair<internal_type, bool> result = call(has_insert<C, T, I>(), *this, arg); // Force the iterator to the correct any type. // We know that this is safe because the // iterator comes from has_insert::apply // which guarantees that the iterator // is the correct type for the placeholder I. typename rebind_any<Base, I>::type iter( result.first, binding_of(*this)); return std::make_pair(iter, result.second); }; }; }} Now you would just need to specify that I is a [forward|bidirectional|random_access]_iterator with a reference type of [const] T&. (Warning, this code is completely untested.) In Christ, Steven Watanabe