Skip to content

Instantly share code, notes, and snippets.

@netguy204
Created July 28, 2013 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save netguy204/6097176 to your computer and use it in GitHub Desktop.
Save netguy204/6097176 to your computer and use it in GitHub Desktop.
An API for doing prototypical inheritance (with monkey patching) in C++.
I'm an A
I'm an imposter!
I'm super imposing!
I'm also an A
#ifndef METACLASS_H
#define METACLASS_H
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define ABS(x) (x < 0 ? -x : x)
template<typename T>
unsigned int member_offset(T mfun) {
void* ptr = *(void**)&mfun;
return ((uintptr_t)ptr) / sizeof(void*);
}
template<typename T>
class CPPMetaClass {
private:
void **vtable_get(T* obj) {
return *(void***)obj;
}
unsigned int vtable_nummembers(T* obj) {
void** vtable = vtable_get(obj);
// assume at least one virtual function (safe)
void* first = vtable[0];
// assume that all other functions are within 1 MB of the first
// (reasonable but magic)
for(int ii = 1; ii < 100; ++ii) { // no more than 100 methods (please!)
ptrdiff_t diff = (ptrdiff_t)vtable[ii] - (ptrdiff_t)first;
if(ABS((ptrdiff_t)diff) > 0x10000) {
return ii;
}
}
return 0;
}
void** vtable_clone(T* obj) {
void** vtable = vtable_get(obj);
unsigned int count = vtable_nummembers(obj) + 1;
void** newtable = (void**)malloc(sizeof(void*) * count);
memcpy(newtable, vtable, sizeof(void*) * count);
return newtable;
}
void** vtable;
void* data;
size_t data_size;
public:
CPPMetaClass(T* archetype) {
vtable = vtable_clone(archetype);
data_size = sizeof(T);
data = malloc(data_size);
memcpy(data, archetype, data_size);
// install our new vtable
*(void**)data = (void*)vtable;
}
template<typename A, typename B>
void override(A memfn, B* newfn) {
vtable[member_offset(memfn)] = (void*)newfn;
}
T* create() {
void* copy = malloc(data_size);
memcpy(copy, data, data_size);
return (T*)copy;
}
};
#endif
#include <stdio.h>
#include "metaclass.h"
class A {
public:
virtual void doThing() {
printf("I'm an A\n");
}
virtual void doAnother() {
printf("I'm also an A\n");
}
};
void dynamicOverride(A* a) {
printf("I'm an imposter!\n");
}
void anotherOverride(A* a) {
printf("I'm super imposing!\n");
}
int main(int argc, char *argv[]) {
A* a = new A();
CPPMetaClass<A> a_meta(a);
a_meta.override(&A::doThing, &dynamicOverride);
A* a2 = a_meta.create();
CPPMetaClass<A> a2_meta(a2);
a2_meta.override(&A::doThing, &anotherOverride);
A* a3 = a2_meta.create();
a->doThing();
a2->doThing();
a3->doThing();
a3->doAnother();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment