Search code examples
c++11proxyrvaluelvaluerange-v3

Is there a way to have read-and-write views in Range-v3?


In Range-v3 one can easily create view of existing containers. For example

#include<range/v3/view/transform.hpp>
#include<cassert>

int main(){
    std::vector<double> v = {1,2,3};
    auto range = v | ranges::v3::view::transform([](auto x){return x*2;});
    assert( range[1] == 4 );
//    range[1] = 4; // error cannot write to an l-value
}

These views are naturally read-only. I wonder if there is any way to create read and write view within Range-v3?

I do appreciate that such thing is much more complicated than read-only, and not always possible, but still I wonder if there is a protocol in Range-v3 to use this directly or implement it.

For example, I have seen examples of derived classes from ranges::view_facade that implement a member function read() (I cannot find the example again, the documentation of Ranges v3 is really scattered around). What about a write(...) member function?

I am looking for some hypothetical bidirectional transform code like this:

#include<range/v3/view/transform.hpp>
#include<cassert>

int main(){
    std::vector<double> v = {1,2,3};
    auto range = v | ranges::v3::view::bitransform([](double x){return x*2;}, [](double x){return x/2;}); // direct and inverse function for read and write
    assert( range[1] == 4 );
    range[1] = 8; // error cannot write to an l-value
    assert( range[1] == 8 );
    assert( v[1] == 4 );
}

Solution

  • You can make transform_view have elements of proxy type:

    #include<vector>
    #include<range/v3/view/transform.hpp>
    #include<cassert>
    
    struct proxy {
        double& x;
        operator double() { return x*2; }
        void operator=(double y) { x = y / 2; }
    };
    
    int main(){
        std::vector<double> v = {1,2,3};
        auto range = v | ranges::v3::view::transform([](auto& x){return proxy{x};});
        assert( range[1] == 4 );
        range[1] = 8;
        assert( range[1] == 8 );
        assert( v[1] == 4 );
    }