This program does not quite conform to the previous related question, yet does basically the same thing: given a collection of elements and a collection of indices, return via an iterator collection elements according to the sequence of indices. Here it goes:
#include <exception>
#include <iostream>
#include <iterator>
#include <list>
#include <sstream>
#include <vector>
template<typename ForwardIterator, typename IndexIterator>
class indirection_iterator {
public:
indirection_iterator(ForwardIterator element_begin,
ForwardIterator element_end,
IndexIterator index_begin,
IndexIterator index_end)
:
m_element_begin{element_begin},
m_element_end{element_end},
m_index_begin{index_begin},
m_index_end{index_end},
m_index_current{index_begin},
m_elements{std::distance(element_begin, element_end)}
{}
indirection_iterator begin()
{
return indirection_iterator{m_element_begin,
m_element_end,
m_index_begin,
m_index_end,
m_index_begin};
}
indirection_iterator end()
{
return indirection_iterator{m_element_begin,
m_element_end,
m_index_begin,
m_index_end,
m_index_end};
}
auto operator*()
{
auto index = *m_index_current;
check_index(index, m_elements);
auto element_iterator = m_element_begin;
std::advance(element_iterator, index);
return *element_iterator;
}
void operator++()
{
std::advance(m_index_current, 1);
}
bool operator!=(indirection_iterator const& other)
{
return m_index_current != other.m_index_current;
}
private:
indirection_iterator(ForwardIterator element_begin,
ForwardIterator element_end,
IndexIterator index_begin,
IndexIterator index_end,
IndexIterator index_current)
:
m_element_begin{element_begin},
m_element_end{element_end},
m_index_begin{index_begin},
m_index_end{index_end},
m_index_current{index_current},
m_elements{std::distance(element_begin, element_end)}
{}
template<typename IndexIter>
using index_type = typename std::iterator_traits<IndexIter>::value_type;
ForwardIterator m_element_begin;
ForwardIterator m_element_end;
IndexIterator m_index_begin;
IndexIterator m_index_end;
IndexIterator m_index_current;
typename std::iterator_traits<ForwardIterator>::difference_type m_elements;
template<typename index_type>
void check_index(index_type index, size_t elements)
{
if (index < 0)
{
std::stringstream ss;
ss << "index(" << index << ") < 0";
throw std::runtime_error{ss.str()};
}
if (index >= elements)
{
std::stringstream ss;
ss << "index(" << index << ") >= elements(" << elements << ")";
throw std::runtime_error{ss.str()};
}
}
};
static std::vector<char> get_alphabet() {
std::vector<char> alphabet;
for (char ch = 'a'; ch <= 'z'; ch++)
{
alphabet.push_back(ch);
}
for (char ch = 'A'; ch <= 'Z'; ch++)
{
alphabet.push_back(ch);
}
alphabet.push_back(',');
alphabet.push_back(' ');
alphabet.push_back('!');
alphabet.push_back('\n');
return alphabet;
}
int main(int argc, const char * argv[]) {
std::vector<char> alphabet = get_alphabet();
std::list<int> char_indices =
{ 33, 4, 11, 11, 14, 52, 53, 48, 14, 17, 11, 3, 54, 55 };
indirection_iterator<decltype(alphabet.cbegin()),
decltype(char_indices.cbegin())> indirection_iter {
alphabet.cbegin(),
alphabet.cend(),
char_indices.cbegin(),
char_indices.cend()
};
std::copy(indirection_iter.begin(),
indirection_iter.end(),
std::ostream_iterator<char>{std::cout});
return 0;
}
Critique request
I would like to hear how to make it more usable and how to make it more idiomatic C++.
private
. \$\endgroup\$ – nwp Jan 17 '18 at 20:23