5

How can I overload the operators of a class, so that using syntax of

classInstance[index] = value;

performs

classInstance.cfgfile.Write(index,value)

background info; feel free to skip.

The application we develop uses a memory-mapped access to a segment of NVRAM - actually, mapped are just two registers, address and data. You write to the address register, then either write or read the data register. After initialization, the reads and writes are performed by a simple [] overload of the class holding the reference to the segment of memory. You refer to the instance's [] giving a namespaced index of the cell you want to read and write and it does its thing.

int& IndirectMemory::operator[](RTCMemIndex idx)
{
    *midx_reg = idx;
    return *mdata_reg;
}

(code stripped of irrelevant elements like mutexes and sanity checks).

Everything works fine... as long as the NVRAM works fine. This specific chip is out of production, and the ones 'out in the wild' began dying of old age currently. Their functionality is of low significance to our use, and we could shift their role to the flash memory with nearly no impact (just a little more flash wear) if the chip goes corrupt. Thing is we want to use the flash record using our config format, which uses getters and setters.

int TCfgFile::ReadKey(std::string Key);
void TCfgFile::WriteKey(std::string Key,int data);

And in many places of the code we have calls to NVRAM through IndirectMemory[Some_Register] = Some_Value; writting assorted things that change frequently and we want to persist through reboot. I'd like to retain this syntax and behavior, but be able to write to the file if NVRAM is detected to be corrupted or manually disabled through a config entry.


The net is rife with examples of using operator[] for setting given value just by returning the reference to it. For example:

unsigned long operator [](int i) const    {return registers[i];}
unsigned long & operator [](int i) {return registers[i];}

In that case if I call, say, reg[3] = 1; the [] will return a reference to the element#3 and the default operator= will write to the reference just fine.

But since I can't return a reference to a key in the file (.WriteKey() just performs a complete write, returning success or error), and operator= doesn't take an index, I'm afraid this simple option won't help.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
SF.
  • 13,549
  • 14
  • 71
  • 107
  • 3
    Proxy class. You make `classInstance[index]` return an instance of a helper class with overloaded `operator=`. That class should capture enough information to be able to call `classInstance.cfgfile.Write(index,value)` from its `operator=(value)` – Igor Tandetnik Jan 21 '19 at 15:00

2 Answers2

4

You can use a proxy class to solve this. Since value can't be passed into classInstance we need to make an object that operator[] can return that will get the value of value and knows which instance to apply the operation to. Using

struct Proxy
{
    classInstance_type& to_apply;
    index_type index;
    Proxy(classInstance_type& to_apply, index_type index) : to_apply(to_apply), index(index) {}
    Proxy& operator=(value_type const & value)
    {
        to_apply.cfgfile.Write(index,value)
        return *this;
    }
};

your class's operator[] would look like

Proxy operator[](index_type index)
{
    return Proxy{*this, index};
}

and then when you do classInstance[index] = value; you call Proxy's operator= which has a reference to the object to call, the index to use, and the value you also need.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • If you want to go the other way - `value = classInstance[index];` - simply give the `Proxy` class an `operator value_type()` conversion operator that reads from the referenced object. – Remy Lebeau Jan 21 '19 at 22:18
1

You can also do this without a proxy class. You can make operator[] return a reference to *this and than overload the = operator of said class to perform Write on whatever was given to operator= in the second argument.

#include <iostream>

struct Foo {
    void Write(int idx, int value) {
        std::cout << "Write(" << idx << ", " << value << ")\n";
    }

    Foo& operator[](int idx) {
        this->index = idx;
        return *this;
    }
    void operator=(int value) {
        this->Write(this->index, value);
    }

    int index;
};

int main() {
    Foo f;
    f[5] = 10;
}

Prints: Write(5, 10)

Stack Danny
  • 7,754
  • 2
  • 26
  • 55