I'm looking at writing my own plug-in app, but I know that deleting class instances that were created in a plug-in module can result in the dreaded "undefined behaviour" because of the different memory spaces. Many examples of plug-ins use create_class and destroy_class functions to resolve this problem, but I wondered about overriding / overloading the class's new and delete operators. This would be used for all third-party library class derivations (e.g. derived GUI classes) and all home-grown classes.
The operators would only be declared in the class declaration:
1 2 3 4 5 6 7
void *operatornew(std::size_t n);
// Other plugin bits
While the actual implementation would be defined in the plug-in's implementation file:
This would need to be implemented in every transferable plug-in class (possibly by a crafty IMPLEMENT_PLUGIN(classname) macro or some other mechanism), but before I commit this to my code I was hoping for feedback. Does this sound like a good idea? The GUI classes in particular are handled by a third-party library, so it's some memory-space safe way of deleting them by the GUI library (in the app) that I'm looking for.
> deleting class instances that were created in a plug-in module can result
> in the dreaded "undefined behaviour" because of the different memory spaces.
Unless both the plug-in and the component that tries to delete the plug-in use the same shared instance of the standard library. Which is anyway required by the separate compilation model that C++ supports.
You would also need to ensure that the plug-ins are compiled with the same compiler using the same (or compatible) compiler options (for instance, the packing for structs). You might want to put in appropriate #pragma directives in PluginBase.h
Wouldn't you be using smart pointers?
Maybe specializations of std::allocate_shared<>, std::make_shared<>() for these plugins?
Sadly no, the problem I face is that my library (wxWidgets, at present - a cross-platform GUI library) uses dumb pointers and deletes them when the parent frames and dialogs close. All of my home-grown classes are (or are going to be) passed to the app as smart pointers, but I wondered about this approach and whether it would still allow me to mix-in a GuiPluginBase class with overridden new/delete operators for my GUI-driven plug-ins.
If it still applies after this info, which #pragma options would you recommend. I'm completely self-taught and haven't come across many directives in my teachings.
> And the overriding new/delete operators approach, how's that in your opinion?
It has some definite advantages. For completeness, the plugin must be completely encapsulated with all member functions implemented out of line in the implementation file. (Or else a member function that, say, needs to resize a std::vector<> would choke - memory allocation/deallocation is involved). It doesn't solve all the problems, though: what happens if a member function returns a std::string by value?
The sane solution is to ensure that plug-in and the component that uses the plug-in use the same shared instance of the standard C++ library.
Ah, I believe there's still a lot for me to learn about plug-ins then. I was working on the model of the plug-in providing disconnected classes (or factories) for the main app to use (e.g. plug-in provides a GUI panel with further controls on it and various event handlers, or a hardware-controlling module, but otherwise plays no further part).
I will need to look up more in this area...can you suggest any good online resources?
For the separate compilation model to work, both translation units must have:
a. identical definitions and object lay-outs of std::string, std::exception, std::logic_error, std:::invalid_argument, std::allocator<>, std::char_traits<>, ...
b. identical implementations of the exception/stack-unwind mechanism
c. identical name-decorations, parameter passing and return conventions
d identical memory allocation / deallocation, from a common shared free-store
e. etc... etc...
My target platform is almost certainly going to be Windows, but I don't have regular access to a Windows machine, so I'm writing in Linux with gcc (cross-compiling for Windows at the end). When I get to finalising the build, I plan on "locking" the version of gcc for proper ABI, etc, so I don't anticipate these problems to rear their ugly heads...or have I honestly missed something?
Great, thanks for the feedback. I'll certainly keep those flags in mind as ones not to use. This is quite an ambitious project I've set for myself, so this kind of response is massively helpful and very encouraging.