如何实现随机访问迭代器的元素序列,少于1个字节?

时间:2017-07-14 17:30:51

标签: c++11 iterator random-access

如何为少于一个字节的元素序列实现随机访问迭代器?例如,6位。

用于说明我想要的代码示例:

template<typename T>
class val_iterator : std::iterator<std::random_access_iterator_tag, T>
{
    // ???
};

template<typename T>
class val_container
{
    void *_data;
public:
    val_container(void *data): _data(data) {}
    // ???
};

int main()
{
    std::vector<unsigned int> vec =
    {
        0xA, // '00 1010'
        0xE, // '00 1110'
        0x1F,// '01 1111'
        0x3F // '11 1111'
    };

    // 4 elements of 6 bits
    const size_t byte_count = 4 * 6 / 8;

    std::array<unsigned char, byte_count> bytes;
    val_container<unsigned char> values(bytes.data());

    val_iterator<unsigned char> val_it = values.begin();
    for(auto it = vec.begin(); it != vec.end(); ++it, ++val_it)
        *val_it = (unsigned char) *it;

    // elements:
    // '00 1010'_'00 1110'_'01 1111'_'11 1111'
    // bytes in memory:
    // '0010 1000'_'1110 0111'_'1111 1111'
    assert(bytes[0] == 0x28);  // '0010 1000'
    assert(bytes[1] == 0xE7);  // '1110 0111'
    assert(bytes[1] == 0xFF);  // '1111 1111'

    assert(values[0] == 0xA);  // '00 1010'
    assert(values[1] == 0xE);  // '00 1110'
    assert(values[2] == 0x1F); // '01 1111'
    assert(values[3] == 0x3F); // '11 1111'
    return 0;
}

或者它不是随机访问迭代器,而是另一类迭代器?

我还认为这个字节序列由每个重复三个字节的块组成,每个块存储4个元素。也许它可以在迭代器中使用,但我还不知道如何。

编辑: 我对字节数组中位的位置不对。在little-endian位中将看起来像这样:image

并且,我尝试实现这个迭代器/容器。我使用此code作为开始,并使用std::vector<bool>来实现写入值的引用。

template<size_t TBitCount, typename TDataType, typename TValueType>
struct val_help
{
    static size_t vals_count_in_block;
    static size_t bytes_count_in_block;
    static TValueType max_value;

    static bool init()
    {
        static bool inited = false;
        if(inited)
            return true;

        constexpr size_t value_size_in_bits = sizeof(TValueType) * 8;
        static_assert(TBitCount >= 1, "TBitCount must be at least 1 bit");
        static_assert(TBitCount <= value_size_in_bits, "TValueType doesn't have enough bit");
        static_assert(sizeof(TDataType) == 1, "sizeof TDataType must be 1 byte");

        size_t bits = 0;
        size_t data_size_in_bits = sizeof(TDataType) * 8;
        do {
            vals_count_in_block++;
            bits += TBitCount;
        } while (bits % data_size_in_bits != 0);

        bytes_count_in_block = bits / 8;

        inited = true;
        return true;
    }

    static size_t get_byte_idx(size_t val_idx)
    {
        return val_idx * bytes_count_in_block / vals_count_in_block;
    }

    static size_t get_lo_shift(size_t val_idx)
    {
        return (val_idx * TBitCount) % 8;
    }
};

template<size_t TBitCount, typename TDataType, typename TValueType>
size_t val_help<TBitCount, TDataType, TValueType>::vals_count_in_block = 0;

template<size_t TBitCount, typename TDataType, typename TValueType>
size_t val_help<TBitCount, TDataType, TValueType>::bytes_count_in_block = 0;

template<size_t TBitCount, typename TDataType, typename TValueType>
TValueType val_help<TBitCount, TDataType, TValueType>::max_value = (1 << TBitCount) - 1;

template<size_t TBitCount, typename TDataType, typename TValueType>
class val_reference
{
    using h = val_help<TBitCount, TDataType, TValueType>;
    TDataType* _data;
    size_t _val_idx;

public:
    val_reference() : _data(nullptr), _val_idx(0) {}
    val_reference(TDataType* data, size_t val_idx) : _data(data), _val_idx(val_idx) {}

    operator TValueType() const
    { return get_value(); }

    val_reference& operator=(TValueType val)
    {
        set_value(val);
        return *this;
    }

    val_reference& operator=(const val_reference rhs) const
    { return *this = TValueType(rhs); }

    bool operator==(const val_reference rhs) const
    { return TValueType(*this) == TValueType(rhs); }

    bool operator<(const val_reference rhs) const
    { return TValueType(*this) < TValueType(rhs); }

    // ToDo other operation

    size_t idx() const
    { return _val_idx; }
private:

    TValueType get_value() const
    {
        size_t byte_idx = h::get_byte_idx(_val_idx);
        auto ptr_to_val = reinterpret_cast<TValueType*>(_data + byte_idx);
        size_t lo_shift = h::get_lo_shift(_val_idx);
        size_t hi_shift = sizeof(TValueType) * 8 - lo_shift;

        bool is_hi_valid = byte_idx + sizeof(TValueType) < h::bytes_count_in_block;
        auto lo = *ptr_to_val >> lo_shift;
        auto hi = is_hi_valid ? *(ptr_to_val + 1) << hi_shift : 0;

        return (hi | lo) & h::max_value;
    }

    void set_value(TValueType value)
    {
        auto val = value & h::max_value;

        size_t byte_idx = h::get_byte_idx(_val_idx);
        auto ptr_to_val = reinterpret_cast<TValueType*>(_data + byte_idx);
        size_t lo_shift = h::get_lo_shift(_val_idx);
        size_t hi_shift = sizeof(TValueType) * 8 - lo_shift;

        TValueType &lo = *ptr_to_val;
        lo = (lo & ~(h::max_value << lo_shift)) | (val << lo_shift);

        bool is_hi_valid = byte_idx + sizeof(TValueType) < h::bytes_count_in_block;
        if(is_hi_valid)
        {
            TValueType &hi = *(ptr_to_val + 1);
            hi = (hi & ~(h::max_value >> hi_shift)) | (val >> hi_shift);
        }
    }
};

template<size_t TBitCount, typename TDataType, typename TValueType>
class val_iterator : public std::iterator<std::random_access_iterator_tag, TValueType>
{
    using h = val_help<TBitCount, TDataType, TValueType>;
    TDataType *_data;
    size_t _val_idx;

public:
    using reference = val_reference<TBitCount, TDataType, TValueType>;
    using pointer = val_reference<TBitCount, TDataType, TValueType>*;
    using iterator = val_iterator<TBitCount, TDataType, TValueType>;
    using difference_type = int;

    val_iterator(TDataType* data) : _data(data), _val_idx(0){}
    val_iterator(TDataType* data, unsigned int val_idx) : _data(data), _val_idx(val_idx){}
    val_iterator(const iterator& rhs) : _data(rhs._data), _val_idx(rhs._val_idx) {}

    iterator& operator=(const iterator& rhs)
    {
        _data = rhs._data;
        _val_idx = rhs._val_idx;
        return *this;
    }

    reference operator*() const
    { return reference(_data, _val_idx); }

    reference operator[](const difference_type& n) const
    { return *(*this + n); }

    iterator& operator++()
    {
        if(_val_idx == h::vals_count_in_block - 1)
        {
            _data += h::bytes_count_in_block;
            _val_idx = 0;
        }
        else
        {
            ++_val_idx;
        }
        return *this;
    }

    iterator& operator--()
    {
        if(_val_idx == 0)
        {
            _data -= h::bytes_count_in_block;
            _val_idx = h::vals_count_in_block - 1;
        }
        else
        {
            --_val_idx;
        }
        return *this;
    }

    iterator operator++(int)
    {
        iterator tmp(*this);
        ++(*this);
        return tmp;
    }

    iterator operator--(int)
    {
        iterator tmp(*this);
        --(*this);
        return tmp;
    }

    iterator& operator+=(const difference_type& n)
    {
        auto idx = _val_idx + n;
        _data += (idx / h::vals_count_in_block) * h::bytes_count_in_block;
        _val_idx = idx % h::vals_count_in_block;
        return *this;
    }

    iterator operator+(const difference_type& n) const
    {
        iterator tmp(*this);
        tmp += n;
        return tmp;
    }

    iterator& operator-=(const difference_type& n)
    {
        if(n <= _val_idx)
        {
            _val_idx -= n;
            return *this;
        }

        auto diff_idx = (n % h::vals_count_in_block) - _val_idx;
        auto idx = n - diff_idx;
        _data -= (idx / h::vals_count_in_block + 1) * h::bytes_count_in_block;
        _val_idx = h::vals_count_in_block - diff_idx;
        return *this;
    }

    iterator operator-(const difference_type& n) const
    {
        iterator tmp(*this);
        tmp -= n;
        return tmp;
    }

    bool operator==(const iterator& rhs) const
    {
        return _data == rhs._data && _val_idx == rhs._val_idx;
    }

    bool operator!=(const iterator& rhs) const
    {
        return !(*this == rhs);
    }

    bool operator<(const iterator& rhs) const
    {
        return _data == rhs._data
            ? _val_idx < rhs._val_idx
            : _data < rhs._data;
    }

    bool operator<=(const iterator& rhs) const
    {
        return *this < rhs || *this == rhs;
    }

    bool operator>(const iterator& rhs) const
    {
        return !(*this <= rhs);
    }

    bool operator>=(const iterator& rhs) const
    {
        return !(*this < rhs);
    }
};

template<size_t TBitCount, typename TDataType, typename TValueType,
    typename std::enable_if<std::is_integral<TValueType>::value, int>::type = 0>
class val_container
{
    using h = val_help<TBitCount, TDataType, TValueType>;
    TDataType* _data;
    size_t _size;

public:
    using value_type = TValueType;
    using size_type = size_t;
    using difference_type = int;
    using reference = val_reference<TBitCount, TDataType, TValueType>;
    using const_reference = TValueType;
    using pointer = val_reference<TBitCount, TDataType, TValueType>*;
    using const_pointer = const TValueType*;
    using iterator = val_iterator<TBitCount, TDataType, TValueType>;
    using const_iterator = val_iterator<TBitCount, TDataType, const TValueType>;

    val_container(TDataType* data = nullptr, size_t size = 0) : _data(data), _size(size)
    {
        static_assert(sizeof(TDataType) == 1, "sizeof TDataType must be 1 byte");
        static_assert(TBitCount >= 1, "TBitCount must be at least 1 bit");

        static volatile bool s = h::init();

        if(size % h::bytes_count_in_block != 0)
            throw std::invalid_argument(std::string("size: ") +  std::to_string(size)+ " % " + std::to_string(h::bytes_count_in_block) + " != 0");
    }
    val_container(TDataType& data, size_t size) : val_container(&data, size) {}

    iterator begin()
    { return iterator(_data); }

    const_iterator begin() const
    { return const_iterator(_data); }

    const_iterator cbegin() const
    { return const_iterator(_data); }

    iterator end()
    { return iterator(_data + _size); }

    const_iterator end() const
    { return const_iterator(_data + _size); }

    const_iterator cend() const
    { return const_iterator(_data + _size); }

    size_type size() const
    { return _size; }

    bool empty() const
    { return begin() == end(); }

    reference operator[](size_type n)
    { return *(iterator(_data) + n); }

    const_reference operator[](size_type n) const
    { return *(const_iterator(_data) + n); }
};

适用于任何TBitCount(我认为)。 这段代码可以改进什么?

1 个答案:

答案 0 :(得分:1)

首先,要实现随机访问迭代器,您需要实现实现RandomAccessIterator概念所需的操作。

你需要为整数和复合赋值实现加法和减法,减去两个迭代器,下标运算符,少于/比运算符更强,更严格和非严格,(in-)相等运算符,pre-和后递增和-decrement,dereference和箭头操作符。

请参阅迭代器概念的文档,了解每个操作的确切所需行为。

其次,您必须考虑迭代器返回的元素小于字节大小的事实。显然,由于一个字节是最小的可寻址对象,因此您不能引用这种较小的对象(或者更确切地说,这样的对象片段)。

您可以使用自定义代理类作为val_iterator::reference。代理的行为应该是在分配时修改引用的位,并且可以隐式转换为引用的位范围的值。这与std::vector<bool>::reference的实施方式相同。