Saturday, July 3, 2010

Access to private members. That's easy!

So, always thought that it's impossible without undefined behavior to access private members of arbitrary classes without being friend.

Today I noticed I've been horribly wrong, after reading some insightful commit to the clang compiler, that enabled it to allow explicit instantiation to disregard accessibilities, as per the Standard. This enables us to access private members of others. As an experiment, I created some class templates

template<typename Tag>
struct result {
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
};

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

So, how is it used? Let's have an example

struct A {
private:
  void f() {
    std::cout << "proof!" << std::endl;
  }
};

struct Af { typedef void(A::*type)(); };
template class rob<Af, &A::f>;

Ah, that's all to expose poor A's "f" member. Now anyone can use them using the member pointer snytax, as does the main function below

int main() {
  A a;
  (a.*result<Af>::ptr)();
}

Of course, as Herb Sutter told us, don't do these things in real code.

13 comments:

PTG said...

Cute hack :-)

Dolazy said...

I recently started to think whether enforcing privateness is really a good thing. It can be useful to have access to private variables if you are debugging with printf statements. Or when you are dealing with badly designed legacy code. If we can have a const_cast, then why not a "private_cast"?

Anonymous said...

Can you give the corresponding revision number of clang ?

Archiminos said...

I think you're forgetting that C++ is not an object-oriented language. It is a language that supports many object-oriented idioms.

I can think of several ways of accessing a private method in C++. Off the top of my head:

- Function Pointers
- Friend classes / functions.
- static member functions.
- copy constructors / assignment operators (allow access to private members of rhs if it's the same type).
- Just make it public(!)

I always view privacy as something to help other programmers. If something is private, I'm saying to any other programmer 'please don't interfere with this stuff when using this class'.

Of course they can still change it if they want to, but they have to consciously make that decision which means they will more likely be aware of the consequences of doing so.

shoe said...

i like to use #define private public and #define protected public

Johannes (litb) said...

Guys, Herb Sutter says "This isn't actually a problem. The issue here is of protecting against Murphy vs. protecting against Machiavelli... that is, protecting against accidental misuse (which the language does very well) vs. protecting against deliberate abuse (which is effectively impossible). In the end, if a programmer wants badly enough to subvert the system, he'll find a way, as demonstrated above in Examples 1 to 3.". You do *not* want to do this at home. You also do want to notice: This blog demonstrates a way that your compiler will have to support in order to be a C++ compiler. Any #define hacks and the-like are not guaranteed to work.

Jesse said...

Wow. Any chance of posting a follow-up to explain a little more what is going on? Does the function pointer declaration typedef void(A::*type)() need to match the private method you are trying to access?

Max Filippov said...

Applying this method to virtual functions one may only call final overriding method of the object's type, ancestor methods are not accessible.

E.g.:

struct A {
private:
virtual void f() {
std::cout << "proof!" << std::endl;
}
};

struct B: public A {
private:
virtual void f() {
std::cout << "not proof!" << std::endl;
}
};

struct Af { typedef void(A::*type)(); };
template class rob;

int main() {
B a;
(a.*result::ptr)();
}

would print "not proof!" (B::f), although we'd like to see "proof!" (A::f).

Anonymous said...

sorry, but I got a compile error
VS 2008
: error C2248: 'A::f' : cannot access private member declared in class 'A'

Dave Abrahams said...

Spending some time cleaning this up and simplifying it... it looks like derivation of rob from result is superfluous.

Dave Abrahams said...

Having analyzed and rewritten the code it turns out I ended up very close to where you started! Here's my version including explanatory comments: https://gist.github.com/1528856

Anonymous said...

very nice. but it won't work if A::f() is overloaded.

Anonymous said...

well, I have to swallow my own words -- it actually works even with overloaded functions, if you use the RIGHT compiler :) GCC handles it well, whereas MSVC complains about not being able to access private member...