- After testing if some object is in a collection, You have to make a second pass over the collection to find searched object's position.
- If collection is random access and it is sorted, searching can be done more efficient by std::binary_search algorithm.
To deal with the first inefficiency I had merged finder and in_impl template classes into one template class with two specializations and common base class. These changes lead to the removal of find_impl function, which was no longer needed. To obtain iterator type for any collection I have created iterator_type metafunction.
The iterator_type metafunction is simple metafunction, which for arrays return pointer type and for other types, it uses decltype keyword to calculate the type of iterator for given collection type. Users can specialize iterator_type metafunction for their own classes. Source code of that utility metafunction is presented below.
template<typename Collection>
struct iterator_type {
typedef decltype(std::end(Collection())) type;
};
template<typename T, size_t S>
struct iterator_type<T[S]> {
typedef T* type;
};
Common base class for specializations of the new in_impl template class is called in_impl_base, and its purpose is to gather common code for both specializations. It is: storing iterators to a position of searched element and end of the collection, calculating whether the element is in the collection and returning position of the element. The source code presented below.
template<typename Collection>
class in_impl_base {
public:
typedef typename iterator_type<Collection>::type iterator;
in_impl_base(const iterator& position, const iterator& end)
: position_(position), end_(end) {}
explicit operator bool() const {
return end_ != position_;
}
iterator position() const {
return position_;
}
private:
iterator position_;
iterator end_;
};
Specializations of the in_impl template class differ only by the value of HasMemberFind template parameter and means of obtaining a position of the searched element. If HasMemberFind equals to true, then Collection::find member function is used. In the other case, the std::find algorithm is applied. To simplify type name used in in_left and in_right classes, there is also template alias in_implementation defined. It is used by both above-mentioned classes instead of old in_impl class. Described code is presented below.
template<typename Element, typename Collection, bool HasMemberFind = false>
class in_impl : public in_impl_base<Collection> {
public:
in_impl(const Element& e, const Collection& c)
: in_impl_base<Collection>(std::find(std::begin(c), std::end(c), e), std::end(c)) {}
};
template<typename Element, typename Collection>
class in_impl<Element, Collection, true> : public in_impl_base<Collection> {
public:
in_impl(const Element& e, const Collection& c)
: in_impl_base<Collection>(c.find(e), std::end(c)) {}
};
template<typename Element, typename Collection> using in_implementation = in_impl<Element, Collection, has_member_find<Collection>::value>;
To test new features, I have changed the check function. By calling print it uses proper specialization of printer template class and prints value stored in the collection under obtained position. The body of the main function remained unchanged. Testing utilities are presented below.
template<typename ValueType>
struct printer {
printer(const ValueType& val) {
std::cout << "\t\tvalue = " << val << std::endl;
}
};
template<typename Key, typename Value>
struct printer<std::pair<Key, Value>> {
printer(const std::pair<Key, Value>& val) {
std::cout << "\t\tvalue = (" << val.first << ", " << val.second << ")" << std::endl;
}
};
template<typename ValueType> void print(const ValueType& val) {
printer<ValueType> p = val;
}
template<typename C>
void check(const int e, const C& c) {
if(auto pos = e in c) {
std::cout << "\t" << e << " is in given collection: " << typeid(c).name() << std::endl;
print(*pos.position());
}
else {
std::cout << "\t" << e << " is not in given collection: " << typeid(c).name() << std::endl;
}
}
These are all changes made to make a port of Python's in keyword into c++. It turned to be even slightly more powerful than its Python's predecessor as it can test element existence and obtain its position in one pass over the collection. Modified source code can be found on GitHub in 1.5.0 tag.