How else can I access a bit field members?

Viewed 386

So I understand that you can't have pointers to bit-fields because the pointers can only distinguish addresses to the byte level, not bit level. References to bit-fields are also not allowed. Are there any other ways that I would be able to reference the members of the bit field indirectly? Ideally I would be able to access them following using array syntax similar to the mys1array line below. I know arrays of references are illegal but perhaps someone has some sage knowledge out there about some other mechanisms which could achieve a similar goal.

typedef struct{
    unsigned short a : 5;
    unsigned short b : 3;
    unsigned short c : 8;
}myStruct;

class myClass{
public:
    myStruct s1;
    //unsigned short &mys1array[] = {&s1.a, &s1.b ,&s1.c};
};
2 Answers

You could use an array of function pointers that are initialized by lambdas to access each element of the bitfield with the different functions.

class myClass {
public:
    myStruct s1;
    static constexpr unsigned short (*accessors)(myStruct const &s)[] = {
        +[](myStruct const &s) -> unsigned short { return s.a; }
        // ...
    };
};

With this you have to pass an instance of myStruct to the functions. Another method is using std::function and use capturing lambdas:

class myClass {
public:
    myStruct s1;
    std::function<unsigned short()> accessors[3];

    myClass(myStruct s)
        : s1(s),
          accessors{
              [this]() -> unsigned short { return this->s1.a; },
              // ...
          }
    {}

    // ...
};

Don't forget that with this, you have to implement copy and move constructors and assignment operators, as the lambda captures this.

You can only access bit fields through the class. You can get indirection by having a pointer or reference to the enclosing class object.

You could write a custom iterator if you wanted to iterate the bitfields within the class, but implementation of such iterator may require some explicit hard-coding since C++ lacks reflection capabilities to automate it. Here is an incomplete proof-of-concept:

struct myStruct {
    unsigned short a : 5;
    unsigned short b : 3;
    unsigned short c : 8;

    struct reference {
        myStruct* parent;
        unsigned char field;
        
        operator unsigned short() {
            switch(field) {
                case 0: return parent->a;
                case 1: return parent->b;
                case 2: return parent->c;
                default: assert(false);
            }
        }
        
        reference& operator=(unsigned short u) {
            switch(field) {
                case 0: parent->a = u; return *this;
                case 1: parent->b = u; return *this;
                case 2: parent->c = u; return *this;
                default: assert(false);
            }
        }
        
        void operator++() {
            ++field;
        }
        
        friend auto operator<=>(const reference&, const reference&) = default;
    };
    
    struct iterator
    {
        //TODO add missing member definitions, const overloads etc.
        
        reference current;
        reference operator*() {
            return current;
        }
        void operator++() {
            ++current;
        }
        
        friend auto operator<=>(const myStructIterator&, const myStructIterator&) = default;
    };
    
    iterator begin() {
        return {this, 0};
    }
    
    iterator end() {
        return {this, 3};
    }
};

int main()
{
    myStruct s {};
    for(int i=3; auto f : s) {
        f = i++;
    }
    for(auto f : s) {
        std::cout << f << '\n';
    }
}

The reference class is sufficient to represent indirection for the bit fields and the iterator allows treating the fields as an iterable range.

Related