Travis Vitek wrote:
Travis Vitek wrote
Martin Sebor wrote:
I've incorporated everyone's feedback and committed an updated
version with a number of enhancements of my own. Among the most
important are the new Goals section with suggested frequencies of
releases, and the integration of the Version Policy (I plan to delete
versions.html). Let me know what you think.
http://incubator.apache.org/stdcxx/releases.html
Martin,
I found this page that documents a few do's and dont's of binary
compatibility. I realize that most of the tricky issues involve inline
functions or templates, but this gives a list of the common pitfalls.
http://tinyurl.com/2gf38p
Travis
Here are some examples that I came up with. Each case is written in a
pseudo diff format.
Excellent! See my comments below.
Travis
// neither source nor binary compatible. not source compatible
// if user might take address of member function A::f.
struct A
{
- int f() const { return 1; }
+ int f(int i = 1) const { return i; }
};
// alternative is source and binary compatible. can be changed
// to a default argument in next source incompatible release.
struct A
{
int f() const { return 1; }
+ int f(int i) const { return i; }
};
// is binary compatible, but not be source compatible because
// the compiler has no way to handle A().f(1)
struct A
{
int f(long i) const { return 2; }
+ int f(unsigned i) const { return 2; }
};
I would tend to throw these in the bag of "obvious no-no's."
// not binary compatible, changing access specifier on windows is a
no-no. if the
// new access modifier is more restricted, this may be source
incompatible, but
// only if the user code calls or takes the address of the function.
class A
{
-private:
+public:
int f() const { return 1; }
};
This one is much less obvious and so it might be worth mentioning
in the document.
// source and binary compatible, not functionally compatible. f() will
square v
// in user code, and ctor will square v in library code. if an instance
is
// created in library and passed to user code, it will be squared
twice. if the
// other way, it will not be squared at all.
//
// if the definitions were outlined, this would be compatible.
class A
{
public:
- A(float f) : v(f) { }
- float f() const { return v*v; }
+ A(float f) : v(f*f) { }
+ float f() const { return v; }
private:
float v;
};
This is an interesting case. Why (when) does it matter that the result
of f() is different? What does it mean for STDCXX-226?
// binary and source compatible, but not functionally compatible
// because call to g() is inlined.
//
// this would be compatible if f() were outlined, or g() behaved
// the same for input values 2 and 3.
struct A
{
- void f() { g(2); }
+ void f() { g(3); }
void g(int i);
};
Same as above.
// it appears that this could be fully compatible in some cases.
// it might not be source/functionally compatible if the user is
// exposed to this type. a switch on an E instance might cause a
// default block to be hit, which could trigger a failure in user
// code.
//
// if A::Z is used as a `last enum' marker, this might introduce
// a binary compatibility issue if a global or member array is
// declared to have A::Z elements.
//
// this might also be binary incompatible if the enum is persisted.
struct A
{
- enum E { W, X, Z };
+ enum E { W, X, Y, Z };
};
I'd say this is both source and binary incompatible.
Consider:
switch (e) {
case A::Z: break;
case 4: break; // okay in version 1 but error on version 2
}
and:
template <int> struct S { };
void foo (S<A::Z>()); // ==> mangled as foo_S_A_2 in version 1
// but foo_S_A_3 in version 2
Thanks again. These are exactly the kind of examples I was hoping for:
innocuous looking changes that are fully compatible in most situations
but can be actually be breaking in some edge cases.
Martin