-
-
Save dabrahams/1528856 to your computer and use it in GitHub Desktop.
#include <iostream> | |
// This is a rewrite and analysis of the technique in this article: | |
// http://bloglitb.blogspot.com/2010/07/access-to-private-members-thats-easy.html | |
// ------- Framework ------- | |
// The little library required to work this magic | |
// Generate a static data member of type Tag::type in which to store | |
// the address of a private member. It is crucial that Tag does not | |
// depend on the /value/ of the the stored address in any way so that | |
// we can access it from ordinary code without directly touching | |
// private data. | |
template <class Tag> | |
struct stowed | |
{ | |
static typename Tag::type value; | |
}; | |
template <class Tag> | |
typename Tag::type stowed<Tag>::value; | |
// Generate a static data member whose constructor initializes | |
// stowed<Tag>::value. This type will only be named in an explicit | |
// instantiation, where it is legal to pass the address of a private | |
// member. | |
template <class Tag, typename Tag::type x> | |
struct stow_private | |
{ | |
stow_private() { stowed<Tag>::value = x; } | |
static stow_private instance; | |
}; | |
template <class Tag, typename Tag::type x> | |
stow_private<Tag,x> stow_private<Tag,x>::instance; | |
// ------- Usage ------- | |
// A demonstration of how to use the library, with explanation | |
struct A | |
{ | |
A() : x("proof!") {} | |
private: | |
char const* x; | |
}; | |
// A tag type for A::x. Each distinct private member you need to | |
// access should have its own tag. Each tag should contain a | |
// nested ::type that is the corresponding pointer-to-member type. | |
struct A_x { typedef char const*(A::*type); }; | |
// Explicit instantiation; the only place where it is legal to pass | |
// the address of a private member. Generates the static ::instance | |
// that in turn initializes stowed<Tag>::value. | |
template class stow_private<A_x,&A::x>; | |
int main() | |
{ | |
A a; | |
// Use the stowed private member pointer | |
std::cout << a.*stowed<A_x>::value << std::endl; | |
}; |
@come-raczy That's an incorrect behaviour. You cannot pass pointer to private member anywhere except the arguments for explicit instantiation.
(Revised 13-Oct-2016!)
This technique can also be used to access private static member variables.
E.g.
struct B
{
private:
static char const* x;
};
char const* B::x = "proof!";
struct B_x { typedef char const** type; };
template class stow_private<B_x,&B::x>;
int main()
{
// Use the stowed private static member pointer
std::cout << *stowed<B_x>::value << std::endl;
};
@dabrahams Just for fun, I tried a variation on the ideas from your code example using variable templates:
https://gist.github.com/mdadams/98710ba3478f306633df20dca47d5cd9
The code in my example works okay with GCC 6.3.0, but Clang 3.9.1 yields undefined references to saved_private_v<Widget_i_> and saved_private_v<Widget_f_> when linking. I am not sure if this is a bug in Clang or I am making some non-portable assumptions about templates in my code.
@dabrahams what are lines 19 and 20 doing? As far as I understand, you declare a static
member value
with type Tag::type
(and you add a typename
because it's a dependent type).
Why do you "repeat" the declaration on lines 19-20? It is not template specialization, because you still have the same template parameters...
@ProExpertProg Sorry, this sample is almost a decade old; I don't remember.
Lol no worries. I'll give it a try compiling it myself without those lines and post the error here for other future googlers. The explanations in the comments are great though!
Why do you "repeat" the declaration on lines 19-20? It is not template specialization, because you still have the same template parameters...
@ProExpertProg Line 17 is a declaration, whereas lines 19-20 provides the definition; the latter is key as it controls the initialization of the (singleton) static data member instance
of the stow_private
class template, for the first instantiation of a given specialization. This initialization, in turn, initializes the static data member value
of the Stowed
class template (for the corresponding Tag
type of the specialization) with the "access rule erased" pointer to the private data member, which we naturally need for this technique to work out.
Or, in other simpler words, the definition at lines 19-20 is required such that the constructor of stow_private
runs for the (first) instantiation of a given specialization.
This makes sense, thank you! I forgot that static members in classes are just declarations and need definitions.
Here's a even more straightforward implementation, with better encapsulations & use cases:
https://github.com/YunHsiao/UnrealSourceInjector/blob/main/SourcePatch/Runtime/Core/Public/Misc/PrivateAccessor.h
@YunHsiao As of new C++20 rules (P0692R1) you can make this even more straightforward, using specialization instead of explicit instantiation definitions. See https://github.com/dfrib/accessprivate (companion article: A foliage of folly).
@dfrib Great write up on the subject, plus your usage of auto non-type template arguments feature yields much simpler declaration!
Although I'm writing a code injector for different versions of Unreal Engine here so C++14 back compat is a must :(
But besides that, afaic it was a perfectly valid usage of this, which provided some really interesting leverages for us.
why having several layers of classes and static members instead of a single class with static method like this:
It seems to work (at least with gcc 4.8.1 using c++11)