patterncppMinor
Array-like container for uints shorter than 8 bits
Viewed 0 times
containeruintsarraybitsthanlikeforshorter
Problem
Follow-up: Array-like container for uints shorter than 8 bits (Rev 1)
First of all: yes, I need this kind of array to generate data for a piece of hardware, and it must be stored and manipulated somewhere. The general consensus regarding bit manipulation seems to be "if you need to do that, there's something wrong".
The array-like container (
Elements are accessed by
Here's the header:
```
#ifndef SFDD_PACKEDBITFIELDARRAY_H
#define SFDD_PACKEDBITFIELDARRAY_H
#include
#include
template
class PackedBitfieldArray
{
// array type for packable bitfields-kinda-types
public:
typedef V value_type;
static constexpr size_t value_bitwidth = 8*sizeof(value_type);
static_assert(Bits > offset_;
}
private:
value_type& data_;
size_t offset_;
};
class const_proxy
{
public:
const_proxy(value_type& data, size_t offset) : data_(data), offset_(offset) {}
operator value_type() const
{
return (data_ & (arg_mask > offset_;
}
private:
const value_type& data_;
size_t offset_;
};
value_type* data() {return data_.data();}
const value_type* data() const {return data_.data();}
proxy operator[](size_t i)
{
size_t i_ = i*Bits/value_bitwidth;
uint8_t offset = i * Bits % value_bitwidth;
return proxy(data()[i_], offset);
}
class iterator : p
First of all: yes, I need this kind of array to generate data for a piece of hardware, and it must be stored and manipulated somewhere. The general consensus regarding bit manipulation seems to be "if you need to do that, there's something wrong".
The array-like container (
PackedBitfieldArray) I'm writing is for uintx_ts, where x = \$2^n\$ (x is an integer power of two). No uintx_t crosses (or may cross) any word boundary of the actual underlying storage type. PackedBitfieldArray is templated with the number of Bits per element, number of Elements (just like std::array) and the default storage type V is uint8_t.Elements are accessed by
proxy and const_proxy objects, and I want to provide an iterator (already included below) and a const_iterator (not yet included because I want to avoid too much duplicate code), for use with algorithms.Here's the header:
```
#ifndef SFDD_PACKEDBITFIELDARRAY_H
#define SFDD_PACKEDBITFIELDARRAY_H
#include
#include
template
class PackedBitfieldArray
{
// array type for packable bitfields-kinda-types
public:
typedef V value_type;
static constexpr size_t value_bitwidth = 8*sizeof(value_type);
static_assert(Bits > offset_;
}
private:
value_type& data_;
size_t offset_;
};
class const_proxy
{
public:
const_proxy(value_type& data, size_t offset) : data_(data), offset_(offset) {}
operator value_type() const
{
return (data_ & (arg_mask > offset_;
}
private:
const value_type& data_;
size_t offset_;
};
value_type* data() {return data_.data();}
const value_type* data() const {return data_.data();}
proxy operator[](size_t i)
{
size_t i_ = i*Bits/value_bitwidth;
uint8_t offset = i * Bits % value_bitwidth;
return proxy(data()[i_], offset);
}
class iterator : p
Solution
This will patch your code:
Now you can try e.g. this in your
Output:
bits = 2, n = 5, [a] = 2
a.arg_mask = 0b00000011
for loop, C-like element access:
a[0] = 0
a[1] = 1
a[2] = 2
a[3] = 3
a[4] = 0
for loop with iterators:
a[...] = 0 (0b00000000)
a[...] = 1 (0b00000001)
a[...] = 2 (0b00000010)
a[...] = 3 (0b00000011)
a[...] = 0 (0b00000000)
for_each with a lambda:
a[...] = 0 (0b00000000)
a[...] = 1 (0b00000001)
a[...] = 2 (0b00000010)
a[...] = 3 (0b00000011)
a[...] = 0 (0b00000000)
underlying data dump:
a[0_] = 228 (0b11100100)
a[1_] = 52 (0b00110100)
That should answer your first two questions: It is OK with this correction.
For the
For
It is your class, if you are fine with
iterator end()
{
return iterator(data_.data(), Elements);
}Now you can try e.g. this in your
main():static const size_t bits = 2;
static const size_t n = 5;Output:
bits = 2, n = 5, [a] = 2
a.arg_mask = 0b00000011
for loop, C-like element access:
a[0] = 0
a[1] = 1
a[2] = 2
a[3] = 3
a[4] = 0
for loop with iterators:
a[...] = 0 (0b00000000)
a[...] = 1 (0b00000001)
a[...] = 2 (0b00000010)
a[...] = 3 (0b00000011)
a[...] = 0 (0b00000000)
for_each with a lambda:
a[...] = 0 (0b00000000)
a[...] = 1 (0b00000001)
a[...] = 2 (0b00000010)
a[...] = 3 (0b00000011)
a[...] = 0 (0b00000000)
underlying data dump:
a[0_] = 228 (0b11100100)
a[1_] = 52 (0b00110100)
That should answer your first two questions: It is OK with this correction.
For the
iterator you can use:- Template (with some SFINEA for iterator->const_iterator)
- Inheritance (common base with the implementation)
- Composition (common struct to place inside as a member) to share some code.
For
operator[] I would not worry about duplication, but you can solve it by const_proxy->proxy private constructor (and friend) and some const_cast to help you. Not nice, I would rather copy three lines and adapt. Again, some templated private helper could solve that as well.It is your class, if you are fine with
forward_iterator, then it is fine. If not, enhance it and use std category tag.Code Snippets
iterator end()
{
return iterator(data_.data(), Elements);
}static const size_t bits = 2;
static const size_t n = 5;Context
StackExchange Code Review Q#62946, answer score: 4
Revisions (0)
No revisions yet.