34

One of my major complaints about C++ is how hard in practice it is to pass std library objects outside of dynamic library (ie dll/so) boundaries.

The std library is often header-only. Which is great for doing some awesome optimizations. However, for dll's, they are often built with different compiler settings that may impact the internal structure/code of a std library containers. For example, in MSVC one dll may build with iterator debugging on while another builds with it off. These two dlls may run into issues passing std containers around. If I expose std::string in my interface, I can't guarantee the code the client is using for std::string is an exact match of my library's std::string.

This leads to hard to debug problems, headaches, etc. You either rigidly control the compiler settings in your organization to prevent these issues or you use a simpler C interface that won't have these problems. Or specify to your clients the expected compiler settings they should use (which sucks if another library specifies other compiler settings).

My question is whether or not C++11 tried to do anything to solve these issues?

9
  • 3
    I don't know the answer to your question, but I can say that your concerns are shared; they're a key to why I won't use C++ in my projects, as we value ABI stability over squeezing out every last cycle of potential efficiency. Nov 21, 2012 at 14:31
  • 2
    Please distinguish. It's hard between DLLs. Between SOs it always worked just fine.
    – Jan Hudec
    Nov 21, 2012 at 14:54
  • 1
    Strictly speaking, this is not a C++ only problem. It is possible to have this problem with other languages.
    – MrFox
    Nov 21, 2012 at 16:00
  • 2
    @JanHudec I can guarantee that between SOs does not work nearly so magically as you seem to indicate. Given symbol visibility and how name mangling often works, you may be more insulated from a problem, but compiling one .so with different flags/etc., and assuming you can link it in a program with other flags is a recipie for disaster.
    – sdg
    Nov 21, 2012 at 18:05
  • 3
    @sdg: With default flags and default visibility it works. If you change them and get in trouble, it's your problem and nobody else's.
    – Jan Hudec
    Nov 22, 2012 at 6:35

3 Answers 3

19

You are correct that anything STL - actually, anything from any third party library which is templated - is best avoided in any public C++ API. You also want to follow the long list of rules at http://www.ros.org/reps/rep-0009.html#definition to inhibit ABI breakage which makes programming public C++ APIs a chore.

And the answer regarding C++11 is no, this standard isn't touching that. More interesting is why not? The answer is because C++17 is very much touching that, and for C++ Modules to be implemented we need exported templates to work, and for that we need a LLVM type compiler such as clang which can dump the full AST to disc and then do caller-dependent lookups to handle the many ODR violating cases in any large C++ project - which, by the way, includes lots of GCC and ELF code.

Lastly, I see a lot of MSVC hate and pro-GCC comments. These are very misinformed - GCC on ELF is fundamentally, and irretrievably, incapable of producing valid and correct C++ code. The reasons for this are many and legion, but I'll quickly quote one case example: GCC on ELF cannot safely produce Python extensions written using Boost.Python where more than one extension based on Boost.Python is loaded into Python. That's because ELF with its global C symbol table is simply incapable by design of preventing ODR violations causing segfaults, whereas PE and MachO and indeed the proposed C++ Modules specification all use per-module symbol tables - which incidentally also means vastly faster process init times. And there are plenty more problems: see a StackOverflow I answered recently at https://stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055 for example where C++ exception throws are irretrievably fundamentally broken on ELF.

Last point: regarding interoping different STLs, this is a big pain for many large corporate users trying to mix third party libraries which are tightly integrated to some STL implementation. The only solution is a new mechanism for C++ to handle STL interop, and while they're at it you might as well fix compiler interop too so you can (for example) mix MSVC, GCC and clang compiled object files and it all just works. I'd watch the C++17 effort and see what turns up there in the next few years - I'd be surprised if nothing does.

7
  • Great response! I only hope Clang improves windows compatibility, and it might set a good default standard compiler. The textual inclusion/header system of C++ is horrible, I'm looking forward to the day that modules simplify C++ code organization, infintely speeds compile times, and improve compiler interoperability with ODR-violating catches. Jan 17, 2013 at 17:25
  • 3
    Personally, I'm actually expecting a substantial increase in compiler times. Traversing an intra-module AST quickly is very hard, and we'll probably need an in-memory shared memory cache of it. However, almost everything else that is bad gets better. BTW, header files are definitely staying around, the current C++ modules has interface files map 1-to-1 to header files. Also, auto-generated interface files will be legal C++, so a legacy header simply gets C macros filtered out and spat out as an interface files. Nice eh? Jan 17, 2013 at 18:39
  • Cool! I have so many doubts about modules. Will the module system take into consideration Textual Inclusion vs Symbolic Inclusion? With the present include directive the compiler has to recompile tens of thousands of lines of code over and over again for every source file. Will the modules system allow someday code without forward declarations? Will it improve/ease building tools? Jan 17, 2013 at 20:25
  • 2
    -1 for suggesting that all third-party templates are suspect. Changing the configuration is independent of whether the thing being configured is a template.
    – DeadMG
    Jan 17, 2013 at 20:37
  • 1
    @Alessandro: The proposed C++ modules explicitly disables C macros. You can use templates, or nowt. The proposed interfaces are legal C++, merely autogenerated, and can be optionally precompiled for speed of reparsing i.e. don't expect any speedup over existing precompiled headers. The last two questions, I actually don't know: it depends :) Jan 17, 2013 at 21:55
9

The specification never ever had this issue. That's because it has concept called "one definition rule", which mandates that each symbol has exactly one definition in the running process.

Windows DLLs violate this requirement. That's why there are all these problems. So it's up to Microsoft to fix it, not C++ standardization committee. Unix never had this problem, because shared libraries work differently there and by default conform to one definition rule (you can explicitly break it, but you obviously only do if you know you can afford it and need to squeeze out the few extra cycles).

Windows DLLs violate one definition rule because:

  • They hardcode from which dynamic library a symbol will be used during static link time and resolve symbols statically within the library that defines them. So if the same weak symbol gets generated in multiple shared libraries and those libraries than get used in single process, the dynamic linker has no chance to merge those symbols. Usually such symbols are static members or class impedimenta of template instances and it than causes problems when passing instances between code in different DLLs.
  • They hardcode whether symbol will be imported from dynamic library already during compilation. Thus code linked with some library statically is incompatible with code linked with the same library dynamically.

Unix using ELF format exports implicitly imports all exported symbols to avoid the first problem and does not distinguish between statically and dynamically resolved symbols until static link time to avoid the second.


The other issue is of compiler flags. That issue exists for any program composed from multiple compilation units, dynamic libraries don't have to be involved. However it's much worse on Windows. On Unix it does not really matter whether you link statically or dynamically, nobody links standard runtime statically anyway (in Linux it might even be illegal) and there is no special debug runtime, so one build is good enough. But the way Microsoft implemented static and dynamic linking, debug and release runtime and some other options means they caused combinatorial explosion of needed library variants. Again platform issue rather than C++ language issue.

17
  • 2
    @DougT.: GCC has nothing to do with it. The platform ABI has. In ELF, the object format used by most Unices, shared libraries export all visible symbols and import all symbols they export. So if something gets generated in multiple libraries, the dynamic linker will use the first definition for all. Simple, elegant and working.
    – Jan Hudec
    Nov 21, 2012 at 15:17
  • 1
    @MartinBa: There is nothing to merge, but it does not matteras long as it's the same and as long as it is not supposed to be merged in the first place. Yes, iff you use incompatible compiler settings on an ELF platform, you get the same mess as anywhere and everywhere. Even if not using shared libraries, so it's somewhat off-topic here.
    – Jan Hudec
    Nov 23, 2012 at 9:20
  • 1
    @Jan - it is relevant to your answer. You write: "... one definition rule ... Windows DLLs violate this requirement ... shared libraries work differently [on UNix] ..." but the question asked pertains to problems with std-lib stuff (defined in headers) and the reason there's no problem on Unix has nothing to do with SO vs. DLL but with the fact, that on Unix (apparently) there is only one compatible version of the standard library while on Windows MS chose to have incompatible (debug) versions (with extended checking etc.).
    – Martin Ba
    Nov 23, 2012 at 10:30
  • 1
    @MartinBa: No, the main reason there is problem on Windows is that the export/import mechanism used on Windows can't properly merge static members and class impedimenta of template classes in all cases and can't merge statically and dynamically linked symbols. Than it's made much worse by the multiple library variants, but the primary problem is that C++ needs flexibility from linker that the Windows dynamic linker does not have.
    – Jan Hudec
    Nov 23, 2012 at 10:42
  • 4
    I think that this implication that the DLL specification is broken and the corresponding demand for Msft to 'fix it' are misplaced. The fact that DLLs don't support certain features of C++ is not a defect of the DLL specification. DLLs are a language-neutral, vendor-neutral packaging mechanism and ABI to expose entry-points to machine code ('function calls') and data blobs. They were never intended to natively support advanced features of any particular language. It's not Msft's, or the DLL specification's fault that some people want them to be something else. Dec 21, 2012 at 6:29
6

No.

There is a lot of work going on to replace the header system, feature which is called Modules and which could have an impact on this, but certainly not a big one.

5
  • 2
    I don't think header system would have any impact on this. The problems are that Windows DLLs violate one definition rule (which means they don't follow C++ spec, so C++ committee can't do anything about it) and that there are so many variants of standard runtime in Windows, which C++ committee can't do anything about either.
    – Jan Hudec
    Nov 21, 2012 at 15:08
  • 1
    No, they don't. How could they, the specification does not even mention something of that kind. Other than that, when a (Windows) program is linked with Windows dlls, the ODR is satisfied: all visible (exported) symbols must obey the ODR. Jan 14, 2013 at 10:54
  • @PaulMichalik C++ does cover linking (phase 9) and it seems to me that at least load-time linking of DLLs/SOs falls within phase 9. That means that symbols with external linkage (whether exported or not) should be linked and conform to the ODR. Dynamic linking with LoadLibrary/dlopen obviously does not fall under those requirements.
    – bames53
    Jan 16, 2013 at 21:29
  • @bames53: IMHO, the specs is far too weak to allow statements of that kind. A .dll/.so could be seen as a "program" on its own. Than, the rules were satisfied. Something like loading other "programs" at run-time is so underspecd by the standard that any statements regarding this are pretty arbitrary. Jan 17, 2013 at 6:55
  • @PaulMichalik If an executable requires load-time linking then prior to load-time linking there are external entities left unresolved and information needed for execution is missing. LoadLibrary and dlopen are outside the spec but load-time linking pretty clearly must be part of phase 9.
    – bames53
    Jan 17, 2013 at 14:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.