views:

79

answers:

4

We have a struct in revision 1 of a shared library that we need to maintain the ABI for:

struct Person
{
    std::string first_name;
    std::string last_name;
}

In the revision 2, we're changing Person to this:

class Person
{
public:
    Person(const std::string &f, const std::string &l);

    std::string first_name;
    std::string last_name;
}

To maintain source compatibility, we'd like to modify reversion 1 of Person so that code compiled against newer header files will run and code not recompiled will run.

Can we do the following with two new non-inline constructors:

class Person
{
public:
    Person();
    Person(const std::string &f, const std::string &l);

    std::string first_name;
    std::string last_name;
}

We're doing this all with g++. In looking in the generated shared library with nm, I don't see a constructor or destructor for the plain struct, so I'm guessing that code that is not recompiled will just construct the Person at the calling site as before which is fine. Any code that is recompiled will use the no-arg constructor.

The only problem I see is if we need to roll back to an older version of the shared library that doesn't have the constructors, then any code compiled against it will break, but I'm not concerned about this situation.

+2  A: 

I think that it should work, assuming that your explicit default ctor does the same thing as the previously used implicit ctor. In this simple example. However it is IMHO hard to predict or know what the compiler will do/change. I would not trust it myself, I would rather recompile the library users, if I were you.

wilx
+2  A: 

It might "work", but you will be breaking the One Definition Rule, and as far as the C++ Standard goes you will be off in Undefined Behaviour land, which is not a good place to be.

anon
More empahsis on the "Not a good Place to be"
Martin York
+3  A: 

What about the following?

class NewPerson : public Person
{
public:
    NewPerson(const std::string &f, const std::string &l)
    {
      first_name = f;
      last_name = l;
    }
}
sbi
That's a really good idea (compared to the others). :-)
Martin York
Unless he has an array or vector of Person somewhere, or is using Person values, which I have the sneaking suspicion may be the case.
anon
Yes, we actually have a vector of "Person"s that are just a bunch of parameters for a database lookup, which is why we didn't care much about the design of the structure.
Blair Zajac
+1  A: 

You should have no problem adding new non-virtual functions to a class or struct without breaking binary compatibility. This is because a class function is implemented as a normal function taking an implicit this as its first parameter.

If you add a new virtual function however, you may break compatibility since the new function will force the vtable to be modified potentially breaking compatibility.

So adding extra constructors (which can never be virtual) will not break compatibility. If you were to add a virtual destructor, you will most probably break compatibility.

doron
Say's who? The implementation details and thus ABI are left completely to the implementation. If you are going to say it does not break them you better say exactly which compiler version and OS you are talking about. As the old code is using the compiler generated default constructor while the new code has a user defined constructor. Who says that these have the same ABI.
Martin York
@Martin, the standards are silent on issues of ABI, so technically you are correct. However I challenge you to find a C++ ABI that implements non-virtual functions in any other way.
doron
@Martin, does that even matter? As long as the binary layout of the class is the same, then it doesn't matter how Person is constructed, either inline in the calling code or through a constructor?
Blair Zajac
@Blair Zajac: Yes it does matter. I refer you to Neil's answer: 'Your in Undefined Behaviour land, which is not a good place to be.' Your trying very hard to justify being lazy (sorry but lazy I could not think of a better term this late at night). Is there a reason you only want to re-compile half the source. In my opinion its just bad engineering practive.
Martin York
@Martin, the ABI is not undefined, for gcc, this is mentioned in http://gcc.gnu.org/gcc-3.2/c++-abi.html.
doron
@deus-ex-machina399: Your link even gives away the weakness of that claim. The ABI of an implementation may change between versions. (here we have the ABI for 3.2). Is this compatable with the current gcc version? Remember that gcc went through several major ABI changes during versoin 3, there was so many that I forget when they stabalized.See http://gcc.gnu.org/onlinedocs/gcc/Compatibility.html
Martin York
@Martin, the library is at the bottom of a very large stack of software that I don't own. It's a C++ client to a versioned asset management system and any software needing an asset goes through it and people in the company write code to it that I don't keep track of.So it sounds like I'll leave the API alone and people will need modify the source to compile against revision 2 API to populate the fields.
Blair Zajac