How can I design a standalone C++17 std::views::join alternative? - c++

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 [, while preserving the functionality which std::views::join provides (i.e. optionally modifiable elements Godbolt)?](refined/edited question)
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 vector std::vector<std::vector<std::vector<int>>> and a twice applied join_view instead of a int as element type I receive a std::vector<int> (Implementation). If we take a look at the types of the nested construction auto deeper_view = join_view(join_view(data_deeper)); this expands in C++ Insights to join_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() and std::end() to their $.begin() counterpart, since these are the one defined for the join_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(),{});
}
};

You need to implement a recursive class template to achieve your goal. Here is a working quick and dirty prototype.
#include <cassert>
#include <iostream>
#include <type_traits>
#include <vector>
template <class T>
struct is_container : public std::false_type {};
// you'll need to declare specializations for the containers you need.
template <class T, class Alloc>
struct is_container<std::vector<T, Alloc>> : public std::true_type {};
// basic definition for our view
template <typename T, typename = void>
struct join_view;
// specialization for non-container types
template <typename T>
struct join_view<T, std::enable_if_t<!is_container<T>::value>> {
using contained_type = T;
using outer_const_iterator = const T*;
using const_iterator = const T*;
join_view(const T& t) : t_(t) {}
const_iterator begin() const { return &t_; }
const_iterator end() const { return begin() + 1; }
const T& t_;
};
// specialization for containers
template <typename Container>
struct join_view<Container, std::enable_if_t<is_container<Container>::value>> {
using contained_type = typename Container::value_type;
using outer_const_iterator = typename Container::const_iterator;
using inner_container_type = join_view<contained_type>;
using inner_const_iterator = typename inner_container_type::const_iterator;
friend inner_container_type;
class const_iterator {
friend join_view;
friend inner_container_type;
public:
const_iterator() = default;
const_iterator(const const_iterator&) = default;
const_iterator(const_iterator&&) = default;
const_iterator& operator=(const const_iterator&) = default;
const_iterator& operator=(const_iterator&&) = default;
private:
const_iterator(const Container* container, const outer_const_iterator& outer,
const inner_const_iterator& inner)
: container_(container), outer_(outer), inner_(inner) {}
const_iterator(const Container* container, outer_const_iterator outer)
: container_(container), outer_(outer) {
assert(outer_ == container_->end());
}
public:
const_iterator& operator++() {
if (++inner_ != inner_container_type{*outer_}.end()) return *this;
if (++outer_ != container_->end())
inner_ = inner_container_type{*outer_}.begin();
return *this;
}
bool operator==(const const_iterator& other) const {
if (outer_ == other.outer_) {
if (outer_ == container_->end()) return true;
return inner_ == other.inner_;
}
return false;
}
bool operator!=(const const_iterator& other) const {
return !(*this == other);
}
const auto& operator*() const
{
return *inner_;
}
private:
const Container* container_ = nullptr;
outer_const_iterator outer_;
inner_const_iterator inner_;
};
join_view(const Container& container) : outer_(container) {}
const_iterator begin() const {
return {&outer_, outer_.begin(),
inner_container_type{*(outer_.begin())}.begin()};
}
const_iterator end() const { return {&outer_, outer_.end()}; }
const Container& outer_;
};
template <typename T>
auto make_join_view(const T& t)
{
return join_view<T>(t);
}
int main() {
static_assert(is_container<std::vector<int>>::value);
static_assert(!is_container<int>::value);
int test_int = 42;
for (auto x : make_join_view(test_int)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<int> v{1, 2, 3};
for (const auto& x : make_join_view(v)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<std::vector<int>> vv{{1}, {2, 3}, {4, 5, 6}};
for (const auto& x : make_join_view(vv)) std::cout << x << std::endl;
std::cout << std::endl;
std::vector<std::vector<std::vector<int>>> vvv{ {{1}, {2, 3}, {4, 5, 6}}, {{11}, {22, 33}, {44, 55, 66}} };
for (const auto& x : make_join_view(vvv)) std::cout << x << std::endl;
}
It's very rough, as it only handles very basic traversals, but should work for most container types.
I think is_container<> can be rewritten to check for the presence of Container::const_iterator. That would allow join_view to work on any container, but also on any view.
Note: make sure your implementation uses the name const_iterator, that's absolutely imperative for this scheme to work.
You can play with the code here: https://godbolt.org/z/jhe93b1rx

Related

Override C++ map iterator dereference only

I have the following class that wraps a C++ map. I'd like to override just the iterator dereference, to return the map's value only instead of the key. Is this possible at all, without having to re-implement the entire std::map iterator (which I should probably avoid as much as possible)?
Here it is:
#include <map>
using std::map;
class X {
using Type = map<int, double>;
using const_iterator = typename Type::const_iterator;
public:
void insert(int key, double value) {
my_map[key] = value;
}
const_iterator cbegin() const { return my_map.cbegin(); }
const_iterator cend() const { return my_map.cend(); }
const_iterator begin() const { return my_map.cbegin(); }
const_iterator end() const { return my_map.cend(); }
private:
Type my_map;
};
int main() {
X x;
double i;
for (const auto& it : x) {
i = it.second; // works
i = it; // fails
}
}
You pretty much do need to implement an entire iterator type to provide new iterator behavior. Luckily, Boost has a couple of tools that can make this much easier: boost::iterator_facade is a tool for creating an iterator type, which takes care of satisfying all the requirement details for the various types of Iterator as defined by the Standard. And for the common case where you want to create an iterator which wraps another iterator, overriding just pieces of its functionality, there's boost::iterator_adaptor.
So you could define your X::const_iterator like this:
#include <map>
#include <boost/iterator_adaptor.hpp>
using std::map;
class X {
using Type = map<int, double>;
public:
class const_iterator : public boost::iterator_adaptor<
const_iterator, // Derived iterator type, for CRTP
typename Type::const_iterator, // Wrapped iterator type
const double> // Value type
{
public:
const_iterator() {}
private:
// Allow X to create X::const_iterator from Type::const_iterator:
explicit const_iterator(typename Type::const_iterator map_iter)
: iterator_adaptor(map_iter) {}
friend X;
// Define the dereference operation:
const double& dereference() const
{ return base()->second; }
// Allow boost's internals to use dereference():
friend boost::iterator_core_access;
};
const_iterator cbegin() const { return const_iterator(my_map.cbegin()); }
};
...
(I intentionally changed the access of the name X::const_iterator from private to public. Someone may want to explicitly name that iterator type.)
Borrowing from Yakk's answer here you can easily modify it to suite your needs.
template<class T>
T value_of(T t) { return std::move(t); }
template<class K, class V>
V value_of(std::pair<K, V> const& p) { return p.second; }
template<class It>
struct range_t {
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<class T>
struct value_t {
T t;
void operator++(){ t++; }
auto operator*() { return value_of(*t); }
friend bool operator==(value_t const& left, value_t const& right)
{ return left.t == right.t; }
friend bool operator!=(value_t const& left, value_t const& right)
{ return left.t != right.t; }
};
template<class T>
range_t<value_t<T>> values_over(T b, T e) {
return {{b}, {e}};
}
template<class C>
auto values_of(C& c) {
using std::begin; using std::end;
return values_over(begin(c), end(c));
}
int main() {
X x;
double i;
for (double const& it : values_of(x)) {
i = it;
}
}

encapsulating std::vector to allow iteration but nothing else

I would like to hide a vector field in my class but allow easy iteration through its elements but nothing else. So that class's client would be able to do
for (auto element : foo.getElements()) { }
but not
foo.getElements()[42];
Is there some simple way of achieving this w/o creating new confusing types?
I cannot say what is and is not a "new confusing type". But this is sufficient for the needs of a range-based for:
template<typename Iter>
class iterator_range
{
public:
iterator_range(Iter beg, Iter end) : beg_(beg), end_(end) {}
Iter begin() const {return beg_;}
Iter end() const {return end_;}
private:
Iter beg_, end_;
};
The Range TS adds more complexity to what constitutes a "range", but this is good enough for range-based for. So your foo.getElements function would look like this:
auto getElements()
{
return iterator_range<vector<T>::iterator>(vec.begin(), vec.end());
}
auto getElements() const
{
return iterator_range<vector<T>::const_iterator>(vec.begin(), vec.end());
};
You can use an higher-order function to only expose iteration functionality:
class something
{
private:
std::vector<item> _items;
public:
template <typename F>
void for_items(F&& f)
{
for(auto& i : _items) f(i);
}
};
Usage:
something x;
x.for_items([](auto& item){ /* ... */ });
The advantages of this pattern are:
Simple to implement (no need for any "proxy" class);
Can transparently change std::vector to something else without breaking the user.
To be completely correct and pedantic, you have to expose three different ref-qualified versions of for_items. E.g.:
template <typename F>
void for_items(F&& f) & { for(auto& i : items) f(i); }
template <typename F>
void for_items(F&& f) const& { for(const auto& i : items) f(i); }
template <typename F>
void for_items(F&& f) && { for(auto& i : items) f(std::move(i)); }
The above code ensures const-correctness and allows elements to be moved when the something instance is a temporary.
Here is a proxy-based approach (though I'm not sure whether the new type meets the requirement of not being confusing).
template<class Container> class IterateOnlyProxy {
public:
IterateOnlyProxy(Container& c) : c(c) {}
typename Container::iterator begin() { return c.begin(); }
typename Container::iterator end() { return c.end(); }
private:
Container& c;
};
The proxy is used as a return type for the getElements() method,
class Foo {
public:
using Vec = std::vector<int>;
using Proxy = IterateOnlyProxy<Vec>;
Proxy& getElements() { return elementsProxy; }
private:
Vec elements{4, 5, 6, 7};
Proxy elementsProxy{elements};
};
and client code can iterate over the underlying container, but that's about it.
Foo foo;
for (auto element : foo.getElements())
std::cout << element << std::endl;
foo.getElements()[42]; // error: no match for ‘operator[]’
If you want to hide a vector field in your class but still do range based for-loop, you could add your own iterator based on vector::iterator.
A simple (and incomplete) example could be like:
#include <iostream>
#include <vector>
class Foo
{
public:
class iterator
{
public:
iterator(std::vector<int>::iterator n) : p(n) {}
bool operator==(iterator& rhs) { return p == rhs.p; }
bool operator!=(iterator& rhs) { return p != rhs.p; }
iterator& operator++() { p++; return *this; }
int& operator*() { return *p; }
private:
std::vector<int>::iterator p;
};
iterator begin() { return iterator(v.begin()); }
iterator end() { return iterator(v.end()); }
private:
std::vector<int> v {1, 2, 3, 4, 5};
};
int main() {
Foo foo;
for(auto y : foo) std::cout << y << std::endl;
return 0;
}

Iterators with complex value type: confusion with value_type and reference

I want to create a custom iterator wrapper, for example, enumerate: given a pair of iterators over type T, it would return an iterable over type std::pair<const int, T&>, where the first element of the pair will take values 0, 1, 2, so on.
I have a problem figuring out what should be value_type and reference of my iterator. I want to support two behaviours:
First, referencing values of the underlying sequence:
for (auto& kv: enumerate(my_vec)) {
kv.second = kv.first;
}
(sort of std::iota);
Second, making a copy of the value:
std::vector<int> a{10, 20, 30};
auto copy = *enumerate(a).begin();
a[0] = 15;
std::cout << copy.first << " " << copy.second; // 0 10
I'm confused what should be the return type of Iterator::operator*(). If it is std::pair<const int, T&> then in the second example value will not be copied. If it is std::pair<const int, T> then in the first example it is impossible to reference underlying values. What should I do and what should be value_type, reference and pointer typedefs of such iterator?
Here is my attempt to implement it. It supports taking references but not copying.
template<typename T>
struct Iterator {
using TT = typename std::iterator_traits<T>::value_type;
using value_type = std::pair<const int, TT>;
using reference = std::pair<const int&, typename std::iterator_traits<T>::reference>;
using pointer = value_type*;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
std::pair<int, T> it;
Iterator(T iterator) : it(0, iterator) {}
bool operator==(const Iterator& other) const { return it.second == other.it.second; }
bool operator!=(const Iterator& other) const { return it.second != other.it.second; }
reference operator*() { return { it.first, *it.second }; }
Iterator& operator++() { ++it.first; ++it.second; return *this; }
};
P.S. I've just checked, boost::adaptors::index suffers from the same problem and does not copy the value.
This problem is similar to that of std::vector<bool>, you want to provide a proxy that acts just like a reference but also supports value semantics.
What's different though, is that the types involved aren't restricted, there is two references involved and all sorts of hairiness pops up. The following is a partial implementation and it illustrates some problems you run into
#include<iterator>
#include<functional>
template<typename F, typename S, bool defined = true>
struct sfinae_difference_type {};
template<typename F, typename S>
struct sfinae_difference_type<F, S,
std::is_same_v<typename std::iterator_traits<F>::difference_type,
typename std::iterator_traits<S>::difference_type>>
{
using difference_type = typename std::iterator_traits<F>::difference_type;
};
template<typename F, typename S>
class pair_iterator : sfinae_difference_type<F, S>
{
using Fvalue_type = typename std::iterator_traits<F>::value_type;
using Svalue_type = typename std::iterator_traits<S>::value_type;
using Freference = typename std::iterator_traits<F>::reference;
using Sreference = typename std::iterator_traits<S>::reference;
F f;
S s;
public:
using value_type = std::pair<Fvalue_type, Svalue_type>;
struct reference
{
Freference first;
Sreference second;
reference() = delete;
reference(const reference& other) : first{other.first}, second{other.second} {}
reference& operator=(const reference& rhs)
{
first = rhs.first;
second = rhs.second;
return *this;
}
operator value_type() { return {f, s}; }
private:
reference(Freference f, Sreference s) : first{f}, second{s} {}
friend pair_iterator;
};
struct pointer
{
// similar to reference
};
pair_iterator() = default;
pair_iterator(const pair_iterator&) = default;
pair_iterator(F f, S s) : f{f}, s{s} {}
pair_iterator& operator++() { ++f; ++s; return *this; }
reference operator*() { return {*f, *s}; }
pointer operator->() { return {f.operator->(), s.operator->()}; }
bool operator==(const pair_iterator& other)
{
return f == other.f && s == other.s;
}
};
You then use it as
#include<vector>
#include<list>
#include<iostream>
int main()
{
std::vector v{1, 2, 3, 4, 5};
std::list l{6, 7, 8, 9, 10};
pair_iterator begin{v.begin(), l.begin()}, end{v.end(), l.end()};
for(; begin != end; ++begin)
std::cout << begin->first << ' ' << begin->second << '\n';
}
Live
Some of the immediately obvious problems:
Implementation is tedious. Having sfinae friendly type aliases and proper proxies requires copious boilerplate.
The semantics of proxies may be confusing. What does copying/assigning one reference to another mean? What is auto is_this_a_copy = *it supposed to do?
What does equality mean? Does both internal iterators have to be equal to be equal? That breaks comparison with end iterators.
All of these have to be hammered out to make it work, and there isn't an easy answer.

c++ sorted view of range - how to create const_iterator?

I am trying to write a class that should act as a sorted view on some underlying sequence of elements. So far I have come up with a non-const version. Now I have problems adapting it to also provide const_iterator functionality.
The code I have so far looks like this:
// forward declare iterator
template <class InputIt>
class sorted_range_iter;
template <class InputIt>
class sorted_range {
friend class sorted_range_iter<InputIt>;
private:
using T = typename InputIt::value_type;
InputIt _first;
InputIt _last;
std::vector<size_t> _indices;
public:
using iterator = sorted_range_iter<InputIt>;
sorted_range() = default;
sorted_range(InputIt first, InputIt last)
: _first(first), _last(last), _indices(std::distance(_first, _last)) {
std::iota(_indices.begin(), _indices.end(), 0);
};
template <class Compare = std::less<T>>
void sort(Compare comp = Compare()) {
std::sort(_indices.begin(), _indices.end(),
[this, &comp](size_t i1, size_t i2) {
return comp(*(_first + i1), *(_first + i2));
});
}
size_t size() const { return _indices.size(); }
T& operator[](size_t pos) { return *(_first + _indices[pos]); }
const T& operator[](size_t pos) const { return (*this)[pos]; }
iterator begin() { return iterator(0, this); }
iterator end() { return iterator(size(), this); }
};
And the corresponding iterator looks like this:
template <class InputIt>
class sorted_range_iter
: public std::iterator<std::forward_iterator_tag, InputIt> {
friend class sorted_range<InputIt>;
private:
using T = typename InputIt::value_type;
size_t _index;
sorted_range<InputIt>* _range;
sorted_range_iter(size_t index, sorted_range<InputIt>* range)
: _index(index), _range(range) {}
public:
T& operator*() { return *(_range->_first + _range->_indices[_index]); }
// pre-increment
const sorted_range_iter<InputIt>& operator++() {
_index++;
return *this;
}
// post-increment
sorted_range_iter<InputIt> operator++(int) {
sorted_range_iter<InputIt> result = *this;
++(*this);
return result;
}
bool operator!=(const sorted_range_iter<InputIt>& other) const {
return _index != other._index;
}
};
An usage example looks like this:
std::vector<int> t{5, 2, 3, 4};
auto rit = ref.begin();
sorted_range<std::vector<int>::iterator> r(begin(t), end(t));
r.sort();
for(auto& x : r)
{
std::cout << x << std::endl;
}
Output:
2
3
4
5
How do I adapt my iterator for the const case? It would be easier if the iterator would be templated on the underlying type (int for example) instead InputIt. Is there a better way to define this class?
I suppose one could solve this for example by using the range-v3 library, however I am trying to not add any more dependencies and rely on C++11/14 functions.
You just are using the wrong type for T. You have:
using T = typename InputIt::value_type;
But value_type is the same for iterator and const_iterator. What they have are different reference types. You should prefer:
using R = typename std::iterator_traits<InputIt>::reference;
R operator*() { ... }
This has the added benefit of working with pointers.
Alternatively, could avoid iterator_traits by just trying to dereference the iterator itself:
using R = decltype(*std::declval<InputIt>());
Side-note, shouldn't sorted_range sort itself on construction? Otherwise, easy to misuse.

Sorting a pair of vectors

I know how to sort a vector of pairs, but how do you sort a pair of vectors? I can think of writing a custom "virtual" iterator over a pair of vectors and sorting that, but that seems quite complex. Is there an easier way? Is there one in C++03? I would like to use std::sort.
This problem arises when processing some data generated in hardware, where a pair of arrays makes more sense than array of pairs (since then there would be all kinds of stride and alignment problems). I realize that otherwise keeping a pair of vector instead of a vector of pairs would be a design flaw (the structure of arrays problem). I'm looking for a fast solution, copying the data to a vector of pairs and then back (I will return it to the HW to do more processing) is not an option.
Example:
keys = {5, 2, 3, 1, 4}
values = {a, b, d, e, c}
and after sorting (by the first vector):
keys = {1, 2, 3, 4, 5}
values = {e, b, d, c, a}
I refer to a "pair of vectors" as the pair of keys and values (stored as e.g. std::pair<std::vector<size_t>, std::vector<double> >). The vectors have the same length.
Let's make a sort/permute iterator, so that we can just say:
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
// output
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
See it Live On Coliru, printing
Keys: 1 2 3 4 5
Values: e b d c a
Based on the idea here, I've implemented this:
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
Now the factory function is simply:
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
Note I've been a little lazy by using boost/tuples/tuple_comparison.hpp for the sorting. This could pose a problem with stable sort when multiple key values share the same value. However, in this case it's hard to define what is "stable" sort anyways, so I didn't think it important for now.
FULL LISTING
Live On Coliru
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace boost { namespace tuples {
// MSVC might not require this
template <typename T, typename U>
inline void swap(boost::tuple<T&, U&> a, boost::tuple<T&, U&> b) noexcept {
using std::swap;
swap(boost::get<0>(a), boost::get<0>(b));
swap(boost::get<1>(a), boost::get<1>(b));
}
} }
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
#include <iostream>
using std::begin;
using std::end;
int main()
{
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
}
Just for comparison, this is how much code the split iterator approach requires:
template <class V0, class V1>
class CRefPair { // overrides copy semantics of std::pair
protected:
V0 &m_v0;
V1 &m_v1;
public:
CRefPair(V0 &v0, V1 &v1)
:m_v0(v0), m_v1(v1)
{}
void swap(CRefPair &other)
{
std::swap(m_v0, other.m_v0);
std::swap(m_v1, other.m_v1);
}
operator std::pair<V0, V1>() const // both g++ and msvc sort requires this (to get a pivot)
{
return std::pair<V0, V1>(m_v0, m_v1);
}
CRefPair &operator =(std::pair<V0, V1> v) // both g++ and msvc sort requires this (for insertion sort)
{
m_v0 = v.first;
m_v1 = v.second;
return *this;
}
CRefPair &operator =(const CRefPair &other) // required by g++ (for _GLIBCXX_MOVE)
{
m_v0 = other.m_v0;
m_v1 = other.m_v1;
return *this;
}
};
template <class V0, class V1>
inline bool operator <(std::pair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return a < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, std::pair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < b; // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
namespace std {
template <class V0, class V1>
inline void swap(CRefPair<V0, V1> &a, CRefPair<V0, V1> &b)
{
a.swap(b);
}
} // ~std
template <class It0, class It1>
class CPairIterator : public std::random_access_iterator_tag {
public:
typedef typename std::iterator_traits<It0>::value_type value_type0;
typedef typename std::iterator_traits<It1>::value_type value_type1;
typedef std::pair<value_type0, value_type1> value_type;
typedef typename std::iterator_traits<It0>::difference_type difference_type;
typedef /*typename std::iterator_traits<It0>::distance_type*/difference_type distance_type; // no distance_type in g++, only in msvc
typedef typename std::iterator_traits<It0>::iterator_category iterator_category;
typedef CRefPair<value_type0, value_type1> reference;
typedef reference *pointer; // not so sure about this, probably can't be implemented in a meaningful way, won't be able to overload ->
// keep the iterator traits happy
protected:
It0 m_it0;
It1 m_it1;
public:
CPairIterator(const CPairIterator &r_other)
:m_it0(r_other.m_it0), m_it1(r_other.m_it1)
{}
CPairIterator(It0 it0 = It0(), It1 it1 = It1())
:m_it0(it0), m_it1(it1)
{}
reference operator *()
{
return reference(*m_it0, *m_it1);
}
value_type operator *() const
{
return value_type(*m_it0, *m_it1);
}
difference_type operator -(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
// the iterators always need to have the same position
// (incomplete check but the best we can do without having also begin / end in either vector)
return m_it0 - other.m_it0;
}
bool operator ==(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 == other.m_it0;
}
bool operator !=(const CPairIterator &other) const
{
return !(*this == other);
}
bool operator <(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 < other.m_it0;
}
bool operator >=(const CPairIterator &other) const
{
return !(*this < other);
}
bool operator <=(const CPairIterator &other) const
{
return !(other < *this);
}
bool operator >(const CPairIterator &other) const
{
return other < *this;
}
CPairIterator operator +(distance_type d) const
{
return CPairIterator(m_it0 + d, m_it1 + d);
}
CPairIterator operator -(distance_type d) const
{
return *this + -d;
}
CPairIterator &operator +=(distance_type d)
{
return *this = *this + d;
}
CPairIterator &operator -=(distance_type d)
{
return *this = *this + -d;
}
CPairIterator &operator ++()
{
return *this += 1;
}
CPairIterator &operator --()
{
return *this += -1;
}
CPairIterator operator ++(int) // msvc sort actually needs this, g++ does not
{
CPairIterator old = *this;
++ (*this);
return old;
}
CPairIterator operator --(int)
{
CPairIterator old = *this;
-- (*this);
return old;
}
};
template <class It0, class It1>
inline CPairIterator<It0, It1> make_pair_iterator(It0 it0, It1 it1)
{
return CPairIterator<It0, It1>(it0, it1);
}
It is kind of rough around the edges, maybe I'm just bad at overloading the comparisons, but the amount of differences needed to support different implementations of std::sort makes me think the hackish solution might actually be more portable. But the sorting is much nicer:
struct CompareByFirst {
bool operator ()(std::pair<size_t, char> a, std::pair<size_t, char> b) const
{
return a.first < b.first;
}
};
std::vector<char> vv; // filled by values
std::vector<size_t> kv; // filled by keys
std::sort(make_pair_iterator(kv.begin(), vv.begin()),
make_pair_iterator(kv.end(), vv.end()), CompareByFirst());
// nice
And of course it gives the correct result.
Inspired by a comment by Mark Ransom, this is a horrible hack, and an example of how not to do it. I only wrote it for amusement and because I was wondering how complicated would it get. This is not an answer to my question, I will not use this. I just wanted to share a bizarre idea. Please, do not downvote.
Actually, ignoring multithreading, I believe this could be done:
template <class KeyType, class ValueVectorType>
struct MyKeyWrapper { // all is public to save getters
KeyType k;
bool operator <(const MyKeyWrapper &other) const { return k < other.k; }
};
template <class KeyType, class ValueVectorType>
struct ValueVectorSingleton { // all is public to save getters, but kv and vv should be only accessible by getters
static std::vector<MyKeyWrapper<KeyType, ValueVectorType> > *kv;
static ValueVectorType *vv;
static void StartSort(std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &_kv, ValueVectorType &_vv)
{
assert(!kv && !vv); // can't sort two at once (if multithreading)
assert(_kv.size() == _vv.size());
kv = &_kv, vv = &_vv; // not an attempt of an atomic operation
}
static void EndSort()
{
kv = 0, vv = 0; // not an attempt of an atomic operation
}
};
template <class KeyType, class ValueVectorType>
std::vector<MyKeyWrapper<KeyType, ValueVectorType> >
*ValueVectorSingleton<KeyType, ValueVectorType>::kv = 0;
template <class KeyType, class ValueVectorType>
ValueVectorType *ValueVectorSingleton<KeyType, ValueVectorType>::vv = 0;
namespace std {
template <class KeyType, class ValueVectorType>
void swap(MyKeyWrapper<KeyType, ValueVectorType> &a,
MyKeyWrapper<KeyType, ValueVectorType> &b)
{
assert((ValueVectorSingleton<KeyType, ValueVectorType>::vv &&
ValueVectorSingleton<KeyType, ValueVectorType>::kv)); // if this triggers, someone forgot to call StartSort()
ValueVectorType &vv = *ValueVectorSingleton<KeyType, ValueVectorType>::vv;
std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &kv =
*ValueVectorSingleton<KeyType, ValueVectorType>::kv;
size_t ai = &kv.front() - &a, bi = &kv.front() - &b; // get indices in key vector
std::swap(a, b); // swap keys
std::swap(vv[ai], vv[bi]); // and any associated values
}
} // ~std
And sorting as:
std::vector<char> vv; // filled by values
std::vector<MyKeyWrapper<size_t, std::vector<char> > > kv; // filled by keys, casted to MyKeyWrapper
ValueVectorSingleton<size_t, std::vector<char> >::StartSort(kv, vv);
std::sort(kv.begin(), kv.end());
ValueVectorSingleton<size_t, std::vector<char> >::EndSort();
// trick std::sort into using the custom std::swap which also swaps the other vectors
This is obviously very appalling, trivial to abuse in horrible ways, but arguably much shorter than the pair of iterators and probably similar in performance. And it actually works.
Note that swap() could be implemented inside ValueVectorSingleton and the one injected in the std namespace would just call it. That would avoid having to make vv and kv public. Also, the addresses of a and b could further be checked to make sure they are inside kv and not some other vector. Also, this is limited to sorting by values of only one vector (can't sort by corresponding values in both vectors at the same time). And the template parameters could be simply KeyType and ValueType, this was written in a hurry.
Here is a solution I once used to sort an array together with an array of indices (--maybe it is from somewhere over here?):
template <class iterator>
class IndexComparison
{
public:
IndexComparison (iterator const& _begin, iterator const& _end) :
begin (_begin),
end (_end)
{}
bool operator()(size_t a, size_t b) const
{
return *std::next(begin,a) < *std::next(begin,b);
}
private:
const iterator begin;
const iterator end;
};
Usage:
std::vector<int> values{5,2,5,1,9};
std::vector<size_t> indices(values.size());
std::iota(indices.begin(),indices.end(),0);
std::sort(indices.begin(),indices.end()
, IndexComparison<decltype(values.cbegin())>(values.cbegin(),values.cend()));
Afterwards, the integers in vector indices are permuted such that they correspond to increasing values in the vector values. It is easy to extend this from less-comparison to general comparison functions.
Next, in order to sort also the values, you can do another
std::sort(values.begin(),values.end());
using the same comparison function. This is the solution for the lazy ones. Of course, you can alternatively also use the sorted indices according to
auto temp=values;
for(size_t i=0;i<indices.size();++i)
{
values[i]=temp[indices[i]];
}
DEMO
EDIT: I just realized that the above sorts into the opposite direction than the one you were asking for.