Friday, December 30, 2011

Access to private members: Safer nastiness.

In one of my previous posts I showed some code that accesses a private member, that now has been posted on the boost mailing list as a possible utility for boost serialization to access private attributes (OH MY, I had no idea such a thing actually could have uses!).

I realized that it would be beneficial to have an alternative implementation because the previous code suffered from the Static Initialization Order Fiasco. The following code does not suffer from that problem anymore:

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};
This defines a friend function that can be called by ADL using the tag type.
// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
Note that the explicit declaration of "get" inside of "A_f" is important, because we want "get" to be visible to ADL, and the rob template specialization is not an associated class of "A_f" (we could have declared it globally too, if we wanted, but then it would be visible for anyone). To overcome that, we could introduce some utility class we derive from:
template<typename Tag, typename Member>
struct TagBase {
  typedef Member type;
  friend type get(Tag);
};

The definition of a Tag then becomes very simple
struct A_f : TagBase<A_f, int A::*> { };
Unfortunately with the TagBase, GCC spits out a warning about the "friend type get(Tag)" that it declares a non-template function. The warning can be ignored, but it's annoying :).

Hope you enjoyed!