user957121
user957121

Reputation: 3076

Questions on some code using boost::zip_iterator

Recently I saw some example code on how to use boost::zip_iterator. However, I can't figure out how it works. Here is the code:

class to_hex2
{
private:
    vector<unsigned char> &v;
    char trans(const char c) const
    {
        if(c >= 'a')
            return c - 'a' + 10;
        else if(c >= 'A')
            return c - 'A' + 10;
        else
            return c - '0';
    }
public:
    to_hex2(vector<unsigned char> &_v): 
        v(_v){}

    typedef boost::tuples::tuple<const char&,const char&> Tuple;
    void operator()(Tuple const &t) const   
    {
        static char tmp;    
        tmp = trans(t.get<0>()) * 0x10;
        tmp += trans(t.get<1>());
        v.push_back(tmp);
    }
};

int main()
{
    char s[] = "1234aBcD";
    vector<unsigned char> v;
    typedef step_iterator<const char*> si_t;    
    for_each(
                boost::make_zip_iterator(
                    boost::tuples::make_tuple(si_t(s),si_t(s+1))),  
                boost::make_zip_iterator(
                    boost::tuples::make_tuple(si_t(s+8),si_t(s+9))),    
                to_hex2(v));
    std::copy(
                v.begin(),v.end(),std::ostream_iterator<unsigned char>(cout," "));
    std::cout<<std::endl<<"v.size="<<v.size();
    return 0;
}

step_iterator is an iterator that iterates two steps instead of one.

My first question is: Is it OK to write s+9 since the index of array s is up to 8(including '\0':-) )? The code seems to run properly although.

My second question is: Since zip_iterator makes it possible to iterate over a vector concurrently, does that mean the result is random? The result I see is constant, in the following picture: enter image description here

Last but not least, could someone please tell me how is the result generated( what's the meaning of it) since there is no up-down arrow in ASCII codes( I googled it and saw it here).

Upvotes: 1

Views: 557

Answers (1)

Luc Touraille
Luc Touraille

Reputation: 82171

It is ok to point one-past-the-end of an array, as long as you don't dereference the pointer. This is very useful because C++ uses half-open ranges, where the last element is excluded.

In the code you posted, s+9 points one-past-the-end of s, but is never dereferenced, so the behavior is well-defined.

Regarding your second question: no, the result of this code is not random. The elements will be iterated over in order, from first to last. When the documentation states that zip_iterator allows parallel iteration over a sequence, it does not mean that the iteration will be performed concurrently by several threads or whatever, it only means that each iteration will advance several iterators instead of only one. Here is a possible implementation of for_each:

template <typename InputIterator, typename Func>
void for_each(InputIterator first, InputIterator last, Func f)
{
    while (first != last)
    {
        f(*first);
        ++first;
    }
}

As you see, for_each works on a single iterator. If you need to iterate over two sequences at a time, then you can use zip_iterator, which encapsulates several iterators. Its operator* returns multiple values (a tuple), and its operator++s increments all the iterators, advancing them simultaneously.

To better understand what is going on in your code, here is a streamlined version, without zip_iterator and for_each:

class to_hex2
{
private:
    vector<unsigned char> &v;
    char trans(const char c) const
    {
        if(c >= 'a')
            return c - 'a' + 10;
        else if(c >= 'A')
            return c - 'A' + 10;
        else
            return c - '0';
    }
public:
    to_hex2(vector<unsigned char> &_v): 
        v(_v){}

    void operator()(const char &first, const char &second) const   
    {
        static char tmp;    
        tmp = trans(first) * 0x10;
        tmp += trans(second);
        v.push_back(tmp);
    }
};

int main()
{
    char s[] = "1234aBcD";
    vector<unsigned char> v;

    to_hex2 transformer(v);

    char *first = s;
    char *second = s + 1;

    for ( ; first != s + 8 && second != s + 9 ; first += 2, second += 2)
    {
        transformer(*first, *second);
    }

    std::copy(v.begin(),v.end(),
              std::ostream_iterator<unsigned char>(cout," "));
    std::cout<<std::endl<<"v.size="<<v.size();
    return 0;
}

Hopefully, this should make it clear that zip_iterator is just a convenient way of making several iterators advance at the same time.

Finally, to understand the purpose of this code, you should probably print the result as integers rather than as characters. You should see this:

18 52 171 205

which are the decimal representation of the hexadecimal numbers contained in the original string (1216 = 1810, 3416 = 5210, AB16 = 17110 and CD16 = 20510). So basically, v contains the representation in base 256 of the original hexadecimal string.

Upvotes: 1

Related Questions