I'm trying to create a small wrapper class around a std::vector to represent the coefficients of a polynomial. The caller needs to be able to iterate through the coefficients, but I don't want to expose the underlying implementation.
Using the pattern described here, here, and elsewhere, I've tried to pass along the iterators as below:
typedef std::vector<unsigned char> charVec;
class gf255_poly
{
public:
// Constructors and Polynomial-y Functions
// ...
// Iterators to go from high to low degree
charVec::const_reverse_iterator h2l_begin() const { return p.rbegin(); };
charVec::const_reverse_iterator h2l_end() const { return p.rend(); };
charVec::reverse_iterator h2l_begin() { return p.rbegin(); };
charVec::reverse_iterator h2l_end() { return p.rend(); };
// Iterators to go from low to high degree
charVec::const_iterator l2h_begin() const { return p.begin(); };
charVec::const_iterator l2h_end() const { return p.end(); };
charVec::iterator l2h_begin() { return p.begin(); };
charVec::iterator l2h_end() { return p.end(); };
protected:
std::vector<unsigned char> p;
};
These gf255_poly objects then get used in methods such as this one:
// Performs polynomial evaluation in GF(2^8)
unsigned char gf255_poly_eval(const gf255_poly &poly, unsigned char x) const
{
unsigned char fx = poly.coefHigh(); // Initialize with coef of highest degree term
// Use Horner's method with consecutively factored terms:
// x^3 + 2x^2 + 3x + 4 -> (((1x + 2)x + 3)x + 4)
charVec::reverse_iterator next_coef;
for (next_coef = poly.h2l_begin(); next_coef != poly.h2l_end(); next_coef++)
fx = gf255_mul(fx, x) ^ *next_coef; // Recall ^ is addition in GF 2^8
return fx;
}
Simple though that seems, there's something going wrong with the types. Visual Studio gives me this error on the line with the for loop, which I can't seem to puzzle out:
error C2664: 'std::_Revranit<_RanIt,_Base>::_Revranit(_RanIt)' : cannot convert parameter 1 from 'std::_Vector_const_iterator<_Ty,_Alloc>' to 'std::_Vector_iterator<_Ty,_Alloc>'
I don't understand this message - I've provided methods that return both iterators and const_iterators. Why can't the compiler choose between them?
Implicit in this question is whether this is a good strategy for hiding the details from the caller at all (since they still have to deal with these std::vector types), and I would appreciate answers that also address this.
charVec::reverse_iterator next_coef = poly.h2l_begin();
next_coef
is a reverse_iterator
. What does h2l_begin()
return?
Well, poly
is a:
const gf255_poly &poly
a const gf255_poly
. So let us look at the overrides of h2l_begin()
:
charVec::const_reverse_iterator h2l_begin() const { return p.rbegin(); };
charVec::reverse_iterator h2l_begin() { return p.rbegin(); };
There are two overloads. Only one is valid, because poly
is const
, and that is this one:
charVec::const_reverse_iterator h2l_begin() const { return p.rbegin(); };
so poly.h2l_begin()
returns a charVec::const_reverse_iterator
.
charVec::const_reverse_iterator
cannot be converted to charVec::reverse_iterator
, because charVec::const_reverse_iterator
allows you to modify the thing being iterated over, while charVec::reverse_iterator
does not.
In short, because poly
is const
, you have promised not to modify it. And then you iterated it over it using types that could modify it. So you get a type error.
Second, and as an aside, note that the compiler never chooses between functions based on return type (except arguably the conversion operator T()
). If you had a non-const poly
stored it in a const_reverse_iterator
, you'd still call the reverse_iterator h2l_begin()
. The reverse_iterator
would just convert to a const_reverse_iterator
.
First thing to do is to upgrade to C++11. This is 2016.
Second, I'd write a range_t<Iterator>
which stores a range of iterators and exposes begin
and end
and conditionally (based on random-access-ness of Iterator
operator[]
, size
, etc. Also remove_front(size_t)
and front()
and empty
and ...
Then I'd write array_view<T>:range_it<T*>
which represents a range of contiguous T
s, with implicit ctors from containers with a T* C::data()
method, raw C-arrays, and T*, size_t
.
Finally, I'd write backwards_t
and backwards
function, which takes a range_t<Iterator>
and returns a range_t< reverse_iterator< Iterator > >
.
Now my gf255_poly
has these:
backwards_t< array_view< unsigned char > > h2l();
backwards_t< array_view< unsigned char const > > h2l() const;
array_view< unsigned char > l2h();
array_view< unsigned char const > l2h() const;
We can expose typedefs for the iterators if we choose (and in C++03 we have little choice).
In C++11 it looks like:
for (auto&& next_coef = poly.h2l())
fx = gf255_mul(fx, x) ^ next_coef; // Recall ^ is addition in GF 2^8
which is nice.