static library, but I still need headers?
Asked Answered
H

4

28

I have a bunch of projects that all could share a "common" static library of classes.

What confuses me is if I make a static library out of these classes and link against it in my projects that I still need the headers of the classes in the static library in my main projects.

What is the benefit of the static library then?

How do companies like Adobe deal with this?

Henrieta answered 10/4, 2010 at 2:43 Comment(0)
G
45

Static libraries allow you to create a library and use that library in many projects.

The need for header files:

Since the project using the library is programmed and compiled independent of the library, that program needs to know the declaration of the things you're using. Otherwise how would your compiler know you're writing valid code?

A compiler only takes source code as input and produces output. It does not deal with compiled object files or static libraries on input.

The need for linking in the library:

So having the headers allows you to write valid code in your project, but when it comes to link time you'll need to provide the definition which is contained inside the static library.

The linker takes all object files (compiled code) and also all static libraries and produces an executable or binary.

More info about static libraries (benefits, comparing dynamic, etc...):

Amongst other things, it is nice to separate your project into libraries so that you don't end up with 1 huge monolithic project.

You do not need to distribute the source code (typically in the .cpp files) this way.

If you were to simply include all the .cpp files in every project that used the common library then you would have to compile the .cpp files each time.

An advantage of static libraries over dynamic libraries is that you can always be sure that your programs will be self contained and that they are using the correct version of the library (since they are compiled into the executable itself). You will also have a slight speed advantage over dynamic linking.

Disadvantages of static libraries over dynamic libraries include that your file sizes will be bigger because each executable needs their own copy, and that you can't swap out a different version of the library since it's not dynamically loaded.

Re your question: How do companies deal with this:

A typical company will make use of both static and dynamic libraries extensively.

Grum answered 10/4, 2010 at 2:47 Comment(4)
@Brian - Got it. Do you have a reference for Dynamic libraries on OS X?Henrieta
Another benefit to either kind of library: reduced compilation time.Sheik
developer.apple.com/Mac/library/documentation/DeveloperTools/… And, if you're talking OS X, you probably want to look into building a framework, which is (basically) a way of packaging together a library and the corresponding headersPolynesia
Correct me if I am wrong, a dynamic shared library .so doesn't have to come with the headers right, unlike the static library .a.Ctn
I
2

The typical way you make use of a static library is to have a target in your Makefile (or whatever build system you use) that installs the headers into an appropriate location at the same time that it installs the library.

So, your static library ends up in /usr/local/lib, and the headers go into /usr/local/include or wherever.

Inverter answered 10/4, 2010 at 3:14 Comment(0)
E
1

Also, when compared with linking against object files, linking against static library may result is a smaller final executable. The reason for this is, if you don't call any of the functions from a particular object file (included in the static library), the linker will not include the code for those functions in you final executable. See Extraneous Library Linkage

Epiphenomenalism answered 14/4, 2010 at 7:45 Comment(0)
H
0

Even though you can produce an executable from sources and library files with a single command, conceptually the process involves two steps (which you can perform separately if you wish):

  1. For each file separately: Preprocess (expand include files and macros) and compile into an object file
  2. Link the resulting object files and libraries mentioned on the command line into an executable.

If you think of how compiling a single cpp file works it is clear that any name you use must be declared. You can do that "manually" (simply write e.g. extern double sqrt(double); before you call the function for the first time in your program); in that case you don't need to include math.h which will contain basically the same line. The compiler which sees the code after the include files have been inserted does not know or care where the line comes from.

In the following example I use the standard math library without including the header; I simply declare the sqrt function "manually".

echo $? displays the exit value of the last command (here: our program) which should be the square root of 4:1

$ cat main.cpp
extern "C" double sqrt(double);

int main()
{
        return sqrt(4.0);
}

$ g++ -c main.cpp
$ g++ -o main main.o -lm
$ ./main; echo $?
2


1 The return value of main (which becomes the exit value of the program) has type int so that the double returned by sqrt undergoes a "narrowing conversion" before it is returned ("narrowing" because information is potentially lost). This can be tricky: Results of computations which are mathematically integers may result in floats that are very close but not quite equal to that integer. If that value is minimally smaller than the expected result, a conversion to int results in a value that's off by 1, surprisingly. The surprise is compounded because the output functions typically round (also up, when necessary), as opposed to the conversion which always cuts off fractions. Example:

$ cat main.cpp
#include <iostream>
#include <iomanip>
#include <limits>
using namespace std;

int main()
{
        double not_1 =  0.3 + 0.3 + 0.3 + 0.1;
        cout << not_1 << endl;
        int max_precision{std::numeric_limits<double>::digits10 + 1};
        cout << setprecision(max_precision) << not_1 << endl;
        cout << (not_1 == 1 ? "0.3 + 0.3 + 0.3 + 0.1 == 1"
                        : " 0.3 + 0.3 + 0.3 + 0.1 != 1 !") << "\n";
        return not_1;
}

$ g++ -o main main.cpp && ./main; echo $?
1
0.9999999999999999
 0.3 + 0.3 + 0.3 + 0.1 != 1 !
0

But the quality of implementation of sqrt in libm is such that it returns 2.0 for an argument of 4.0, helped by the fact that small-ish integers are lossless representable in floating point formats. Our original program in the answer does not suffer from precision issues.

Hhour answered 17/5 at 9:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.