Is there any way to prepare a struct for future additions? - c++

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

What about this, or am I thinking too simple?
struct PluginInfo2: public PluginInfo
{
public:
std::string s_License;
};
In your application you are probably passing around only pointers to PluginInfos, so version 2 is compatible to version 1. When you need access to the version 2 members, you can test the version with either dynamic_cast<PluginInfo2 *> or with an explicit pluginAPIVersion member.

Either your plugin is compiled with the same version of C++ compiler and std library source (or its std::string implementation may not be compatible, and all your string fields will break), in which case you have to recompile the plugins anyway, and adding fields to the struct won't matter
Or you want binary compatibility with previous plugins, in which case stick to plain data and fixed size char arrays ( or provide an API to allocate the memory for the strings based on size or passing in a const char* ), in which case it's not unheard of to have a few unused fields in the struct, and then change these to be usefully named items when the need arises. In such cases, it's also common to have a field in the struct to say which version it represents.
But it's very rare to expect binary compatibility and make use of std::string. You'll never be able to upgrade or change your compiler.

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 size member into the struct:
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
};
When you create such a beast, you need to set the size member accordingly:
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
When you compile your code against a newer version of the API, where the struct has additional members, its size changes, and that is noted in its size member. The API functions, when being passed such a struct presumably will first read its size member and branch into different ways to handle the struct, 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" the struct the C way:
PluginInfo pluginInfo;
std::memset( &pluginInfo, 0, sizeof(pluginInfo) );
pluginInfo.size = sizeof(pluginInfo);
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:
class CPPPluginInfo : PluginInfo {
public:
CPPPluginInfo()
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
}
CPPPluginInfo(const char* author /* other data */)
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
s_Author = author;
// set other data
}
};
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 struct will in a later version of the API get some additional member:
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
int fancy_API_version2_member;
};
When a plugin linked to the old version of the API now initializes its struct like this
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
its struct will 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 to PluginInfo, it will pass the address of an old PluginInfo, short one data member, to the new API's function. However, for the version 2 API function, pluginInfo->size will be smaller than sizeof(PluginInfo), so it will be able catch that, and treat the pointer as pointing to an object that doesn't have the fancy_API_version2_member. (Presumably, internal of the host app's API, PluginInfo is the new and shiny one with the fancy_API_version2_member, and PluginInfoVersion1 is the new name of the old type. So all the new API needs to do is to cast the PluginInfo* it got handed be the plugin into a PluginInfoVersion1* 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 PluginInfo contains the fancy_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 whether pluginInfo->size is greater than the sizeof their own PluginInfo. 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 the size member. 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 PluginInfo object 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.

what rwong suggest (std::map<std::string, std::string>) is a good direction. This is makes it possible to add deliberate string fields. If you want to have more flexibility you might declare an abstract base class
class AbstractPluginInfoElement { public: virtual std::string toString() = 0;};
and
class StringPluginInfoElement : public AbstractPluginInfoElement
{
std::string m_value;
public:
StringPluginInfoElement (std::string value) { m_value = value; }
virtual std::string toString() { return m_value;}
};
You might then derive more complex classes like PluginVersion etc. and store a map<std::string, AbstractPluginInfoElement*>.

One hideous idea:
A std::map<std::string, std::string> m_otherKeyValuePairs; would be enough for the next 500 years.
Edit:
On the other hand, this suggestion is so prone to misuse that it may qualify for a TDWTF.
Another equally hideous idea:
a std::string m_everythingInAnXmlBlob;, as seen in real software.
(hideous == not recommended)
Edit 3:
Advantage: The std::map member is not subject to object slicing. When older source code copies an PluginInfo object that contains new keys in the property bag, the entire property bag is copied.
Disadvantage: many programmers will start adding unrelated things to the property bag, and even starts writing code that processes the values in the property bag, leading to maintenance nightmare.

Here's an idea, not sure whether it works with classes, it for sure works with structs: You can make the struct "reserve" some space to be used in the future like this:
struct Foo
{
// Instance variables here.
int bar;
char _reserved[128]; // Make the class 128 bytes bigger.
}
An initializer would zero out whole struct before filling it, so newer versions of the class which would access fields that would now be within the "reserved" area are of sane default values.
If you only add fields in front of _reserved, reducing its size accordingly, and not modify/rearrange other fields you should be OK. No need for any magic. Older software will not touch the new fields as they don't know about them, and the memory footprint will remain the same.

Related

Using different array-types interchangeably

I'm sure this question has been answered somewhere, but I'm not sure, what to even search for:
Say I have a class that stores some data in a std::vector, but I want to be able to use two differenct types interchangeably (for example to save memory) depending on the "mode" I set the class to. Something like this:
class MyClass
{
int Mode; // Could be, say, 0 or 1
std::vector<float> MyData; // Use this when Mode is 0
std::vector<short> MyData; // Use this when Mode is 1
void DoStuffWithData()
{
// ...
}
}
I know, this could probably be done with template, but I have not yet managed to wrap my head around this (if anybody could give me an example, please do so). Of course I could work with overloaded functions and use a lot of if-statements to switch between the cases or use a void-pointer that is set to one of the vectors and then cast to its type, but I think, this would make pretty messy code.
What would be a nice and clean way to use (mostly) the same code by just referring to MyData?
Edit
Mode would change during runtime. Basically the class would record and work with data it collects in the background. I want to be able to just change Mode for a new "recording" (of which I might start multiple during runtime). This would then set both vectors to size zero and use the one selected. Again - I know, I could overload functions and just use some if-statements, but I would like to keep the code short and clean (and maybe learn some things along the way).
You can't have two members called MyData. You can use templates instead, as that's essentially what they're meant for. This would be a simple example:
template<class T>
class MyClass {
std::vector<T> MyData;
void DoStuffWithData()
{
// ...
}
};
int main() {
MyClass<short> shortData;
MyClass<float> floatData;
MyClass<long> longData; // you're no longer limited to just two types
// ...
return 0;
}
Notice there's no Mode anymore, as you'll just choose the desired type when declaring each variable.

Is it a poor programming practice to use function typedefs extensivly in classes?

Would this general concept be considered "bad"? The concept of using function typedefs for precalculating which function's are better optimized to handle the data stored? Or should I just stick to if and switch statements to keep other programmers from cringing? Aside from cringing at the names in this throw together example:
#ifndef __PROJECT_CIMAGE_H_
#define __PROJECT_CIMAGE_H_
#define FORMAT_RGB 0
#define FORMAT_BGR 1
typedef unsigned char ImageFormat;
class CImage
{
protected:
// image data
Components* data;
ImageFormat format;
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
// specify the different functions for each supported format
void DeleteRedComponentRGB();
void DeleteGreenComponentRGB();
void DeleteBlueComponentRGB();
void DeleteRedComponentBGR();
void DeleteGreenComponentBGR();
void DeleteBlueComponentBGR();
// Add in references to which functions to use.
lpfnDeleteRedComponentProc DRC;
lpfnDeleteGreenComponentProc DGC;
lpfnDeleteBlueComponentProc DBC;
public:
Image(); // Allocate some basic data
~Image(); // Deallocate stored data
// change the image format
void SetImageFormat(ImageFormat format)
{
// shift through the data and adjust it as neccissary.
switch (format)
{
case FORMAT_RGB:
// use functions specially suited for the RGB format
DRC = DeleteRedComponentRGB;
DGC = DeleteGreenComponentRGB;
DBC = DeleteBlueComponentRGB;
break;
case FORMAT_BGR:
// use functions specially suited for the BGR format
DRC = DeleteRedComponentBGR;
DGC = DeleteGreenComponentBGR;
DBC = DeleteBlueComponentBGR;
break;
}
}
// Set's the specifyed component to 0 throughout the entire image
void DeleteRedComponent() { DRC(); }
void DeleteGreenComponent() { DGC(); }
void DeleteBlueComponent() { DBC(); }
// more, similarly pourposed, functions here...
};
#endif // __PROJECT_CIMAGE_H_
There are many problems with the above code.
You use #define uselessly, and typedef where you should have an enum
enum class ImageFormat:unsigned char { // unsigned char optional
FORMAT_RGB, // =0 optional
FORMAT_BGR // =1 optional
};
Second, you have a cluster of virtual behavior you want to swap out in a clump. How does this not scream interface class to you?
struct ChannelSpecific {
virtual void DeleteGreen( CImage* ) = 0;
virtual void DeleteBlue( CImage* ) = 0;
virtual void DeleteRed( CImage* ) = 0;
// etc
};
template< ImageFormat format >
struct ChannelSpecificImpl;
template<>
struct ChannelSpecificImpl<FORMAT_RGB>:ChannelSpecific {
void DeleteGreen( CImage* ) final { /* etc...*/ }
// etc...
};
template<>
struct ChannelSpecificImpl<FORMAT_BGR>:ChannelSpecific {
// etc...
};
The overhead to calling the above virtual functions is marginally higher than a function pointer (due to the vtable being less likely to be in the cache), but in cases where you are doing a whole pile of operations in a row you can find the format and explicitly cast the worker and call the final methods with no function-pointer or virtual table overhead (up to and including allowing the methods to be inlined).
As a second advantage, a whole pile of the operations you want to perform on channels ends up being exceedingly uniform, and just a matter of what the offset of each channel is. So I can do away with the two above specializations by simply doing this:
enum class Channel { Red, Green, Blue };
template<ImageFormat, Channel> struct channel_traits;
template<> struct channel_traits<FORMAT_RGB, Red>:std::integral_constant< size_t, 0 > {};
template<> struct channel_traits<FORMAT_RGB, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_RGB, Blue>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Red>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_BGR, Blue>:std::integral_constant< size_t, 0 > {};
and now I get to write my ChannelSpecificImpl<ImageFormat> without specializations -- I just need to access the above traits classes, and I get to write my code once, and use it multiple times.
Inside CImage I store a single ChannelSpecific pointer, which holds no data, just algorithms. When I swap out the image format, the ChannelSpecific pointer is swapped out. If I find I have a bottleneck in how I'm using ChannelSpecific because of too much vtable overhead, I get to refactor and put a mega-function in it.
If I hate the fact that I'm passing in the CImage all the time, I can give ChannelSpecific state of a pointer to the CImage internally, and now the code gets to use this->cimage to access the CImage.
On the other hand, code like what you wrote above has its place. I'd consider it better than massive case switch statements.
Note that a bit of the above code is C++11 specific (enum class, enum with storage specifier, final), but if you drop those features the solution is still viable.
Also note that your switch statement ends up looking like:
switch (format) {
case FORMAT_RGB:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_RGB>());
case FORMAT_BGR:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_BGR>());
which is far less to maintain and less likely to contain bugs. If you hate the free store (and more specifically, have found that format changes are common enough that the ::new call is a performance hit that matters), create a boost::variant or a C++11 union of each of the possible ChannelSpecificImpl. (std::unique_ptr<ChannelSpecific> channelSpecific, or std::shared_ptr, depending on various things -- use unique_ptr by default.)
Finally, if you get tired of maintaining that switch statement (as I tend to), making a cascading if based magic switch via template metaprogramming isn't that hard -- or even an array of function pointer factories that produce a ChannelSpecific* and an explicit array lookup to call one of them. (sadly, there isn't a variardic template expansion that produces an actual switch statement, but compilers might optimize chained sequential ifs into the same structure anyhow).
If you get nothing from the above code, the important part is that you don't want to hand write each of the zero functions. You want to not repeat yourself, write it once, factor out the differences between the foramts into a traits class, and have template functions on the format and channel produce the function that does the work and be written once. If you don't do this, you will either have to generate your code via macros and have an undebuggable mess, generate your code via some other method (and not be able to debug the generator, just the generated code), or you will have some corner case bug that occur only when doing some specific operation to some specific channel that your QA will miss. Maybe not today, maybe not tomorrow, but someday when a change is made and someone screws up the update to the 18th format specific function but only in the blue channel.
I'm in the midst of attacking an old per-channel imaging library that was done in this "virtual C-style function pointer swap out" pretty much exactly as you are proposing, and each function I touch gets rewritten using the above technique. I am reducing the amount of code by huge swaths, increasing reliability, and sometimes even getting performance boosts. Why? Because I was able to check common assumptions -- pixelstride equal to pixel packing, pixelstride in source and dest equal -- and generate a less-branchy version for that case, and fall back to a more-branchy for the corner cases, then apply that to a whole myriad of different pixel iteration code in one fell swoop. Maintaining N different pixel iterating code with that kind of micro optimization on top of the existing micro optimizations would be expensive: doing it this way means that I get to write it once, and reap the benefits N fold.
Typedefs are a good way to make the code much more readable. If you have a problem with the typedefs then it means it just does not contribute to the code's readability. Just changing the typedef name will solve the problem but you would need to change it everywhere in the existing codebase.
#Yakk comments on using virtual instead of function pointers are on the money; as well as the better solution also offered.
Given the reservations on the design here, its worth noting that:
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
creates distinct new type names for each component, even though they have the same signature. If I were going down this path I'd have a single type name which would make clear the expected common behavior.

What is the proper way to handle a large number of interface implementations?

For one of my current projects I have an interface defined for which I have a large number of implementations. You could think of it as a plugin interface with many plugins.
These "plugins" each handle a different message type in a network protocol.
So when I get a new message, I loop through a list of my plugins, see who can handle it, and call into them via the interface.
The issue I am struggling with is how to allocate, initialize, and "load" all the implementations into my array/vector/whatever.
Currently I am declaring all of the "plugins" in main(), then calling an "plugin_manager.add_plugin(&plugin);" for each one. This seems less than ideal.
So, the actual questions:
1. Is there a standardized approach to this sort of thing?
2. Is there any way to define an array (global?) pre-loaded with the plugins?
3. Am I going about this the wrong way entirely? Are there other (better?) architecture options for this sort of problem?
Thanks.
EDIT:
This compiles (please excuse the ugly code)... but it kind of seems like a hack.
On the other hand, it solves the issue of allocation, and cleans up main()... Is this a valid solution?
class intf
{
public:
virtual void t() = 0;
};
class test : public intf
{
public:
test(){}
static test* inst(){ if(!_inst) _inst = new test; return _inst; }
static test* _inst;
void t(){}
};
test* test::_inst = NULL;
intf* ints[] =
{
test::inst(),
NULL
};
Store some form of smart pointer in a container. Dynamically allocate the plugins and register them in the container so that they can be used later.
One possible approach for your solution would be, if you have some form of message id that the plugin can decode, to use a map from that id to the plugin that handles that. This approach allows you to have fast lookup of the plugin given the input message.
One way of writing less code would be to use templates for the instantiation function. Then you only need to write one and put it in the interface, instead of having one function per implementation class.
class intf
{
public:
virtual void t() = 0;
template<class T>
static T* inst()
{
static T instance;
return &instance;
}
};
class test : public intf { ... };
intf* ints[] =
{
intf::inst<test>(),
NULL
};
The above code also works around two bugs you have in your code: One is a memory leak, in your old inst() function you allocate but you never free; The other is that the constructor sets the static member to NULL.
Other tips is to read more about the "singleton" pattern, which is what you have. It can be useful in some situations, but is generally advised against.

Can I safely replace a bool member with unsigned char in a public header?

I ship a public header that users compile against to utilize classes contained within. I need to fix an issue in the shared library that implements one of these public classes and have a requirement that existing users need not recompile their libraries when installing a patch. The fix requires a bool member to be able to hold more information than false,true.
The class looks like
class PublicAPI{
...
public:
bool getSync();
void setSync(bool sync);
...
private
bool sync_;
...
};
I'd like to change bool sync_; to unsigned char sync_;:
class PublicAPI{
...
public:
bool getSync();
void setSync(bool sync);
...
private
unsigned char sync_;
...
};
Note that the getters and setters won't and I don't think can change. They will be ok, since the user will not need to be aware of the additional values sync_ contains and I will handle retaining my values internally. Obviously this is an ugly hack, but I think technically it's ok because the size of the class won't change.
My concern is that a compiler on one of the platforms I ship on Linux, AIX, HPUX, Solaris, Windows will play some game with the bool type I'm not aware of and break something.
Any thoughts on how safe or dangerous this is?
I don't think there's a way to do what you want that's guaranteed to be safe. Some compiler might have different sizes for bool and unsigned char.
It seems pretty unlikely that this would be the case on most compilers and you can probably get away with it. Better of course is to "suggest" a recompile but allow that it may work without recompiling.
This is a perfect example where pimpl'ing your implementation would have eliminated any concern as the internal type would have been fully hidden within your library rather than expost.
I don't know about GCC, but I did some similar hacks with MSVC 6 a long time ago (most of them were replacing a DWORD with a pointer). Worked just fine.
Of course, as many others already wrote, sizeof(whateverYouUse) must equal sizeof(bool).
That shouldn't be a problem though, since you can always use
class PublicAPI{
...
public:
bool getSync();
void setSync(bool sync);
...
private
unsigned char sync_[sizeof(bool)];
...
};
And then use sync_[0] internally.
The safest way would be to use a union of bool and unsigned char. When bool is larger than char, the union would be aligned like a bool, but still contain the char.
Both bool and char are POD types, so there is no big deal. All access is via the two non-inlined members (you are sure they're not inline? I.e. not defined inside the class?) which means you always access the union via the char member.
I'm pretty sure that technically, you'd invoke undefined behaviour. However, I think that in practicality no compiler will throw on that change.
Technically, it's undefined behavior, and in practice, it almost
certainly won't work---the function names will be mangled
(decorated) differently, and the linker will fail to find the
function.
In most such cases, the solution is to override, with the old
function forwarding to the new one, e.g.
public:
void setSync(bool sync);
void setSync(int sync);
, with setSync(bool sync) implemented as:
void PublicAPI::setSync(bool sync)
{
setSync(sync ? 1 : 0);
}
Don't make the new implementation of setSync inline. (If the
old one was in fact inline, you're screwed.)
if sizeof(bool)!=sizeof(char) and because of this the size of the class will be different. This may cause you troubles if you have a module that is compiled with an old header and have a new binary.
To avoid compiling issues, just be sure you cast and should be ok.
You don't need to change the header file at all, if you can bring yourself to program like this:
#include <iostream>
using namespace std ;
static bool b ;
int main()
{
unsigned char u0 = 99 ;
*reinterpret_cast<unsigned char*>(&b) = u0 ;
unsigned char u1 = *reinterpret_cast<unsigned char*>(&b) ;
cout << int(u1) << endl ;
}

Extending a class and maintaining binary backward compatibility

I'm trying to add new functionality to an existing library. I would need to add new data to a class hierarchy so that the root class would have accessors for it. Anyone should be able to get this data only sub-classes could set it (i.e. public getter and protected setter).
To maintain backward compatibility, I know I must not do any of the following (list only includes actions relevant to my problem):
Add or remove virtual functions
Add or remove member variables
Change type of existing member variable
Change signature of existing function
I can think of two ways to add this data to hierarchy: adding a new member variable to root class or adding pure virtual accessor functions (so that data could be stored in sub-classes). However, to maintain backward compatilibity I can not do either of these.
The library is using extensively pimpl idiom but unfortunately the root class I have to modify does not use this idiom. Sub-classes, however, use this idiom.
Now only solution that I can think of is simulating member variable with static hash-map. So I could create a static hash-map, store this new member to it, and implement static accessors for it. Something like this (in pseudo c++):
class NewData {...};
class BaseClass
{
protected:
static setNewData(BaseClass* instance, NewData* data)
{
m_mapNewData[instance] = data;
}
static NewData* getNewData(BaseClass* instance)
{
return m_mapNewData[instance];
}
private:
static HashMap<BaseClass*, NewData*> m_mapNewData;
};
class DerivedClass : public BaseClass
{
void doSomething()
{
BaseClass::setNewData(this, new NewData());
}
};
class Outside
{
void doActions(BaseClass* action)
{
NewData* data = BaseClass::getNewData(action);
...
}
};
Now, while this solution might work, I find it very ugly (of course I could also add non-static accessor functions but this wouldn't remove the ugliness).
Are there any other solutions?
Thank you.
You could use the decorator pattern. The decorator could expose the new data-elements, and no change to the existing classes would be needed. This works best if clients obtain their objects through factories, because then you can transparently add the decorators.
Finally, check binary compatibility using automated tools like abi-compliance-checker.
You can add exported functions (declspec import/export) without affecting binary compatibility (ensuring you do not remove any current functions and add your new functions at the end), but you cannot increase the size of the class by adding new data members.
The reason you cannot increase the size of the class is that for someone that compiled using the old size but uses the newly extended class would mean that the data member stored after your class in their object (and more if you add more than 1 word) would get trashed by the end of the new class.
e.g.
Old:
class CounterEngine {
public:
__declspec(dllexport) int getTotal();
private:
int iTotal; //4 bytes
};
New:
class CounterEngine {
public:
__declspec(dllexport) int getTotal();
__declspec(dllexport) int getMean();
private:
int iTotal; //4 bytes
int iMean; //4 bytes
};
A client then may have:
class ClientOfCounter {
public:
...
private:
CounterEngine iCounter;
int iBlah;
};
In memory, ClientOfCounter in the old framework will look something like this:
ClientOfCounter: iCounter[offset 0],
iBlah[offset 4 bytes]
That same code (not recompiled but using your new version would look like this)
ClientOfCounter: iCounter[offset 0],
iBlah[offset 4 bytes]
i.e. it doesn't know that iCounter is now 8 bytes rather than 4 bytes, so iBlah is actually trashed by the last 4 bytes of iCounter.
If you have a spare private data member, you can add a Body class to store any future data members.
class CounterEngine {
public:
__declspec(dllexport) int getTotal();
private:
int iTotal; //4 bytes
void* iSpare; //future
};
class CounterEngineBody {
private:
int iMean; //4 bytes
void* iSpare[4]; //save space for future
};
class CounterEngine {
public:
__declspec(dllexport) int getTotal();
__declspec(dllexport) int getMean() { return iBody->iMean; }
private:
int iTotal; //4 bytes
CounterEngineBody* iBody; //now used to extend class with 'body' object
};
If your library is open-source then you can request to add it to the upstream-tracker. It will automatically check all library releases for backward compatibility. So you can easily maintain your API.
EDIT: reports for qt4 library are here.
It is hard to maintain binary compatibility - it is much easier to maintain only interface compatibility.
I think that the only reasonable solution is to break supporting current library and redesign it to only export pure virtual interfaces for classes.
That interfaces could never be modified in the future, but you can add new interfaces.
In that interfaces you could only use primitive types like pointers and specified size integers or floats. You should not have interfaces with for example std::strings or other non-primitive types.
When returning pointers to data allocated in DLL, you need to provide a virtual method for deallocation, so that the application deallocates the data using DLL's delete.
Adding data members to the root will break binary compatibility (and force a rebuild, if that is your concern), but it won't break backward compatibility and neither will adding member functions (virtual or not). Adding new member functions is the obvious way to go.