diff --git a/make_single_header.py b/make_single_header.py new file mode 100644 index 00000000..ac7c9eea --- /dev/null +++ b/make_single_header.py @@ -0,0 +1,44 @@ +import os + +def add_header(header_name, index, included_headers, target_file): + lines = [line for line in open(header_name)] + + for line in lines: + line = line.rstrip() + is_line_to_be_skipped = line.startswith('#include "') + if line.startswith('#include <'): + if line in included_headers: + is_line_to_be_skipped = True + else: + included_headers.append(line) + + if not is_line_to_be_skipped: + target_file.write(line + '\n') + + +def add_internal_headers(included_headers, target_file): + internal_headers = ['iterbase', 'iterator_wrapper', 'iter_tuples', 'iteratoriterator'] + for internal_header in internal_headers: + add_header('internal/' + internal_header + '.hpp', 0, included_headers, target_file) + + +def add_headers(included_headers, target_file): + add_header('./filter.hpp', 7, included_headers, target_file) + add_header('./starmap.hpp', 8, included_headers, target_file) + add_header('./zip.hpp', 9, included_headers, target_file) + directory = os.fsencode('./') + for index, file in enumerate(sorted(os.listdir(directory))): + filename = os.fsdecode(file) + if filename.endswith('.hpp') and not filename in ['single_header.hpp', 'filter.hpp', 'starmap.hpp', 'zip.hpp', 'zip_longest.hpp']: + add_header(filename, 10 + index, included_headers, target_file) + + +def run(): + with open('single_header.hpp', 'w') as target_file: + included_headers = [] + add_internal_headers(included_headers, target_file) + add_headers(included_headers, target_file) + + +if __name__ == '__main__': + run() diff --git a/single_header.hpp b/single_header.hpp new file mode 100644 index 00000000..85afec55 --- /dev/null +++ b/single_header.hpp @@ -0,0 +1,5007 @@ +#ifndef ITERBASE_HPP_ +#define ITERBASE_HPP_ + +// This file consists of utilities used for the generic nature of the +// iterable wrapper classes. As such, the contents of this file should be +// considered UNDOCUMENTED and is subject to change without warning. This +// also applies to the name of the file. No user code should include +// this file directly. + +#include +#include +#include +#include +#include +#include +#include +#include + +// see gcc bug 87651 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87651 +#ifdef __GNUC__ +#define NO_GCC_FRIEND_ERROR __GNUC__ < 8 +#else +#define NO_GCC_FRIEND_ERROR 1 +#endif + +namespace iter { + namespace impl { + namespace get_iters { + // begin() for C arrays + template + T* get_begin_impl(T (&array)[N], int) { + return array; + } + + // Prefer member begin(). + template ().begin())> + I get_begin_impl(T& r, int) { + return r.begin(); + } + + // Use ADL otherwises. + template ()))> + I get_begin_impl(T& r, long) { + return begin(r); + } + + template + auto get_begin(T& t) -> decltype(get_begin_impl(std::declval(), 42)) { + return get_begin_impl(t, 42); + } + + // end() for C arrays + template + T* get_end_impl(T (&array)[N], int) { + return array + N; + } + + // Prefer member end(). + template ().end())> + I get_end_impl(T& r, int) { + return r.end(); + } + + // Use ADL otherwise. + template ()))> + I get_end_impl(T& r, long) { + return end(r); + } + + template + auto get_end(T& t) -> decltype(get_end_impl(std::declval(), 42)) { + return get_end_impl(t, 42); + } + } + using get_iters::get_begin; + using get_iters::get_end; + + template + struct type_is { + using type = T; + }; + + template + using AsConst = decltype(std::as_const(std::declval())); + + // iterator_type is the type of C's iterator + // TODO: See bug + // https://developercommunity.visualstudio.com/content/problem/252157/sfinae-error-depends-on-name-of-template-parameter.html + // for why we use T instead of Container. Should be + // changed back to Container when that bug is fixed in + // MSVC. + template + using iterator_type = decltype(get_begin(std::declval())); + + // iterator_type is the type of C's iterator + template + using const_iterator_type = decltype( + get_begin(std::declval&>())); + + // iterator_deref is the type obtained by dereferencing an iterator + // to an object of type C + template + using iterator_deref = decltype(*std::declval&>()); + + // const_iteator_deref is the type obtained through dereferencing + // a const iterator& (note: not a const_iterator). ie: the result + // of Container::iterator::operator*() const + template + using const_iterator_deref = + decltype(*std::declval&>()); + + // the type of dereferencing a const_iterator + template + using const_iterator_type_deref = + decltype(*std::declval&>()); + + template + using iterator_traits_deref = + std::remove_reference_t>; + + template + struct IsIterable : std::false_type {}; + + // Assuming that if a type works with begin, it is an iterable. + template + struct IsIterable>> : std::true_type {}; + + template + constexpr bool is_iterable = IsIterable::value; + + namespace detail { + template + struct ArrowHelper { + using type = void; + void operator()(T&) const noexcept {} + }; + + template + struct ArrowHelper { + using type = T*; + constexpr type operator()(T* t) const noexcept { + return t; + } + }; + + template + struct ArrowHelper().operator->())>> { + using type = decltype(std::declval().operator->()); + type operator()(T& t) const { + return t.operator->(); + } + }; + + template + using arrow = typename detail::ArrowHelper::type; + } + + // type of C::iterator::operator->, also works with pointers + // void if the iterator has no operator-> + template + using iterator_arrow = detail::arrow>; + + // applys the -> operator to an object, if the object is a pointer, + // it returns the pointer + template + detail::arrow apply_arrow(T& t) { + return detail::ArrowHelper{}(t); + } + + // For iterators that have an operator* which returns a value + // they can return this type from their operator-> instead, which will + // wrap an object and allow it to be used with arrow + template + class ArrowProxy { + private: + using TPlain = typename std::remove_reference::type; + T obj; + + public: + constexpr ArrowProxy(T&& in_obj) : obj(std::forward(in_obj)) {} + + TPlain* operator->() { + return &obj; + } + }; + + template + struct is_random_access_iter : std::false_type {}; + + template + struct is_random_access_iter::iterator_category, + std::random_access_iterator_tag>::value>> : std::true_type {}; + + template + using has_random_access_iter = is_random_access_iter>; + // because std::advance assumes a lot and is actually smart, I need a dumb + + // version that will work with most things + template + void dumb_advance_unsafe(InputIt& iter, Distance distance) { + for (Distance i(0); i < distance; ++i) { + ++iter; + } + } + + template + void dumb_advance_impl( + Iter& iter, const EndIter& end, Distance distance, std::false_type) { + for (Distance i(0); i < distance && iter != end; ++i) { + ++iter; + } + } + + template + void dumb_advance_impl( + Iter& iter, const EndIter& end, Distance distance, std::true_type) { + if (static_cast(end - iter) < distance) { + iter = end; + } else { + iter += distance; + } + } + + // iter will not be incremented past end + template + void dumb_advance(Iter& iter, const EndIter& end, Distance distance) { + dumb_advance_impl(iter, end, distance, is_random_access_iter{}); + } + + template + ForwardIt dumb_next(ForwardIt it, Distance distance = 1) { + dumb_advance_unsafe(it, distance); + return it; + } + + template + ForwardIt dumb_next( + ForwardIt it, const ForwardIt& end, Distance distance = 1) { + dumb_advance(it, end, distance); + return it; + } + + template + Distance dumb_size(Container&& container) { + Distance d{0}; + auto end_it = get_end(container); + for (auto it = get_begin(container); it != end_it; ++it) { + ++d; + } + return d; + } + + template + struct are_same : std::true_type {}; + + template + struct are_same + : std::integral_constant::value && are_same::value> {}; + + // DerefHolder holds the value gotten from an iterator dereference + // if the iterate dereferences to an lvalue references, a pointer to the + // element is stored + // if it does not, a value is stored instead + // get() returns a reference to the held item + // get_ptr() returns a pointer to the held item + // reset() replaces the currently held item + template + class DerefHolder { + private: + static_assert(!std::is_lvalue_reference::value, + "Non-lvalue-ref specialization used for lvalue ref type"); + // it could still be an rvalue reference + using TPlain = std::remove_reference_t; + + std::optional item_p_; + + public: + using reference = TPlain&; + using pointer = TPlain*; + + static constexpr bool stores_value = true; + + DerefHolder() = default; + + reference get() { + assert(item_p_.has_value()); + return *item_p_; + } + + pointer get_ptr() { + assert(item_p_.has_value()); + return &item_p_.value(); + } + + void reset(T&& item) { + item_p_.emplace(std::move(item)); + } + + explicit operator bool() const { + return static_cast(item_p_); + } + }; + + // Specialization for when T is an lvalue ref + template + class DerefHolder { + public: + using reference = T&; + using pointer = T*; + + private: + pointer item_p_{}; + + public: + static constexpr bool stores_value = false; + + DerefHolder() = default; + + reference get() { + assert(item_p_); + return *item_p_; + } + + pointer get_ptr() { + assert(item_p_); + return item_p_; + } + + void reset(reference item) { + item_p_ = &item; + } + + explicit operator bool() const { + return item_p_ != nullptr; + } + }; + + // allows f(x) to be 'called' as x | f + // let the record show I dislike adding yet another syntactical mess to + // this clown car of a language. + template + struct Pipeable { + template + friend decltype(auto) operator|(T&& x, const Pipeable& p) { + return static_cast(p)(std::forward(x)); + } + }; + + // Pipeable Callable generator, where ItImpl is templated on the first + // argument to the call. + template