Access Private Members

Have you ever wondered if there is a standard conformant way to access a private member in C++. One could #define private publicor try to reinterpret_cast to a type with similar layout or violate the language in a similar way. So is there a better way to do it?

// library code
template<typename type>
struct access {
  inline static type member_pointer;
};

template<typename type, type pointer>
struct create_access : access<type> {
  struct assign_pointer {
    assign_pointer() { access<type>::member_pointer = pointer; }
  };
  inline static assign_pointer by_calling_default_ctor;
};

// application code
#include <iostream>

struct Alexander {
private:
  void quote() {
    std::cout << "Performance constraints are good for creativity."
              << std::endl;
  }
};

using type = void(Alexander::*)();
template struct create_access<type, &Alexander::quote>;

int main() {
  Alexander S;
  (S.*access<type>::member_pointer)();
}

The above code calls the private function quoteof the struct Sin the last line of main and the application prints out the quote. But how does it work? The trick is that we use an indirection to call the private member. More specific we call a member function pointer on the object that was acquired at compile-time, thus avoiding the access checking.

(S.*access<type>::member_pointer)();

The above line does the call. (obj.*pmf)()is the syntax for calling a member function on objthrough the pointer pmf. Now we need to understand how to get hold of such a pointer. If quote was a public member we would manage with the following code:

using type = void(Alexander::*)();
type pmf = &Alexander::quote;
Alexander S;
(S.*pmf)();

But since the member is private we are going to fail with an error that says that 'void Alexander::quote()' is private within this context. To avoid the error we initialize the pointer through an explicit template instantiation in our application code:

using type = void(Alexander::*)();
template struct create_access<type, &Alexander::quote>;

To understand what is going on we need to know that the instantiation will allocate objects with static storage duration at compile-time or dynamically pre main’s fist statement1 and that create_accessand access make use of this fact. When the create_acesstemplate is instantiated first an instance of its base type access is created that provides storage for a member pointer. Then its own member assign_pointerwill be default initialized. Its constructor assigns the member-pointer that is provided as a non-type template parameter to the storage of access.

template<typename type, type pointer>
struct create_access : access<type> {
  struct assign_pointer {
    assign_pointer() { access<type>::member_pointer = pointer; }
  };
  inline static assign_pointer by_calling_default_ctor;
};

template<typename type>
struct access {
  inline static type member_pointer;
};

Now that access<type>::member_pointerhas been assigned the value of &S::quote we are ready to call the function.

This post has been inspired by litb’s blogpost access to private members thats easy. I think that the use of inline variables fixes the static initialization fiasco that the original code has suffered from. That post in turn is based on Herb Sutter’s GotW #76.

  1. I am not 100% sure on the allocation type. If you know exactly, please leave a comment.
Posted in c++

Leave a Reply

Your email address will not be published. Required fields are marked *