3

I have written a templates class for storing multiple bools in an integer. Right now, setting and getting each bool is done with explicit functions

    bool isBitSet(int index)
    {
        return static_cast<bool>((block_ >> index) % 2)
    }

    void setBitOn(int index)
    {
        block_ |= 1 << index;
    }

I believe that the following would work for getting a value, but how would setting work since we can't directly return a reference for a bit?

    const bool operator [] (int index) const 
    {
        return static_cast<bool>((block_ >> index) % 2);
    }
chew socks
  • 1,406
  • 2
  • 17
  • 37
  • And all other uses of the subscript operator goes kaboom! – Mark Garcia Feb 11 '13 at 06:49
  • It's not a good idea to overload an operator for something else that the regular types do. – Rapptz Feb 11 '13 at 06:51
  • 2
    Why wouldn't you just use `std::bitset` instead of making one? – chris Feb 11 '13 at 06:52
  • @Rapptz - This is all inside a class, so shouldn't there be no affect when [] is called by something of a different class? – chew socks Feb 11 '13 at 06:54
  • 4
    Return something that is not a reference to a bit but behaves like one (has a conversion to `bool` and an overloaded assignment that does the right thing). – n. m. could be an AI Feb 11 '13 at 06:55
  • @chris - simply because the implementation interested me and it turned out not to be too difficult to do. So basically, as an exercise. – chew socks Feb 11 '13 at 06:55
  • 1
    @chewsocks I meant as a general practice. It's not a good idea to overload `operator[]` to do something else other than what it actually does (accessing an array element). If you want to do something else you can do `operator()` instead. This is talked about in Effective C++, I believe. – Rapptz Feb 11 '13 at 06:59
  • @Rapptz Do you avoid using `std::vector`, then, too? The class in this question is just as array-like as that. –  Feb 11 '13 at 07:02
  • @Rapptz - OK, I see. Is the implementation exactly the same, besides changing the brackets to parentheses? – chew socks Feb 11 '13 at 07:04
  • @hvd yeah I *hate* std::vector. And yes chewsocks, it's exactly the same except changing the brackets to parentheses. – Rapptz Feb 11 '13 at 07:05
  • @n.m. - I agree that would be the way to go, but I don't know howto link accessing operator and the assignment operator. – chew socks Feb 11 '13 at 07:06
  • @hvd - How did you come to the conclusion that Rapptz does not like `std::vector`? – chew socks Feb 11 '13 at 07:07
  • @chewsocks `std::vector` provides `operator[]` with the same meaning as the class in this question. It doesn't *actually* return an array element, but it behaves close enough to it for `operator[]` to be the most sensible operator to use. –  Feb 11 '13 at 07:08
  • `std::vector::operator[]` returns a reference to the element specified. – Rapptz Feb 11 '13 at 07:10
  • I'm trying to read through the `stl_vector.h` source, but I'm admittedly not following it too well. – chew socks Feb 11 '13 at 07:14
  • *your class accessing operator* returns an object of another class that overloads *an assignment operator of that other class*. That's the link. – n. m. could be an AI Feb 11 '13 at 07:39
  • @Rapptz Firstly, that's not what you said. You said `operator[]` should access an array element. Secondly, `std::vector::operator[]` doesn't return a reference to `bool`, it returns a special helper class exactly like what you oppose here. –  Feb 11 '13 at 07:51
  • @hvd I'm sick of discussing with you and it seems you failed to understand what I meant or I have failed at interpreting it properly and in plain English, so instead I will [kindly point you to what I meant in a longer manner](http://stackoverflow.com/a/4421708/1381108). – Rapptz Feb 11 '13 at 08:05
  • @Rapptz See Csq's answer, as far as the user is concerned, it *does* behave like an array of `bool`s (and it was clear from the question that it would), so I don't see any problem with using `operator[]` for it. Your longer explanation says the same thing, reversed, roughly: don't use `operator[]` if it doesn't act like the built-in `[]` operator. That doesn't apply here. So what is your problem, exactly? –  Feb 11 '13 at 08:36

1 Answers1

6

The same is done in std::vector<bool> and in std::bitset in the standard library. As stated in the reference, std::vector<bool> it returns a proxy class that has its operators overloaded to act as an element of the vector.

You could to that as well.

For a user-friendly example see again the reference for a public interface, it is something like this:

template <class Allocator>
class vector<bool, Allocator> {
  // ...
  public:
    class reference {
        friend class vector;
        reference();
      public:
        ~reference();
        operator bool() const;
        reference& operator=(bool x);
        reference& operator=(const reference&);
        void flip();
    };
  // ...
};

To implement this class you should store a member pointer to your actual data block and a mask to operate with.

For a real example, in the g++ headers look for member class of std::vector<bool> called std::vector<bool>::_Bit_reference in the file bits/stl_bvector.h.


To clarify the OP with an example:

Let's say you have a class containing 320 bools. You could write it as:

class boolcontainer {
  uint32_t data[10];
public:
  //default ctor. to initialize the elements with zeros
  boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
}

You want to add an operator[]. To add a const one is easy:

class boolcontainer {
  uint32_t data[10];
public:
  bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
}

to have a non-const one you need much more. First you need to create a class that represents a reference to your value. You must have some kind of pointer to where the value is stored and (in this case) you need a bitmask to specify one concrete bit. To be able to handle this as a bool& you need to add some operators, namely conversion to bool and operator=:

class reference {
  uint32_t *dataptr;
  uint32_t mask;
public:
  //constructor just initializing members
  reference(uint32_t *dataptr_, uint32_t mask_) : dataptr(dataptr_), mask(mask_) {}

  //conversion to bool
  operator bool() const {
    //just like in the getter, but the bitmask is stored now locally
    return *dataptr & mask;
  }

  //sets one single bit represented by mask to b
  reference& operator=(bool b) {
    if (b) {
      *dataptr |= mask;
    } else {
      *dataptr &= ~mask;
    }
    return *this;
  }

  //TODO copy ctor., operator==, operator<
};

Note that the above struct will behave as a bool& -- reading from it reads the value from the data point represented by the pointer and the mask, and similarly, writing to it overwrites the bit at the represented location. I also wrote a constructor that initializes the members.

Now all you need is that your boolcontainer's operator[] should return an object of the above class:

class boolcontainer {
  uint32_t data[10];
public:

  boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }

  class reference {
     ... //see above
  }

  //keep the const version for efficiency
  bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }

  //non-const version returns our reference object.
  reference operator[](int i) { return reference(&data[i/32], 1 << (i%32)); }
};

And now some code to test it (prints only the first 40 values):

#include <iostream>
#include "boolcontainer.h"

void printboolcontainer(const boolcontainer &bc)
{
    //note that this is the constant version
    for (int i = 0; i < 40; ++i) {
        std::cout << bc[i];
    }
    std::cout << std::endl;
}

int main()
{
    boolcontainer bc;
    printboolcontainer(bc);
    bc[0] = true;
    bc[3] = true;
    bc[39] = true;
    printboolcontainer(bc);
}
Csq
  • 5,775
  • 6
  • 26
  • 39