1

We have been converting our Java and .NET API library to C++, and are trying to figure out what is the best way to distribute a compiled version to other developers to use with their custom applications. Should it be a static library or dynamic library?

We need to create for both Win32 and Win64 (and I suppose both a Debug and Release version of each target OS). Given all the frustration I've encountered trying to make sure all referenced libraries are matched (/MT versus /MD), I'm wondering if there is a decision to make here which will simplify it for other developers.

When I run dumpbin /all <static library file name> | find /i "msvc on a static library, I don't see any runtime reference (unlike when I do the same on a .exe or .dll). Does that indicate that the runtime isn't linked yet, and that gives developer more flexibility to make the /MT or /MD when they develop and build their own app?

Which approach would make the developers' life easier?

Sam Goldberg
  • 6,711
  • 8
  • 52
  • 85

2 Answers2

5

Static libraries are easier to create but much harder to distribute. The client programmer is going to link them into their program so it is very important that your compile settings are compatible with theirs. You must distribute at least 4 versions, corresponding with the 4 different CRT versions (/MD, /MDd, /MT, /MTd). And you need to multiply this by the number of Visual Studio versions that are in common use. If you don't know what the client programmer is going to use then that's potentially a very large list.

Not a problem with a DLL, you only supply a single .h for the exported function declarations, a .lib that's the import library for the DLL (no code, just contains names) and the .dll itself.

It is however harder to create an interface for your DLL that's usable from any C or C++ compiler. You cannot expose any standard C++ library classes, returning an std::string is not going to work for example. You cannot create any function that allocates memory that needs to be released by the caller. You cannot in general throw exceptions across the boundary. Doing any of these things tend to cause very hard to diagnose runtime problems for the client programmer, caused by mismatched memory allocators and class object layout differences. The COM object model is an example of such an interface.

This is not the kind of problem you'd have with static libraries. Somewhat by accident, they require a runtime and compiler version match. If the client programmer is stuck with a static library that's the wrong flavor then they'll have all of those problems as well.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Hi Hans, could you explain why, with the static library I built, I couldn't use `dumpbin` to show which runtime it was built for? Not seeing any reference to msvv* made me think that the runtime was being deferred until the library was linked with. – Sam Goldberg Aug 02 '13 at 20:09
  • The runtime dependency is indirect through #includes in the original source code. It is the linker that nails it down. #including the headers for one version but linking with another is the problem, the mismatch generates very ugly linker errors. For VS2005 and VS2008 you can see it because of the manifest link instruction. But that was removed again in VS2010 and up. Which has another way to enforce it, you'll get a /FAILIFMISMATCH linker error. This is the ultimate kind of linker hell, you'll get a support call. – Hans Passant Aug 02 '13 at 20:16
  • Is Visual Studio changing the base directory of the #include files you reference depending on which Runtime you choose? – Sam Goldberg Aug 02 '13 at 20:50
  • No. That's another problem, the linker picks one of four .lib files to link. Based on the /MT vs /MD setting. Which is why you always need to supply 4 versions of the library. The /FAILIFMISMATCH linker error is a compiler version number problem. – Hans Passant Aug 02 '13 at 20:58
  • One problem I found with a DLL though, is that the user is required to compile with the same version of Visual Studio as the DLL was created with. See [this problem](http://stackoverflow.com/questions/18021517/visual-c-release-build-is-string-getting-corrupted-when-passed-across-dll-be) which took quite a while for me to diagnose, even though both were compiled on same machine, and same runtime type selected. So with a DLL you are tying the user to a particular version of the compiler. (And maybe also doing that with static library?) – Sam Goldberg Aug 02 '13 at 21:22
  • 1
    I explicitly addressed this in my answer, I even mentioned std::string. They are fine when you use them in the *implementation*, you just can't expose them across the library boundary. If you cannot live without the standard C++ library classes then DLLs do not solve the versioning problem. – Hans Passant Aug 02 '13 at 21:31
  • 1
    Hi Hans, regarding your answer about /MD, /MDd, /MT, /MTd: it's a good point I had not thought of. But one way to look at it is that it is better for a _developer_ linking your library to get a nasty linker error, than an end-user to get a runtime error. For example, if you distribute your library as a DLL and use /MTd but the process that loads it used /MT, if there is a malloc in your library and a corresponding free in the process that loads it, there will be horrible crashes since two different heaps are used. The linker is doing everyone a favor by complaining. – David Stone Aug 07 '13 at 19:53
  • And similarly with SxS (side-by-side) hell, if you distribute your library as a DLL copmiled with /MD or /MDd and the end-user doesn't have the right version of the VC runtimes on their system, I think the DLL cannot be loaded by the process. – David Stone Aug 07 '13 at 19:55
4

There are significant advantages to both static and dynamic (shared) libraries. Maybe one option for you is to distribute both types of libraries, and let the library users decide which to use.

For myself, I usually use static libraries unless I think the advantages of a dynamic library are meaningful for the project.

Among the advantages of a static library:

  • Once linked in at compile-time by the user of the library, the code in the library is always in the same module that needs to call it. So no DLL hell, and no side-by-side hell. I cannot count the number of times I've tried to run a program on Windows only for it to fail because I don't have the right version of the MSVC runtime (DLL) installed. This is really a pain and if it can be avoided, it makes everyone's life easier.
  • Similarly, the symbols for the code in the library will end up in the .pdb of the module that calls the library, rather than in another .pdb (the .pdb of the .dll) that you have to keep track of, copy around, etc.
  • This is minor, but for static libraries only the functions/data you need to link it end up in the executable. Whereas for a DLL, it's the whole enchilada.

Among the advantages of a dynamic library

  • The obvious advantage is that it allows the library to be replaced at runtime, even by an end-user, without a relink.
  • This is minor in most environments, but if a lot of executables link the library in, having a DLL means less disk space since the same data/code isn't repeated in every executable.
  • Something that isn't always appreciated: if the library is going to be loaded by multiple processes at the same time, providing it as a DLL means - ideally - only one copy of the read-only data (and even the writable data until and unless it is written to by a particular process) needs to be in memory. All the processes that runtime-link the DLL share the same bytes in memory. The memory shared is both total virtual memory (RAM+pagefile) and physical memory. However this is all only best-case - if the DLL can't be loaded at the same virtual address in two processes, they can't share it.
David Stone
  • 1,132
  • 7
  • 18
  • "Similarly, the symbols in the .pdb of the module that calls the library." I didn't follow what you are saying with that statement. Could you please elaborate a little bit? – Sam Goldberg Aug 02 '13 at 21:18
  • Hi Sam, looks like that statement was unclear the way I wrote it, I edited it now. Let me know if it still doesn't make sense. – David Stone Aug 05 '13 at 12:50