For a C++17 restricted project I would like to have a standalone implementation of C++20 std::views::join()
. I am aware of the existence of range-v3 but unfortunately the person in charge is unwilling to include further 3rd party libraries.
My aim is the write the C++17 equivalent of this (Implementation) --> solved see below
std::vector<std::vector<int>> data{{1,2},{1,2,3},{1,2,3,4}};
for(const auto & element : std::views::join(data)){
std::cout << element << "\n";
}
and the more difficult part this (Godbolt)
std::vector<std::vector<std::vector<int>>> data{{{1,2},{3,4}},{{5,6,7}}};
auto nested_join = std::views::join(std::views::join(data));
for(const auto element : nested_join){
std::cout << element << "\n";
}
I want to additionally emphasis the nesting of calls (std::views::join(std::views::join(data))
), as they are the current road-block. To be more specific
How can I design my
join_view
class to be nestable [/stackable, e.g.join_view(join_view(T))
] [, while preserving the functionality whichstd::views::join
provides (i.e. optionally modifiable elements Godbolt)?](refined/edited question)
Update: A
std::views::join
object can first be created for a range of ranges (e.g. astd::vector<std::vector<T>>
godbolt), as, I suspect, the standard intends it to be comparable, in the performance sense, with writing the explizit nested loops. I think restricting one self to this basecase may lead to a better implementation. My version provided down below fullfills this, is though design wise flawed, resulting in segfaults for nesting ofjoin_view
.
I have successfully implemented a C++17 solution for the first part (Implementation), class code is provided at the end. The join_view
wrapper class works by holding a reference to the nested object (which may or may not be const) and provides begin()
and end()
function to allow a range based for loop. These return an internal iterator (I think it fulfills the LegacyInputIterator requirements), for which the required operators (++,*,==,!=) are implemented.
Now lets consider my unsuccessful attempts to implement the second part.
Let's just try if I have written a super code that works as well for nesting the
join_view
construction. Nope. For a triple nested vectorstd::vector<std::vector<std::vector<int>>>
and a twice appliedjoin_view
instead of aint
as element type I receive astd::vector<int>
(Implementation). If we take a look at the types of the nested constructionauto deeper_view = join_view(join_view(data_deeper));
this expands in C++ Insights tojoin_view<std::vector<...>> deeper_view = join_view(join_view<std::vector<...>>(data_deeper));
which is obviously a sign of an issue?I then tried changing all calls of the
std::begin()
andstd::end()
to their$.begin()
counterpart, since these are the one defined for thejoin_view
wrapper. Unfortunately this did also not help but now the C++ Insights output is unreadable and I cant follow it anymore.
Now I am no longer sure that this could even work therefore I am asking the question from above: How can I redesign my join_view
class to be nestable?
I am aware of the following stackoverflow questions regarding std::views::join
[join view how, join boost problem, join string_view problem, join compilation issue], but these do not consider implementation. I have as well tried understanding and reading the implementation of ranges-v3 join wrapper, which has provided to be difficult.
Current partially working status of standalone join_view
wrapper:
template<typename T>
class join_view{
private:
T & ref_range;
using outer_iterator = decltype(ref_range.begin());
using inner_iterator = decltype((*ref_range.begin()).begin());
public:
join_view(T & range) : ref_range{range} {}
class iterator{
private:
outer_iterator outer;
inner_iterator inner;
public:
iterator(outer_iterator outer_, inner_iterator inner_): outer{outer_}, inner{inner_} {}
auto& operator*(){
return *inner;
}
auto& operator++(){
++inner;
if(inner != (*outer).end()){
return *this;
}
++outer;
inner = (*outer).begin();
return *this;
}
auto operator==(const iterator & other){
return outer == other.outer;
}
auto operator!=(const iterator & other){
return outer != other.outer;
}
};
auto begin(){
return iterator(ref_range.begin(), (*ref_range.begin()).begin());
}
auto end(){
return iterator(ref_range.end(),{});
}
};