2

I want to create universal "wrapper" for stl containers, something like:

template<template <typename, typename...> class Container = std::vector >
class ContainerWrapper{
  add();
  size();
  find();
  resize();
  sort(); 
  /**/
}

+iterators. I would want member functions to have different implementations, depending on Container provided methods. Is C++ template system enough to create this? Is this even possible, using only standard (no boost, no messing with preprocessor)?

I know how to do it the hard way - write template specialization for each stl container. But I would want it to work with other containers too, and also i'm looking for more universal way to do it.

Also, what is better here? Inheriting from Container or having Container as component?

max66
  • 65,235
  • 10
  • 71
  • 111
Kaznov
  • 1,035
  • 10
  • 17
  • 2
    Regarding the standard containers, they are not designed to be inherited. – François Andrieux Mar 29 '17 at 20:32
  • As each `ContainerWrapper` is an unrelated type, why do you want this? I mean, not every std container can be `resized`. What happens when passed a `std::set`, and how does that make your solution better than just taking a `std::set` in the first place? Are you addicted to things being members of a type? Why not just free functions? Like `sort(container)`. – Yakk - Adam Nevraumont Mar 29 '17 at 20:38
  • 2
    @FrançoisAndrieux As my comment to the answer; they are not designed to be inherited *polymorphically*. – Nir Friedman Mar 29 '17 at 20:52

2 Answers2

1

STL containers are not meant to be inherited. They do not have virtual destructors. It was discussed having the containers as final, but it was not done because it would a breaking change.

So using composition is your best bet.

Community
  • 1
  • 1
Mac
  • 3,397
  • 3
  • 33
  • 58
  • 3
    This is not correct, you are conflating inheritance and polymorphism. `protected` is a superset of of `public` and therefore non-polymorphic inheritance is a valid way to use *any* class (unless it's marked `final`). – Nir Friedman Mar 29 '17 at 20:47
1

Some time ago I've developed something similar for a project of mine.

I've extracted a full working example (the original code is more complex) to show how to use a method addVal() (that call push_back(), push(), insert() or push_front()) to add a value to the wrapped container.

This code works (if I remember correctly) for std::vector, std::set, std::multiset, std::unordered_set, std::unordered_multiset, std::deque, std::queue, std::priority_queue, std::forward_list and std::stack.

Other containers (as std::array, by example) may requires different specializations for cntWrp.

I don't want explaint every single line of code but, if you have questions, I can try to respond (or, if you prefer, I can give you the link of my github project).

The example

#include <set>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <type_traits>

class emptyClass
 { };

template <typename ... Ts>
struct funcType;

template <typename T, typename ... Ts>
struct funcType<T, Ts...>
 { using type
      = typename std::conditional<T::result,
         typename T::type, typename funcType<Ts...>::type>::type; };

template <>
struct funcType<>
 { using type = emptyClass; };


#define methodCheck_1(meth)                            \
                                                       \
   class helpMeth_1_##meth {};                         \
                                                       \
   template <typename T, typename A>                   \
   struct isWithMethod_1_##meth                        \
    {                                                  \
      template<typename U>                             \
      static decltype(U().meth(A())) func (U*);        \
                                                       \
      template<typename U>                             \
      static emptyClass func (...);                    \
                                                       \
      static const bool result                         \
         = ! std::is_same<emptyClass,                  \
                decltype(func<T>(nullptr))>::value;    \
                                                       \
      using  type = helpMeth_1_##meth;                 \
    }

methodCheck_1(insert);
methodCheck_1(push);
methodCheck_1(push_back);
methodCheck_1(push_front);

template <typename>
class cntWrp;

template <template <typename ...> class C, typename X, typename ... Xs>
class cntWrp< C<X, Xs...> >
 {
   private:

      using addModeType = typename funcType<
         isWithMethod_1_push_back<C<X, Xs...>, X>,
         isWithMethod_1_insert<C<X, Xs...>, X>,
         isWithMethod_1_push<C<X, Xs...>, X>,
         isWithMethod_1_push_front<C<X, Xs...>, X>>::type;

      static constexpr addModeType  addMode {};

      void addVal (X const & x, helpMeth_1_push_back const)
       { val.push_back(x); }

      void addVal (X const & x, helpMeth_1_push const)
       { val.push(x); }

      void addVal (X const & x, helpMeth_1_insert const)
       { val.insert(x); }

      void addVal (X const & x, helpMeth_1_push_front const)
       { val.push_front(x); }

      void addVal (X const & x, emptyClass const)
       { throw std::runtime_error("cntWr<>::addVal without mode"); }

   public:

      C<X, Xs...> val {};

      cntWrp ()
       { }

      cntWrp (C<X, Xs...> const & v0) : val { v0 }
       { }

      void addVal (X const & x)
       { addVal(x, addMode); }
 };

int main ()
 {
   cntWrp<std::set<int>>  csi;

   csi.addVal(2);
   csi.addVal(7);
   csi.addVal(5);

   std::cout << "set:" << std::endl;

   for ( auto const elem : csi.val )
      std::cout << elem << std::endl;

   cntWrp<std::vector<int>> cvi;

   cvi.addVal(2);
   cvi.addVal(7);
   cvi.addVal(5);

   std::cout << "vector:" << std::endl;

   for ( auto const elem : cvi.val )
      std::cout << elem << std::endl;
 }
max66
  • 65,235
  • 10
  • 71
  • 111