如何为少于一个字节的元素序列实现随机访问迭代器?例如,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(我认为)。 这段代码可以改进什么?
答案 0 :(得分:1)
首先,要实现随机访问迭代器,您需要实现实现RandomAccessIterator概念所需的操作。
你需要为整数和复合赋值实现加法和减法,减去两个迭代器,下标运算符,少于/比运算符更强,更严格和非严格,(in-)相等运算符,pre-和后递增和-decrement,dereference和箭头操作符。
请参阅迭代器概念的文档,了解每个操作的确切所需行为。
其次,您必须考虑迭代器返回的元素小于字节大小的事实。显然,由于一个字节是最小的可寻址对象,因此您不能引用这种较小的对象(或者更确切地说,这样的对象片段)。
您可以使用自定义代理类作为val_iterator::reference
。代理的行为应该是在分配时修改引用的位,并且可以隐式转换为引用的位范围的值。这与std::vector<bool>::reference
的实施方式相同。