yaww x
yaww x

Reputation: 25

How to access private members of a private type in C++?

I want to access private members without modifying the .h file. This is an example of a .h file

class X
{
private:
   class A
   {...};
   vector<A> arr;
}

Q:How can I access X::arr ?

class X
{
private:
    int a;
};

template<typename Tag, typename Tag::type Member>
struct XAccessor 
{ 
    friend typename Tag::type get(Tag) 
    {
        return Member;
    } 
};

struct X_A
{ 
    typedef int X::* type;
    friend type get(X_A);
};;

template struct XAccessor<X_A, &X::a>;

...
auto x = new X;
x->*get(X_A())=11;
...

I found this approach online, but when I changed typedef int X::* type to typedef vector<X::A> X::* type, it gave me an error saying that X::A is not accessible.

Upvotes: -1

Views: 220

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39424

If you can steal the data member pointer, you can extract the type from it. Here is a solution that works with zero modifications to the original X.

Say we have a class X which we can't modify, containing a private data member of private type Hidden. In your example, Hidden = std::vector<A>.

class X
{
private:
    struct Hidden {};
    Hidden a;
};

We can create a function that returns the data member pointer X::*a, without naming the type X::Hidden:

// the following three declarations give us a data member pointer to X::a
auto steal();

template <auto Ptr>
struct Thief {
    friend auto steal() {
        return Ptr;
    }
};

// explicit instantiation of specialization Thief<&X::a>
template struct Thief<&X::a>;

The reason why this works is that member access restrictions are disabled when explicitly instantiating templates, so Thief<&X::a> will not fail to compile. This is intentional design, because otherwise you would be unable to explicitly instantiate any templates involving non-public members.

Thief defines a hidden friend steal(), which returns the data member pointer, and acts as a "jailbreak" for encapsulation. steal() is just a free function, and doesn't contain &X::a or X::Hidden in any template parameters or function parameters, so nothing restricts our access to it.

We also create a type trait so we can extract the data type from this pointer:

// trait which lets us extract Hidden from a data member pointer
template <typename T>
struct member_type;

template <typename C, typename M>
struct member_type<M C::*> {
    using type = M;
};

Finally, we are able to access both the private type X::Hidden, and the private data member X::a:

// X_Hidden = X::Hidden
using X_Hidden = member_type<decltype(steal())>::type;

// steal private data member at runtime
X_Hidden get_a(const X &x) {
    X_Hidden X::*a = steal();
    return x.*a;
}

See live demo on Compiler Explorer.

Upvotes: 8

Related Questions