I have the following struct which will be used to hold plugin information. I am very sure this will change (added to most probably) over time. Is there anything better to do here than what I have done assuming that this file is going to be fixed?
struct PluginInfo
{
public:
std::string s_Author;
std::string s_Process;
std::string s_ReleaseDate;
//And so on...
struct PluginVersion
{
public:
std::string s_MajorVersion;
std::string s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
//For things we aren't prepared for yet.
void* p_Future;
};
Further, is there any precautions I should take when building shared objects for this system. My hunch is I’ll run into lots of library incompatibilities. Please help. Thanks
As was said by someone else, for binary compatibility you will most likely restrict yourself to a C API.
The Windows API in many places maintains binary compatibility by putting a
sizemember into the struct:When you create such a beast, you need to set the
sizemember accordingly:When you compile your code against a newer version of the API, where the
structhas additional members, its size changes, and that is noted in itssizemember. The API functions, when being passed such astructpresumably will first read itssizemember and branch into different ways to handle thestruct, depending on its size.Of course, this assumes that evolution is linear and new data is always only added at the end of the
struct. That is, you will never have different versions of such a type that have the same size.However, using such a beast is a nice way of ensuring that user introduce errors into their code. When they re-compile their code against a new API,
sizeof(pluginInfo)will automatically adapt, but the additional members won’t be set automatically. A reasonably safety would be gained by “initializing” thestructthe C way:However, even putting aside the fact that, technically, zeroing memory might not put a reasonable value into each member (for example, there could be architectures where all bits set to zero is not a valid value for floating point types), this is annoying and error-prone because it requires three-step construction.
A way out would be to design a small and inlined C++ wrapper around that C API. Something like:
The class could even take care of storing the strings pointed to by the C
struct‘s members in a buffer, so that users of the class wouldn’t even have to worry about that.Edit: Since it seems this isn’t as clear-cut as I thought it is, here’s an example.
Suppose that very same
structwill in a later version of the API get some additional member:When a plugin linked to the old version of the API now initializes its
structlike thisits
structwill be the old version, missing the new and shiny data member from version 2 of the API. If it now calls a function of the second API accepting a pointer toPluginInfo, it will pass the address of an oldPluginInfo, short one data member, to the new API’s function. However, for the version 2 API function,pluginInfo->sizewill be smaller thansizeof(PluginInfo), so it will be able catch that, and treat the pointer as pointing to an object that doesn’t have thefancy_API_version2_member. (Presumably, internal of the host app’s API,PluginInfois the new and shiny one with thefancy_API_version2_member, andPluginInfoVersion1is the new name of the old type. So all the new API needs to do is to cast thePluginInfo*it got handed be the plugin into aPluginInfoVersion1*and branch off to code that can deal with that dusty old thing.)The other way around would be a plugin compiled against the new version of the API, where
PluginInfocontains thefancy_API_version2_member, plugged into an older version of the host app that knows nothing about it. Again, the host app’s API functions can catch that by checking whetherpluginInfo->sizeis greater than thesizeoftheir ownPluginInfo. If so, the plugin presumably was compiled against a newer version of the API than the host app knows about. (Or the plugin write failed to properly initialize thesizemember. See below for how to simplify dealing with this somewhat brittle scheme.)There’s two ways to deal with that: The simplest is to just refuse to load the plugin. Or, if possible, the host app could work with this anyhow, simply ignoring the binary stuff at the end of the
PluginInfoobject it was passed which it doesn’t know how to interpret.However, the latter is tricky, since you need to decide this when you implement the old API, without knowing exactly what the new API will look like.