Sharing C++ object between application and plugin with C interface

Started by
18 comments, last by Khatharr 11 years, 5 months ago
Hello,

I have an application in C++ that uses several types of objects. The application can interact with a plugin via C interfaces, which means that objects cannot be shared.

My question is:

knowing that the application and the plugin have the exact same "class myClass" definition, how would I share that object in a simple way between the application and plugin? (i.e. no memory allocation or such, just reading the class members for instance).

As an example, following class:

class myClass
{
public:
myClass();
myClass(int a value);
virtual ~myClass();

void doSomething();

myClass* child1;
myClass* child2;
int someValue;
char* someBuffer;
}


I can create an instance of above class in my application, then send it to the plugin in following way:

myClass* myObject=new myClass(42);
pluginEntryFunction_doSomething((void*)myObject);


On the plugin side, I would have:

extern "C" __declspec(dllexport) void pluginEntryFunction(void* _theObject)
{
// Following is wrong and dangerous:
myClass* theObject=(myClass*)_theObject;
}


But that would be wrong and dangerous since the plugin might have used a different compiler or arranged the class structure differently (e.g. the member variable "someValue" might be located in the memory after "someBuffer")

But there is probably a way to handle this situation, by first telling the plugin how the data arrangement is made in the application. Something like:

myObject* myObject=new myClass(42);
int member_someValueOffset=((void*)(&someValue))-((void*)myObject);
int member_someBufferOffset=((void*)(&someBuffer))-((void*)myObject);
pluginEntryFunction_initialize(member_someValueOffset,member_someBufferOffset);


Or is there a better way of doing this?

Thanks for any insight!
Advertisement

extern "C" __declspec(dllexport) void pluginEntryFunction(void* _theObject)
{
// Following is wrong and dangerous:
myClass* theObject=(myClass*)_theObject;
}


That seems incredibly generic to me. Maybe that's why you're running into issues? Instead maybe prefer something long the lines of:


extern "C" __declspec(dllexport) void pluginParseBuffer(char* theBuffer)
{
//Do something...
}


It's not a good idea to define the same type in and out of your library for exactly the reasons you described. While breaking up a generic function into more specific operations is certainly more verbose on the client's end, they can always be wrapped in a function to perform the appropriate calls in order there.
Thank you greenvertex!

Actually you are right, except that my object contains 2 other objects of the same kind ("child1" and "child2" here above), which might also contain another 2, and so on.
So basically the plugin will have to explore the object cascade (or object hierarchy), which makes your proposed solution not working in that case.
Not necessarily, you can write an algorithm that traverses the object tree in client code, applying some algorithm from your shared library to each node. Again, it's not a "fix all" function call, but it's still beneficial to assume as little as you can about client code from a library.
Thanks again greenvertex.

There is another requirement that I didn't mention: it needs to be extremely fast, since the tree traversing is done over and over again. I cannot afford to "serialize" and "deserialize" the object each time the plugin function is called
Then don't. If you have some complex operation to perform on each node's buffer that is generic enough to reside in an external library, have that operation take as an argument the address and length of the buffer. No serialization required.
My only suggestion is to encapsulate the parts that have to be accessed in plugin into POD types.

Extracting member offsets of course works, except when you are using multiple inheritance. But it makes my eyes hurt, unlike using POD types in C++ code.
Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/
Thank you Lauris!

I will try to go that way
Well, after all, I can't do without complex objects (i.e. containing std::vector)...

To simplify the problem: the object to pass contains a vector, and a pointer to another object of the same kind. The object can be explored like a tree.
On the plugin side, I do not modify the object, I just traverse the tree and read the vector values.

Traversing the tree should be fine if I verify on the plugin side that the member variables have the same memory layout.
Reading vector values should be also fine, except for reading its length (i.e. with myVector.size() ).

What are my options?
Well, this complicates things. Unless you want to make duplicate hierarchy of POD types or use custom vector-like containers - but neither of these is probably a good idea.

How important part of your program these plugins will be?
Is it realistic that there will be plugins that are compiled with different compilers/settings?

Maybe you can write some simple tests that verify the internal layout of your objects (and std::vector) and simply ignore plugins that do not have identical layouts?
Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/

This topic is closed to new replies.

Advertisement