GCC: Difference between revisions
mNo edit summary |
|||
(3 intermediate revisions by the same user not shown) | |||
Line 59: | Line 59: | ||
g++ -g -Wall myprog.C -o myprog | g++ -g -Wall myprog.C -o myprog | ||
== Compiling C and C++ Programs =+ | |||
gcc is the GNU C Compiler and g++ is the GNU C++ compiler. Below are several examples that show how to use g++ to compile C++ programs. | |||
Example 1: Compiling a simple program | |||
Consider the following example: Let "hello.C" be a file that contains the following C++ code. | |||
#include "iostream.h" | |||
int main() | |||
{ | |||
cout << "Hello\n"; | |||
} | |||
== standard way == | |||
The standard way to compile this program is with the command | |||
g++ hello.C -o hello | |||
This command compiles hello.C into an executable program named "hello" that you run by typing './hello' at the command line. It does nothing more than print the word "hello" on the screen. | |||
Alternatively, the above program could be compiled using the following two commands. | |||
g++ -c hello.C | |||
g++ hello.o -o hello | |||
The end result is the same, but this two-step method first compiles hello.C into a machine code file named "hello.o" and then links hello.o with some system libraries to produce the final program "hello". In fact the first method also does this two-stage process of compiling and linking, but the stages are done transparently, and the intermediate file "hello.o" is deleted in the process. | |||
== optimized code == | |||
Generate optimized code on a Linux machine. | |||
g++ -O myprog.C -o myprog | |||
Compile myprog.C when it contains Xlib graphics routines. | |||
g++ myprog.C -o myprog -lX11 | |||
If "myprog.c" is a C program, then the above commands will all work by replacing g++ with gcc and "myprog.C" with "myprog.c". Below are a few examples that apply only to C programs. | |||
Compile a C program that uses math functions such as "sqrt". | |||
gcc myprog.C -o myprog -lm | |||
== electric fence == | |||
Compile a C program with the "electric fence" library. This library, available on all the Linux machines, causes many incorrectly written programs to crash as soon as an error occurs. It is useful for debugging as the error location can be quickly determined using gdb. However, it should only be used for debugging as the executable myprog will be much slower and use much more memory than usual. | |||
gcc -g myprog.C -o myprog -lefence | |||
== Multiple source files == | |||
Compiling a program with multiple source files | |||
If the source code is in several files, say "file1.C" and "file2.C", then they can be compiled into an executable program named "myprog" using the following command: | |||
g++ file1.C file2.C -o myprog | |||
The same result can be achieved using the following three commands: | |||
g++ -c file1.C | |||
g++ -c file2.C | |||
g++ file1.o file2.o -o myprog | |||
The advantage of the second method is that it compiles each of the source files separately. If, for instance, the above commands were used to create "myprog", and "file1.C" was subsequently modified, then the following commands would correctly update "myprog". | |||
g++ -c file1.C | |||
g++ file1.o file2.o -o myprog | |||
Note that file2.C does not need to be recompiled, so the time required to rebuild myprog is shorter than if the first method for compiling myprog were used. When there are numerous source file, and a change is only made to one of them, the time savings can be significant. This process, though somewhat complicated, is generally handled automatically by a makefile. | |||
= Separate Compilation Tutorial = | = Separate Compilation Tutorial = | ||
Line 207: | Line 270: | ||
This is a clumsy way to go about declaring functions that are implemented in a separate file. | This is a clumsy way to go about declaring functions that are implemented in a separate file. | ||
Suppose that there are lots of programs that would like to use the functions contained in ``help.c''. For every program, we are going to have to remember to copy the function headers from ``help.c'' as function declarations. This is a tedious and error prone procedure. | |||
Once we have copied over the headers, we will have multiple copies of the same information. The declaration of the functions appear in the using file (in our case, ``main.c''), while the implementations are somewhere else. What might happen as we begin to change the two programs? Over time, if we're not careful, the declarations in ``main.c'' may become different from the headers in ``help.c''. At that point, our declarations will be worse than useless-they'll be wrong! | Once we have copied over the headers, we will have multiple copies of the same information. The declaration of the functions appear in the using file (in our case, ``main.c''), while the implementations are somewhere else. What might happen as we begin to change the two programs? Over time, if we're not careful, the declarations in ``main.c'' may become different from the headers in ``help.c''. At that point, our declarations will be worse than useless-they'll be wrong! | ||
Line 213: | Line 276: | ||
Here's how we can deal with both of these problems. Create a new file that contains nothing but the declarations of the functions in ``help.c''. In fact, we have already created just such a file: | Here's how we can deal with both of these problems. Create a new file that contains nothing but the declarations of the functions in ``help.c''. In fact, we have already created just such a file: | ||
help.h | help.h | ||
/* Header file for "help.c" | /* Header file for "help.c" | ||
Created: Joe Zachary, October 22, 1992 | |||
Created: Joe Zachary, October 22, 1992 | Modified: | ||
Modified: | */ | ||
/* Requires that "n" be positive. Returns the sum of the | |||
*/ | |||
/* Requires that "n" be positive. Returns the sum of the | |||
first "n" integers. */ | first "n" integers. */ | ||
int sum (int n); | |||
int sum (int n); | /* Requires that "n" be positive. Returns the product of the | ||
/* Requires that "n" be positive. Returns the product of the | |||
first "n" integers. */ | first "n" integers. */ | ||
int product (int n); | |||
int product (int n); | |||
Along with each declaration, we include some comments explaining what the function does. The idea is that this file is all a programmer needs to look at to use the functions in ``help.c''. The programmer will need to look at ``help.c'' only if he or she is interested in how the functions are implemented. | Along with each declaration, we include some comments explaining what the function does. The idea is that this file is all a programmer needs to look at to use the functions in ``help.c''. The programmer will need to look at ``help.c'' only if he or she is interested in how the functions are implemented. | ||
Line 591: | Line 643: | ||
./main | ./main | ||
.a are static libraries. If you use code stored inside them, it's taken from them and embedded into your own binary. In Visual Studio, these would be .lib files. | |||
.so are dynamic libraries. If you use code stored inside them, it's not taken and embedded into your own binary. Instead it's just referenced, so the binary will depend on them and the code from the so file is added/loaded at runtime. In Visual Studio/Windows these would be .dll files (with small .lib files containing linking information). |
Latest revision as of 12:15, 20 September 2022
Compiling with GCC[edit]
Example[edit]
Assuming a single source file named main.cpp, the command to compile and link an non-optimized executable is as follows (Compiling without optimization is useful for initial development and debugging, although -Og is officially recommended for newer GCC versions).
g++ -o app -Wall main.cpp -O0
To produce an optimized executable for use in production, use one of the -O options (see: -O1, -O2, -O3, -Os, -Ofast):
g++ -o app -Wall -O2 main.cpp
If the -O option is omitted, -O0, which means no optimizations, is used as default (specifying -O without a number resolves to -O1).
Alternatively, use optimization flags from the O groups (or more experimental optimizations) directly. The following example builds with -O2 optimization, plus one flag from the -O3 optimization level:
g++ -o app -Wall -O2 -ftree-partial-pre main.cpp
To produce a platform-specific optimized executable (for use in production on the machine with the same architecture), use:
g++ -o app -Wall -O2 -march=native main.cpp
Either of the above will produce a binary file that can be run with .\app.exe on Windows and ./app on Linux, Mac OS, etc.
The -o flag can also be skipped. In this case, GCC will create default output executable a.exe on Windows and a.out on Unix-like systems. To compile a file without linking it, use the -c option:
g++ -o file.o -Wall -c file.cpp
This produces an object file named file.o which can later be linked with other files to produce a binary:
g++ -o app file.o otherfile.o
More about optimization options can be found at gcc.gnu.org. Of particular note are -Og (optimization with an emphasis on debugging experience -- recommended for the standard edit-compile-debug cycle) and -Ofast (all optimizations, including ones disregarding strict standards compliance).
The -Wall flag enables warnings for many common errors and should always be used. To improve code quality it is often encouraged also to use -Wextra and other warning flags which are not automatically enabled by -Wall and -Wextra.
If the code expects a specific C++ standard, specify which standard to use by including the -std= flag. Supported values correspond to the year of finalization for each version of the ISO C++ standard. As of GCC 6.1.0, valid values for the std= flag are c++98/c++03, c++11, c++14, and c++17/c++1z. Values separated by a forward slash are equivalent.
g++ -std=c++11 <file>
GCC includes some compiler-specific extensions that are disabled when they conflict with a standard specified by the -std= flag. To compile with all extensions enabled, the value gnu++XX may be used, where XX is any of the years used by the c++ values listed above.
The default standard will be used if none is specified. For versions of GCC prior to 6.1.0, the default is -std=gnu++03; in GCC 6.1.0 and greater, the default is -std=gnu++14.
Note that due to bugs in GCC, the -pthread flag must be present at compilation and linking for GCC to support the C++ standard threading functionality introduced with C++11, such as std::thread and std::wait_for. Omitting it when using threading functions may result in no warnings but invalid results on some platforms.
Frequently used compilation options[edit]
C and C++ compilers allow for many options for how to compile a program, and the examples below demonstrate how to use many of the more commonly used options. In each example, "myprog.C" contains C++ source code for the executable "myprog". In most cases options can be combined, although it is generally not useful to use "debugging" and "optimization" options together.
Compile myprog.C so that myprog contains symbolic information that enables it to be debugged with the gdb debugger.
g++ -g myprog.C -o myprog
Have the compiler generate many warnings about syntactically correct but questionable looking code. It is good practice to always use this option with gcc and g++.
g++ -Wall myprog.C -o myprog
Generate symbolic information for gdb and many warning messages.
g++ -g -Wall myprog.C -o myprog
== Compiling C and C++ Programs =+
gcc is the GNU C Compiler and g++ is the GNU C++ compiler. Below are several examples that show how to use g++ to compile C++ programs. Example 1: Compiling a simple program Consider the following example: Let "hello.C" be a file that contains the following C++ code.
#include "iostream.h" int main() { cout << "Hello\n"; }
standard way[edit]
The standard way to compile this program is with the command
g++ hello.C -o hello
This command compiles hello.C into an executable program named "hello" that you run by typing './hello' at the command line. It does nothing more than print the word "hello" on the screen. Alternatively, the above program could be compiled using the following two commands.
g++ -c hello.C g++ hello.o -o hello
The end result is the same, but this two-step method first compiles hello.C into a machine code file named "hello.o" and then links hello.o with some system libraries to produce the final program "hello". In fact the first method also does this two-stage process of compiling and linking, but the stages are done transparently, and the intermediate file "hello.o" is deleted in the process.
optimized code[edit]
Generate optimized code on a Linux machine.
g++ -O myprog.C -o myprog
Compile myprog.C when it contains Xlib graphics routines.
g++ myprog.C -o myprog -lX11
If "myprog.c" is a C program, then the above commands will all work by replacing g++ with gcc and "myprog.C" with "myprog.c". Below are a few examples that apply only to C programs.
Compile a C program that uses math functions such as "sqrt".
gcc myprog.C -o myprog -lm
electric fence[edit]
Compile a C program with the "electric fence" library. This library, available on all the Linux machines, causes many incorrectly written programs to crash as soon as an error occurs. It is useful for debugging as the error location can be quickly determined using gdb. However, it should only be used for debugging as the executable myprog will be much slower and use much more memory than usual.
gcc -g myprog.C -o myprog -lefence
Multiple source files[edit]
Compiling a program with multiple source files If the source code is in several files, say "file1.C" and "file2.C", then they can be compiled into an executable program named "myprog" using the following command:
g++ file1.C file2.C -o myprog
The same result can be achieved using the following three commands:
g++ -c file1.C g++ -c file2.C g++ file1.o file2.o -o myprog
The advantage of the second method is that it compiles each of the source files separately. If, for instance, the above commands were used to create "myprog", and "file1.C" was subsequently modified, then the following commands would correctly update "myprog".
g++ -c file1.C g++ file1.o file2.o -o myprog
Note that file2.C does not need to be recompiled, so the time required to rebuild myprog is shorter than if the first method for compiling myprog were used. When there are numerous source file, and a change is only made to one of them, the time savings can be significant. This process, though somewhat complicated, is generally handled automatically by a makefile.
Separate Compilation Tutorial[edit]
All of the programs that we have looked at to this point have been contained within a single file. This is the exception rather than the rule. As programs become larger, it is important to spread them across files of a reasonable size. In this lesson we will look at why this is important, and how it can be done.
Compiling Files[edit]
Up to this point, whenever you wanted to compile a program you compiled the file /* This program writes "Hello world" to the display Created: Joe Zachary, September 18, 1992 Modified: */ #include <stdio.h> void main () { printf("Hello world\n"); }
It is a simple program that prints out ``Hello world, and you can compile and run it without ever displaying it in Emacs. To compile it, go to a Unix Shell window, connect to the directory into which you downloaded the program, and enter the following command:
gcc -Wall -g -c hello.c
This command will result in a new file being created. Use the ls command to list your directory. There should now be a file called ``hello.o. This is called an object file, and is the machine code translation produced by the compiler
At this point you still don't have an executable program that you can run. The next step is to link the object file to produce such an executable. To do this, type the following into your UNIX Shell window:
gcc -o hello hello.o -lm
Linking hooks up your compiled code with some additional C-specific code. Once again, a new file has been created in your directory. List the directory. You should see a file called ``hello in your directory. This is the executable. The name of the executable came from the command that you just typed-it is the name that follows the -o in the command.
Now that you have an executable you can run it in the familiar way. Simply type ``hello into the UNIX Shell window.
To summarize, the steps involved in compiling, linking, and running a program are:
Compile the ``.c file containing the source code with a command such as
gcc -Wall -g -c hello.c
The -c in the command is important--it tells C to produce an object file called (in this case) ``hello.o. The -Wall part is optional but is strongly encouraged--it tells C to produce all possible warning messages. A warning message will usually lead you to a bug in your program. The -g tells the compiler to include information needed by the debugger.
Link the ``.o file to produce an executable with a command such as
gcc -o hello hello.o -lm
The -o hello in the command is important--it tells C what to name the executable. The -lm part tells C to link in the math libraries. If your program doesn't use any of the match functions from ``math.h, you can leave this part out.
Run the executable in the usual way.
If your entire program is contained within a single file, you can do the compilation and linking in one step by entering the command
gcc -Wall -g -o hello hello.c -lm
Multiple Files[edit]
Consider the idea of dividing programs into more than one file. By that we mean putting the various functions that make up the program into more than one file.
Now let's take a look at a program divided into more than one file.
main.c
/* Used to illustrate separate compilation. Created: Joe Zachary, October 22, 1992 Modified: */ #include <stdio.h> void main () { int n; printf("Please enter a small positive integer: "); scanf("%d", &n); printf("The sum of the first n integers is %d\n", sum(n)); printf("The product of the first n integers is %d\n", product(n)); }
and help.c
/* Used to illustrate separate compilation Created: Joe Zachary, October 22, 1992 Modified: */ /* Requires that "n" be positive. Returns the sum of the first "n" integers. */ int sum (int n) { int i; int total = 0; for (i = 1; i <= n; i++) total += i; return(total); } /* Requires that "n" be positive. Returns the product of the first "n" integers. */ int product (int n) { int i; int total = 1; for (i = 1; i <= n; i++) total *= i; return(total); }
Notice that main.c makes use of the two functions defined in ``help.c.Try compiling ``main.c this way. What happens?
The compiler will complain that the two functions ``sum and ``product are undeclared. (Remember-you need to do declaration before use.) And the linker will complain that the same two functions are undefined when it tries to put together an executable. The problem boils down to the fact that the two functions are sitting off in another file.
Now try compiling the buffer containing ``help.c. What happens?
The linker will complain that the function ``main is undefined when it tries to put together an executable. Again, the problem is that the entire program isn't in one file.
Compiling Multiple Files[edit]
the procedure for compiling, linking, and running the program that we've spread across two files here?
First you need to compile each of the ``.c files separately. You can do this by using the -c option of the compiler from a Unix shell window:
gcc -Wall -g -c main.c gcc -Wall -g -c help.c
Next you need to link the two resulting ``.o files into a single executable program. You can do this by using the -o option of the compiler:
gcc -o demo main.o help.o -lm
This results in an executable called ``demo. Finally you need to run the resulting executable. You do this by:
demo
Declaring Functions[edit]
There's still a problem remaining. Whenever you compile ``main.c, the compiler complains that ``sum and ``product are undeclared functions. Even so, the linker succeeds in putting together an executable.
Since neither of the functions is declared in ``main.c, the compiler has no way of checking that we are actually using them properly. If we make a mistake (for example, use too many arguments, too few arguments, or the wrong type of arguments) we won't find out until the program gives us incorrect results or even crashes at runtime. If we are to enjoy the benefits of type checking, we must be sure that everything is declared.
It is actually not too difficult to fix up this problem. Simply add the following two lines to ``main.c up above the main procedure.
int sum (int n); int product (int n);
They are (more or less) the header lines from the implementations of the functions in ``help.c. Here (because they end with a semicolon instead of a right brace) they serve as declarations of the functions.
Save ``main.c and rebuild the executable. If you've done everything correctly, the errors should go away.
Include Files[edit]
This is a clumsy way to go about declaring functions that are implemented in a separate file. Suppose that there are lots of programs that would like to use the functions contained in ``help.c. For every program, we are going to have to remember to copy the function headers from ``help.c as function declarations. This is a tedious and error prone procedure.
Once we have copied over the headers, we will have multiple copies of the same information. The declaration of the functions appear in the using file (in our case, ``main.c), while the implementations are somewhere else. What might happen as we begin to change the two programs? Over time, if we're not careful, the declarations in ``main.c may become different from the headers in ``help.c. At that point, our declarations will be worse than useless-they'll be wrong!
Here's how we can deal with both of these problems. Create a new file that contains nothing but the declarations of the functions in ``help.c. In fact, we have already created just such a file: help.h
/* Header file for "help.c" Created: Joe Zachary, October 22, 1992 Modified: */ /* Requires that "n" be positive. Returns the sum of the first "n" integers. */ int sum (int n); /* Requires that "n" be positive. Returns the product of the first "n" integers. */ int product (int n);
Along with each declaration, we include some comments explaining what the function does. The idea is that this file is all a programmer needs to look at to use the functions in ``help.c. The programmer will need to look at ``help.c only if he or she is interested in how the functions are implemented.
Now, modify ``main.c by eliminating the declarations you added, and replacing them with the line
#include "help.h"
As you probably recall, this is a direction to the compiler to behave as if the contents of the file ``help.h were actually typed in here. Notice that we have surrounded the name of the file with quotes instead of angle brackets as is the case with, for example, stdio.h. Why?
Click here for the answer
Save ``main.c and compile, link, and run it. How does this address problem (1) identified above?
Click here for the answer
However, we still haven't addressed issue (2). There is nothing to stop us from modifying the declarations in ``help.h, rendering it inconsistent with ``help.c.
Fortunately, there's a way to deal with this problem too. Put the line
#include "help.h"
at the top of ``help.c. Now save ``help.c and rebuild the executable. You should encounter no errors.
Now edit ``help.c again by changing one of the declarations. For example, change ``sum so that it takes a float as an argument instead of an int. Save and recompile ``help.c. What happens?
Click here for the answer
Summary[edit]
To summarize, here is the suggested procedure for dealing with multiple-file programs in C.
For each file ``file.c containing functions that will be used elsewhere, create a file ``file.h containing the header information and comments for those functions. Put the line
#include "file.h"
near the beginning of ``file.c. This way, the compiler will warn you if the two sets of declarations ever become different. Put the line
#include "file.h"
near the beginning of all files that use the functions in ``file.c. This way, the complier will warn you if you use the functions incorrectly.
You can then separately compile the ``.c files, and then link together to create an executable. In the next lesson we will look at Makefiles, which provide a more convenient way of compiling and linking multiple file programs.
C Libraries For Linux[edit]
In general, libraries are created from many library source files, and are either built as archive files (libmine.a) that are statically linked into executables that use them, or as shared object files (libmine.so) that are dynamically linked into executables that use them. To link in libraries of these types, use the gcc command line options -L for the path to the library files and -l to link in a library (a .so or a .a):
-L{path to file containing library} -l${library name}
For example, if I have a library named libmine.so in /home/newhall/lib/ then I'd do the following to link it into my program:
$ gcc -o myprog myprog.c -L/home/newhall/lib -lmine
You may also need to specify and include path so the compiler can find the library header file: -I /home/newhall/include
If you create your own shared object files and do not install them in /usr/lib, then you need to set your LD_LIBRARY_PATH environment variable so that the runtime linker can find them and load them at run time. For example, if I put my .so files in a directory named lib in my home directory, I'd set my LD_LIBRARY_PATH enviroment to the following:
# if running bash: export LD_LIBRARY_PATH=/home/newhall/lib:$LD_LIBRARY_PATH # if running tcsh: setenv LD_LIBRARY_PATH /home/newhall/lib:$LD_LIBRARY_PATH
USING AND LINKING LIBRARY CODE[edit]
To use a Library that is not linked into your program automatically by the compiler, you need to (1) include the library's header file in your C source file (test.c in the example below), and (2) tell the compiler to link in the code from the library .o file into your executable file:
step 1: Add an include line (#include "somelib.h") in a program source file (e.g., test.c). step 2: Link the program's .c file with the library object file (i.e. specify the somelib.o file as a command line argument to gcc): % gcc -o myprog test.c somelib.o
The resulting executable file (myprog) will contain machine code for all the functions defined in test.c plus any mylib library functions that are called by
CREATING AND USING YOUR OWN LIBRARY CODE[edit]
To create a Library of code you need to do the following:
- Create an INTERFACE to your library: mylib.h
- Create an IMPLEMENTATION of your library: mylib.c
- Create a LIBRARY OBJECT FILE (.o) that can be linked with programs that use your library
- or create a SHARED OBJECT FILE (.so) from many .o files that can be dynamically linked with programs that use your library
- or create an ARCHIVE FILE (.a) from many .o files that can be statically linked with programs that use your library
- USE the library in other C code: (a) #include "mylib.h" (b) link in the libary code into a.out file
- Set LD_LIBRARY_PATH environment variable for finding shared objects in non-standard locations at runtime</nowiki>
Details:
INTERFACE:[edit]
the header file to your library should contain definitions for everything exported by your library: function prototypes with comments for users of your library functions definitions for types and global variables exported by your library
You should have "boiler plate" code (#ifndef ... #endif) around the header file's contents, to ensures that the preprocessor only includes the mylib.h file one time.
Here is what an example .h file might look like:
#ifndef _MYLIB_H_ #define _MYLIB_H_ // a constant definition exported by library: #define MAX_FOO 20 // a type definition exported by library: struct foo_struct { int x; float y; }; typedef struct foo_struct foo_struct; // a global variable exported by library // "extern" means that this is not a variable declaration, it // just defines that a variable named total_foo of type int // exits and you can use it (its declaration is in some library source file) extern int total_foo; // a function prototype for a function exported by library: extern int foo(float y, float z); // a very bad function name #endif
IMPLEMENTATION:[edit]
create a mylib.c file that #includes "mylib.h" and contains the implementation of every function in your library.
#include "mylib.h" ... int total_foo; int foo(float y, float z) { ... }
create a LIBRARY OBJECT FILE[edit]
that can be linked into other programs that use your library (use the -c option to gcc to tell it just to create an object file (a .o file) rather than an executable:
gcc -o mylib.o -c mylib.c
you can then use the mylib.o file as the "library file" and statically link it into other programs that use it, or... alternately, you can create a
SHARED OBJECT FILE[edit]
from one or more .o files that can be linked into other programs that use your library A shared object file is the Unix name for a dynamically linked library whose code is loaded into the a.out file at runtime. To create a .so file use the -shared flag to gcc. Here is what an example build might look like:
gcc -shared -o libmylib.so mylib.o blah.o grr.o -lm
you could also build an
ARCHIVE FILE[edit]
(a statically linked library, libmylib.a) from one or more .o files. If you link with a static library, its code is copied into the a.out file at runtime. See gcc documentation for more information on how to build .a and .so files.
USE the library in other programs:[edit]
step 1: Add an include line (#include "mylib.h") in all program source files that use library definitions (e.g., test.c).
step 2: Link the program's .c file with the library object file (i.e. specify the mylib.o file as a command line argument to gcc):
gcc test.c mylib.o
OR to link in libmylib.so (or libmylib.a):
gcc test.c -lmylib
OR to link with a library not in the standard path:
gcc test.c -L/home/newhall/lib -lmylib
The resulting a.out out will contain machine code for all the functions defined in test.c plus any mylib library functions that are called by the test.c code.
RUNNING[edit]
an executable linked with a shared object file: If the shared object file in not in /usr/lib, then you need to set your LD_LIBRARY_PATH environment variable so that the runtime linker can find and load your .so file into the executable at runtime:
- in bash:
export LD_LIBRARY_PATH=/home/newhall/lib:$LD_LIBRARY_PATH
- in tcsh:
setenv LD_LIBRARY_PATH /home/newhall/lib:$LD_LIBRARY_PATH
Libraries For Windows[edit]
Modularize your program into separate components to ease deployment and installation Photo by Panos Sakalakis on Unsplash
By reading this piece, you will learn to create your own shared libraries in Windows operating system. Based on the documentation provided by Microsoft, a DLL is:
“…a library that contains code and data that can be used by more than one program at the same time. This helps promote code reuse and efficient memory usage.
By using a DLL, a program can be modularized into separate components. For example, an accounting program may be sold by module. Each module can be loaded into the main program at run time if that module is installed. Because the modules are separate, the load time of the program is faster, and a module is only loaded when that functionality is requested.”
A program that utilizes DLL has the following advantages:
- Uses fewer resources
- Promotes modular architecture
- Eases deployment and installation
There are 4 sections in this tutorial:
- Setup
- Implementation
- Linux
- Conclusion
Let’s proceed to the next section and start installing the necessary software.
Setup[edit]
Make sure that you have a way to compile C/C++ code into executable files and shared libraries. I will be using GCC as the compiler for this project.
Image by Author
Shared libraries are called dynamic-link libraries (.dll) in Windows platform. In other platforms, it is identified as shared object using the .so extension.
Implementation[edit]
Visual Studio provides us with their own Developer Command Prompt that allows us to compile executable files. Find the Native Tools Command Prompt for VS in the Visual Studio directory. Image by Author
I am using x64 for this tutorial to compile 64-bit dynamic-link libraries since the Python version that I have is based on 64-bit architecture. If you are compiling 32-bit dynamic-link libraries, you can only use it in 32-bit environment. Open it up and you should see the following terminal. Image by Author
Before that, create the following folders in your root directory. Image by Author
- include — For storing header files. Header files will be used as reference on how to interact with the compiled shared libraries. Header files are essential if other developers wish to use your shared libraries to develop their own program.
- obj — For storing temp obj files created during compilation. You can safely delete these files.
- src — For storing the essential code of your functions. During deployment, you will exclude these files from the package. Provide only shared libraries and executable to the customers that used your program.
Header
Inside the include folder, let’s create a new file called add.h. Feel free to name it anything that you preferred. Open it up and add the following code.
#ifndef _ADD_H #define _ADD_Hextern "C" { __declspec(dllexport) int add(int, int); __declspec(dllexport) int sub(int, int); __declspec(dllexport) int mul(int, int); }#endif
If you are coding in c, simply use the whole header without extern and delspec(dllexport).
int add(int, int); int sub(int, int); int mul(int, int);
Implementation code[edit]
Head over to the src folder and create a new file. I am naming it add.cpp for simplicity. Implement the necessary code inside it.
#include "add.h" extern "C" { __declspec(dllexport) int add(int a, int b) { return a + b; } __declspec(dllexport) int sub(int a, int b){ return a - b; } __declspec(dllexport) int mul(int a, int b){ return a * b; } }
Main[edit]
Go back to the root directory and create a new file called main.cpp. Image by Author
We will build an executable file from it. Write the following code inside the file. Feel free to modify it accordingly as I just wanted to test it out with some simple arithmetic calculations.
#include <iostream> #include "add.h" int main() { std::cout << add(2, 2) << " | " << sub(3, 1) << " | " << mul(3, 3) << std::endl; return 0; }
Object Files[edit]
Move back to the command prompt that we have opened earlier. Make sure that it points to the root directory where the main.cpp is located. Run the following command.
cl /EHsc /c .\src\*.cpp /I .\include /Fo".\obj\\" /c — The location of source files. Use the asterisk mark to include all the cpp files inside the src folder. /I —The location of header files. By default, it will include files with .h extension. /Fo — The location of temporary obj files that will be created during compilation. Please be noted that you should not have any extra space after this command.
You should see the following output Image by Author
You can find a newly created file called app.obj inside the obj folder.
Dynamic-link libraries[edit]
The next step is to link it and create a shared libraries. At the command prompt, run the following command.
link /DLL /OUT:add.dll .\obj\*.obj
You should see the following output Image by Author
At the root folder, you should have the following files. Image by Author
- add.dll — The shared library for your program. It is required by your executable file to run the corresponding functions.
- add.exp — The Exports library file. You can safely delete this file.
- add.lib — The static library that is required during compilation to make executable file.
Executable file[edit]
The last part is to create the executable file. Run the following command. We will be using .lib file instead of .dll as static library is required for compilation while dynamic-link library is used for running the executable after compilation.
cl /EHsc main.cpp /Fetest.exe /I .\include /link *.lib
You should see the following output Image by Author
Inside the root folder, you should have the test.exe file in which you can run it directly. Image by Author
Do not be surprised if the program close shortly after you opened it. This is mainly because it has finished running the code and close itself. Let’s try to run it inside the terminal to capture the output. Just enter the name of the executable. The extension .exe can be omitted.
test.exe
You should see the following output which indicates that the program works as expected. Image by Author
You can simply deploy the following files for usage. Make sure that both are located inside the same directory.
test.exe add.dll
3. Linux (Ubuntu)
Please be noted that UNIX does not use DLL. The file format used in Linux is called shared object and ends with the .so extension.
Based on the comments from other readers, you can use g++ to compile it as well but it requires a bit of work. The following instructions is based on gcc and might be out of date. It is highly recommended to check out other tutorials as well and proceed with care. gcc
For Linux user, you need to install gcc. First and foremost, update the package list.
sudo apt-get update
You can choose to install it directly as follow:
sudo apt-get install gcc
or through the build essential package
sudo apt-get install build-essential
Run the following command to confirm that you have installed gcc properly.
gcc --version
If you are using other architecture such as CentOS, simply replace apt-get with the corresponding command.
Compiling in Linux is via the following command. Kindly check out the gcc documentation on how to link the header and implementation files and modify it accordingly
gcc main.cpp -o main
Simply run the executable file as follow
./main
.a are static libraries. If you use code stored inside them, it's taken from them and embedded into your own binary. In Visual Studio, these would be .lib files.
.so are dynamic libraries. If you use code stored inside them, it's not taken and embedded into your own binary. Instead it's just referenced, so the binary will depend on them and the code from the so file is added/loaded at runtime. In Visual Studio/Windows these would be .dll files (with small .lib files containing linking information).