Giter Club home page Giter Club logo

range-v3's Introduction

range-v3

Range library for C++14/17/20. This code was the basis of a formal proposal to add range support to the C++ standard library. That proposal evolved through a Technical Specification, and finally into P0896R4 "The One Ranges Proposal" which was merged into the C++20 working drafts in November 2018.

About:

Ranges are an extension of the Standard Template Library that makes its iterators and algorithms more powerful by making them composable. Unlike other range-like solutions which seek to do away with iterators, in range-v3 ranges are an abstraction layer on top of iterators.

Range-v3 is built on three pillars: Views, Actions, and Algorithms. The algorithms are the same as those with which you are already familiar in the STL, except that in range-v3 all the algorithms have overloads that take ranges in addition to the overloads that take iterators. Views are composable adaptations of ranges where the adaptation happens lazily as the view is iterated. And an action is an eager application of an algorithm to a container that mutates the container in-place and returns it for further processing.

Views and actions use the pipe syntax (e.g., rng | adapt1 | adapt2 | ...) so your code is terse and readable from left to right.

Documentation:

Check out the (woefully incomplete) documentation here.

Other resources (mind the dates, the library probably has changed since then):

License:

Most of the source code in this project are mine, and those are under the Boost Software License. Parts are taken from Alex Stepanov's Elements of Programming, Howard Hinnant's libc++, and from the SGI STL. Please see the attached LICENSE file and the CREDITS file for the licensing and acknowledgments.

Supported Compilers

The code is known to work on the following compilers:

  • clang 5.0 (or later)
  • GCC 6.5 (or later)
  • Clang/LLVM 6 (or later) on Windows (older versions may work - we haven't tested.)
  • Visual Studio 2019 (or later) on Windows, with some caveats due to range-v3's strict conformance requirements:
    • range-v3 needs /permissive- and either /std:c++latest, /std:c++20, or /std:c++17

Development Status: This code is fairly stable, well-tested, and suitable for casual use, although currently lacking documentation. In general, no promise is made about support or long-term stability. This code will evolve without regard to backwards compatibility.

A notable exception is anything found within the ranges::cpp20 namespace. Those components will change rarely or (preferably) never at all.

Build status

  • on GitHub Actions: GitHub Actions Status

Building range-v3 - Using vcpkg

You can download and install range-v3 using the vcpkg dependency manager:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install range-v3

The range-v3 port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please create an issue or pull request on the vcpkg repository.

Building range-v3 - Using Conan

You can download and install range-v3 using the Conan dependency manager.

Setup your CMakeLists.txt (see Conan documentation on how to use MSBuild, Meson and others):

project(myproject CXX)

add_executable(${PROJECT_NAME} main.cpp)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) # Include Conan-generated file
conan_basic_setup(TARGETS) # Introduce Conan-generated targets

target_link_libraries(${PROJECT_NAME} CONAN_PKG::range-v3)

Create conanfile.txt in your source dir:

[requires]
range-v3/0.12.0

[generators]
cmake

Install and run conan, then build your project as always:

pip install conan
mkdir build
cd build
conan install ../ --build=missing
cmake ../
cmake --build .

Building range-v3 - Using build2

You can use build2, a dependency manager and build-system combined, to use range-v3 (or work on it):

Currently this package is available in these package repositories:

Usage:

For example, to make your build2 project depend on range-v3:

  • Add one of the repositories to your configurations, or in your repositories.manifest, if not already there; for example:
    :
    role: prerequisite
    location: https://pkg.cppget.org/1/alpha # v0.11.0 is there.
    
  • Add this package as a dependency to your manifest file (example for v0.11.x):
    depends: range-v3 ~0.11.0
    
  • Import the target and use it as a prerequisite to your own target using range-v3 in the appropriate buildfile:
    import range_v3 = range-v3%lib{range-v3}
    
    lib{mylib} : cxx{**} ... $range_v3

Then just build your project as usual (with b or bdep update), build2 will figure out the rest.

For build2 newcomers or to get more details and use cases, you can read this document and the build2 toolchain introduction.

Say Thanks!

I do this work because I love it and because I love C++ and want it to be as excellent as I know it can be. If you like my work and are looking for a way to say thank you, you can leave a supportive comment on my blog. Or you could leave me some kudos on my Open Hub range-v3 contribution page. Just click the Give Kudos button here.

range-v3's People

Contributors

ahmedcharles avatar asutton avatar bekenn avatar brevzin avatar caseycarter avatar cjdb avatar dvirtz avatar ericniebler avatar floopcz avatar gnzlbg avatar h-2 avatar johelegp avatar julian-becker avatar khlebnikov avatar lasote avatar maikel avatar manu343726 avatar marehr avatar memsharded avatar mikegitb avatar morinmorin avatar mrpi avatar no-more-secrets avatar pleroux0 avatar rhalbersma avatar sse4 avatar tete17 avatar tivek avatar tower120 avatar yehezkelshb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

range-v3's Issues

[concept] Swappable bug & concepts::valid_expr

This code fails to compile: (tested clang 3.5 & g++ 4.9.1)

#include <range/v3/utility/concepts.hpp>

int main(int argc, char** argv) {
    static_assert(ranges::v3::Swappable<int&>(), "");  
    return 0;
}

In the implementation, concepts::valid_expr is used, but swap returns void, which cannot be an arg.
Why is concepts::valid_expr needed at the first place?

Redesign range_adaptor

I made a terrible design decision in range_adaptor. Derived classes define custom behaviors by defining a (possibly stateful) adaptor that accepts base cursors and does something to them. The mistake is that the adaptor takes cursors instead of iterators. It is needlessly complex. Redesign.

Warnings

I get a lot of un-used functions, variables, and shadowing warnings.

I'm willing to fix them but want to discuss what is the best way to proceed:

  • actually do fix them:
    • remove unused variables,
    • use different names to prevent shadowing,
    • do not generate un-used functions,
    • ..., or
  • just pop the warnings out of the headers and pop them back in
    • need to detect the compiler and use whatever pragmas it offers

Or maybe there is a better way.

FWIW i'm compiling with clang/libc++ HEAD and the following flags:

 "-Wall" "-Wextra" "-std=c++1y" "-stdlib=libc++"
 "-pedantic" "-Wshadow" "-Woverloaded-virtual"
 "-pedantic-errors" "-Wcast-align" "-Wcomment" "-Wcast-qual"
 "-Wchar-subscripts" "-Wdisabled-optimization" "-Wfloat-equal" "-Wformat=2"
 "-Winvalid-pch" "-Wformat-nonliteral" "-Wformat-security" "-Wformat-y2k"
 "-Wimport" "-Winit-self" "-Winline" "-Wreturn-type" "-Wmissing-braces"
 "-Wmissing-field-initializers" "-Wmissing-include-dirs" "-Wredundant-decls"
 "-Wpacked" "-Wparentheses" "-Wpointer-arith" "-Wsequence-point"
 "-Wsign-compare" "-Wstack-protector" "-Wstrict-aliasing=2" "-Wswitch"
 "-Wswitch-default" "-Wtrigraphs" "-Wuninitialized" "-Wunknown-pragmas"
 "-Wunreachable-code" "-Wunused" "-Wunused-function" "-Wunused-label"
 "-Wunused-parameter" "-Wunused-value" "-Wunused-variable"
 "-Wvariadic-macros" "-Wvolatile-register-var" "-Wwrite-strings"
 "-Woverloaded-virtual" "-Wsign-promo" "-Wstrict-overflow=5"
 "-Wswitch-default"
 "-fdiagnostics-show-template-tree" "-ftemplate-backtrace-limit=0"
 "-Wno-attributes"

Concept requirements on ranges::sort (or ordered_less) may be surprising

I found an instance where ranges::sort(vec) fails to compile but std::sort(vec.begin(), vec.end()) compiles. The type of vec is std::vector<rule_t>, and the definition of rule_t is:

struct rule_t {
  rule_t(std::string lhs, std::vector<std::string> rhs, double prob)
    : lhs_{std::move(lhs)}, rhs_{std::move(rhs)}, prob_{prob}
  {}

  std::string const & lhs() const { return lhs_; }
  std::vector<std::string> const & rhs() const { return rhs_; }
  double const & prob() const { return prob_; }

private:
  std::string lhs_;
  std::vector<std::string> rhs_;
  double prob_;
};
bool operator<(rule_t const &left, rule_t const &right) {
  return std::tie(left.lhs(), left.rhs()) < std::tie(right.lhs(), right.rhs());
}

This is because the concept requirements on ordered_less (and ranges::less as well) are stricter than the (non-existent) ones in the standard library.

  1. Should ranges::sort default to requiring TotallyOrdered? ranges::less may be less (ha ha) surprising to users. (In my case I can and will just define operator== etc, but I can imagine there may be cases where that is not desirable.)

  2. Is it necessary for less to require the presence of operators >, <=, and >=? I understand that all of these operators should be defined if any are, but it still feels surprising that less is not callable on a type that defines operator<. (At least when we do get concepts the error messages will finally be understandable).

Why is filter deprecated?

I find both filter and remove_if useful. Replacing filter(pred) with remove_if(not(pred)) makes IMO the code less readable. The intent is just harder to understand due to the double negation: do something on the elements that are not the elements for which the predicate is not true (my brain melts).

Compilation error of view/counted under clang

I get the following compilation error in the master branch with clang ToT (from two weeks ago, I'll try with ToT and update the issue):

range-v3/include/range/v3/view/counted.hpp:89:39: error: call to non-static member function without an object
      argument
                        RANGES_ASSERT(next(j.base(), n) == i);
                                      ^~~~

That is, next (here) tries to call the member function next (here) instead of the intended non-member non-friend next. Qualifying the call with ::ranges::next does the trick. A risky guess would be that this doesn't trigger in VS due to missing two-phase look-up.

Lifetime issue of std::initializer_list in tests for non-mutating algos

Hi Eric,

First, thanks for your great work. I like ranges and your library :)

I got interested in this comment (in PR #23):

Initializer list literals (e.g. used in-situ) are never "temporary". They're like string constants.

since if this is true it might be nice to propose allowing braced-init-lists in binary operators (e.g. {1, 2, 3} | view::xxxx) to C++17.

So I double-checked the comment about the lifetime, but I couldn't find parts that support this. N3936 [dcl.init.list] says

p5
An object of type std::initializer_list is constructed from an initializer list as if the implementation allocated a temporary array [...]

p6
The array has the same lifetime as any other temporary object (12.2), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary. [...]

Thus, the lifetime of the underlying array in

// is_heap_until.cpp
S const * r = ranges::is_heap_until({S{1}, S{0}, S{0}, S{0}, S{0}, S{0}, S{1}}, std::greater<int>(), &S::i);
CHECK((r-1)->i == 1);
CHECK(r->i == 0);

ends before CHECK(...). Doesn't (r-1)->i == 1 (and r->i == 0) lead to undefined behavior?
(c.f. (ranges::is_heap_until({S{1}, S{0}, S{0}, S{0}, S{0}, S{0}, S{1}}, std::greater<int>(), &S::i) - 1)->i == 1 doesn't have lifetime issue.)

Thanks,
Michel

The current single_view should be an action not a view

Currently, the views are lightweight and non-owning, however the single_view is an owning view. It seems semantically the current single_view should be an action. And then add a non-owning single_view that only accept lvalues, something like this:

template<class T>
struct single_view
{
    T* value;
    single_view(T& x) : value(&x)
    {}

    T* begin() { return this->value; }
    T* end() { return this->value + 1; }

    std::add_const_t<T>* begin() const { return this->value; }
    std::add_const_t<T>* end() const { return this->value + 1; }
};

template<class T>
single_view<T> single(T& x)
{
    return single_view<T>(x);
}

So then action::single would capture by value(how it currently does) and view::single would capture by reference.

Handle proxy iterators

One really nice feature would be to be able to sort (or applying any mutating algorithm, really), a zip view.

Example:

int keys[] = {4,2,1,6};
int values[] = {1,2,3,4};
sort ( view::zip(keys, values) );

AFAIK there is no easy way to do that in C++11, and it does not work with range-v3 atm because sort expects a lvalue-ref to a range.

In general in the current standard the problem of implementing a zip_iterator with that semantics is that it iterator::reference must be iterator::value_type&. Having a zip_iterator whose value type is tuple<Ts ... > and reference is tuple<Ts& ... > is (I think) probably the desired semantics.

Even then an overload of swap( tuple<Ts & ...>, tuple <Ts & ...> ) that can swap two tuples of references passed by value would be necessary.

Just random thoughts, apologies if this is has already been discussed.

CRLF/LF mix-up and UTF-8 with BOM

When I was fixing doc/comment typo, I found that some files
(LICENSE, include/range/v3/view/remove_if.hpp and test/view/CMakeLists.txt)
mix up CRLF and LF line endings.

For example, the last commit (639d31d)
resolved CRLF/LF mix-up in CMakeLists.txt (you can see spurious changes in the linked page) but introduced a new mix-up in test/view/CMakeLists.txt.

Maybe .gitattributes can help avoiding these mix-up?

view::ints for other integer types

view::ints returns.. ints. I just needed an ints utility for longs and had to build my own wrapper function object using take and iota:

template <class Integral> using irange_t = take_view<iota_view<Integral>>;

template <class Integral>
auto irange(Integral from, Integral to) -> irange_t<Integral> {
  return {iota_view<Integral>{from}, detail::iota_minus(to, from) + 1};
}

Is there a better way to do this? (besides adding concept checking?)

Maybe it would be worth it to make ints more generic and allow the user to specify the integer type, something like:

template<class Integral = int>
struct ints_fn : iota_view<Integral> { ... }

and then letting the function object be a variable template:

template<class Integral = int>
constexpr ints_fn<Integral> ints{};

I don't know if this is possible or there are better alternatives.

Faster root cause identification in concepts violation

When a class violates a concept, it should be as quick and clear as possible to pin down the root cause of the violation. Is there currently such a way to do it?

Example scenario:
I have a custom class DataFrame with a custom iterator DataFrameIterator. I had forgotten to mark operator* of DataFrameIterator as const.

I called ranges::for_each and got the error no matching function for call to object of type ‘const for_each_fn’.

My lengthy way to find out the root cause (the non-const operator*) is described below. I describe it just to show that it was quite lengthy for a simple problem. Is there some better current way of going about finding the root cause of such issues? If not, that would be high on the list of making ranges more user friendly for anyone using custom datatypes.

For example, a models_assert<InputIterable>(df) or even better models_assert<for_each_fn::requirements>(df) which would give a compile time error with relevant information (as close as possible to the root cause) would be excellent. In concepts.hpp:116 models_ uses some SFINAE and metaprogramming to give true_type if requires_ compiles and it compiles for all recursively refined types. Using similar metaprogramming, but not SFINAE, it should be straight forward (?) to make such a models_assert. Understanding enough of the metaprogramming framework to do that is a bit outside my current time schedule though :-(.

Is there any ongoing work in this direction?

The root cause identification

I started by manually extracting the concept checking pieces of for_each_fn::operator():

typename P = ident,
typename I = range_iterator_t<Rng>,
typename V = iterator_common_reference_t<I>,
typename X = concepts::Invokable::result_t<P, V>,
CONCEPT_REQUIRES_(InputIterable<Rng &>() && Invokable<P, V>() && Invokable<F, X>())

Into equivalent usings and asserts at the call sites. It became something like:

using P = ident;
using I = range_iterator_t<Rng>;
using V = iterator_common_reference_t<I>;
using X = concepts::Invokable::result_t<P, V>;
static_assert(InputIterable<Rng &>(),””);
static_assert(Invokable<P, V>(),””);
static_assert(Invokable<F, X>(),””);

Here static_assert(InputIterable<Rng &>(),””); failed.

I checked the definition of InputIterable and saw that it refined Iterable and had a requires_ method which added that concepts::model_of<InputIterator>(begin(t)).
A static_assert(Iterable <Rng &>(),””); succeeded and thus the problem narrowed down to that DataFrameIteratordid not satisfy InputIterator.

I continued in the same fashion.

static_assert(InputIterator<ty>(),””); //fails

//InputIterator refines WeakInputIterator, Iterator and EqualityComparable
static_assert(WeakInputIterator<ty>(),””); //fails
static_assert(Iterator<ty>(),””); //ok
static_assert(EqualityComparable<ty>(),””); //ok

//WeakInputIterator refines WeakIterator and Readable
static_assert(WeakIterator<ty>(),””); //ok
static_assert(Readable<ty>(),””); //fails

//Readable refines SemiRegular
static_assert(SemiRegular<ty>(),””); //ok

Thus one root cause was narrows down to the specifics of Readable (as coded in its requires_ method). Others might possibly exist in the specifics of InputIteratorand WeakInputIterator.

Readable::requires_ look like the following (with comments removed):

auto requires_(I i) -> decltype(
  concepts::valid_expr(
    concepts::model_of<Convertible, reference_t<I>, common_reference_t<I>>(),
    concepts::model_of<Convertible, value_t<I> &, common_reference_t<I>>(),
    concepts::same_type(indirect_move(i), indirect_move(i, *i))
));

By some additional code navigation I narrowed down a problem to that

DataFrameIterator i; adl_move_detail::indirect_move(i);

wouldn’t compile, again with a “No matching function” error, but that indirect_move(i, *i)did compile. indirect_move(i) however only calls indirect_move(i, *i). However, it takes i as const ref argument, and I thus tried with

const DataFrameIterator i; indirect_move(i, *i);

I now got the informative answer: Indirection requires pointer operand (‘const I’ (aka ‘const RowIterator<self_t>’) invalid), and I could fix the problem to add const to operator*. However, this had taken embarrassingly long (something like two hours) to find a very simple problem.

HELP WANTED, example/fibonacci.cpp is crashing

If anybody wants to help out with ranges and is capable with gdb, here's your chance. I can't figure out why example/fibonacci.cpp is crashing. It's an infinite recursion problem, but I'm a gdb n00b, and gdb seems to behave unexpectedly when I try to debug it. Any help is GREATLY appreciated.

Element access on RandomAccessRanges

RandomAccessRanges support O(1) access to all of its elements, however, there is no syntax sugar for this yet (via the [] and the () operators).

    std::vector<int> v {1, 2, 3, 4};
    auto rng = ranges::range(ranges::begin(v), ranges::end(v));
    CHECK(rng[0] == 1);
    CHECK(rng[1] == 2);
    CHECK(rng[2] == 3);
    CHECK(rng[3] == 4);
    CHECK(rng(0) == 1);
    CHECK(rng(1) == 2);
    CHECK(rng(2) == 3);
    CHECK(rng(3) == 4);

I personally prefer the () operator since it can be easily extended to handle multidimensional ranges but the [] should also be provided for backwards compatibility (and on multidimensional ranges [] could offer one dimensional indexing which is also useful).

random_shuffle test failure

The tests for random_shuffle assumes that two separate calls to the algorithm on equal ranges will produce different permutations (or at least, that the chances of them producing the same permutation on ranges of size 100 is small enough to safely ignore).

test\algorithm\random_shuffle.cpp(55)
CHECK(!ranges::equal(ia, ib));

This assumption does not hold for me (mingw32, gcc 4.9.1). I'm pretty sure it's due to the fact that random_shuffle constructs a new random_device every time it is called, and uses that to seed the engine. But on my system, random_device is deterministic (as I believe it is allowed to be), so this results in the same seed every time.

Perhaps random_shuffle should use a persistent random_device? Either as static local to the operator, or as a data member of random_shuffle_fn.

UB in the insertion sort example program in Appendix 4 of D4128

This function:

std::unique_ptr<int> data(int i)
{
    std::unique_ptr<int> a(new int[i]);
    auto rng = ranges::view::counted(a.get(), i);
    ranges::iota(rng, 0);
    return a;
}

is storing memory allocated with new[] into std::unique_ptr<int>, should be std::unique_ptr<int[]>.

Duplicate symbols when importing all.hpp in two cpp files

When compiling two cpp files, each including "all.hpp", then 42 duplicate symbols are detected by the linker. This is the case for both "gcc (MacPorts gcc49 4.9.1_1) 4.9.1" and "Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)", on OS X 10.10.

Repro steps:
Add file range-v3/include/a.cpp:

include "range/v3/all.hpp"

int main(int argc, const char* argv[]){
return 0;
}

Add file range-v3/include/b.cpp:

include "range/v3/all.hpp"

In directory range-v3/include/ compile using
/usr/bin/clang -I. -std=c++11 a.cpp b.cpp
or
gcc -I. -std=c++11 a.cpp b.cpp

Result:
[...] ld: 42 duplicate symbols for architecture x86_64 [...]

`accumulate` compile error using a lambda with ref parameter

Hi, I'm using this library in a project, and I encountered this problem. The following code doesn't compile (either on Clang 3.5.0 or gcc 4.9.1; my OS is Arch Linux):

#include <range/v3/numeric/accumulate.hpp>

void foo() {
  auto v = std::vector<std::string>{"foo", "bar", "baz"};
  auto s = ranges::accumulate(v, std::string{}, [](auto &a, auto const &b) { return a += b; })
}

It compiles if both parameters are by-value or const&, but it doesn't work if one or both is a reference. I can't figure out why it isn't working (maybe something to do with the Accumulateable concept?), but it seems like something that is reasonable to do.

Thanks in advance, and thanks for the library, it's very nice.

Some remarks/questions on D Ranges in the proposal

I just want to write some remarks/questions on the proposal part about D ranges. I am not entirely familiar with actual D ranges itself. I do understand the overall concept, but could be wrong about the details.

D-style ranges can only ever shrink, and they have no notion of position within sequence. If one were to try to implement C++’s iterators on top of D’s ranges, one would immediately run into trouble implementing ForwardIterator’s operator==. As D’s ranges do not represent position, there would be no way to test two ranges to see if their heads referred to the same element. (The front member function that returns the front of a D range is not required to return a reference, nor would that be sufficient to implement a hypothetical hasSameFront function; a repeating range might return the same element multiple times, leading to false positives.)

But wouldn't the D range have a == that would satisfy this requirements? Or perhaps have a front_equal and back_equal, since a D range can refer to two positions?

On the other hand, two C++ iterators can easily be used to implement a D-style range; thus, every range-based design can be implemented in terms of iterators.

D style ranges can be used to iterate over things like std::tuple or other sequences that have varying types. These can't be implemented using C++ iterators without losing type information.

Mutable lambdas in view::transform

The following code fails to compile when the lambda is mutable, but compiles fine when the 'mutable' keyword is removed:

auto v = std::vector<std::string>{"a", "ab", "abc"};
auto r = ranges::view::transform(v, [](auto const &s) mutable { return s.size(); });
RANGES_FOR(auto const& e, r) {
  std::cout << e;
}

This appears to be because semiregular_invokable_ref_t<Fun> resolves to reference_wrapper<semiregular<invokable_t<Fun>> const>>. Is this an intended consequence? I can work around it by const_casting inside the lambda, but it would be nice if mutable lambdas could just work. (At least view::filter seems to have the same issue).

Feature request: Allow delimiting on a predicate

For example: view::while_true(range, predicate) returns a slice of range starting at its beginning and ending at the first element of the range for which the predicate is false.

It does the same thing as view::delimit, but works on a predicate instead of on value equality.

Bikeshed: view::while_true, view::until

Test examples automatically

Examples do not run when using ctest and I think it is important to run them with the address sanitizer (and other sanitizers in the future).

I think that the easiest thing is to just also add them as tests such that they run when ctest is executed.

noexcept propagation

Currently function objects like for example begin and end are not conditionally noexcept.

The draft N3279 is IMO extremely conservative with the use of noexcept.

Shouldn't the noexcept specification of user-defined types be propagated by the library as far as it can by being conditionally noexcept?

For example if a user defined type implements noexcept begin and end, and then uses the range library in generic code that is conditionally noexcept, this code will be always noexcept(false) unless the range library propagates noexcept correctly.

OTOH propagating noexcept correctly in generic code is a true PITA and IMO a huge burden for library authors.

Extensions to `as_range`

With as_range I can obtain a range from a container. There are, however, other things I'd like to get a range from:

  • a pair of indices
  • an index and a predicate
  • a pair of iterators
  • an iterator and a predicate
  • an iterator and a distance

It would be nice to have utility functions to easily construct these either as make functions or as an extension to as_range. A make function that returns a range for some of these cases is given here.

range_adapter with labmda function is not default-constructible

And this disables the following code :

auto l1 = [](){return 1;};
auto l2 = [](int i){return i==1;};
view::generate(l1) | view::filter(l2) | view::filter(l2);

is this intended behavior?

Indeed, the compile error happens in view::all_fn::from_iterable, and from_iterable member function for range is disabled.

Do not use identifier 'requires'

This library has a great potential to become popular. It also chooses the right naming, like 'requires'. And these two things together may constitute a problem. C++ Extensions for Concepts (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4040.pdf) likely to be published son (2015?) make 'requires' and 'concept' keywords, and will collide with this library. In the best case, this library will break in concept-enabled compilers. In the worst, if your library becomes sufficiently popular, it will prevent the addition of keyword 'requires' and for backward compatibility reasons C++ will be forced to use some obscure substitute, like 'concept_requires'.

It is not really an 'issue' with this library, but still a change worth considering.

Are _if and _n algorithms needed?

I always thought that the "find_if / count_if / ... _if / ... _if_not" algorithms were a workaround for the lack of view::filter (an idiom I commonly use with Boost.Range v2 is count(rng | filter1 | filter2)). It seems to be the case for the "_n" algorithms too (we have view::counted, view::delimited, view::take, view::take_while...).

I think that it is worth to discuss if they are still needed.

For example:

  • count_if returns an iterator_difference so it should be replaceable by count( range | view::filter(f)) in all cases.
  • find_if returns an iterator so it can't be replaced since it would then return an iterator into a dead range.

Allow injecting variables into comprehension

Right now, if I try to write a function that returns the unique items like this:

template<class Range>
auto unique_items(Range& r)
{
    std::unordered_set<range_value_t<Range>> values;

    return view::for_each(r, [=](auto&& x) mutable
    {
        return yield_if(values.insert(x).second, x);
    });
}


int main()
{
    std::vector<int> ints = { 1, 1, 2, 3, 1, 2, 4, 5, 5 };
    auto unique_ints = unique_items(ints);
    std::cout << "First: " << std::endl;
    RANGES_FOR(int i, unique_ints)
    {
        std::cout << i << std::endl;
    }

    std::cout << "Second: " << std::endl;
    RANGES_FOR(int i, unique_ints)
    {
        std::cout << i << std::endl;
    }
}

The first loop prints the unique items, but the second time it iterates its empty. Of course, this is partly due to using a mutable lambda. It would nice to be able to inject variables and not have to use a mutable lambda, perhaps write something like a let mechanism:

template<class Range>
auto unique_items(Range& r)
{
    return view::for_each(r, 
        []{ return std::unordered_set<range_value_t<Range>>(); }, 
    [=](auto&& x, auto& values)
    {
        return yield_if(values.insert(x).second, x);
    });
}

I not sure the best way to write it would be.

Or perhaps, there is another way where the range could be made lazy, although a mutable lambda would still be needed:

template<class Range>
auto unique_items(Range& r)
{
    return lazy_range([&]
    {
        std::unordered_set<range_value_t<Range>> values;

        return view::for_each(r, [=](auto&& x) mutable
        {
            return yield_if(values.insert(x).second, x);
        });
    });
}

Parameter pack issue on VS2015

I just had a go at compiling with the VS 2015 preview, but I get tons of:

error C3546: '...': there are no parameter packs available to expand act.concepts meta.hpp 899

For example, it would be this line:

template<typename ...List, typename Fun>
struct transform_<list<List...>, Fun, void>
{
    using type = list<apply<Fun, List>...>; // <---
};

While I have some (limited) knowledge about templates and variadics, I have no clue how to even approach this, even with this SO post, which seems quite related.

I understand that this library is more of a proof-of-concept, (and I think the author uses gcc under Windows ;-) ), and yea, VS still lacks behind a lot in terms of C++11/14. But I think VS 2015 would be a really really reasonable and awesome target for this library to run on. I would love to use it!

Add a test/jamfile

I have tried to run the test using CMake but I have not reached :(

I have added a jamfile in my forked repository
viboes@2cf8b8a

How I can do a pull request?

Ambiguous call when passing initializer_list to range algorithm

When creating a range algorithm using:

//... alg_fn
RANGES_CONSTEXPR range_algorithm<alg_fn> alg{};

alg({1,2,3,4}, ...); // works
std::initializer_list<int> list;
alg(list, ...); // ambiguous call

The std::initializer_list overload within range_algorithm and the operator() of the algorithm itself are equally good.

Issue with iota and adapters with clang

Consider the following code:

auto odd_ints = ranges::view::ints(1)|ranges::view::remove_if([](auto&& x){return x%2==0;});

It compiles on gcc 4.9.1 (from macports "gcc49 @4.9.1_1"). Clang pre 3.6 (from macports clang-3.6 @3.6-r222672_0+analyzer+arm_runtime+assertions) does however fail with the error:

src/main.cpp:114:86: error: invalid operands to binary expression ('ranges::v3::iota_view<int, void>' and 'int')
  auto odd_ints = ranges::view::ints(1)|ranges::view::remove_if([](auto&& x){return x%2==0;});
                                                                                    ~^~

Thus, x seems to be of type ranges::v3::iota_view<int, void> and not int (or const int&).
Changing the argument of the filter predicate to intmakes it compile:

  auto odd_ints = ranges::view::ints(1)|ranges::view::remove_if([](int x){return x%2==0;});

While interestingly, trying to explicitly convert x to int does not:

src/main.cpp:114:86: error: no matching conversion for C-style cast from 'ranges::v3::iota_view<int, void>' to 'int'

  auto odd_ints = ranges::view::ints(1)|ranges::view::remove_if([](auto&& x){return ((int)x)%2==0;});
                                                                                     ^~~~~~

Is this a clang-bug which should be reported(?), or is it a ranges-bug which exploits non-standard behavior of gcc?

Customization point for for_each, etc

For certain classes of Ranges/Iterables, there may be ways to implement for_each that are easier to optimize (and auto-vectorize) for the compiler and thus gives faster code.
My use-case is a chunked_vector, being a vector of vectors, where for_each(chunked_vector<T>& d,F f) is essentially implemented as for_each(d.chunks_,[f&](vector<T>& chunk){for_each(chunk,f);}).
The inner loop here probably becomes easier to auto-vectorize than one based on somewhat complicated iterators.

So, the question is: is there a customization point in ranges that make it possible to add such specializations? Otherwise, this is a feature request for such customization points.

istream_range filtered with take(N) should stop reading at N

for (auto i : istream<std::string>(std::cin) | view::take(3))
    std::cout << i << '\n';
for (auto i : istream<std::string>(std::cin) | view::take(3))
    std::cout << i << '\n';

I would expect that snippet to print 6 consecutive strings from standard input. Instead, it prints 3, skips one, and then prints the next 3.

drop

Implement drop_front versions: view::drop(n) and view::drop_while(pred).

They allow to implement view::drop_back and view::drop_back_while by using view::reverse.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.