13

As a rule, exceptions must not propagate module boundaries as for example explained in Herb Sutters C++ Coding Standards (item 62). When compiled with different compilers or just compiler settings this might crash.

I can understand the issue in case e.g. of dynamic link libraries. But I wonder whether it also holds for static libraries. Is a static library a module in the sense of the above rule? If the library is compiled with other compiler settings (e.g. alignment) might the program crash, if an exception is thrown out of the static library and caught in the application?

Kit Fisto
  • 4,385
  • 5
  • 26
  • 43
  • this sounds like a compiler specific, no? – BЈовић Dec 21 '12 at 13:17
  • Yes and no. Actually the question is, whether throwing and catching those exceptions is compiler specific or not. – Kit Fisto Dec 21 '12 at 13:21
  • 14
    "As a rule" this is only Herbs personal opinion, fed by FUD and even more bad experience with horrible dll hell and crappy compiler scenarios he faced in his past. The same way he sets a "rule" that you should always only ever delete in the same module where you new, which has also only to do with the bad experience he made with one extraordinarily bad implementation. If you are working with sane implementations, ditch this rule. If you have to work with broken implementations, don't have this as a rule, but as a "common ugly workaround". – PlasmaHH Dec 21 '12 at 13:27
  • @PlasmaHH: Your are completely wrong. If a DLL is compiled with different compiler settings the objects in it have a different memory layout and accessing them from the main module will crash. You can easily check this with "sane" compilers of your own choice. – Kit Fisto Dec 21 '12 at 13:47
  • 4
    @KitFisto but that applies to all objects not just exceptions - so the advice should be don't mix incompatible compilers. +1 to PlasmaHH – Jonathan Wakely Dec 21 '12 at 13:48
  • @JonathanWakely Suppose you have to provide a library for some task X and think about deliviring a static lib for it. Then you have to decide whether your library can safely be used if you pass exceptions (or any other object) over its boundaries. The choice of compilers is not yours in this case. – Kit Fisto Dec 21 '12 at 13:51
  • Right, you have no choice: you should use a compiler compatible with the compiler used by the user of the library, and if you do that you can pass exceptions and other objects – Jonathan Wakely Dec 21 '12 at 13:54
  • 1
    @PlasmaHH I've often heard about the restriction concerning `delete` in the same DLL as `new`, but it's never given me any problems, neither with Microsoft compilers nor with g++. As long as `new` and `delete` forward to `malloc` and `free`, and `malloc` and `free` are in their own DLL (so you don't get different copies of them), there should be no problem. On the other hand, things like `dynamic_cast` may not work across DLL's, even if they are compiled identically. And of course, if one module was compiled with iterator debugging, and the other not... – James Kanze Dec 21 '12 at 14:14
  • 1
    @JamesKanze: I am not a windows programmer, but afaik the problem comes when you new/malloc in one dll that uses one version of the crt, and free/delete in a dll that is using another version of the crt, since all crt instances have their own "private" heap – PlasmaHH Dec 21 '12 at 14:26
  • 1
    @JamesKanze try new std::string in a module compiles with debug settings and delete it in a module built with release settings on windows. You won't get very far... – jcoder Dec 21 '12 at 14:43
  • @PlasmaHH That's why you compile with options (/MD or /MDd) which cause the libraries which contain `malloc` and `free` to be DLL as well. A DLL is only loaded once, regardless of how many times it is used. – James Kanze Dec 21 '12 at 15:25
  • @J99 That's a different issue. That doesn't work with any compiler I know; debug versions of `std::string` (and all of the other containers) have a different layout than the non-debug versions. The only thing special about VC++ in this regard: until VC++10, the DLLs which are loaded with /MD or /MDd contained instances of `std::string`, so if you turned off iterator debugging, but still used /MDd, your code would occasionally crash. – James Kanze Dec 21 '12 at 15:28
  • @J99: the same is true for all compilers: violating the ODR invokes UB. – PlasmaHH Dec 21 '12 at 15:40
  • Ok i understand it's not quite the same issue. – jcoder Dec 21 '12 at 15:45

3 Answers3

12

Generally, a static library has to be compiled by the same compiler and the same compiler settings (mostly) to be compatible with the deliverable (a dynamic library or an executable).

You can, then, throw exceptions outside the boundaries of a static library because it's not much different than a set of .obj files your compiler generated. And you obviously can throw exceptions between different .obj modules.

EDIT:

To sum up the comments:

  1. You can only use a static library if you're using the same compiler and compiler settings used to compile the library.
  2. You can throw exceptions between modules compiled with the same compiler and compiler settings.
  3. From 1) and 2) follows that you can throw exceptions from a static library because, if you're using it, that means you're using the same compiler and compiler settings, hence you can throw exceptions.
Alex
  • 7,728
  • 3
  • 35
  • 62
  • 1
    +1 for "object files are more or less static libraries". You might actually add a short review what's different. – Bartek Banachewicz Dec 21 '12 at 13:26
  • "obviously can throw exceptions between different .obj modules": Are you sure this holds also if the obj are compiled by different compilers? – Kit Fisto Dec 21 '12 at 13:32
  • @KitFisto of course it doesn't. The assumption is that you don't share object files (or static libraries) between compilers. In fact, the structure of object files and static libraries is dependent on the compiler so you can't compile a module with CodeGear's C++Builder and pass it to Visual C++'s linker. Such modules can be shared through dynamic libraries, where the binary interfaces match (and they only match to some extent, for example you can't directly share classes or anything that requires name mangling). – Alex Dec 21 '12 at 13:36
  • @Alex Yes, sure, but e.g. AFAIK libs from VS 2008 can be linked with code from VS 2010. As Christopher states in his answer to [this question](http://stackoverflow.com/questions/171816/vc9-and-vc8-lib-compatibility) the in-memory representation is yet not identical. – Kit Fisto Dec 21 '12 at 13:39
  • @KitFisto if we're talking about two specific compilers (or compiler versions) they may or may not be compatible, and that depends entirely on the compiler vendor. As the [thread](http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/8042a534-aa8b-4f99-81ee-e5ff39ae6e69/) you link to suggests, Microsoft doesn't make any guarantees that they are compatible. They _may_ be compatible if you only use specific features whose internal representation hasn't changed, but you'd be doing it at your own risk. – Alex Dec 21 '12 at 13:56
  • @Alex This sound like you are weakening your answer: "You can throw excpetions outside the boundaries of a static library" if the two compilers are compatible. But in this case I can also make them pass the DLL boundary. The actual question was whether it is safe in general to assume that these exceptions will not crash the app. – Kit Fisto Dec 21 '12 at 14:04
  • @KitFisto According to Microsoft, libs from VS2008 are _not_ guaranteed to work with VS2010. In practice, _unless_ the libraries have been compiled to the same ABI, they cannot be linked, and the C++ ABI can change not only between major releases, but also depending on the compiler options. (I've had problems with both g++ and Microsoft just by changing the compiler options.) In practice, the C ABI is far more stable, as it is usually used to define the system ABI as well; if you limit your interface to C, then you'll have less problems. – James Kanze Dec 21 '12 at 14:10
  • DLL libraries can be designed in a way that will allow you to call them from applications compiled with different compilers. Doing so imposes limits, such as "export only C-style functions", "return classes as pure virtual interfaces COM style", "don't throw exceptions", etc. You **can't** do the same for static libraries because they are, in general, not compatible with other compilers at all, so you can drop all those requirements - your static library will only work on the same compiler and compiler settings anyway! Hope that makes sense now. – Alex Dec 21 '12 at 14:15
  • @Alex While I do not believe that your first sum up item is valid in all cases, I now think that "in general" throwing exceptions from the static lib is safe. Thanks. – Kit Fisto Dec 21 '12 at 14:42
  • @Alex Who says you can't do it with static libraries? (Nobody does, but I don't see where the issues would be any different.) – James Kanze Dec 21 '12 at 15:29
  • @JamesKanze you can't do it because the format of the static libraries (or, equivalently, object files) can be different between compilers. For example, not everyone uses COFF, C++Builder mangles names differently, etc. – Alex Dec 21 '12 at 16:12
  • @Alex Just as with dynamic modules, you have to follow certain rules, like using a C interface. Otherwise, the problems are (potentially) the same. – James Kanze Dec 22 '12 at 00:26
  • @JamesKanze I don't think they are the same. For example, you are compiling a 32-bit dynamic library for Windows. The output format the compiler has to generate is then defined by Windows. Suppose you're creating an equivalent static library. The output the compiler generates in this case is defined by the _compiler_, not Windows. Nothing forces different compilers to be compatible in any way when compiling static libraries. – Alex Dec 22 '12 at 18:36
  • @Alex Nothing forces different compilers to use the same technique for dynamic loading, either. But you're sort of right: with a static library, everything is under control of the compiler (or more correctly, the compiler driver, or the translation system). While there's nothing to prevent a translation system from implementing its own dynamic loader, it doesn't make sense, and any reasonable system will just use the one already present in the OS. – James Kanze Dec 22 '12 at 21:58
6

Herb Sutters' description is also suitable for static library:

There is no ubiquitous binary standard for C++ exception handling. Don't allow exceptions to propagate between two pieces of code unless you control the compiler and compiler options used to build both sides; otherwise, the modules might not support compatible implementations for exception propagation. Typically, this boils down to: Don't let exceptions propagate across module/subsystem boundaries.

billz
  • 44,644
  • 9
  • 83
  • 100
  • How do you know he also talks of static libraries when he says "module"? The question for me is whether compilers will allow me to link to a static library if the classes in it are not compatible on a binary level. – Kit Fisto Dec 21 '12 at 13:42
  • He mentions binary, rest is my unerstanding – billz Dec 21 '12 at 13:51
1

It depends on what Herb means by "module". And the issues don't only concern exceptions; they can concern anything using a C++ interface.

There is certainly no problem when exceptions cross translation unit boundaries of sources compiled as part of the same component. Between components, if they are all part of the same application, and you ensure that they are all compiled using the same compiler, with the same compiler options, it may be safe, although there can be problems when crossing between dynamic libraries, depending on how the libraries are loaded. (In general, this is only a problem on Unix systems, where the visibility of symbols in dynamically loaded components is controlled by the options passed to the dynamic loader.) As a general rule: arrange to have all of your application compiled with the same compiler and the same compiler options, and you should have no real problems within the application (although you may have to ensure that all dynamic components are explicitly loaded, at least under Unix). Between "applications", where you are loading or being loaded by "foreign" software, Herb's restrictions don't go far enough. In practice, the interface where you cross between the applications must be defined in C. And there may still be restrictions, depending on how your code is loaded and what other dynamically loaded components are being used.

Linking statically will remove the problems with regards to how the library is loaded, but changes nothing else.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Sorry, but I don't see what your answer to the question is: Is a static library a module in the sense of Herb Sutters rules? Yes or no? – Kit Fisto Dec 21 '12 at 14:24
  • I don't know about Herb Sutter's rules, but in this case, a module is something that is generated separately: it could be a static library. My whole point is, however, that whether the library is static or dynamic isn't the question; the question is whether it is compiled as part of your application, with the same compiler and the same options, or not. – James Kanze Dec 21 '12 at 15:23