RenderCore/Utilities/CKCollection.h (120 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import <RenderCore/CKDefines.h> #if CK_NOT_SWIFT #ifndef CKCollection_h #define CKCollection_h #import <algorithm> #import <functional> #import <type_traits> #import <RenderCore/RCAssert.h> #import <RenderCore/CKFunctionalHelpers.h> #import <RenderCore/CKOptional.h> namespace CK { namespace Collection { template <typename Collection, typename Predicate> bool containsWhere(const Collection& collection, Predicate &&predicate) { return std::find_if(collection.begin(), collection.end(), predicate) != collection.end(); } template <typename Collection> bool contains(const Collection& collection, typename Collection::const_reference value) { return std::find(collection.begin(), collection.end(), value) != collection.end(); } /* Returns elements that are present in the first collection but not in the second. Element equivalence is determined by calling a binary predicate passed as the last parameter. */ template <typename Collection1, typename Collection2, typename Predicate> auto difference(const Collection1 &c1, const Collection2 &c2, Predicate &&areEqual) { return filter(c1, [&](const auto &x1) { return !containsWhere(c2, [&](const auto &x2) { return areEqual(x1, x2); }); }); } /* Returns elements that are present both collections. Element equivalence is determined by calling a binary predicate passed as the last parameter. */ template <typename Collection1, typename Collection2, typename Predicate> auto intersection(const Collection1 &c1, const Collection2 &c2, Predicate &&areEqual) { return filter(c1, [&](const auto &x1) { return containsWhere(c2, [&](const auto &x2) { return areEqual(x1, x2); }); }); } /* Given a collection of other collections, returns a lower-dimensional collection by "flattening" its elements into it, e.g. `flatten({{1, 2}, {3, 4}) == {1, 2, 3, 4}`. */ template <typename Collection> auto flatten(const Collection &c) { auto r = std::vector<typename Collection::value_type::value_type> {}; for (const auto &x : c) { r.insert(r.end(), x.begin(), x.end()); } return r; } /* Returns string representations of elements of the collection separated by comma and new line. String representation of each element is obtained by calling the passed element description provider function. */ template <typename Collection, typename ElementDescriptionFunc> auto descriptionForElements(const Collection &c, ElementDescriptionFunc &&d) { static_assert(std::is_convertible<ElementDescriptionFunc, std::function<NSString *(typename Collection::const_reference)>>::value, "Description provider needs to take a const reference to an element of the collection and return an NSString *"); auto elementStrs = static_cast<NSMutableArray<NSString *> *>([NSMutableArray array]); for (const auto &e : c) { [elementStrs addObject:d(e)]; } return [elementStrs componentsJoinedByString:@",\n"]; } /** Returns an optional wrapping the value for a given key if it is present in the map, \c none otherwise. */ template <typename Map> auto valueForKey(const Map& map, const typename Map::key_type& key) -> Optional<typename Map::mapped_type> { auto const it = map.find(key); if (it == map.end()) { return none; } return (*it).second; } } } namespace CK { template <typename Range, typename Element> auto find(Range &&range, const Element &element) { return std::find(std::begin(range), std::end(range), element); } template <typename Range, typename UnaryPredicate> auto find_if(Range &&range, UnaryPredicate&& predicate) { return std::find_if(std::begin(range), std::end(range), std::forward<UnaryPredicate>(predicate)); } } template <typename T> class CKCocoaCollectionAdapter { static_assert(std::is_convertible<T, id>::value, "Only elements of ObjC types are supported"); public: // Modelled after http://en.cppreference.com/w/cpp/iterator/istream_iterator class Iterator: public std::iterator<std::input_iterator_tag, T> { public: Iterator() : _enumerator(nil), _value(nil) {}; Iterator(NSEnumerator *enumerator) : _enumerator(enumerator), _value(enumerator.nextObject) {}; T operator* () const { return _value; } void operator++ () { _value = _enumerator.nextObject; } bool operator == (const Iterator &rhs) const { return _value == rhs._value; } bool operator != (const Iterator &rhs) const { return !(*this == rhs); } private: NSEnumerator *_enumerator; T _value; }; using value_type = typename Iterator::value_type; using const_reference = const value_type &; CKCocoaCollectionAdapter(id collection) : _collection(collection) { assertCollectionRespondsToSelector(collection, @selector(objectEnumerator)); assertCollectionRespondsToSelector(collection, @selector(count)); } auto begin() const { return Iterator {[_collection objectEnumerator]}; } auto end() const { return Iterator {}; } auto size() const { return [_collection count]; } auto empty() const { return size() == 0; } private: static auto assertCollectionRespondsToSelector(id c, SEL sel) { RCCAssert([c respondsToSelector:sel], @"%@ is not a collection since it doesn't respond to %@", c, NSStringFromSelector(sel)); } id _collection; }; #endif /* CKCollection_h */ #endif