Kadir Erdem Demir
Kadir Erdem Demir

Reputation: 3595

Creation of a vector wrapper from an object which only allows accessing vector with index

I created a class for using chainable Filter and Map operations mostly for avoiding redundant for loops.

For example

std::vector<ObjectASharedPtr> temp = GetListOfA();
FunctionalIteratableWrapper<ObjectASharedPtr> wrapper( temp );
wrapper.Filter([]( ObjectASharedPtr objA ){ return objA->isApple(); } ).Map([]( ObjectASharedPtr objA ){ return objA->Bite(); })

Short version of my class can be seen in the bottom of the question to not to spoil the question with a long piece of code .

It works perfect when I get the vector as "std::vector". Than I can easily initiate my wrapper.

But now there is a legacy code which does not gives the data as a list directly but instead with index :

auto count = LegacyObjList->getCount(); 
for ( int i = 0; i < count; i++ )
    LegacyObj.getObjByIndex(i);

Currently whenever I want to instantiate my FunctionalIteratableWrapper with this kind of struct which only allow the access with index I am needing to do :

std::vector<LegacyObj> tempVec;
auto count = LegacyObjList->getCount(); 
for ( int i = 0; i < count; i++ )
    tempVec.push_back(LegacyObj.getObjByIndex(i));

FunctionalIteratableWrapper<LegacyObj> wrapper(temp);

I want to avoid this for loop that I am using now for each time creating my wrapper with such object which only allows access with index. What should be best solution?

template< typename Value >
class FunctionalIteratableWrapper
{
public:


    /*!
     * \brief A copy ctor like ctor. Which initiates this struct directly from vector of Iteratable.
     * \param list vector of Iteratable
     */
    FunctionalIteratableWrapper( const std::vector<Value>& list  )
    {
        iteratableList = list;
    }

    /*!
     * \brief Default ctor
     */
    FunctionalIteratableWrapper()
    {

    }

    template <typename F>
    FunctionalIteratableWrapper& Filter( F filterFunction, bool isReturnOneElement = false )
    {
        std::vector<Value> newList;
        for ( size_t i = 0; i < iteratableList.size(); i++)
        {
            if ( filterFunction(iteratableList[i]) )
            {
                newList.push_back(iteratableList[i]);
                if ( isReturnOneElement )
                   break;
            }
        }
        iteratableList = newList;
        return *this;
    }

,
    template < typename T, typename F >
    T Find( F filterFunction)
    {
        for ( size_t i = 0; i < iteratableList.size(); i++)
        {
            if ( filterFunction(iteratableList[i]) )
                return Cast<typename T::element_type>(iteratableList[i]);
        }
        return T();
    }

    template <typename T, typename F>
    FunctionalIteratableWrapper& Map( F applyFunction)
    {
        for ( size_t i = 0; i < iteratableList.size(); i++)
        {
            auto castedTerrain = Cast<typename T::element_type>(iteratableList[i]);
            if ( castedTerrain )
                applyFunction(castedTerrain);
        }
        return *this;
    }

    void Append( Value newElement )
    {
        iteratableList.push_back( newElement );
    }



    std::vector<Value> iteratableList;
};

Upvotes: 0

Views: 98

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

The easy way to handle this is to write an ADL helper function.

First change the constructor:

FunctionalIteratableWrapper( std::vector<Value> list  ):
  iterableList(std::move(list))
{
}

we now support cheap move-in.

Next, create a namespace:

namespace my_utility {
  namespace helper1 {
    template<class T, class A>
    std::vector<T, A> to_vector( std::vector<T, A> in ) {
      return std::move(in);
    }
  }
  namespace helper2 {
    using ::my_utility::helper1::to_vector;
    template<class T>
    auto as_vector( T&& t )
    -> decltype( to_vector( std::forward<T>(t) ) )
    {
      return to_vector( std::forward<T>(t) );
    }
  }
  using ::my_utility::helper2::as_vector;
}

now calling ::my_utility::as_vector(x) does an ADL lookup on to_vector(x). If it doesn't find it, it tries to deduce x as std::vector<T,A> and returns a copy.

Now we add a constructor:

template<class T,
  class=std::enable_if_t< !std::is_same< std::decay_t<T>, FunctionalIteratableWrapper >::value >
>
FunctionalIteratableWrapper( T&& t ):
  iterableList(::my_utility::as_vector(std::forward<T>(t))
{
}

we are almost there!

In the namespace of LegacyObj, write this:

std::vector<LegacyObj> to_vector( LegacyObjList const* pList ) {
  if (!pList _obj) return {};
  std::vector<LegacyObj> retval;
  int size = pList->getCount();
  retval.reserve(size);
  for (int i = 0; i < size; ++i)
    retval.push_back(pList->getObjByIndex(i);
  return retval;
}

and it will be magically found by as_vector (well, using ADL).

Now a LegacyObjList const* can be implicitly converted to your FunctionalInterableWrapper.

Code not tested. You can write to_vector first, and manually test it, and get 99% of the way there.

Then do the ::my_utility::as_vector trick.

Finally, see if you can get the constructor that calls as_vector to work.

Each adds value, and can be written separately from the others.

Upvotes: 1

Related Questions