Skip to main content

Shared Object (.so) as a "facade" of "class factory"!

Well, this sounds very confusing, but I couldn't find a better title for that, might be I am not good at that! Whatever!! :-)

Well the problem was, we had a problem :-) we called it dynamic library update problem.

The program at the client side is made up of a bunch of shared objects (.SOs) and obviously a binary. But a set of classes in binary kept changing every now and then, hence this resulted in upgrade of the binary and eventually the installer every time, and that too dynamically (on runtime). Hence, this very well known design came in picture with a bit of change.



For clarity of the problem and the solution this is the simplest picture which one can see. A binary with a class which kept changing very frequently.


To overcome the problem the classes "which kept changing" are pulled out of the binary, and bundled into a shared object. On top of it created an Abstract class which can be accessible in binary as well as the "so".

Shared object had APIs exposed to create objects of the classes which are inherited from the Abstract class.



The abstract class will surely have some pure virtual functions, so the binary was only worried about those functions/methods and not about whats happening under the hood. In other words binary is the boss who cares about the results and nothing else.


So the picture changed a bit here, something like this.




Here "AbstractClass" has been "realized" in libtest.so and binary is using it as a pointer to hold the class objects. Hence, inside the shared object the inheritance is this simple :




If you want more classes to come out from library, extend (inherit) the base class as much as needed. For simplicity of example one is sufficient.


And finally, the getInheritedClassObject is nothing much but a (extern) C method, returning a pointer to an object of type InheritedClass. [file: SimpleInterface.h & SimpleInterface.cpp]. One can increase number of functions or objects depending upon number of classes.


Here is the header for the method, which will be opened using "dlsym".

#ifndef INTERFACE_H
#define INTERFACE_H

#include "InheritedClass.h"

#ifdef __cplusplus
extern "C" {
#endif

InheritedClass *getInheritedClassObject();

#ifdef __cplusplus
}
#endif

#endif


How would it be used in binary?

That's quite simple, just open the shared object using dlopen and get the pointer of method using dlsym. Once you have the function pointer call the function simply to get an object.


main.cpp
#include <iostream>
#include <dlfcn.h>
#include "AbstractClass.h"

using namespace std;

int main(){

void *dlPointer;
AbstractClass* (*getInheritedClassObject)();
AbstractClass *abstractClass ;
dlPointer = dlopen("/home/anurag/TempTest/libtest.so.1.0", RTLD_LAZY);
if (dlPointer==NULL)
{
std::cout<< "Error :: "<< dlerror();
}
char *error=NULL;
getInheritedClassObject = (AbstractClass *(*)())dlsym(dlPointer, "getInheritedClassObject");
if ((error = dlerror()) != NULL)
{
std::cout<<"Error opening symbol"<< error ;
}
abstractClass = getInheritedClassObject();
abstractClass->someFunction();

return 0;
}



Third line in main.cpp is the inclusion of AbstractClass.h, which is realized in the shared object. We needed a pointer to a function, which will point to the function which can return an AbstractClass pointer. Though in SimpleInterface.cpp the method getInheritedClassObject is returning a pointer to an object of InheritedClass, thats fine, one can assign derived class pointer to a base class pointer, simple C++ thumb rule.

When dlsym is called it is returning us a pointer to getInheritedClassObject of shared object, and we are assigning it to getInheritedClassObject function pointer defined in main.cpp.


And, huffff, finally you have everything with you, just call the abstractClass->someFunction();
The function which is overridden in InheritedClass.


Download the code from here for libtest.so
and create the shared library by going to the directory where the code has been extracted and issuing these two commands.


g++ -Wall -fPIC -c *.cpp
gcc -shared -Wl,-soname,libtest.so.1 -o libtest.so.1.0   *.o

and main.cpp from here

to create a binary out of this issue the following command

g++ main.cpp -ldl

Caution: Change the path of shared object in the dlopen call.


-- Thats all folks -- I am done!!!
Suggestion/Queries/Question if you have any, drop them in comments.

Chao!!!





Comments