I am creating wrappers for C structs containing register definitions with heavy use of bitfields. I would like to create reference getters for all of them, to provide consistent shorthand API (real structures may use deep nesting with long names only a HW engineer would love).
For regular values, returning a reference is easy, but for bitfields, I had to create a wrapper similar to std::bitset::reference
:
#include <cstdint>
#include <cstdio>
class storage {
public:
auto get_x() { return x_ref{ *this }; }
auto get_y() { return y_ref{ *this }; }
auto& get_z() { return z; }
private:
uint8_t x : 3;
uint8_t y : 5;
uint8_t z;
struct x_ref {
storage& store;
operator uint8_t() { return store.x; }
auto& operator=(uint8_t value) { store.x = value; return *this; }
};
struct y_ref {
storage& store;
operator uint8_t() { return store.y; }
auto& operator=(uint8_t value) { store.y = value; return *this; }
};
};
int main(int argc, char **argv) {
storage s{};
s.get_x() = 3;
s.get_y() = 5;
s.get_z() = 7;
uint8_t x = s.get_x(), y = s.get_y(), z = s.get_z();
std::printf("%u, %u\n", x, y, z);
}
Is there any way to create x/y_ref
as a generic template? I would like to be able to write something like auto get_x() { return ref<&storage::x>{ *this }; }
which is not a valid syntax for bitfields. std::bitset::reference
does not have this problem, since it always stores a reference to uint8_t&
(or larger word) and index, so it doesn't have anything to template on.
Here is one way to do it, but as Jarod42 said the lambdas are about as long as writing a struct helper.
I intentionally made the assignment return a void
rather than the conventional return *this;
. Chaining assignment is probably not the right thing to do here.
#include <cstdint>
#include <cstdio>
template <typename GET, typename SET>
struct var_ref {
GET getter;
SET setter;
using store_t = decltype(getter());
var_ref(GET g, SET s) : getter{g}, setter{s} {}
// Using implicit conversion as the getter.
operator store_t() { return getter(); }
// Using void= as the setter.
void operator=(store_t value) { setter(value); }
};
class storage {
uint8_t x : 3;
uint8_t y : 5;
uint8_t z;
public:
auto get_x() { return var_ref{[this]{ return x; }, [this](uint8_t value) { x = value; }}; }
auto get_y() { return var_ref{[this]{ return y; }, [this](uint8_t value) { y = value; }}; }
auto& get_z() { return z; }
};
int main() {
storage s{};
s.get_x() = 3;
s.get_y() = 5;
s.get_z() = 7;
uint8_t x = s.get_x(), y = s.get_y(), z = s.get_z();
std::printf("%u, %u, %u\n", x, y, z);
}