I tried to use boost range iterator to implement a LINQ. When it comes to the distinction with the given predicate, I got some problems. To describe my problem directly and simply, I wrote a minimal example down blow:
// test.cc
#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <vector>
using namespace std;
template <typename R>
class Linq {
private:
R range_;
public:
typedef typename R::value_type value_type;
Linq(R& range) : range_(range) {}
Linq(const R& range) : range_(range) {}
~Linq() = default;
template <typename F>
auto Distinct(const F& f) {
auto ur = boost::range::unique(range_, f);
return Linq(ur);
}
auto Distinct() -> Linq<boost::range_detail::uniqued_range<R>> {
boost::range_detail::uniqued_range<R> ur =
range_ | boost::adaptors::uniqued;
return Linq<boost::range_detail::uniqued_range<R>>(ur);
}
template <typename F>
void for_each(const F& f) {
std::for_each(std::begin(range_), std::end(range_), f);
}
};
template <template <typename T> class linq_range_tmpl, typename R>
using linq_range_private =
linq_range_tmpl<decltype(std::begin(std::declval<R>()))>;
template <typename R>
using linq_range_iterator = linq_range_private<boost::iterator_range, R>;
// All the Linq object should be created by From function
template <typename R>
Linq<linq_range_iterator<R>> From(const R& range) {
linq_range_iterator<R> ir(range);
return Linq<decltype(ir)>(ir);
}
int main() {
std::vector<string> vec = {"Hello", "Hello", "Phoenix", "Jack", "Rose"};
auto filter = [](const string& str1, const string& str2) {
return str1.length() == str2.length();
};
auto Printer = [](const string& str) { cout << str << " "; };
From(vec).Distinct().for_each(Printer);
cout << endl;
//From(vec).Distinct(filter).for_each(Printer); // this statement will launch a compile error
cout << endl;
}
By the way, my operating environment is as follow:
As you can see, when I put the statement From(vec).Distinct(filter).for_each(Printer)
into comment, everything works perfectly. The execution result is:
$ Hello Phoenix Jack Rose
The result of distint method with the given predicate should look like
$ Hello Phoenix Jack
But if I let that statement out of comment, I will get a lot of error messages:
$ g++ test.cc -std=c++17
In file included from /usr/include/c++/9/algorithm:62,
from test.cc:1:
/usr/include/c++/9/bits/stl_algo.h: In instantiation of ‘_ForwardIterator std::__unique(_ForwardIterator, _ForwardIterator, _BinaryPredicate) [with _ForwardIterator = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; _BinaryPredicate = __gnu_cxx::__ops::_Iter_comp_iter<main()::<lambda(const string&, const string&)> >]’:
/usr/include/c++/9/bits/stl_algo.h:1036:27: required from ‘_FIter std::unique(_FIter, _FIter, _BinaryPredicate) [with _FIter = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; _BinaryPredicate = main()::<lambda(const string&, const string&)>]’
/usr/include/boost/range/algorithm/unique.hpp:56:25: required from ‘typename boost::range_return<ForwardRange, re>::type boost::range::unique(ForwardRange&, BinaryPredicate) [with boost::range_return_value re = boost::return_begin_found; ForwardRange = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >; BinaryPredicate = main()::<lambda(const string&, const string&)>; typename boost::range_return<ForwardRange, re>::type = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
/usr/include/boost/range/algorithm/unique.hpp:92:54: required from ‘typename boost::range_return<SinglePassRange, boost::return_begin_found>::type boost::range::unique(ForwardRange&, BinaryPredicate) [with ForwardRange = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >; BinaryPredicate = main()::<lambda(const string&, const string&)>; typename boost::range_return<SinglePassRange, boost::return_begin_found>::type = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
test.cc:21:35: required from ‘auto Linq<R>::Distinct(const F&) [with F = main()::<lambda(const string&, const string&)>; R = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
test.cc:59:28: required from here
/usr/include/c++/9/bits/stl_algo.h:975:14: error: no match for ‘operator=’ (operand types are ‘const std::__cxx11::basic_string<char>’ and ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type’ {aka ‘const std::__cxx11::basic_string<char>’})
975 | *++__dest = _GLIBCXX_MOVE(*__first);
| ^
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/stdexcept:39,
from /usr/include/c++/9/array:39,
from /usr/include/c++/9/tuple:39,
from /usr/include/c++/9/functional:54,
from /usr/include/c++/9/pstl/glue_algorithm_defs.h:13,
from /usr/include/c++/9/algorithm:71,
from test.cc:1:
/usr/include/c++/9/bits/basic_string.h:665:7: note: candidate: ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ <near match>
665 | operator=(const basic_string& __str)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:665:7: note: passing ‘const std::__cxx11::basic_string<char>*’ as ‘this’ argument discards qualifiers
/usr/include/c++/9/bits/basic_string.h:732:7: note: candidate: ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ <near match>
732 | operator=(basic_string&& __str)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:732:7: note: conversion of argument 1 would be ill-formed:
In file included from /usr/include/c++/9/algorithm:62,
from test.cc:1:
/usr/include/c++/9/bits/stl_algo.h:975:14: error: binding reference of type ‘std::__cxx11::basic_string<char>&&’ to ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type’ {aka ‘const std::__cxx11::basic_string<char>’} discards qualifiers
975 | *++__dest = _GLIBCXX_MOVE(*__first);
| ^
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/stdexcept:39,
from /usr/include/c++/9/array:39,
from /usr/include/c++/9/tuple:39,
from /usr/include/c++/9/functional:54,
from /usr/include/c++/9/pstl/glue_algorithm_defs.h:13,
from /usr/include/c++/9/algorithm:71,
from test.cc:1:
/usr/include/c++/9/bits/basic_string.h:704:7: note: candidate: ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
704 | operator=(const _CharT* __s)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:704:31: note: no known conversion for argument 1 from ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type’ {aka ‘const std::__cxx11::basic_string<char>’} to ‘const char*’
704 | operator=(const _CharT* __s)
| ~~~~~~~~~~~~~~^~~
/usr/include/c++/9/bits/basic_string.h:715:7: note: candidate: ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
715 | operator=(_CharT __c)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:715:24: note: no known conversion for argument 1 from ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type’ {aka ‘const std::__cxx11::basic_string<char>’} to ‘char’
715 | operator=(_CharT __c)
| ~~~~~~~^~~
/usr/include/c++/9/bits/basic_string.h:795:7: note: candidate: ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(std::initializer_list<_Tp>) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
795 | operator=(initializer_list<_CharT> __l)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:795:42: note: no known conversion for argument 1 from ‘std::remove_reference<const std::__cxx11::basic_string<char>&>::type’ {aka ‘const std::__cxx11::basic_string<char>’} to ‘std::initializer_list<char>’
795 | operator=(initializer_list<_CharT> __l)
| ~~~~~~~~~~~~~~~~~~~~~~~~~^~~
/usr/include/c++/9/bits/basic_string.h:809:8: note: candidate: ‘template<class _Tp> std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_If_sv<_Tp, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&> std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _Tp&) [with _Tp = _Tp; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
809 | operator=(const _Tp& __svt)
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:809:8: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/9/bits/move.h:55,
from /usr/include/c++/9/bits/stl_pair.h:59,
from /usr/include/c++/9/utility:70,
from /usr/include/c++/9/algorithm:60,
from test.cc:1:
/usr/include/c++/9/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = std::integral_constant<bool, false>::value; _Tp = std::__cxx11::basic_string<char>&]’:
/usr/include/c++/9/bits/basic_string.h:117:8: required by substitution of ‘template<class _CharT, class _Traits, class _Alloc> template<class _Tp, class _Res> using _If_sv = std::enable_if_t<std::__and_<std::is_convertible<const _Tp&, std::basic_string_view<_CharT, _Traits> >, std::__not_<std::is_convertible<const _Tp*, const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>*> >, std::__not_<std::is_convertible<const _Tp&, const _CharT*> > >::value, _Res> [with _Tp = std::__cxx11::basic_string<char>; _Res = std::__cxx11::basic_string<char>&; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
/usr/include/c++/9/bits/basic_string.h:809:8: required by substitution of ‘template<class _Tp> std::__cxx11::basic_string<char>::_If_sv<_Tp, std::__cxx11::basic_string<char>&> std::__cxx11::basic_string<char>::operator=<_Tp>(const _Tp&) [with _Tp = std::__cxx11::basic_string<char>]’
/usr/include/c++/9/bits/stl_algo.h:975:14: required from ‘_ForwardIterator std::__unique(_ForwardIterator, _ForwardIterator, _BinaryPredicate) [with _ForwardIterator = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; _BinaryPredicate = __gnu_cxx::__ops::_Iter_comp_iter<main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/9/bits/stl_algo.h:1036:27: required from ‘_FIter std::unique(_FIter, _FIter, _BinaryPredicate) [with _FIter = __gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; _BinaryPredicate = main()::<lambda(const string&, const string&)>]’
/usr/include/boost/range/algorithm/unique.hpp:56:25: required from ‘typename boost::range_return<ForwardRange, re>::type boost::range::unique(ForwardRange&, BinaryPredicate) [with boost::range_return_value re = boost::return_begin_found; ForwardRange = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >; BinaryPredicate = main()::<lambda(const string&, const string&)>; typename boost::range_return<ForwardRange, re>::type = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
/usr/include/boost/range/algorithm/unique.hpp:92:54: required from ‘typename boost::range_return<SinglePassRange, boost::return_begin_found>::type boost::range::unique(ForwardRange&, BinaryPredicate) [with ForwardRange = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >; BinaryPredicate = main()::<lambda(const string&, const string&)>; typename boost::range_return<SinglePassRange, boost::return_begin_found>::type = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
test.cc:21:35: required from ‘auto Linq<R>::Distinct(const F&) [with F = main()::<lambda(const string&, const string&)>; R = boost::iterator_range<__gnu_cxx::__normal_iterator<const std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > > >]’
test.cc:59:28: required from here
/usr/include/c++/9/type_traits:2385:11: error: no type named ‘type’ in ‘struct std::enable_if<false, std::__cxx11::basic_string<char>&>’
2385 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
I am bothered by this issue for several days. I did everything I can do to solve this problem, but it still doesn't work. I've exhausted my tricks. Can anyone give me some hint to solve it?
auto ur = boost::range::unique(range_, f);
tries to in-place modify range_
but its elements are readonly (const
). The adaptor uniqued
does NOT do that. Probaly you mixed them up by accident. Going by the names of things, this would seem to be a better match:
template <typename F>
auto Distinct(F const& f) {
auto ur = range_
| boost::adaptors::filtered(f)
| boost::adaptors::uniqued;
return Linq<decltype(ur)>(ur);
}
Now, 'filter' is a misnomer, because it would require a predicate. What you meant could be comparator
? However:
All in all, it seems you're doing too much work for merely getting some syntactic sugar for existing Range libraries.
See also Is there a LINQ library for C++? for some design thoughts about this idea.