Tuesday, September 02, 2003

Proxied data members

There's a neat trick I've used for a while when writing C++ code: proxied data members. The idea is straight-forward. The user of your class can write normal-looking access to data member, but behind the scenes your class calls accessor member functions on the user's behalf. I was unable to find a similar technique in Boost but it is nothing new. Proxied data members relies on a helper class which curries an object pointer and member function pointers, applying them as needed via assignment and conversion operators.

The simplest form for a read-only data member is:

#include <iostream>

using namespace std;

template<class P, class T>
class read_only {
  typedef T (P::*read_t) ( ) const;

  P &parent;
  read_t read;

// protected:
public: // no friend template parameters
  read_only (P &parent, read_t read) : parent (parent), read (read) { }

public:
  operator T ( ) const { return (parent.*read) ( ); }
};

class bob_t {
  int i;

  int read_i ( ) const { return i; }

public:
  read_only<bob_t, int> roi;

  bob_t ( ) : i (42), roi (*this, &bob_t::read_i) { }
};

int 
main ( )
{
  bob_t bob;

  // bob.roi = 24; // doesn't compile
  int i = bob.roi;

  cout << i << endl;
}

There are a lot of details which this example glosses over including returning a const reference instead of a copy, handling other types of functions besides member functions (use Boost.Function) and access to the constructor of read_only<P, T>. Ideally, the constructor would be private and I could declare friend P;, but that isn't legal C++.

The idiom for a write-only data member is similar.

No comments: