RenderCore/Utilities/CKFunctionalHelpers.h (135 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 #import <Foundation/Foundation.h> #import <algorithm> #import <functional> #import <type_traits> #import <vector> #import <RenderCore/CKMacros.h> namespace CK { /** Takes an iterable, applies a function to every element, and returns a vector of the results. Adapted from http://stackoverflow.com/questions/14945223/map-function-with-c11-constructs */ template<typename Func> auto mapWithIndex(id<NSFastEnumeration> collection, CK_NOESCAPE Func &&func) -> std::vector<decltype(func(std::declval<id>(), std::declval<NSUInteger>()))> { std::vector<decltype(func(std::declval<id>(), std::declval<NSUInteger>()))> to; NSUInteger index = 0; for (id obj in collection) { to.push_back(func(obj, index)); index++; } return to; } template <typename T, typename Func> auto mapWithIndex(const T &iterable, CK_NOESCAPE Func &&func) -> std::vector<decltype(func(std::declval<typename T::value_type>(), std::declval<NSUInteger>()))> { typedef decltype(func(std::declval<typename T::value_type>(), std::declval<NSUInteger>())) value_type; std::vector<value_type> res; res.reserve(iterable.size()); NSUInteger index = 0; for (auto it = iterable.begin(); it != iterable.end(); ++it, ++index) { res.push_back(func(*it, index)); } return res; } template <typename T, typename Func> auto map(const T &iterable, CK_NOESCAPE Func &&func) -> std::vector<decltype(func(std::declval<typename T::value_type>()))> { // Convenience type definition typedef decltype(func(std::declval<typename T::value_type>())) value_type; // Prepares an output vector of the appropriate size std::vector<value_type> res; res.reserve(iterable.size()); // Let std::transform apply `func` to all elements // (use perfect forwarding for the function object) std::transform( std::begin(iterable), std::end(iterable), std::back_inserter(res), std::forward<Func>(func) ); return res; } template<typename Func> auto map(id<NSFastEnumeration> collection, CK_NOESCAPE Func &&func) -> std::vector<decltype(func(std::declval<id>()))> { return mapWithIndex(collection, [&func](id obj, NSUInteger) { return func(obj); }); } template <typename T, typename Func> auto filter(const T &iterable, CK_NOESCAPE Func &&func) -> std::vector<typename T::value_type> { std::vector<typename T::value_type> to; for (const auto &obj : iterable) { if (func(obj)) { to.push_back(obj); } } return to; } template<typename Func> auto filter(id<NSFastEnumeration> collection, CK_NOESCAPE Func &&func) -> std::vector<id> { std::vector<id> to; for (id obj in collection) { if (func(obj)) { to.push_back(obj); } } return to; } namespace detail { template <class ContainerA, class ContainerB> std::vector<typename std::decay<ContainerA>::type::value_type> chainImpl(ContainerA &&a, ContainerB &&b) { std::vector<typename std::decay<ContainerA>::type::value_type> newVector(std::forward<ContainerA>(a)); newVector.reserve(newVector.size() + b.size()); for (auto &&i: b) { newVector.push_back(std::move(i)); } return newVector; } } // namespace detail // std::initializer_list<T> isn't deduced as a template argument, so // we need to provide explicit overloads for it. I didn't want to // expose detail::chainImpl in its full generality either, so I'm // also providing explicit overloads for flavors of vectors. In // short, what follows are overloads for all 9 possible 2-element // pairs drawn from the set {const vector<T> &, vector<T> &&, // initializer_list<T>}. template <class T> std::vector<T> chain(const std::vector<T> &a, const std::vector<T> &b) { return detail::chainImpl(a, b); } template <class T> std::vector<T> chain(const std::vector<T> &a, std::vector<T> &&b) { return detail::chainImpl(a, std::move(b)); } template <class T> std::vector<T> chain(std::vector<T> &&a, const std::vector<T> &b) { return detail::chainImpl(std::move(a), b); } template <class T> std::vector<T> chain(std::vector<T> &&a, std::vector<T> &&b) { return detail::chainImpl(std::move(a), std::move(b)); } template <class T> std::vector<T> chain(std::vector<T> &&a, std::initializer_list<T> b) { return detail::chainImpl(std::move(a), b); } template <class T> std::vector<T> chain(const std::vector<T> &a, std::initializer_list<T> b) { return detail::chainImpl(a, b); } template <class T> std::vector<T> chain(std::initializer_list<T> a, const std::vector<T> &b) { return detail::chainImpl(a, b); } template <class T> std::vector<T> chain(std::initializer_list<T> a, std::vector<T> &&b) { return detail::chainImpl(a, std::move(b)); } template <class T> std::vector<T> chain(std::initializer_list<T> a, std::initializer_list<T> b) { return detail::chainImpl(a, b); } /** This function takes a vector and returns a new vector after adding an additional object between every entry in the vector Example: inputs: { 2, 4, 6 }, factory ^(int) { return 0; } output: { 2, 0, 4, 0, 6 } */ template <typename T, typename Func> auto intersperse(const std::vector<T> &a, CK_NOESCAPE Func &&factory) -> std::vector<T> { if (a.size() < 2) { return a; } std::vector<T> newVector; for (int i = 0; i < a.size(); i++) { newVector.push_back(a.at(i)); if (i != a.size() - 1) { newVector.push_back(factory()); } } return newVector; } }; #endif