caseycarter / cmcstl2 Goto Github PK
View Code? Open in Web Editor NEWAn implementation of C++ Extensions for Ranges
License: Other
An implementation of C++ Extensions for Ranges
License: Other
...being careful not to remove the non-extension overloads that accept initializer_list
.
See ericniebler/range-v3@d3b06a2 for the corresponding change in range-v3.
template <class S, class I>
concept WeakSentinel =
WeakIterator<I>() && Semiregular<S>() &&
requires (const I& i, const S& s) {
{ i == s } -> Boolean;
{ i != s } -> Boolean;
// Axiom: bool(i != s) == !bool(i == s)
};
template <class R>
requires WeakIterator<decltype(begin(declval<remove_reference_t<R>&>()))>()
using IteratorType = decltype(begin(declval<remove_reference_t<R>&>()));
template <class R>
requires WeakSentinel<decltype(end(declval<remove_reference_t<R>&>())), IteratorType<R>>
using SentinelType = decltype(end(declval<remove_reference_t<R>&>()));
template <class R>
concept bool WeakRange =
requires { typename SentinelType<R>; };
template <class R>
concept bool WeakInputRange =
WeakRange<R> && WeakInputIterator<IteratorType<R>>();
template <class R, class T>
concept bool WeakOutputRange =
WeakRange<R> && WeakOutputIterator<IteratorType<R>, T>();
template <class R>
concept bool Range =
WeakRange<R> && Sentinel<SentinelType<R>, IteratorType<R>>();
I postulate that there is no model of InputIterator
/ OutputIterator
that is not equivalent to some model of WeakInputIterator
/ WeakOutputIterator
, i.e, InputIterator
and OutputIterator
are extraneous and do not increase the expressiveness of the design. If we relax every Input
/Output
Range
/Iterator
requirement to Weak
Input
/Output
Range
/Iteratator
the Input
/Output
Iterator
concepts could be deprecated.
On my machine, alg.find_end takes just over a minute to compile; that's around a third of the full test run compile time just for this test.
I'll just leave this here. Caveat: largely untested and probably buggy as hell.
// Copyright Eric Niebler 2016
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
namespace any_iterator_detail
{
enum class op { copy, move, nuke, bump, comp };
struct counted
{
std::atomic<long> cnt{ 1 };
};
ranges::InputIterator{I}
struct shared_iterator : counted
{
shared_iterator(I i) : it(std::move(i)) {}
I it;
};
union blob
{
counted* big;
std::aligned_storage<2 * sizeof(void*)>::type tiny;
};
template <typename It>
using is_small =
std::integral_constant<bool, (sizeof(It) <= sizeof(blob::tiny))>;
using small_tag = std::true_type;
using big_tag = std::false_type;
inline bool uninit_noop(op, blob*, blob*)
{
return false;
}
template<typename Reference>
[[noreturn]] inline Reference uninit_deref(blob const &)
{
std::terminate();
}
template <typename Reference, ranges::InputIterator I>
static Reference deref_small(blob const & p)
{
return **static_cast<I const *>(static_cast<void const *>(&p.tiny));
}
template <typename Reference, ranges::InputIterator I>
static Reference deref_big(blob const& p)
{
return *static_cast<shared_iterator<I> const *>(p.big)->it;
}
ranges::InputIterator{I}
bool exec_small(op o, blob* src, blob* dst)
{
switch (o)
{
case op::copy:
::new (static_cast<void*>(&dst->tiny))
I(*static_cast<I const *>(static_cast<void*>(&src->tiny)));
break;
case op::move:
::new (static_cast<void*>(&dst->tiny))
I(std::move(*static_cast<I*>(static_cast<void*>(&src->tiny))));
// fallthrough
case op::nuke:
static_cast<I*>(static_cast<void*>(&src->tiny))->~I();
break;
case op::bump:
++*static_cast<I*>(static_cast<void*>(&src->tiny));
break;
case op::comp:
return *static_cast<I const *>(static_cast<void*>(&src->tiny))
== *static_cast<I const *>(static_cast<void*>(&dst->tiny));
}
return false;
}
ranges::InputIterator{I}
bool exec_big(op o, blob* src, blob* dst)
{
switch (o)
{
case op::copy:
++(dst->big = src->big)->cnt;
break;
case op::move:
dst->big = std::exchange(src->big, nullptr);
break;
case op::nuke:
if (0 == --src->big->cnt)
delete static_cast<shared_iterator<I>*>(src->big);
break;
case op::bump:
++static_cast<shared_iterator<I>*>(src->big)->it;
break;
case op::comp:
return static_cast<shared_iterator<I> const *>(src->big)->it
== static_cast<shared_iterator<I> const *>(dst->big)->it;
}
return true;
}
template<class Reference, class ValueType>
struct any_input_cursor
{
private:
blob data_ = { nullptr };
Reference (*deref_)(blob const &) = &uninit_deref<Reference>;
bool (*exec_)(op, blob*, blob*) = &uninit_noop;
ranges::InputIterator{I} any_input_cursor(I i, small_tag)
{
::new (static_cast<void*>(&data_.tiny)) I(std::move(i));
deref_ = &deref_small<Reference, I>;
exec_ = &exec_small<I>;
}
ranges::InputIterator{I} any_input_cursor(I i, big_tag)
{
data_.big = new shared_iterator<I>(std::move(i));
deref_ = &deref_big<Reference, I>;
exec_ = &exec_big<I>;
}
public:
using value_type = ValueType;
using single_pass = std::true_type;
any_input_cursor() = default;
ranges::InputIterator{I} any_input_cursor(I i)
requires ranges::ConvertibleTo<ranges::reference_t<I>, Reference>()
: any_input_cursor(std::move(i), is_small<I>{}) {}
any_input_cursor(any_input_cursor &&that)
{
that.exec_(op::move, &that.data_, &data_);
ranges::swap(deref_, that.deref_);
ranges::swap(exec_, that.exec_);
}
any_input_cursor(any_input_cursor const &that)
{
that.exec_(op::copy, const_cast<blob*>(&that.data_), &data_);
deref_ = that.deref_;
exec_ = that.exec_;
}
any_input_cursor& operator=(any_input_cursor &&that)
{
if (&that != this)
{
this->~any_input_cursor();
::new (static_cast<void*>(this))
any_input_cursor(std::move(that));
}
return *this;
}
any_input_cursor& operator=(any_input_cursor const &that)
{
if (&that != this)
{
this->~any_input_cursor();
::new (static_cast<void*>(this)) any_input_cursor(that);
}
return *this;
}
~any_input_cursor()
{
exec_(op::nuke, &data_, nullptr);
}
Reference read() const
{
return deref_(data_);
}
bool equal(any_input_cursor const &that) const
{
return exec_(op::comp, const_cast<blob*>(&data_),
const_cast<blob*>(&that.data_));
}
void next()
{
exec_(op::bump, &data_, nullptr);
}
};
}
template<class Reference, class ValueType =
std::remove_cv_t<std::remove_reference_t<Reference>>>
using any_input_iterator =
ranges::basic_iterator<
any_iterator_detail::any_input_cursor<Reference, ValueType>>;
...to not require the relaxation specified in [algorithm.general]/12:
Despite that the algorithm declarations nominally accept parameters by value, it is unspecified when and if the argument expressions are used to initialize the actual parameters except that any such initialization shall be sequenced before (ISO/IEC 14882:2014 §1.9) the algorithm returns.
which will be removed by the PR for ericniebler/stl2#261. Already done for the algorithms with overloads in Annex A.2 (equal
, is_permutation
, mismatch
, swap_ranges
, transform
) by b160823.
...as specified in ericniebler/stl2@05093ad.
augment the iterators in test_iterators.hpp with configurable equality comparison (for input and output) and configurable proxy/non-proxy reference capability.
Once that's done, add some tests for iter_move
and iter_swap
of the iterator adaptors implemented in 8bda2c3.
IIRC, our foo_swappable
traits are equivalent to the paper's foo_swappable_with
traits.
The number of occurrences of qualified names is increasing, algorithms will make it grow like wildfire. Relocate everything into std::experimental::ranges_v1
sooner rather than later. Better: make the top-level namespace configurable so we can co-exist with a vendor implementation of the TS.
... so that common_type can use the core concepts in its implementation.
...are missing from the implementation.
on or about line 720.
...needs a default implementation.
...and add stl2/concept.hpp that pulls them all in per the proposal.
Steal the framework from https://github.com/Manu343726/gcc-concepts-bugs and set up Travis.
The "" header has been changed to reduce the number of other headers it includes:
http://gcc.gnu.org/gcc-6/porting_to.html
std::accumulate needs .
diff --git a/test/iterator/common_iterator.cpp b/test/iterator/common_iterator.cpp
index 3fc19e456246..715a3a953093 100644
--- a/test/iterator/common_iterator.cpp
+++ b/test/iterator/common_iterator.cpp
@@ -10,6 +10,7 @@
// Project home: https://github.com/caseycarter/cmcstl2
//
#include <algorithm>
+#include <numeric>
#include <stl2/iterator.hpp>
#include "../simple_test.hpp"
#include "../test_iterators.hpp"
Merge the range concepts and primitive operations into iterator.hpp as specified in the proposal.
Some concept, somewhere, uses *declval<const I&>()
with I
being a basic_iterator
. Because it doesn't have a const
overload of operator*
, the program is ill-formed.
Patch here: https://gist.github.com/asutton/c381a942e02c3f4c8335cfc25d57bd00
...to the extent possible.
The IndirectCallable
family - and visit(variant)
- both need Common<void,void>()
and CommonReference<void, void>()
. Both are currently unsatisfied due to substitution failure forming void&&
as a parameter type in the pertinent requires clauses. (Oddly enough, GCC accepts void
as a parameter type, but not void&
or void&&
.)
There's really no point now that std::addressof
will be constexpr
in C++17. Users should instead require _Is<is_object>
and replace &foo
with detail::addressof(foo)
from detail/memory/addresof.hpp which is as constexpr
as possible.
Toying around with some container stuff, so I've defined these:
template <class T>
using reverse_iterator_t = decltype(__stl2::rbegin(declval<T&>()));
template <class T>
using reverse_sentinel_t = decltype(__stl2::rend(declval<T&>()));
template <class X>
concept bool ReversibleContainer() {
return Container<X>() &&
BidirectionalRange<X>() &&
requires(X x, const X cx) {
typename reverse_iterator_t<X>; // works
typename reverse_sentinel_t<X>; // fails when X = forward_list<int>
typename const_reverse_iterator_t<X>; // works
};
}
Interestingly, reverse_iterator_t
works, but reverse_sentinel_t
causes the attached diagnostic.
Is there any particular reason why reverse_iterator_t<foward_list<int>>
doesn't manifest as an error, but reverse_sentinel_t<forward_list<int>>
does?
The following functions have deprecated overloads:
Consider adding Movable
and DefaultConstructible
requirements to Dereferenceable
, since Readable
and MoveWritable
both now require them.
#include <stl2/iterator.hpp>
template <stl2::InputIterator>
constexpr bool f() { return false; }
template <stl2::ForwardIterator>
constexpr bool f() { return true; }
int main() {
static_assert(f<int*>());
}
error: call of overloaded ‘f()’ is ambiguous
Conforming implementations are not allowed to add constexpr to functions whose specification does not require it; the constexpr extensions in the library need a configuration toggle.
...now that fold expressions are working.
Tests in alg.find_end, alg.mismatch, and alg.equal OOM the compiler.
The calls to three-argument equal with an argument that has std
as an associated namespace need explicit qualification to disambiguate, e.g., Line 219:
CHECK(stl2::equal(make_range(input_iterator<const int*>(ia),
sentinel<const int*>(ia+s)),
make_range(input_iterator<const int*>(ia),
sentinel<const int*>(ia + s)),
std::equal_to<int>()));
The stl2 overloads of equal
are obviously more-constrained. Determine why the std overloads are being selected by overload resolution, and develop a plan to correct the problem.
Currently unable to pass less<T>{}
to a function or class that takes a Callable
parameter. Am I missing something in this example?
void lt(Callable f)
{
cout << f(1, 2) << '\n';
}
int main()
{
lt(ranges::less<int>{});
}
Anything not integrated by now is likely not important enough to be integrated.
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2219
Status is WP - just update to N4567.
For a callable F
, the Callable
concepts require Function<FunctionType<F>, /* ... */>()
, so FunctionType<F>
indirectly must satisfy CopyConstructible
. Given that FunctionType<F>
is very often a reference type, and CopyConstructible
does not intentionally admit reference types, this is a mess.
The specifications of the algorithms otherwise ignore the value semantics (destruction, copy, move) of callables. Library functions accept them as value parameters, so they must at least satisfy Destructible
. Library functions forward them to other library functions that accept them as value parameters, so they must at least satisfy MoveConstructible
. Should they be CopyConstructible
? The question boils down to whether or not we want a library function implementation to be able to pass callables to multiple other library functions in "raw" form or if it must first cook them with as_function
. E.g., given:
template <InputIterator I, Sentinel<I> S, class Pred, class Proj>
requires IndirectRegularCallable<Pred, Projected<I, Proj>>()
I foo_helper1(I, S, Pred, Proj);
template <InputIterator I, Sentinel<I> S, class Pred, class Proj>
requires IndirectRegularCallable<Pred, Projected<I, Proj>>()
I foo_helper2(I, S, Pred, Proj);
template <InputIterator I, Sentinel<I> S, class Pred, class Proj>
requires IndirectRegularCallable<Pred, Projected<I, Proj>>()
I foo(I i, S s, Pred pred, Proj proj);
can I implement foo
as:
return foo_helper2(foo_helper1(i, s, pred, proj), s, pred, proj);
Or must I instead write:
auto&& pred_ = as_function(pred);
auto&& proj_ = as_function(proj);
return foo_helper2(foo_helper1(i, s, pred_, proj_), s, pred_, proj_);
For the time being, I think it's best to err on the side of the most stringent requirements - allowing the most freedom for algorithm implementations - and require callables to satisfy CopyConstructible
. STL1 requires predicates to be CopyConstructible
, so this is not a breaking change in any case.
What is the status on updating the STL containers to use concepts (and ranges) as well?
Have there been any discussions about this?
With gcc trunk:
build % CXXFLAGS="-O3" cmake -DCMAKE_CXX_COMPILER=/var/tmp/gcc_test/usr/local/bin/g++ ..
build % make
...
/var/tmp/cmcstl2/test/detail/temporary_vector.cpp: In function ‘int main()’:
/var/tmp/cmcstl2/test/detail/temporary_vector.cpp:27:31: warning: requested alignment 256 is larger than 128 [-Wattributes]
struct alignas(alignment) foo { char c; };
^
/var/tmp/cmcstl2/test/detail/temporary_vector.cpp:28:5: error: static assertion failed
static_assert(alignof(foo) == alignment);
^
test/detail/CMakeFiles/temporary_vector.dir/build.make:62: recipe for target 'test/detail/CMakeFiles/temporary_vector.dir/temporary_vector.cpp.o' failed
make[2]: *** [test/detail/CMakeFiles/temporary_vector.dir/temporary_vector.cpp.o] Error 1
CMakeFiles/Makefile2:1631: recipe for target 'test/detail/CMakeFiles/temporary_vector.dir/all' failed
make[1]: *** [test/detail/CMakeFiles/temporary_vector.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
There is some issue with inheriting constructors. It would be nice to know if any of our proposed interfaces are running afoul of recent language changes.
Reported as GCC bug 67038. Currently affects the iterator dispatch test in test.iterator, and the implementation of iter_swap2. Don't forget to update affected code when/if the bug report is addressed.
...is lacking now that many of the iterators are hand-coded. Dig out the prior basic_iterator
implementations and use them as test cases in test/utility/basic_iterator.cpp
.
I was skimming through the library and was wondering if it is possible to detect an user-defined constexpr addressof operator using the approach followed here.
It would be nice to integrate the algorithms from the parallelism TS.
The algorithm header has been changed to reduce the number of other headers it includes:
http://gcc.gnu.org/gcc-6/porting_to.html
std::accumulate needs numeric.
diff --git a/test/iterator/common_iterator.cpp b/test/iterator/common_iterator.cpp
index 3fc19e456246..715a3a953093 100644
--- a/test/iterator/common_iterator.cpp
+++ b/test/iterator/common_iterator.cpp
@@ -10,6 +10,7 @@
// Project home: https://github.com/caseycarter/cmcstl2
//
#include <algorithm>
+#include <numeric>
#include <stl2/iterator.hpp>
#include "../simple_test.hpp"
#include "../test_iterators.hpp"
Don't forget that these were checked in (c506b29) without the proper tagging.
...should be constrained in accordance with the results of ericniebler/stl2#152. The current constraints are only equivalent on an implementation whose <
provides a total ordering for pointer values.
The Constructible
constraint on this line should be ExplicitlyConvertibleTo
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.