21 min read

 In this article written by John Torjo and Wisnu Anggoro, authors of the book Boost.Asio C++ Network Programming Second Edition, the authors state that “Many programmers have used libraries since this simplifies the programming process. Because they do not need to write the function from scratch anymore, using a library can save much code development time”. In this article, we are going to get acquainted with Boost C++ libraries. Let us prepare our own compiler and text editor to prove the power of Boost libraries. As we do so, we will discuss the following topics:

  • Introducing the C++ standard template library
  • Introducing Boost C++ libraries
  • Setting up Boost C++ libraries in MinGW compiler
  • Building Boost C++ libraries
  • Compiling code that contains Boost C++ libraries

(For more resources related to this topic, see here.)

Introducing the C++ standard template library

The C++ Standard Template Library (STL) is a generic template-based library that offers generic containers among other things. Instead of dealing with dynamic arrays, linked lists, binary trees, or hash tables, programmers can easily use an algorithm that is provided by STL.

The STL is structured by containers, iterators, and algorithms, and their roles are as follows:

  • Containers: Their main role is to manage the collection of objects of certain kinds, such as arrays of integers or linked lists of strings.
  • Iterators: Their main role is to step through the element of the collections. The working of an iterator is similar to that of a pointer. We can increment the iterator by using the ++ operator and access the value by using the * operator.
  • Algorithms: Their main role is to process the element of collections. An algorithm uses an iterator to step through all elements. After it iterates the elements, it processes each element, for example, modifying the element. It can also search and sort the element after it finishes iterating all the elements.

Let us examine the three elements that structure STL by creating the following code:

/* stl.cpp */
#include <vector>
#include <iostream>
#include <algorithm>

int main(void) {
int temp;
std::vector<int> collection;
std::cout << "Please input the collection of integer numbers,
input 0 to STOP!n";
while(std::cin >> temp != 0) {
   if(temp == 0) break;
   collection.push_back(temp);
}
std::sort(collection.begin(), collection.end());
std::cout << "nThe sort collection of your integer numbers:n";
for(int i: collection) {
   std::cout << i << std::endl;
}
}

Name the preceding code stl.cpp, and run the following command to compile it:

g++ -Wall -ansi -std=c++11 stl.cpp -o stl

Before we dissect this code, let us run it to see what happens. This program will ask users to enter as many as integer, and then it will sort the numbers. To stop the input and ask the program to start sorting, the user has to input 0. This means that 0 will not be included in the sorting process. Since we do not prevent users from entering non-integer numbers such as 3.14, or even the string, the program will soon stop waiting for the next number after the user enters a non-integer number. The code yields the following output:

We have entered six integer: 43, 7, 568, 91, 2240, and 56. The last entry is 0 to stop the input process. Then the program starts to sort the numbers and we get the numbers sorted in sequential order: 7, 43, 56, 91, 568, and 2240.

Now, let us examine our code to identify the containers, iterators, and algorithms that are contained in the STL.

std::vector<int> collection;

The preceding code snippet has containers from STL. There are several containers, and we use a vector in the code. A vector manages its elements in a dynamic array, and they can be accessed randomly and directly with the corresponding index. In our code, the container is prepared to hold integer numbers so we have to define the type of the value inside the angle brackets <int>. These angle brackets are also called generics in STL.

collection.push_back(temp);
std::sort(collection.begin(), collection.end());

The begin() and end() functions in the preceding code are algorithms in STL. They play the role of processing the data in the containers that are used to get the first and the last elements in the container. Before that, we can see the push_back() function, which is used to append an element to the container.

for(int i: collection) {
std::cout << i << std::endl;
}

The preceding for block will iterate each element of the integer which is called as collection. Each time the element is iterated, we can process the element separately. In the preceding example, we showed the number to the user. That is how the iterators in STL play their role.

#include <vector>
#include <algorithm>

We include vector definition to define all vector functions and algorithm definition to invoke the sort() function.

Introducing the Boost C++ libraries

The Boost C++ libraries is a set of libraries to complement the C++ standard libraries. The set contains more than a hundred libraries that we can use to increase our productivity in C++ programming. It is also used when our requirements go beyond what is available in the STL. It provides source code under Boost Licence, which means that it allows us to use, modify, and distribute the libraries for free, even for commercial use.

The development of Boost is handled by the Boost community, which consists of C++ developers from around the world. The mission of the community is to develop high-quality libraries as a complement to STL. Only proven libraries will be added to the Boost libraries.

For detailed information about Boost libraries go to www.boost.org. And if you want to contribute developing libraries to Boost, you can join the developer mailing list at lists.boost.org/mailman/listinfo.cgi/boost.

The entire source code of the libraries is available on the official GitHub page at github.com/boostorg.

Advantages of Boost libraries

As we know, using Boost libraries will increase programmer productivity. Moreover, by using Boost libraries, we will get advantages such as these:

  • It is open source, so we can inspect the source code and modify it if needed.
  • Its license allows us to develop both open source and close source projects. It also allows us to commercialize our software freely.
  • It is well documented and we can find it libraries all explained along with sample code from the official site.
  • It supports almost any modern operating system, such as Windows and Linux. It also supports many popular compilers.
  • It is a complement to STL instead of a replacement. It means using Boost libraries will ease those programming processes which are not handled by STL yet. In fact, many parts of Boost are included in the standard C++ library.

Preparing Boost libraries for MinGW compiler

Before we go through to program our C++ application by using Boost libraries, the libraries need to be configured in order to be recognized by MinGW compiler. Here we are going to prepare our programming environment so that our compiler is able use Boost libraries.

Downloading Boost libraries

The best source from which to download Boost is the official download page. We can go there by pointing our internet browser to www.boost.org/users/download. Find the Download link in Current Release section. At the time of writing, the current version of Boost libraries is 1.58.0, but when you read this article, the version may have changed. If so, you can still choose the current release because the higher version must be compatible with the lower. However, you have to adjust as we’re goning to talk about the setting later. Otherwise, choosing the same version will make it easy for you to follow all the instructions in this article.

There are four file formats to be choose from for download; they are .zip, .tar.gz, .tar.bz2, and .7z. There is no difference among the four files but their file size. The largest file size is of the ZIP format and the lowest is that of the 7Z format. Because of the file size, Boost recommends that we download the 7Z format. See the following image for comparison:

We can see, from the preceding image, the size of ZIP version is 123.1 MB while the size of the 7Z version is 65.2 MB. It means that the size of the ZIP version is almost twice that of the 7Z version. Therefore they suggest that you choose the 7Z format to reduce download and decompression time. Let us choose boost_1_58_0.7z to be downloaded and save it to our local storage.

Deploying Boost libraries

After we have got boost_1_58_0.7z in our local storage, decompress it using the 7ZIP application and save the decompression files to C:boost_1_58_0.

The 7ZIP application can be grabbed from www.7-zip.org/download.html.

The directory then should contain file structures as follows:

Instead of browsing to the Boost download page and searching for the Boost version manually, we can go directly to sourceforge.net/projects/boost/files/boost/1.58.0. It will be useful when the 1.58.0 version is not the current release anymore.

Using Boost libraries

Most libraries in Boost are header-only; this means that all declarations and definitions of functions, including namespaces and macros, are visible to the compiler and there is no need to compile them separately. We can now try to use Boost with the program to convert the string into int value as follows:

/* lexical.cpp */
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>

int main(void) {
try    {
   std::string str;
   std::cout << "Please input first number: ";
   std::cin >> str;
   int n1 = boost::lexical_cast<int>(str);
   std::cout << "Please input second number: ";
   std::cin >> str;
   int n2 = boost::lexical_cast<int>(str);
   std::cout << "The sum of the two numbers is ";
   std::cout << n1 + n2 << "n";
   return 0;
}
catch (const boost::bad_lexical_cast &e) {
   std::cerr << e.what() << "n";
   return 1;
}
}

Open the Notepad++ application, type the preceding code, and save it as lexical.cpp in C:CPP. Now open the command prompt, point the active directory to C:CPP, and then type the following command:

g++ -Wall -ansi lexical.cpp –Ic:boost_1_58_0 -o lexical

We have a new option here, which is –I (the “include” option). This option is used along with the full path of the directory to inform the compiler that we have another header directory that we want to include to our code. Since we store our Boost libraries in c: boost_1_58_0, we can use –Ic:boost_1_58_0 as an additional parameter.

In lexical.cpp, we apply boost::lexical_cast to convert string type data into int type data. The program will ask the user to input two numbers and will then automatically find the sum of both numbers. If a user inputs an inappropriate number, it will inform that an error has occurred.

The Boost.LexicalCast library is provided by Boost for casting data type purpose (converting numeric types such as int, double, or floats into string types, and vice versa). Now let us dissect lexical.cpp to for a more detailed understanding of what it does:

#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>

We include boost/lexical_cast.hpp because the boost::lexical_cast function is declared lexical_cast.hpp header file whilst string header is included to apply std::string function and iostream header is included to apply std::cin, std::cout and std::cerr function.

Other functions, such as std::cin and std::cout, and we saw what their functions are so we can skip those lines.

#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>

We used the preceding two separate lines to convert the user-provided input string into the int data type. Then, after converting the data type, we summed up both of the int values.

We can also see the try-catch block in the preceding code. It is used to catch the error if user inputs an inappropriate number, except 0 to 9.

catch (const boost::bad_lexical_cast &e)
{
std::cerr << e.what() << "n";
return 1;
}

The preceding code snippet will catch errors and inform the user what the error message exactly is by using boost::bad_lexical_cast. We call the e.what() function to obtain the string of the error message.

Now let us run the application by typing lexical at the command prompt. We will get output like the following:

I put 10 for first input and 20 for the second input. The result is 30 because it just sums up both input. But what will happen if I put in a non-numerical value, for instance Packt. Here is the output to try that condition:

Once the application found the error, it will ignore the next statement and go directly to the catch block. By using the e.what() function, the application can get the error message and show it to the user. In our example, we obtain bad lexical cast: source type value could not be interpreted as target as the error message because we try to assign the string data to int type variable.

Building Boost libraries

As we discussed previously, most libraries in Boost are header-only, but not all of them. There are some libraries that have to be built separately. They are:

  • Boost.Chrono: This is used to show the variety of clocks, such as current time, the range between two times, or calculating the time passed in the process.
  • Boost.Context: This is used to create higher-level abstractions, such as coroutines and cooperative threads.
  • Boost.Filesystem: This is used to deal with files and directories, such as obtaining the file path or checking whether a file or directory exists.
  • Boost.GraphParallel: This is an extension to the Boost Graph Library (BGL) for parallel and distributed computing.
  • Boost.IOStreams: This is used to write and read data using stream. For instance, it loads the content of a file to memory or writes compressed data in GZIP format.
  • Boost.Locale: This is used to localize the application, in other words, translate the application interface to user’s language.
  • Boost.MPI: This is used to develop a program that executes tasks concurrently. MPI itself stands for Message Passing Interface.
  • Boost.ProgramOptions: This is used to parse command-line options. Instead of using the argv variable in the main parameter, it uses double minus () to separate each command-line option.
  • Boost.Python: This is used to parse Python language in C++ code.
  • Boost.Regex: This is used to apply regular expression in our code. But if our development supports C++11, we do not depend on the Boost.Regex library anymore since it is available in the regex header file.
  • Boost.Serialization: This is used to convert objects into a series of bytes that can be saved and then restored again into the same object.
  • Boost.Signals: This is used to create signals. The signal will trigger an event to run a function on it.
  • Boost.System: This is used to define errors. It contains four classes: system::error_code, system::error_category, system::error_condition, and system::system_error. All of these classes are inside the boost namespace. It is also supported in the C++11 environment, but because many Boost libraries use Boost.System, it is necessary to keep including Boost.System.
  • Boost.Thread: This is used to apply threading programming. It provides classes to synchronize access on multiple-thread data. It is also supported in C++11 environments, but it offers extensions, such as we can interrupt thread in Boost.Thread.
  • Boost.Timer: This is used to measure the code performance by using clocks. It measures time passed based on usual clock and CPU time, which states how much time has been spent to execute the code.
  • Boost.Wave: This provides a reusable C preprocessor that we can use in our C++ code.

There are also a few libraries that have optional, separately compiled binaries. They are as follows:

  • Boost.DateTime: It is used to process time data; for instance, calendar dates and time. It has a binary component that is only needed if we use to_string, from_string, or serialization features. It is also needed if we target our application in Visual C++ 6.x or Borland.
  • Boost.Graph: It is used to create two-dimensional graphics. It has a binary component that is only needed if we intend to parse GraphViz files.
  • Boost.Math: It is used to deal with mathematical formulas. It has binary components for cmath functions.
  • Boost.Random: It is used to generate random numbers. It has a binary component which is only needed if we want to use random_device.
  • Boost.Test: It is used to write and organize test programs and their runtime execution. It can be used in header-only or separately compiled mode, but separate compilation is recommended for serious use.
  • Boost.Exception: It is used to add data to an exception after it has been thrown. It provides non-intrusive implementation of exception_ptr for 32-bit _MSC_VER==1310 and _MSC_VER==1400, which requires a separately compiled binary. This is enabled by #define BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR.

Let us try to recreate the random number generator. But now we will use the Boost.Random library instead of std::rand() from the C++ standard function. Let us take a look at the following code:

/* rangen_boost.cpp */
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <iostream>

int main(void) {
int guessNumber;
std::cout << "Select number among 0 to 10: ";
std::cin >> guessNumber;
if(guessNumber < 0 || guessNumber > 10) {
   return 1;
}
boost::random::mt19937 rng;
boost::random::uniform_int_distribution<> ten(0,10);
int randomNumber = ten(rng);
if(guessNumber == randomNumber) {
   std::cout << "Congratulation, " << guessNumber << " is your
   lucky number.n";
}
else {
   std::cout << "Sorry, I'm thinking about number " <<
   randomNumber << "n";
}
return 0;
}

We can compile the preceding source code by using the following command:

g++ -Wall -ansi -Ic:/boost_1_58_0 rangen_boost.cpp -o rangen_boost

Now, let us run the program. Unfortunately, for the three times that I ran the program, I always obtained the same random number as follows:

As we can see from this example, we always get number 8. This is because we apply Mersenne Twister, a Pseudorandom Number Generator (PRNG), which uses the default seed as a source of randomness so it will generate the same number every time the program is run. And of course it is not the program that we expect.

Now, we will rework the program once again, just in two lines. First, find the following line:

#include <boost/random/mersenne_twister.hpp>

Change it as follows:

#include <boost/random/random_device.hpp>

Next, find the following line:

boost::random::mt19937 rng;

Change it as follows:

boost::random::random_device rng;

Then, save the file as rangen2_boost.cpp and compile the rangen2_boost.cpp file by using the command like we compiled rangen_boost.cpp. The command will look like this:

g++ -Wall -ansi -Ic:/boost_1_58_0 rangen2_boost.cpp -o rangen2_boost

Sadly, there will be something wrong and the compiler will show the following error message:

cc8KWVvX.o:rangen2_boost.cpp:(.text$_ZN5boost6random6detail20generate _uniform_intINS0_13random_deviceEjEET0_RT_S4_S4_N4mpl_5bool_ILb1EEE[_ ZN5boost6random6detail20generate_uniform_intINS0_13random_deviceEjEET 0_RT_S4_S4_N4mpl_5bool_ILb1EEE]+0x24f): more undefined references to boost::random::random_device::operator()()' follow

collect2.exe: error: ld returned 1 exit status

This is because, as we have discussed earlier, the Boost.Random library needs to be compiled separately if we want to use the random_device attribute.

Boost libraries have a system to compile or build Boost itself, called Boost.Build library. There are two steps we have to achieve to install the Boost.Build library. First, run Bootstrap by pointing the active directory at the command prompt to C:boost_1_58_0 and typing the following command:

bootstrap.bat mingw

We use our MinGW compiler, as our toolset in compiling the Boost library. Wait a second and then we will get the following output if the process is a success:

Building Boost.Build engine

Bootstrapping is done. To build, run:

   .b2

To adjust configuration, edit 'project-config.jam'.
Further information:
- Command line help: .b2 --help - Getting started guide: http://boost.org/more/getting_started/windows.html - Boost.Build documentation: http://www.boost.org/build/doc/html/index.html

In this step, we will find four new files in the Boost library’s root directory. They are:

  • b2.exe: This is an executable file to build Boost libraries.
  • bjam.exe: This is exactly the same as b2.exe but it is a legacy version.
  • bootstrap.log: This contains logs from the bootstrap process
  • project-config.jam: This contains a setting that will be used in the building process when we run b2.exe.

We also find that this step creates a new directory in C:boost_1_58_0toolsbuildsrcenginebin.ntx86 , which contains a bunch of .obj files associated with Boost libraries that needed to be compiled.

After that, run the second step by typing the following command at the command prompt:

b2 install toolset=gcc

Grab yourself a cup of coffee after running that command because it will take about twenty to fifty minutes to finish the process, depending on your system specifications. The last output we will get will be like this:

...updated 12562 targets...

This means that the process is complete and we have now built the Boost libraries. If we check in our explorer, the Boost.Build library adds C:boost_1_58_0stagelib, which contains a collection of static and dynamic libraries that we can use directly in our program.

bootstrap.bat and b2.exe use msvc (Microsoft Visual C++ compiler) as the default toolset, and many Windows developers already have msvc installed on their machines. Since we have installed GCC compiler, we set the mingw and gcc toolset options in Boost’s build. If you also have mvsc installed and want to use it in Boost’s build, the toolset options can be omitted.

Now, let us try to compile the rangen2_boost.cpp file again, but now with the following command:

c:CPP>g++ -Wall -ansi -Ic:/boost_1_58_0 rangen2_boost.cpp - Lc:boost_1_58_0stagelib -lboost_random-mgw49-mt-1_58 - lboost_system-mgw49-mt-1_58 -o rangen2_boost

We have two new options here, they are –L and –l. The -L option is used to define the path that contains the library file if it is not in the active directory. The –l option is used to define the name of library file but omitting the first lib word in front of the file name. In this case, the original library file name is libboost_random-mgw49-mt-1_58.a, and we omit the lib phrase and the file extension for option -l.

The new file called rangen2_boost.exe will be created in C:CPP. But before we can run the program, we have to ensure that the directory which the program installed has contained the two dependencies library file. These are libboost_random-mgw49-mt-1_58.dll and libboost_system-mgw49-mt-1_58.dll, and we can get them from the library directory c:boost_1_58_0_1stagelib.

Just to make it easy for us to run that program, run the following copy command to copy the two library files to C:CPP:

copy c:boost_1_58_0_1stageliblibboost_random-mgw49-mt-1_58.dll c:cpp
copy c:boost_1_58_0_1stageliblibboost_system-mgw49-mt-1_58.dll c:cpp

And now the program should run smoothly.

In order to create a network application, we are going to use the Boost.Asio library. We do not find Boost.Asio—the library we are going to use to create a network application—in the non-header-only library. It seems that we do not need to build the boost library since Boost.Asio is header-only library. This is true, but since Boost.Asio depends on Boost.System and Boost.System needs to be built before being used, it is important to build Boost first before we can use it to create our network application.

For option –I and –L, the compiler does not care if we use backslash () or slash (/) to separate each directory name in the path because the compiler can handle both Windows and Unix path styles.

Summary

We saw that Boost C++ libraries were developed to complement the standard C++ library We have also been able to set up our MinGW compiler in order to compile the code which contains Boost libraries and build the binaries of libraries which have to be compiled separately. Please remember that though we can use the Boost.Asio library as a header-only library, it is better to build all Boost libraries by using the Boost.Build library. It will be easy for us to use all libraries without worrying about compiling failure.

Resources for Article:

 


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here