Reputation: 3595
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
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