Skip to content

Instantly share code, notes, and snippets.

@insooth
Last active September 6, 2017 12:33
Show Gist options
  • Save insooth/2708008718b32054a5b2f93f7f3ca5ad to your computer and use it in GitHub Desktop.
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
#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