/* * Souffle - A Datalog Compiler * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved * Licensed under the Universal Permissive License v 1.0 as shown at: * - https://opensource.org/licenses/UPL * - /licenses/SOUFFLE-UPL.txt */ /************************************************************************ * * @file ContainerUtil.h * * @brief Datalog project utilities * ***********************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include namespace souffle { // ------------------------------------------------------------------------------- // General Container Utilities // ------------------------------------------------------------------------------- template using Own = std::unique_ptr; template using VecOwn = std::vector>; template Own mk(Args&&... xs) { return Own(new B(std::forward(xs)...)); } /** * Use to range-for iterate in reverse. * Assumes `std::rbegin` and `std::rend` are defined for type `A`. */ template struct reverse { reverse(A& iterable) : iterable(iterable) {} A& iterable; auto begin() { return std::rbegin(iterable); } auto end() { return std::rend(iterable); } }; /** * A utility to check generically whether a given element is contained in a given * container. */ template bool contains(const C& container, const typename C::value_type& element) { return std::find(container.begin(), container.end(), element) != container.end(); } // TODO: Detect and generalise to other set types? template bool contains(const std::set& container, const A& element) { return container.find(element) != container.end(); } /** * Version of contains specialised for maps. * * This workaround is needed because of set container, for which value_type == key_type, * which is ambiguous in this context. */ template bool contains(const C& container, const typename C::value_type::first_type& element) { return container.find(element) != container.end(); } /** * Returns the first element in a container that satisfies a given predicate, * nullptr otherwise. */ template typename C::value_type getIf(const C& container, std::function pred) { auto res = std::find_if(container.begin(), container.end(), [&](const typename C::value_type item) { return pred(item); }); return res == container.end() ? nullptr : *res; } /** * Get value for a given key; if not found, return default value. */ template typename C::mapped_type const& getOr( const C& container, typename C::key_type key, const typename C::mapped_type& defaultValue) { auto it = container.find(key); if (it != container.end()) { return it->second; } else { return defaultValue; } } /** * A utility function enabling the creation of a vector with a fixed set of * elements within a single expression. This is the base case covering empty * vectors. */ template std::vector toVector() { return std::vector(); } /** * A utility function enabling the creation of a vector with a fixed set of * elements within a single expression. This is the step case covering vectors * of arbitrary length. */ template std::vector toVector(const T& first, const R&... rest) { return {first, rest...}; } /** * A utility function enabling the creation of a vector of pointers. */ template std::vector toPtrVector(const std::vector>& v) { std::vector res; for (auto& e : v) { res.push_back(e.get()); } return res; } /** * Applies a function to each element of a vector and returns the results. */ template B */> auto map(const std::vector& xs, F&& f) { std::vector ys; ys.reserve(xs.size()); for (auto&& x : xs) { ys.emplace_back(f(x)); } return ys; } // ------------------------------------------------------------------------------- // Cloning Utilities // ------------------------------------------------------------------------------- template auto clone(const std::vector& xs) { std::vector ys; ys.reserve(xs.size()); for (auto&& x : xs) { ys.emplace_back(clone(x)); } return ys; } // ------------------------------------------------------------- // Ranges // ------------------------------------------------------------- /** * A utility class enabling representation of ranges by pairing * two iterator instances marking lower and upper boundaries. */ template struct range { // the lower and upper boundary Iter a, b; // a constructor accepting a lower and upper boundary range(Iter a, Iter b) : a(std::move(a)), b(std::move(b)) {} // default copy / move and assignment support range(const range&) = default; range(range&&) = default; range& operator=(const range&) = default; // get the lower boundary (for for-all loop) Iter& begin() { return a; } const Iter& begin() const { return a; } // get the upper boundary (for for-all loop) Iter& end() { return b; } const Iter& end() const { return b; } // emptiness check bool empty() const { return a == b; } // splits up this range into the given number of partitions std::vector partition(int np = 100) { // obtain the size int n = 0; for (auto i = a; i != b; ++i) { n++; } // split it up auto s = n / np; auto r = n % np; std::vector res; res.reserve(np); auto cur = a; auto last = cur; int i = 0; int p = 0; while (cur != b) { ++cur; i++; if (i >= (s + (p < r ? 1 : 0))) { res.push_back({last, cur}); last = cur; p++; i = 0; } } if (cur != last) { res.push_back({last, cur}); } return res; } }; /** * A utility function enabling the construction of ranges * without explicitly specifying the iterator type. * * @tparam Iter .. the iterator type * @param a .. the lower boundary * @param b .. the upper boundary */ template range make_range(const Iter& a, const Iter& b) { return range(a, b); } // ------------------------------------------------------------------------------- // Equality Utilities // ------------------------------------------------------------------------------- /** * Cast the values, from baseType to toType and compare using ==. (if casting fails -> return false.) * * @tparam baseType, initial Type of values * @tparam toType, type where equality comparison takes place. */ template bool castEq(const baseType* left, const baseType* right) { if (auto castedLeft = dynamic_cast(left)) { if (auto castedRight = dynamic_cast(right)) { return castedLeft == castedRight; } } return false; } /** * A functor class supporting the values pointers are pointing to. */ template struct comp_deref { bool operator()(const T& a, const T& b) const { if (a == nullptr) { return false; } if (b == nullptr) { return false; } return *a == *b; } }; /** * A function testing whether two containers are equal with the given Comparator. */ template bool equal_targets(const Container& a, const Container& b, const Comparator& comp) { // check reference if (&a == &b) { return true; } // check size if (a.size() != b.size()) { return false; } // check content return std::equal(a.begin(), a.end(), b.begin(), comp); } /** * A function testing whether two containers of pointers are referencing equivalent * targets. */ template class Container> bool equal_targets(const Container& a, const Container& b) { return equal_targets(a, b, comp_deref()); } /** * A function testing whether two containers of unique pointers are referencing equivalent * targets. */ template class Container> bool equal_targets(const Container>& a, const Container>& b) { return equal_targets(a, b, comp_deref>()); } /** * A function testing whether two maps of unique pointers are referencing to equivalent * targets. */ template bool equal_targets( const std::map>& a, const std::map>& b) { auto comp = comp_deref>(); return equal_targets( a, b, [&comp](auto& a, auto& b) { return a.first == b.first && comp(a.second, b.second); }); } /** * Compares two values referenced by a pointer where the case where both * pointers are null is also considered equivalent. */ template bool equal_ptr(const T* a, const T* b) { if (a == nullptr && b == nullptr) { return true; } if (a != nullptr && b != nullptr) { return *a == *b; } return false; } /** * Compares two values referenced by a pointer where the case where both * pointers are null is also considered equivalent. */ template bool equal_ptr(const std::unique_ptr& a, const std::unique_ptr& b) { return equal_ptr(a.get(), b.get()); } template using copy_const_t = std::conditional_t, const B, B>; /** * Helpers for `dynamic_cast`ing without having to specify redundant type qualifiers. * e.g. `as(p)` instead of `dynamic_cast(p.get())`. */ template auto as(A* x) { static_assert(std::is_base_of_v, "`as` does not allow cross-type dyn casts. " "(i.e. `as` where `B <: A` is not true.) " "Such a cast is likely a mistake or typo."); return dynamic_cast*>(x); } template std::enable_if_t, copy_const_t*> as(A& x) { return as(&x); } template B* as(const Own& x) { return as(x.get()); } /** * Checks if the object of type Source can be casted to type Destination. */ template bool isA(Source&& src) { return as(std::forward(src)) != nullptr; } } // namespace souffle