9

I know adding static member function is fine, but how about an enum definition? No new data members, just it's definition.


A little background:

I need to add a static member function (in a class), that will recognize (the function) the version of an IP address by its string representation. The first thing, that comes to my mind is to declare a enum for IPv4, IPv6 and Unknown and make this enum return code of my function.

But I don't want to break the binary backward compatibility.

And a really bad question (for SO) - is there any source or question here, I can read more about that? I mean - what breaks the binary compatibility and what - does not. Or it depends on many things (like architecture, OS, compiler..)?


EDIT: Regarding the @PeteKirkham 's comment: Okay then, at least - is there a way to test/check for changed ABI or it's better to post new question about that?

EDIT2: I just found a SO Question : Static analysis tool to detect ABI breaks in C++ . I think it's somehow related here and answers the part about tool to check binary compatibility. That's why I relate it here.

Community
  • 1
  • 1
Kiril Kirov
  • 37,467
  • 22
  • 115
  • 187
  • 4
    Probably not, though completely compiler dependent. The C++ standard says nothing about binary object format. – Pete Kirkham Nov 18 '11 at 14:45
  • Okay then, added `gcc` tag. I know, that the standard does not say anything about that, but I thought that there could be another standard for this, something related to `ABI` or something :? – Kiril Kirov Nov 18 '11 at 14:54
  • 1
    @Kiril: the ABI will be defined *either* by the C++ implementation, *or* by the vendor of a particular platform, or some combination of the two. It's best to use a platform-specific ABI as far as possible, because then different C++ implementations on the same platform at least have a chance of producing link-compatible binaries. However, some things are always implementation-specific, such as the size and layout of standard library classes, which is why implementations aren't necessarily even link-compatible with themselves (debug vs non-debug) even though following the same C++ ABI. – Steve Jessop Nov 18 '11 at 15:13
  • ... so for a given platform, the C ABI is usually a safer bet than any C++ ABI. It's smaller, and implementations are going to have to follow it for `extern "C"` stuff if they want to be able to link against the platform's system calls and suchlike. – Steve Jessop Nov 18 '11 at 15:14
  • @SteveJessop - ah, this confused me more. Do you know any good source, I can read more about that? I'm not familiar with these kind of things at all and obviously, I really need to do something about that :) Thanks! – Kiril Kirov Nov 18 '11 at 15:19
  • Not sure. Start by taking a look at Matthieu's link to the Itanium ABI. There's way more detailed information there than you need to know, but it gives you an idea of how an ABI is defined and what's involved. Of course you don't need to know all this to know the answer to your specific question, which is basically what James Kanze says -- adding an enum to a class doesn't change the object layout, and doesn't change the vtable layout, so in practice it won't break binary compatibility. – Steve Jessop Nov 18 '11 at 15:22

2 Answers2

6

The real question here, is obviously WHY make it a class (static) member ?

It seems obvious from the definition that this could perfectly be a free function in its own namespace (and probably header file) or if the use is isolated define in an anonymous namespace within the source file.

Although this could still potentially break ABI, it would really take a funny compiler to do so.

As for ABI breakage:

  • modifying the size of a class: adding data members, unless you manage to stash them into previously unused padding (compiler specific, of course)
  • modifying the alignment of a class: changing data members, there are tricks to artificially inflate the alignment (union) but deflating it requires compiler specific pragmas or attributes and compliant hardware
  • modifying the layout of a vtable: adding a virtual method may change the offsets of previous virtual methods in the vtable. For gcc, the vtable is layed out in the order of declaration, so adding the virtual method at the end works... however it does not work in base classes as vtable layout may be shared with derived classes. Best considered frozen
  • modyfing the signature of a function: the name of the symbol usually depends both on the name of the function itself and the types of its arguments (plus for methods the name of the class and the qualifiers of the method). You can add a top-level const on an argument, it's ignored anyway, and you can normally change the return type (this might entails other problems though). Note that adding a parameter with a default value does break the ABI, defaults are ignored as far as signatures are concerned. Best considered frozen
  • removing any function or class that previously exported symbols (ie, classes with direct or inherited virtual methods)

I may have forgotten one or two points, but that should get you going for a while already.

Example of what an ABI is: the Itanium ABI.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Well, if I make this function `static` it will fit perfectly in the current design of the whole wrapper (for any kind of socket transport). Also, as far as I know, for `gcc` this will no break the binary compatibility. So, adding `enum` definition will not break it, right? – Kiril Kirov Nov 18 '11 at 15:10
  • Ha, thanks for the link, it looks great:) But is there a way to check for broken ABI? Some kind of tool or something? – Kiril Kirov Nov 18 '11 at 15:12
  • @KirilKirov: unfortunately I don't know of any tool. The `enum` declaration and `static` functions *should* not break the ABI of any sane compiler. I can vouch for gcc and Clang :) – Matthieu M. Nov 18 '11 at 15:16
5

Formally... If you link files which were compiled against two different versions of your class, you've violated the one definition rule, which is undefined behavior. Practically... about the only things which break binary compatibilty are adding data members or virtual functions (non-virtual functions are fine), or changing the name or signature of a function, or anything involving base classes. And this seems to be universal—I don't know of a compiler where the rules are different.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Yes, the "undefined behavior" is what scares me, as the change is in a core lib, used for a lot of apps. Thanks for the answer, +1 – Kiril Kirov Nov 18 '11 at 15:15
  • @KirilKirov I understand your worries. In this case, I think if any compiler were going to create a problem here, it already would have. I suggested publicly a long time ago that compilers store a hash of the definition in the object file, so that the linker could check and generate an error. They haven't, and from an implementation point of view, there's no reason for any changes other than those a mentioned to cause a break in binary compatibility. So it's a risk, but it's a calculated risk, which is probably safe. – James Kanze Nov 18 '11 at 16:11