Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ overload[] with transformation

I have a C datastructure representing a vector of boolean values; for reasons outside of my control the bools' are stored internally as integers with two magical values (not 0 and 1 ...) representing true and false. I have created a C++ class wrapping this C structure, and it works nicely. I have implemented the set()and get()methods as:

void Class::set(size_t index , bool value) {
     if (value)
        c_ptr[index] = SPECIAL_TRUE_VALUE;
     else
        c_ptr[index] = SPECIAL_FALSE_VALUE;
}

This works ok; but ideally I would like to overload operator[], however it is not clear to me how/if I can do that - due to special transformation between bool and the integer values?

like image 504
user422005 Avatar asked May 06 '15 18:05

user422005


2 Answers

struct pseudo_reference {
  operator bool()const&&{
    return c->get(index);
  }
  pseudo_reference operator=(bool b)&&{
    c->set(index, b);
    return {c,index};
  }

  // sometimes having named functions is useful:
  bool get() const&& {
    return std::move(*this);
  }
  void set(bool b)&& {
    std::move(*this) = b;
  }
  pseudo_reference()=delete;
private:
  Class* c;
  size_t index;
  pseudo_reference(pseudo_reference&&o)=default; // not exposed
  pseudo_reference(Class* pc, size_t i):c(pc),index(i){}
  friend class Class;
};

In Class:

pseudo_reference operator[](size_t i){
  return {this, i};
}
bool operator[](size_t i)const{
  return c_ptr[index] == SPECIAL_TRUE_VALUE;
}

I stored both a pointer and an index, so I avoid reimplementing the logic of get/set in my pseudo_reference. Such pseudo_references are likely to be short-lived, so size optimization probably isn't important.

I blocked all non-rvalue operations to discourage storing a pseudo_reference. You can make said operations non-rvalue restricted relatively harmlessly, but in my experience pseudo_references are values that behave like references, so it is better if they don't persist.

Someone can still store a pseudo_reference via auto&& x = c[33];, but using it without moveing it won't be possible. Hopefully that catches most error-prone uses of it. auto x = c[33]; won't work.

like image 89
Yakk - Adam Nevraumont Avatar answered Sep 22 '22 13:09

Yakk - Adam Nevraumont


To implement operator[](), you need to return a proxy object that does the actual assignment when it appears on the left-hand-side of =:

struct proxy {
    proxy& operator=( bool value ) {
        c_.c_ptr[ index_ ] = value ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE;
        return *this;
    }
    operator bool() const { // for when it's just used normally, not =
        return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
    }
private:
    Class &c_;
    size_t const index_;
    proxy( Class &c, size_t index ) : c_( c ), index_( index ) { }
    friend class Class;
}

class Class {
public:
    proxy operator[]( size_t index ) {
        return proxy( *this, index );
    }
    bool operator[]( size_t index ) const { // read-only access is easy
        return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
    }
    // ...
};

Or something like that.

like image 41
Paul J. Lucas Avatar answered Sep 22 '22 13:09

Paul J. Lucas