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