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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

cwestinCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.