Last active
September 6, 2017 12:33
-
-
Save insooth/2708008718b32054a5b2f93f7f3ca5ad to your computer and use it in GitHub Desktop.
Access inaccessible members and member functions through explicit template instantiation that escapes type system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
// based on https://github.com/hliberacki/cpp-member-accessor | |
// LIVE: http://coliru.stacked-crooked.com/a/cc61098b18ab2c20 | |
// works only for member-function-pointers and member-pointers | |
template <class T> | |
struct proxy | |
{ | |
using type = typename T::type; // type of the item to be accessed | |
static type handle; // global handle that points to the accessed item | |
}; | |
// unique access<T, P> does "assignment" of P to proxy<T>::handle | |
// global value ONCE for all the objects of access<T, P> type | |
template<typename T, typename T::type P> // P: member function pointer OR member pointer type | |
class assigner | |
{ | |
struct setter | |
{ | |
setter() | |
{ | |
proxy<T>::handle = P; // assigns to the global variable once setter created | |
} | |
}; | |
static setter assigned; // done once during instantiation of access | |
}; | |
// allocated memory for proxy<T>::handle -- required by C++ | |
template<typename T> | |
typename proxy<T>::type proxy<T>::handle; | |
template<typename T, typename T::type P> | |
typename assigner<T, P>::setter assigner<T, P>::assigned; | |
class A | |
{ | |
int m = 999; | |
void foo() { std::cout << "foo: private!\n"; } | |
void bar() { std::cout << "bar: private!\n"; } | |
}; | |
struct MemFun { using type = void (A::*)(); }; // member function pointer | |
struct Mem { using type = int (A::*); }; // member pointer | |
// generate storage and DO assignment | |
template class assigner<MemFun, &A::foo>; // EXPLICIT template instantiation | |
//template class assigner<MemFun, &A::bar>; // overwrites! | |
template class assigner<Mem, &A::m>; | |
// ------------------------------------------------------- | |
template<class T> | |
struct X { }; | |
template<int (A::*)> | |
struct Y {}; | |
template<class T, typename T::type P> | |
struct Z {}; | |
int main() | |
{ | |
A a; | |
(a.*proxy<MemFun>::handle)(); | |
std::cout << a.*proxy<Mem>::handle << std::endl; | |
/* | |
1. create global storage "handle" for value of type T::type passed to proxy | |
(storage is per TYPE instance, not per object of that type) | |
2. assign passed mem-to-fun-ptr or mem-ptr item to generated global storage | |
through assigner<T, P> where T::type is the type of the item, and P is the | |
item itself | |
3. apply assigned global item to actual object | |
What is happening here? Mem-fun-ptr or mem-ptr is type checked against wrong | |
assignment. See: | |
*/ | |
Mem::type x; // OK ...but cannot assign: | |
// Mem::type x = &A::m; // error: 'int A::m' is private within this context | |
// decltype(&A::m); // same error as above | |
/* | |
...so how's | |
template class assigner<Mem, &A::m>; | |
that possible? | |
CANNOT create an object: | |
*/ | |
// X<&A::m>; // error: expected a type, got '&A::m' | |
// Y<&A::m> y; // error: 'int A::m' is private within this context | |
// | |
// | |
// Z<Mem, &A::m> z; // ERROR: 'int A::m' is private within this context | |
// | |
// | |
/* | |
BUT can make an instantiation of a template that accesses inaccessible member. | |
Template instantiation will access the member. And that's the whole trick. | |
Once can escape the type system to access inaccessible member or member fun | |
through explicit template instantiation. | |
*/ | |
return 0; | |
} | |
template class Z<Mem, &A::m>; // WORKS!!! | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment