GNU/Linux Application Programming (Programming Series)

Let s first look at the simplest type of library development in GNU/Linux. The static library is linked statically with the application image. This means that once the image is built, the external library is not necessary to be present for the image to execute properly as its part of the resulting image.

To demonstrate the construction of libraries, let s look at a sample set of source files. We ll build a simple random number generator wrapper library using the GNU/Linux random functions. Let s first look at the API for our library. The header file,  randapi.h , is shown in Listing 6.1.

Listing 6.1: Random Number Wrapper API Header (on the CD-ROM at ./source/ch6/_statshrd/randapi.h)

1: /* 2: * randapi.h 3: * 4: * Random Functions API File 5: * 6: */ 7: 8: 9: #ifndef __RAND_API_H 10: #define __RAND_API_H 11: 12: extern void initRand(void); 13: 14: extern float getSRand(void); 15: 16: extern int getRand(int max); 17: 18: #endif /* __RAND_API_H */

 

Our API consists of three functions. The first function, initrand , is an initialization function that prepares our wrapper libraries for use. It must be called once prior to calling any of the random functions. Function getSRand() returns a random floating-point value between 0.0 and 1.0. Finally, function getRand(x) returns a random integer between 0 and ( x “1).

While this functionality could be implemented in a single file, we ll split it between two files for the purposes of demonstration. The next file, initrand.c , provides the initialization function for the wrapper API (see Listing 6.2). The single function, initRand() , simply initializes the random number generator using the current time as a seed.

Listing 6.2: Random Number Initialization API (on the CD-ROM at ./source/ch6/_statshrd/initapi.c )

1: /* 2: * Random Init Function API File 3: * 4: */ 5: 6: #include <stdlib.h> 7: #include <time.h> 8: 9: 10: /* 11: * initRand() initializes the random number generator. 12: * 13: */ 14: 15: void initRand() 16: { 17: time_t seed; 18: 19: seed = time(NULL); 20: 21: srand(seed); 22: 23: return; 24: }

 

Our final API file,  randapi.c , provides the random number functions (see Listing 6.3). The integer and floating-point random number wrapper functions are provided here.

Listing 6.3: Random Number Wrapper Functions (on the CD-ROM at ./source/ch6/_statshrd/randapi.c )

1: /* 2: * randapi.c 3: * 4: * Random Functions API File 5: * 6: */ 7: 8: #include <stdlib.h> 9: 10: 11: /* 12: * getSRand() returns a number between 0.0 and 1.0. 13: * 14: */ 15: 16: float getSRand() 17: { 18: float randvalue; 19: 20: randvalue = ((float)rand() / (float)RAND_MAX); 21: 22: return randvalue; 23: } 24: 25: 26: /* 27: * getRand() returns a number between 0 and max-1. 28: * 29: */ 30: 31: int getRand(int max) 32: { 33: int randvalue; 34: 35: randvalue = (int)((float)max * rand() / (RAND_MAX+1.0)); 36: 37: return randvalue; 38: }

 

That s it for our API. Note that both  initapi.c and  randapi.c use the single header file  randapi.h to provide their function prototypes . Let s now take a quick look at the test program that utilizes the API and then get back to the task at hand ”libraries!

Listing 6.4 provides the test application that uses the wrapper function API. This application provides a quick test of the API by identifying the average value provided, which should represent the average around the middle of the random number range.

Listing 6.4: Test Application for the Wrapper Function API (on the CD-ROM at ./source/ch6/statshrd/test.c )

1: #include "randapi.h" 2: 3: #define ITERATIONS 1000000L 4: 5: int main() 6: { 7: long i; 8: long isum; 9: float fsum; 10: 11: /* Initialize the random number API */ 12: initRand(); 13: 14: /* Find the average of getRand(10) returns (0..9) */ 15: isum = 0L; 16: for (i = 0 ; i < ITERATIONS ; i++) { 17: 18: isum += getRand(10); 19: 20: } 21: 22: printf("getRand() Average %d\n", (int)(isum / ITERATIONS)); 23: 24: 25: /* Find the average of getSRand() returns */ 26: fsum = 0.0; 27: for (i = 0 ; i < ITERATIONS ; i++) { 28: 29: fsum += getSRand(); 30: 31: } 32: 33: printf("getSRand() Average %f\n", (fsum / (float)ITERATIONS)); 34: 35: return; 36: }

 

If we wanted to build all source files discussed here and integrate them into a single image, we could do the following:

$ gcc initapi.c randapi.c test.c -o test

This would compile all three files and then link them together into a single image called test . This use of gcc provides not only compilation of the source files, but also linking to a single image. Upon executing the image, we d see the averages for each of the random number functions:

$ ./test getRand() Average 4 getSRand() Average 0.500001 $

As expected, the random number generated generates an average value that s in the middle of the random number range.

Let s now get back to the subject of libraries, and rather than build the entire source together, we ll build a library for our random number functions. This is achieved using the ar utility (archive). Below, we ll demonstrate the building of our static library along with the construction of the final image.

$ gcc -c -Wall initapi.c $ gcc -c -Wall randapi.c $ ar -cru libmyrand.a initapi.o randapi.o $

In this example, we first compile our two source files (  initapi.c and  randapi.c ) using gcc . We specify the -c option to tell gcc to compile only (don t link) and also to turn on all warnings. Next, we use the ar command to build our library (libmyrand.a ). The cru options are a standard set of options for creating or adding to an archive. The c option specifies to create the static library (unless it already exists, in which case the option is ignored). The r option tells ar to replace existing objects in the static library (if they already exist). Finally, the u option is a safety option to specify that objects are replaced in the archive only if the objects to be inserted are newer than existing objects in the archive (of the same name ).

We now have a new file called libmyrand.a , which is our static library containing two objects: initapi.o and randapi.o . Let s now look at how we can build our application using this static library. Consider the following:

$ gcc test.c -L. -lmyrand -o test $ ./test getRand() Average 4 getSRand() Average 0.499892 $

Here we use gcc to first compile the file  test.c and then link the test.o object with libmyrand.a to produce the test image file. The -L . option tells gcc that libraries can be found in the current subdirectory ( . represents the directory). Note that we could also provide a specific subdirectory for the library, such as -L/usr/mylibs . The -L option identifies the library to use. Note that myrand isn t the name of our library, but instead libmyrand.a . When the -L option is used, it automatically surrounds the name specified with lib and .a . Therefore, if the user had specified -ltest , gcc would look for a library called libtest.a .

Now that we see how to create a library and use it to build a simple application, let s return to the ar utility to see what other uses it has. We can inspect a static library to see what s contained within it by using the -t option:

$ ar -t libmyrand.a initapi.o randapi.o $

If desired, we can also remove objects from a static library. This is done using the -d option, such as:

$ ar -d libmyrand.a initapi.o $ ar -t libmyrand.a randapi.o $

It s important to note that ar won t actually show a failure for a delete. To see an error message if the delete fails, add a -v option as shown below:

$ ar -d libmyrand.a initapi.o $ ar -dv libmyrand.a initapi.o No member named

Категории