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

This topic is 1900 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

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!

##### Share on other sites
 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.

##### Share on other sites
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.

##### Share on other sites
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.

##### Share on other sites
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

##### Share on other sites
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.

##### Share on other sites
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.

##### Share on other sites
Thank you Lauris!

I will try to go that way

##### Share on other sites
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?

##### Share on other sites
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?

##### Share on other sites
Couldn't you include the nested object pointer in the PODs and then write a function that takes the pointer as an argument and uses it to traverse the tree internally?

 enum myClassBranchSide {BRANCH_LEFT, BRANCH_RIGHT}; myClassPOD* getBranchPOD(void* myClassChildPointer, myClassBranchSide side);  Edited by Khatharr

##### Share on other sites
Why don't you just write the plugins in C++?

Then you're just passing pointers back and forth.

Maybe you could explain your problem at a slightly higher level -- because you seem to have got stuck somewhere deep into doing implementation. Whereas plugin architectures which cope with C++ objects are a fairly well understood subject.

##### Share on other sites

Why don't you just write the plugins in C++?

Then you're just passing pointers back and forth.

I think he explicitly said that his problem was the potential incompatibility of C++ ABI between the application and the plugin. if he could restrict the plugins to be compiled with precisely the same compiler and settings as the main program, there would'nt be a problem from the start - regardless whether the interface is C or C++.

##### Share on other sites
Thank you again for all the good inputs. And yes, I have no control on what compiler will be used for the plugin, so I can't go with C++.

Verifying the memory layout might be a bit tricky when working with a complex object as a std::vector. (i.e. how do I access the internal data member memory location?)

So, basically, I will duplicate some of the needed data as POD. My objects will take a bit more memory, but that is ok. Typically for my std::vector, I will have two additional POD data:

- memory location where the vector data is stored (i.e. &myVector[0])
- size of the vector data (i.e. myVector.size())

##### Share on other sites
It's not really necessary to duplicate the POD data, is it? You can just have the exportable data stored in the object within a POD and then hand out a pointer to that.

[source lang="cpp"]class ExampleClass {
public:
struct ECPOD {
int data;
char otherData;
float stillMoreData;
};

ECPOD* getPODptr() {
return &amp;amp;myPOD;
}

ECPOD myPOD;

void setData(int newData) {
myPOD.data = newData;
}

private:
int privateMember;
};
[/source]

I suppose it depends on whether you want the data to be externally modifiable. Edited by Khatharr

##### Share on other sites
Thanks Khatharr.

Take the example of a very simple class:
 class Node { Node* childNode; std::vector<float>* vertices; // not safe to use vertices or vertices.size() across dll boundary } 

The vector is always problematic, even if I just want to read values out of it.

But if I duplicate the needed data from the vector as POD, I can use it, like in this example:
 class NodePOD { NodePOD* childNode; // ok to use across dll boundary float* verticePointer; // ok to use across dll boundary unsigned int verticeCount; // ok to use across dll boundary std::vector<float>* vertices; // not safe to use across dll boundary }

##### Share on other sites
I can only see two ways to deal with the issue:
1) Force people to use a particular compiler. Considering the amount of issues involved it's a rather reasonable requirement.
2) Wrap everything on the plugin side into a C API. That includes data access. The Node pointers the plugins gets are just typedefs for void* or maybe a forward declared struct (my pure C is a bit rusty on that). If the plugin needs to read anything from the node, it passes the opaque pointer to the C API function (together with everything else needed, like an array index) which returns the read value.

A third way would be to abandon C++ in this scenario and write everything in pure C. For convenience, the real application could build a thin C++ wrapper around the C API.

##### Share on other sites
If you need to export a C interface, export a C interface. Don't try to second-guess the internals of the C++ runtime. You will only know grief.

A C interface consists of pointers and functions. If you need to, say, traverse the vector contained in your object, you need a function that takes a pointer to your object and returns the size of the vector, and another that takes a pointer to your object and an index and returns the value of the vector at that index.

If you have a [font=courier new,courier,monospace]void*[/font] anywhere, you're doing it wrong.

##### Share on other sites

If you need to export a C interface, export a C interface. Don't try to second-guess the internals of the C++ runtime. You will only know grief.

A C interface consists of pointers and functions. If you need to, say, traverse the vector contained in your object, you need a function that takes a pointer to your object and returns the size of the vector, and another that takes a pointer to your object and an index and returns the value of the vector at that index.

If you have a [font=courier new,courier,monospace]void*[/font] anywhere, you're doing it wrong.

This.
1) You can not reliably send or receive stuff to/from plugins using stl containers - even if you stick to a single compiler. If you so much as mismatch a preprocessor flag of some kind that affects the compiler's stl implementation (such as _HAS_ITERATOR_DEBUGGING), it may even appear to work at first, until one day under certain circumstances it just crashes for no explainable reason. Seriously, stay away from this.

2) Unless you like the idea of locking yourself and others into a single specific compiler for the application and all plugins, don't use a c++ interface. Use a C interface. Instead of sending a vector, send a pointer and a count of elements. Stuff like that. Even when sending POD structs without extra funniness, you will need to ensure that packing is identical between plugins and your application.

2.5) If you were just making a dll library, c++ would be slightly more viable, though you would still be restricted to certain compilers. For instance you would have to always use Visual Studio 2005 and up - stuff like that. Since this is a plugin system and you are passing things back and forth (as opposed to a library that contains everything it needs on its own), you should really, really stick with a simple C interface. Otherwise it will be very painful for reasons mentioned above. C is just not meant to interoperate with C++ (though C++ is meant to interoperate with C).

3) It may take extra work to redesign some of this, but it is not as bad as it seems. The only time you need to set up a structure to send to your plugin is immediately before making the external function call. There is no reason this should affect performance.

4) By all means feel free to experiment, but you will eventually come to these same conclusions. Edited by achild

##### Share on other sites

Thanks Khatharr.

Take the example of a very simple class:
 class Node { Node* childNode; std::vector<float>* vertices; // not safe to use vertices or vertices.size() across dll boundary } 

The vector is always problematic, even if I just want to read values out of it.

But if I duplicate the needed data from the vector as POD, I can use it, like in this example:
 class NodePOD { NodePOD* childNode; // ok to use across dll boundary float* verticePointer; // ok to use across dll boundary unsigned int verticeCount; // ok to use across dll boundary std::vector<float>* vertices; // not safe to use across dll boundary }

I don't really see what you're getting at. A vector is definitely not POD. You can't export the vector since you don't know if the other side can use or interpret it. Anything that's not POD has to be interfaced at public module boundaries. There's no way around it - it's the cost of portability.

That being said, it doesn't have to be heavy at all. You can store an iterator in the class to allow iteration and make a couple functions to manage that pretty easily.

[source lang="cpp"]export float* getNextFloat(Node* sourceNode) {
return ++(sourceNode->iterator);
}

export void rewind(Node* sourceNode) {
sourceNode->iterator = sourceNode->vertices.begin();
}

export float* getFloatByIndex(Node* sourceNode, int index) {
return sourceNode->vertices[index];
}
[/source]

If it's not fast enough for traversal then I guess you could just restructure the class. Do you really need a vector there? Can it not be an array? Edited by Khatharr