3

I need to check, if a containers erase function returns the iterator. I'd normally check for the function signature via e.g. boost. But in case of a boost class (e.g. flat_set) erase is inherited and thus not found by the check. But I really need it. SFINAE to check for inherited member functions only shows a C++11 solution which I can't use yet.

I tried something like this:

    template <typename T> 
    class has_member_func_erase_it_constit
    { 
        typedef typename T::iterator iterator;
        typedef typename T::const_iterator const_iterator;
        typedef BOOST_TYPEOF_TPL(&T::erase) eraseType;

        typedef typename boost::function_types::result_type<eraseType>::type result;
    public: 
        static const bool value = boost::is_same<iterator, result>::value; 
    };

    template<class T>
    struct EraseReturnsIterator
    {
        static CONSTEXPR bool value = has_member_func_erase_it_constit<T>::value;
    };

But it fails since erase is overloaded. I'd probably need decltype or something like that to check the return type from a compile-time invocation of erase with const_iterator, but I can't find one.

How is this possible in pre C++11?

This does also not work if there is an erase function returning void:

    template <typename T> 
    class has_member_func_erase_it
    { 
        typedef typename T::iterator iterator;
        typedef typename T::const_iterator const_iterator;

        typedef char yes[1];
        typedef char no [2];

        static T makeT();
        static iterator makeIt();

        typedef BOOST_TYPEOF_TPL(makeT().erase(makeIt())) result;

    public: 
        static const bool value = boost::is_same<iterator, result>::value; 
    };
Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • I suspect that you're going to be more specific; some containers have > 1 methods named `erase`. For example, `vector` has both `erase(iterator)` and `erase(iterator, iterator)` – Marshall Clow Aug 05 '15 at 23:01
  • How many containers will you meet before you upgrade to C++11? 10? Hard code them. Ifdef C++11 to do proper thing, maybe asserting they agree. – Yakk - Adam Nevraumont Aug 05 '15 at 23:47
  • I mean check vor "void erase(iterator)" vs "iterator erase(iterator)". But I found a solution that works and will post it later today. It is some really nice piece of evil template trickery. – Flamefire Aug 06 '15 at 08:09

1 Answers1

0

The following works:

    /// "Stores a type"
    template<typename T> struct Type2Type{
        typedef T type;
    };

    /// Evil hackery to put an expression into a type
    template<typename T>
    Type2Type<T> operator,(T, Type2Type<void>);

    template<typename T>
    T declval();

    template<class T>
    struct EraseReturnsIterator
    {
        typedef typename T::iterator iterator;
        typedef BOOST_TYPEOF_TPL((declval<T>().erase(declval<iterator>()), Type2Type<void>())) result;

        static CONSTEXPR bool value = boost::is_same<iterator, typename result::type>::value;
    };

Basicly we just call the function whose return type we need. If no function of this type ever returned void than BOOST_TYPEOF_TPL would already work. Unfortunately erase CAN return void which breaks the implementation as it tries to pass "void&" somewhere down the stack.

So to avoid the void (no pun intended) we put it in a type, that holds a type. To be able to do this for expressions, we overload the comma operator. This way result equals "Type2Type" which we can easily read. Done!

Idea for the comma-overload: https://stackoverflow.com/a/5553460/1930508

Community
  • 1
  • 1
Flamefire
  • 5,313
  • 3
  • 35
  • 70