C++ objects with virtual function and UNIX shared memory

I want to make some objects in shared memory. These objects have got virtual functions. The first process (server) makes shared memory and objects in it. Other processes attach this memory and use objects. Can I make these processes not copy or fork of server process?
For example:
class a {
public:
   virtual void func();
};

class b : public a {
   virtual void func();
};
...
Server:
char *ptrshare;   // pointer to shared memory
a *ptra=new a;
b *ptrb=new b;
...
memcopy(ptrshare,ptra,sizeof(a));
ptrshare+=sizeof(a);
memcopy(ptrshare,ptrb,sizeof(b));
ptrshare+=sizeof(b);
...

Client :
char *clientptrshare;
a *ptra=(a *)clientptrshare;
ptra->func();  // ?!!!

I tried this in HP UX. class a was in module a.cpp,
b - b.cpp etc.
If I make programs:
CC server.o a.o b.o c.o -o server
CC client.o a.o b.o c.o -o client
That's OK.
CC server.o a.o b.o c.o -o server
CC client.o a.o c.o b.o -o client
That's wrong.
viachesAsked:
Who is Participating?
 
cwestinConnect With a Mentor Commented:
What was wrong with the last link line?  You've got to be more explicit.

Yes, this can be done without copying or forking(), but I need to know more details.  You also need to be aware that this may not be portable, depending on the solution you choose.

I suspect your problem has to do with the order of the link line.  Do you understand how virtual functions work?  i.e., do you realize that in your class instances, there is an (invisible) pointer, known as the "vtable pointer" to a structure full of function pointers (the vtable)?  The issue is that the address of the vtable may differ in the different binary images.  When you create "server" the vtable for class a is at a certain adress, call it servera.  But in "client" the vtable for the class may reside at a different address, call it clienta.  When you instantiated the class, the instance's vtable was initialized to point to servera.  But servera != clienta.  Therefore, when client tries to invoke a->virtualfunc(), the vtable pointer is pointing somewhere else.

In general, because of this, and some other related problems that crop up with the use of shared libraries, you can't use C++ virtual functions on objects in shared memory unless the binaries are all identical (implying that the vtables will always be at the same address.)  You can however get the same effect by introducing a level of indirection.  I've used this technique before:  I declare the virtual function explicitly.  Declare a structure that contains the function pointers you want virtual:

struct myfuncs
{
  void (*foo)(myclass *pthis, int x, int y);
   int (*bar)(myclass *pthis, char *p);
};

Initialize this structure with your functions.

const myfuncs MyFuncs =
{
  fooFunc, barFunc
};

  You must have an instance of this in each binary that the functions will be called from.  Then, create invocation macros for the virtual functions that look like this:

#define myclassfoo(pthis, x, y) ((*MyFuncs.foo)(pthis, x, y))
#define myclassbar(pthis, p) ((*MyFuncs.foo)(pthis, p))

Use these macros to call your "virtual functions."  This works because in each image, the macro will be using the locally linked reference to the function pointer rather than one in another process.

If you want polymorphism, things get a bit more complicated.  Then, you have to more closely copy the effects of using a real vtable, but without using pointers.  I've also used this technique before.  You have to assign unique integers to each vtable involved.  In your class instance, put an integer variable that will serve as an explicit "vtable index" (instead of a vtable *pointer*.)  When you instantiate the class, set the vtable index appropriately.  Create invocation macros that use the index to index into an array of pointers to vtables such as the one above.  You have to create and initialize the array of vtable pointers in each process, where it will have the correct local values.  Then, the invocation macros look like this:

#define abstractfoo(pthis, x, y) (*((vtable_type *)vtable_array[(pthis)->vtable_index]).foo)(pthis, x, y))

With
const void *vtable_array[] =
{ (void *)foo_vtable, (void *)bar_vtable, ...};

Again, each process will have its own correct local copy of the function pointers and vtables.  This will also work as a technique to make polymorphism work for classes in shared libraries on platforms that load the library into different addresses in each process.

0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.