Introduction
This tutorial will attempt to describe how to subscribe for a function invoke event by modifying the object vtable entry and in specific, Demonstrate code sample about hooking for object destructors notification by modifying vtable. Generally this may not be required in C++ coding but for the reader it is additional information about vtables and virtual functions.
Background
The requirement arise where we have more than 100,000 objects(say Book) stored in a container(say Library) and the Library keeps on modified by a worker thread. The Books from the container is again visualized in GUI control with active Book. The problem rose when the active Book is deleted by worker thread and the GUI thread tries to access the same.
Couple of solutions evaluated and v-table overloading is one among, Internet has lot of topic on discussion about constructing v-table but those may not looks generic since the compiler if free to choose the v-table format. So its decided to use the compiler itself to construct our custom v-table
The only assumption made in this approach is the v-table pointer stored in the first 4 bytes of object memory layout , if this assumption is not correct then the sample code will not work.
Custom v-table
More complex part of achieving the goal is constructing the v-table but the v-table memory layout might be compiler specific, So we thought of using compiler intelligence to build our custom v-table by inheriting from Book class say book_New(without any state info like member variable or accessing base class member variables) and overload the virtual functions where ever necessary. Now the v-table of book_New shall be hooked into the instance on book object.
Using the code
The sample code is implemented as a template to hook object destruction (which is our primary purpose). The sample code will support hooking only one object and only one observer but the template shall be modified to support multiple objects and hooking other member functions too.
Let’s take look at Book class
class Book {
protected:
char* name_;
public:
Book(char* pstr) {
name_=_strdup(pstr);
};
Book(){};
void Display() {printf("Book Name:%s\n",name_);};
virtual ~Book() {
printf("virtual BOOK::~BOOK for :%s\n",name_);
delete name_ ;
};
};
It’s a simple C++ class remembers the book name and has virtual destructor, the reason for keeping virtual destructor is required since we want to hook for destruction event.
Destructor Hook
The next piece of code we will look at is the DestructorHook template class
IDestructorObserver interface
class IDestructorObserver {
public:
virtual void OnDestroy(T* pObj) = 0;
};
It’s a simple C++ interface which has a event call back for destruction and pass the object in question as a parameter. The container should implement this interface in order to get the destruction notification of the object of interest.
Now let’s examine the member variables of the template class
T* data_; IDestructorObserver* lifeTimeObserver_;
void* vtable_;
data_ refers to the object of interest and here this variable ultimately store the Book object, lifeTimeObserver_ used for dispatching event to the interested parties, and finally vtable_ is used for storing the vtable pointer of the Book to restore upon unhooking. These data members shall be kept on vector or map type of data structure to support multiple object and multiple observers.
Instance method will simply initialize the lifeTimeObserver_ member and returns the Hook objects. The returned hook object shall be used for hooking and object of type T
static DestructorHook<T>& Instance(IDestructorObserver* proc) {
staticObj.lifeTimeObserver_ =proc;
staticObj.name_=_strdup("Librarian Hook");
return staticObj;
};
Hook method will back up the vtable of the hooked object and keep it for feature reference and then overwrite with our custom vtable as listed below
void Hook(T* objPtr) {
UnHook();
memcpy(&vtable_,objPtr,sizeof(void*));
memcpy(objPtr,this,sizeof(void*));
data_ = objPtr;
}
Destructor function, this function will get called when the hooked object is deleted, the event shall be dispatched to the subscriber after filtering self destruction
virtual ~DestructorHook() {
staticObj.unHook();
if(this == staticObj.data_) {
staticObj.lifeTimeObserver_->OnDestroy(staticObj.data_);
staticObj.data_ = 0;
}
}
And finally the main function which initializes Books object, the Librarian object will subscribe for destruction event. Later the Book objects destroyed and librarian will get notification for the hooked object
int _tmain(int argc, _TCHAR* argv[]) {
Book * pBook[5];
char name[20] = {0};
for(int i = 0 ; i < 5; i++) {
sprintf_s(name,"book%d",(i+1));
pBook[i] = new Book(name);
}
Librarian librarian;
BookDestructorHook& hook_ = BookDestructorHook::Instance(static_cast<BookDestructorHook::IDestructorObserver*>(&librarian));
hook_.Hook(pBook[3]);
printf("Display available books\n");
for(int i = 0 ; i < 5; i++) {
pBook[i]->Display();
}
printf("\n\nDeleting all Book Librarian should get notification for\t");
pBook[3]->Display();
for(int i = 0 ; i < 5; i++) {
delete pBook[i];
}
return 0;
}
Output :
Conclusion
This article shows a very simple example of how to modify C++ vtable entry to handle object destruction event. Though we didn't used this approach as we preferred reference counting mechanism, But personally believe this alternative approach shall be considered in time critical application where tens of thousands of Book are created and destroyed continuously but container is interested only in a single object. By adapting this approach the remaining object should not carry the burden of reference counting implementation.