Retain bidirectionality with Ranges-v3 view::join - c++

I've run into some difficulty with the view::join function object of the (amazing) Ranges-v3 library. My client code is dependent upon the presence of a back method (and would greatly appreciate random access iterators) for an aggregate view of a collection of ranges.
After reviewing the relevant documentation it seems that a back method is compatible with instantations of the join_view class template, but I've been unable to instantiate it as such.
#include <iostream>
#include <vector>
#include <range/v3/all.hpp>
struct Foo{
std::vector<int> i = {1,2,3,4};
const std::vector<int>& data() const { return this->i; }
};
int main(){
std::vector< Foo > foos = { Foo(), Foo(), Foo() };
auto data = []( auto&& foo ){ return foo.data() | ranges::view::all; };
auto flat = foos | ranges::view::transform(data) | ranges::view::join;
std::cout << flat.back() << std::endl; // compiler error
}
The relevant bits of the compiler error message are:
main.cpp:17:28: error: no matching function for call to 'ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void>::back()'
std::cout << flat.back() << std::endl; // compiler error
/usr/local/include/range/v3/range_interface.hpp:116:34: note: candidate: template<class D, int _concept_requires_115, typename std::enable_if<((_concept_requires_115 == 43) || ((std::is_same<D, ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void> >() && ranges::v3::concepts::models<ranges::v3::concepts::BoundedView, T>()) && ranges::v3::concepts::models<ranges::v3::concepts::BidirectionalView, T>())), int>::type <anonymous> > ranges::v3::range_reference_t<D> ranges::v3::range_interface<Derived, Inf>::back() [with D = D; int _concept_requires_115 = _concept_requires_115; typename std::enable_if<((_concept_requires_115 == 43) || ((std::is_same<D, Derived>() && ranges::v3::concepts::models<ranges::v3::concepts::BoundedView, D>()) && ranges::v3::concepts::models<ranges::v3::concepts::BidirectionalView, D>())), int>::type <anonymous> = <enumerator>; Derived = ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void>; bool Inf = false]
range_reference_t<D> back()
/usr/local/include/range/v3/range_interface.hpp:115:17: error: no type named 'type' in 'struct std::enable_if<false, int>'
CONCEPT_REQUIRES_(Same<D, Derived>() && BoundedView<D>() && BidirectionalView<D>())>
The first requirement seems to be enforcing proper use of the CRTP, which is satisfied. So the join_view violates either the BoundedView or BidirectionalView concepts (or both). I was able to quickly eliminate the former as a possiblity.
auto flat = foos
| ranges::view::transform(data)
| ranges::view::join
| ranges::view::bounded;
std::cout << flat.back() << std::endl; // compiler error
In this case, flat satisfies the BoundedView concept, but the error message remains unchanged.
To verify the BidirectionalView, I attempted examined the iterators of the join_view, but encountered (what is suspect) is a bug.
auto it = flat.begin();
std::cout << *it << std::endl; // correct
++it; std::cout << *it << std::endl; // correct
--it; std::cout << *it << std::endl; // doesn't actually decrement
auto other = --it;
std::cout << *it << ' ' << *other << std::endl; // also doesn't decrement
I've written up a live version for easy inspection.
Has anyone had any luck instantiating a bidirectional join_view? Any suggestion of how I might achieve similar behavior without copying the underlying data?

Range-v3's join view satisfies InputRange, but not Forward or anything stronger. It has to do with how the join is accomplished. While iterating over an inner range, the range needs to be stored somewhere. That somewhere is in a member of the join_view object. In other words, join_view is mutated while you iterate over it. For that reason, it cannot model any range category stronger than Input.

Related

boost::make_recursive_variant has no member named 'apply_visitor'

First time using Boost, and trying to grok how to use boost::variant. Please find below an example program and the error message (GCC v5.3.0, -std=c++14)
#include <vector>
#include <iostream>
#include <sstream>
#include <boost/variant.hpp>
using int_tree_t =
boost::make_recursive_variant <
int,
std::vector<boost::recursive_variant_>>;
auto operator << (std::ostream& out, const int_tree_t& tree) ->
std::ostream&
{
struct stringify :
public boost::static_visitor <std::string>
{
public:
auto operator () (int i) const ->
std::string
{
return std::to_string (i);
}
auto operator () (const std::vector<int_tree_t>& vec) const ->
std::string
{
std::stringstream ss;
ss << "(";
auto it = std::begin (vec);
if (!vec.empty ()) {
ss << *it;
}
for (; it != std::end (vec); ++it) {
ss << " " << *it;
}
ss << ")";
return ss.str ();
}
};
std::stringstream ss;
ss.flags (out.flags ());
ss << boost::apply_visitor (stringify (), tree);
return (out << ss.str ());
}
Error message:
In file included from /usr/local/include/boost/variant/apply_visitor.hpp:16:0,
from /usr/local/include/boost/variant/detail/hash_variant.hpp:23,
from /usr/local/include/boost/variant/variant.hpp:34,
from /usr/local/include/boost/variant.hpp:17,
from test2.cpp:5:
/usr/local/include/boost/variant/detail/apply_visitor_unary.hpp: In instantiation of ‘typename Visitor::result_type boost::apply_visitor(const Visitor&, Visitable&) [with Visitor = operator<<(std::ostream&, const int_tree_t&)::stringify; Visitable = const boost::make_recursive_variant<int, std::vector<boost::recursive_variant_, std::allocator<boost::recursive_variant_> > >; typename Visitor::result_type = std::__cxx11::basic_string<char>]’:
test2.cpp:50:43: required from here
/usr/local/include/boost/variant/detail/apply_visitor_unary.hpp:84:43: error: ‘const struct boost::make_recursive_variant<int, std::vector<boost::recursive_variant_, std::allocator<boost::recursive_variant_> > >’ has no member named ‘apply_visitor’
return visitable.apply_visitor(visitor);
^
If I understand correctly, boost::make_recursive_variant is supposed to create a type which acts identical to boost::variant... but the error message appears to indicate that it doesn't have the apply_visitor function, which is necessary for visitation. I've read over [this(http://www.boost.org/doc/libs/1_60_0/doc/html/variant/tutorial.html) page, but there's nothing there about having to use a "special" visitation pattern with recursive variants.
How should I be writing this program?
make_recursive_variant is a metafunction, not a type in its own right. So you have to evaluate the metafunction:
using int_tree_t =
boost::make_recursive_variant <
int,
std::vector<boost::recursive_variant_>>::type;
↑↑↑↑↑↑
Otherwise, you're using a metafunction type instead of a variant type.

Why am I getting this conversion error when I pass a vector by const reference? [duplicate]

This question already has an answer here:
vector iteration loop throwing an error?
(1 answer)
Closed 7 years ago.
Here's a short program that prints out the terms of a std::vector object. The vector itself is passed in as a const reference for efficiency.
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;
void print_all_terms(const std::vector<int>&);
int main()
{
std::vector<int> sequence_1(4, 100);
print_all_terms(sequence_1);
return(0);
}
void print_all_terms(const std::vector<int>& sequence)
{
for (std::vector<int>::iterator it = sequence.begin() ;
it != sequence.end() ;
++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
However, when I run the program, I get an error:
error: conversion from '__gnu_cxx::__normal_iterator<const int*, std::vector<int, std::allocator<int> > >' to non-scalar type '__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >' requested
This is caused by the fact that the iterator it is declared as a std::vector<int>::iterator, which resolves to
__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >
while the begin() function is returning an object of type
__gnu_cxx::__normal_iterator<const int*, std::vector<int, std::allocator<int> > >
The only difference is the const in the second one. But I don't see why that const should be there - yes, the variable sequence is passed in as a constant reference, but it's the reference that is a const, not the sequence itself.
You need a const_iterator, change the for loop as follows:
for (std::vector<int>::const_iterator it = sequence.begin() ;
it != sequence.end() ; ++it)
If you have C++11 compiler, you can simplify it using auto
for (auto it = sequence.begin() ;
it != sequence.end() ; ++it)
Or you can use range for range loop available with c++11
for (auto & val: sequence)
{
std::cout << val << " ";
}

How to call a template function correctly?

I am new to template function and cannot figure this error. Hope you can help.
#include <iostream>
#include <vector>
/*
* Print to the screen the content of a vector
* Define function template in the header
*/
template <typename T> void print_vector(T& v) {
for(typename std::vector<T>::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
int main() {
std::vector<int> field;
field.resize(12, 1);
/*
for( std::vector<int>::const_iterator i = field.begin(); i != field.end(); ++i)
std::cout << *i << ' ';
*/
print_vector(field);
}
My code failed to compile with a very long error message that I can't even insert here.
error: conversion from ‘std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}’ to non-scalar type ‘std::vector<std::vector<int>, std::allocator<std::vector<int> > >::const_iterator {aka __gnu_cxx::__normal_iterator<const std::vector<int>*, std::vector<std::vector<int>, std::allocator<std::vector<int> > > >}’ requested
utility.h:21:59: error: no match for ‘operator!=’ in ‘i != (& v)->std::vector<_Tp, _Alloc>::end<int, std::allocator<int> >()’
utility.h:21:59: note: candidates are:
In file included from /usr/include/x86_64-linux-gnu/c++/4.7/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from /usr/include/c++/4.7/bits/locale_classes.h:42,
from /usr/include/c++/4.7/bits/ios_base.h:43,
from /usr/include/c++/4.7/ios:43,
from /usr/include/c++/4.7/istream:40,
from /usr/include/c++/4.7/fstream:40,
from utility.h:4:
/usr/include/c++/4.7/ext/new_allocator.h:134:5: note: template<class _Tp> bool __gnu_cxx::operator!=(const __gnu_cxx::new_allocator<_Tp>&, const __gnu_cxx::new_allocator<_Tp>&)
/usr/include/c++/4.7/ext/new_allocator.h:134:5: note: template argument deduction/substitution failed:
utility.h:21:59: note: ‘std::vector<std::vector<int>, std::allocator<std::vector<int> > >::const_iterator {aka __gnu_cxx::__normal_iterator<const std::vector<int>*, std::vector<std::vector<int>, std::allocator<std::vector<int> > > >}’ is not derived from ‘const __gnu_cxx::new_allocator<_Tp>’
When you call
std::vector<int> field;
...
print_vector(field);
the T in print_vector1 is the type of field, i.e., std::vector<int>. Therefore the typename std::vector<T>::const_iterator in
for(typename std::vector<T>::const_iterator i = v.begin();
is a std::vector<std::vector<int> >::const_iterator, to which v.begin() (itself being a std::vector<int>::iterator) is not convertible. Use
for(typename T::const_iterator i = v.begin();
instead.
1 That is to say: in the function that's made from the function template print_vector for this case.
In your function:
template <typename T>
void print_vector(T& v) {
for(typename std::vector<T>::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
T is deduced as std::vector<int>, so you're trying to convert a std::vector<int>::iterator (the result of v.begin()) into a std::vector<std::vector<int>>::const_iterator (the type of i), which is not a valid conversion.
Instead, just make a more specialized template function:
template <typename T>
void print_vector(std::vector<T>& v) {
// as before
}
Either replace std::vector<T>::const_iterator i with simply T::const_iterator i if you want to pass it any iterable, or specialize it appropriately with void print_vector(std::vector<T>& v) if you wish for it to only work against vectors of any types.
If you run a substitution as you have it now you will see you are giving a vector to something creating a vector out of the type, hence getting a vector of vector.
Also GCC 4.8 and 4.9 have improved messages and add a more precise error caret.
If you can't move compiler, which is understandable, then you can always feed small snippets like that to an online one like ideone.com. The error would most likely have been a lot more concise and readable in 4.8.
Oh, and if you have access to C++11 features (I don't remember what made it in 4.7) you can auto those type declarations AND use something like cbegin() instead of begin to get the type resolution to pick a const_iterator instead of a normal one. If you're learning about templates, and have no work environment constraints, I would strongly recommend a move to C++11 and a more recent GCC, it will make easing in a lot easier.
You called the function properly. But then you confused the template-argument (type of the container) with the containers element-type.
Anyway, there's no reason for it not being const.
Thus, it should look like this instead:
template <typename T> void print_vector(const T& v) {
for(typename T::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
Or using the standard-library and lambdas:
template <typename T> void print_vector(const T& v) {
using std::begin; // Using ADL for begin and end to support raw arrays
using std::end;
std::for_each(begin(v), end(v), [](auto&& x){std::cout<<*i<<' ';});
}
Or using auto and range-for-loops (my favorite):
template <typename T> void print_vector(const T& v) {
for(auto&& i : v)
std::cout << *i << ' ';
}
Or using C++1z (projected for 2017):
void print_vector(const auto& v) {
for(auto&& i : v)
std::cout << *i << ' ';
}

map, lambda, remove_if

So, i've problem with std::map, lambda and stl algorithm(remove_if). Actually, same code with std::list or std::vector works well.
My test example :
#include <map>
#include <iostream>
#include <algorithm>
struct Foo
{
Foo() : _id(0) {}
Foo(int id) : _id(id)
{
}
int _id;
};
typedef std::map<int, Foo> FooMap;
int main()
{
FooMap m;
for (int i = 0; i < 10; ++i)
m[i + 100] = Foo(i);
int removeId = 6;
// <<< Error here >>>
std::remove_if(m.begin(), m.end(), [=](const FooMap::value_type & item) { return item.second._id == removeId ;} );
for (auto & item : m )
std::cout << item.first << " = " << item.second._id << "\n";
return 0;
}
Error message :
In file included from /usr/include/c++/4.6/utility:71:0,
from /usr/include/c++/4.6/algorithm:61,
from main.cxx:1:
/usr/include/c++/4.6/bits/stl_pair.h: In member function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const int, _T2 = Foo, std::pair<_T1, _T2> = std::pair<const int, Foo>]’:
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: instantiated from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Rb_tree_iterator<std::pair<const int, Foo> >, _Predicate = main()::<lambda(const value_type&)>]’
main.cxx:33:114: instantiated from here
/usr/include/c++/4.6/bits/stl_pair.h:156:2: error: assignment of read-only member ‘std::pair<const int, Foo>::first’
I don't understand what's wrong here. So, i gladly to read some advices/directions about it. My goal - use new lambda-style with std::map and algorithms, such as remove_if.
g++ 4.6, -std=c++0x.
The problem is that std::map<K,V>::value_type is std::pair<const K, V>, aka .first is const and not assignable. Lambdas have nothing to do with the problem here.
std::remove_if "removes" items by moving the elements of the container around, so that everything that does not fit the predicate is at the front, before the returned iterator. Everything after that iterator is unspecified. It does that with simple assignment, and since you can't assign to a const variable, you get that error.†
The name remove can be a bit misleading and in this case, you really want erase_if, but alas, that doesn't exist. You'll have to make do with iterating over all items and erasing them by hand with map.erase(iterator):
for(auto it = map.begin(), ite = map.end(); it != ite;)
{
if(it->second._id == remove_id)
it = map.erase(it);
else
++it;
}
This is safe because you can erase individual nodes in the tree without the other iterators getting invalidated. Note that I did not increment the iterator in the for loop header itself, since that would skip an element in the case where you erase a node.
† By now, you should have noticed that this would wreak havoc in the std::map's ordering, which is the reason why the key is const - so you can't influence the ordering in any way after an item has been inserted.
You could use find and erase for the map. It's not as convenient as remove_if, but it might be the best you've got.
int removeId = 6;
auto foundIter = m.find(removeId);
// if removeId is not found you will get an error when you try to erase m.end()
if(foundIter != m.end())
{
m.erase(foundIter);
}

Error with T::iterator, where template parameter T might be vector<int> or list<int>

I'm trying to write a function to print a representation of common STL containers (vector, list, etc..). I gave the function a template parameter T which, for example, might represent vector. I'm having problems getting an iterator of type T.
vector<int> v(10, 0);
repr< vector<int> >(v);
...
template <typename T>
void repr(const T & v)
{
cout << "[";
if (!v.empty())
{
cout << ' ';
T::iterator i;
for (i = v.begin();
i != v.end()-1;
++i)
{
cout << *i << ", ";
}
cout << *(++i) << ' ';
}
cout << "]\n";
}
...
brett#brett-laptop:~/Desktop/stl$ g++ -Wall main.cpp
main.cpp: In function ‘void repr(const T&)’:
main.cpp:13: error: expected ‘;’ before ‘i’
main.cpp:14: error: ‘i’ was not declared in this scope
main.cpp: In function ‘void repr(const T&) [with T = std::vector<int, std::allocator<int> >]’:
main.cpp:33: instantiated from here
main.cpp:13: error: dependent-name ‘T::iterator’ is parsed as a non-type, but instantiation yields a type
main.cpp:13: note: say ‘typename T::iterator’ if a type is meant
I tried 'typename T::iterator' as the compiler suggested, but only got a more cryptic error.
Edit: Thanks for the help guys! Here's a working version for anyone who wants to use this function:
template <typename T>
void repr(const T & v)
{
cout << "[";
if (!v.empty())
{
cout << ' ';
typename T::const_iterator i;
for (i = v.begin();
i != v.end();
++i)
{
if (i != v.begin())
{
cout << ", ";
}
cout << *i;
}
cout << ' ';
}
cout << "]\n";
}
You need typename to tell the compiler that ::iterator is supposed to be a type. The compiler doesn't know that it's a type because it doesn't know what T is until you instantiate the template. It could also refer to some static data member, for example. That's your first error.
Your second error is that v is a reference-to-const. So, instead of ::iterator you have to use ::const_iterator. You can't ask a constant container for a non-const iterator.
Change T::iterator i; to typename T::const_iterator i; because ::iterator is of type T and v is a const &.
Before a qualified dependent type, you need typename.
Without typename, there is a C++ parsing rule that says that qualified dependent names should be parsed as non-types even if it leads to a syntax error.
typename states that the name that follows should be treated as a type. Otherwise, names are interpreted to refer to non-types.
Maybe this will help:
Typename is mandatory before a qualified-dependent name which refers to a type, unless that name is naming a base class, or in an initialization list. Typename is optional before a qualified (but non-dependent name) is used within a template, except again when naming a base class or in an initialization list.