ISanych
ISanych

Reputation: 22680

Create typedef only when typedef exists in template parameter

I'm trying to create universal container wrapper.

template<typename type>
class ContainerWrapper
{
public:
  using allocator_type = typename type::allocator_type;
  using size_type = typename type::size_type;
  using difference_type = typename type::difference_type;
  using pointer = typename type::pointer;
  using const_pointer = typename type::const_pointer;
  using reference = typename type::reference;
  using const_reference = typename type::const_reference;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;
  using reverse_iterator = typename type::reverse_iterator;
  using const_reverse_iterator = typename type::const_reverse_iterator;
  using value_type = typename type::value_type;

  iterator begin() noexcept { return container.begin(); }
  const_iterator begin() const noexcept { return container.begin(); }
  iterator end() noexcept { return container.end(); }
  const_iterator end() const noexcept { return container.end(); }
  reverse_iterator rbegin() noexcept { return container.rbegin(); }
  const_reverse_iterator rbegin() const noexcept { return container.rbegin(); }
  reverse_iterator rend() noexcept { return container.rend(); }
  const_reverse_iterator rend() const noexcept { return container.rend(); }
  const_iterator cbegin() const noexcept { return container.cbegin(); }
  const_iterator cend() const noexcept { return container.cend(); }
  const_reverse_iterator crbegin() const noexcept { return container.crbegin(); }
  const_reverse_iterator crend() const noexcept { return container.crend(); }
protected:
  ContainerWrapper() {}
  type& getContainer() { return container; }
  const type& getContainer() const { return container; }
private:
  type container;
};

But not all containers have all kinds of iterators. Is it possible to expose container types only when they exists? Something like

using const_reverse_iterator = typename std::enable_if_t<std::is_class<typename type::const_reverse_iterator>::value, typename type::const_reverse_iterator>::type;

I could use C++11 only (gcc 4.7). Of course I could create different wrappers for different containers, but I prefer to have one universal wrapper.

Upvotes: 1

Views: 149

Answers (3)

AOK
AOK

Reputation: 503

Is there a reason why your aren't using auto for you functions?

Using HolyBlackCat's code, you should consider grouping types and functions that will clearly (or should) come together, and using auto to avoid ambiguous function calls:

template <typename...> struct void_type { using type = void; };
template<typename type, typename = void>
struct conditional_derive_iterator {
protected:
    conditional_derive_iterator(type*const container) {}
};
template<typename type>
struct conditional_derive_iterator
    <type, typename void_type<typename type::iterator>::type>
{
    // grouping types
    using iterator = typename type::iterator;
    using const_iterator = typename type::const_iterator;
    // this should work with gcc. avoids ambiguity. cleaner code
    inline constexpr auto begin() const noexcept { return c->begin(); }
    inline constexpr auto end() const noexcept { return c->end(); }
protected:
    // this will let you get access to the container
    conditional_derive_iterator(type*const container) : c(container) {}
private:
    type*const c;
};

template<typename type>
class ContainerWrapper :
    public conditional_derive_iterator<type>
{
public:
    // for demo purpose. however, make sure to pass &container to your bases
    ContainerWrapper(const type& c) :
        container(c),
        conditional_derive_iterator(&container) {}
    // continue code...
};

class UselessType{};

void main()
{
    ContainerWrapper<UselessType> a(UselessType{});
    ContainerWrapper<std::vector<int>> b({1,2,3});
    auto itera = a.begin(); // this will not compile
    auto iterb = b.begin(); // this will compile
}

While auto func()... is c++14 and up, I believe that auto func() -> return_type is c++11, so begin/end would look like this:

inline constexpr auto begin() const noexcept -> decltype(iterator{}, const_iterator{}) { return c->begin(); }

Here is an example: https://repl.it/K9g5/0

You should also take a look at this Member Detector: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector

It could come in handy.

Upvotes: 1

HolyBlackCat
HolyBlackCat

Reputation: 96286

You could create a base class for each such type, and specialize that base with SFINAE:

template <typename...> struct void_type
{
    using type = void;
};

template <typename T, typename = void> struct ContainerWrapper_Base_const_reverse_iterator{};
template <typename T> struct ContainerWrapper_Base_const_reverse_iterator
    <T, typename void_type<typename T::const_reverse_iterator>::type>
{
    using const_reverse_iterator = typename T::const_reverse_iterator;
};

// ... Similar bases for each conditional alias.

template <typename type> class ContainerWrapper
: public ContainerWrapper_Base_const_reverse_iterator<type>
    // ... Inherit from all those bases
{
    // ...
};

You could possibly put similar types into a single base, if you're sure that they're always defined together or not defined at all.

Also, you might want to generate those bases with preprocessor, possibly with x-macros.

Also note that the above code could be simplified with C++17 std::void_t or a handwritten template <typename...> using void_t = void;, but neither work properly in GCC 4.7.

Upvotes: 3

Deduplicator
Deduplicator

Reputation: 45664

Is a raw array container-y enough for you?
Anyway:

Use SFINAE, overload-resolution and inheritance.

namespace detail {
    using std::rbegin;
    template <class T>
    auto reverse_iter_typedefs(long) {
        struct {} r;
        return r;
    }
    template <class T, class X = decltype(rbegin(std::declval<T&>()))>
    auto reverse_iter_typedefs(int) {
        struct {
            using reverse_iterator = decltype(rbegin(std::declval<T&>()));
        } r;
        return r;
    }
}

template <class T>
ContainerWrapper : decltype(detail::reverse_iter_typedefs<T>(1)), ...

Upvotes: 3

Related Questions