C/C + + development boost library reference manual sorting [learning notes]

Document declaration:
The following materials belong to my study notes produced in the process of learning. If there are errors or omissions, please correct them. And the document will be continuously supplemented and improved with the deepening of learning in the later stage. Thank you for your reference.

The notes are only for learning and communication. Please indicate the source for reprint. Thank you for your cooperation.
If there are omissions of relevant knowledge points, you can leave a message in the comment area and update it as soon as you see it.
Author: Aliven888

introduction

  this document is sorted out according to the document library on the official website. Due to the limited space, please jump to the reference manual for C/C + + development boost library (2) [learning notes] for the first half of the documents.

Chapter 1 Introduction

1.1. C + + and Boost

  Boost C + + library is a set of modern libraries based on C + + standards. Its source code is released under the Boost Software License, allowing anyone to use, modify and distribute it freely. These libraries are platform independent and support most well-known and less well-known compilers.

  the Boost community is responsible for developing and publishing the Boost C + + library. The community is composed of a large group of C + + developers from all over the world. They use the website www.Boost.com Org and several mailing lists coordinate with each other. The mission of the community is to develop and collect high-quality libraries as a supplement to the C + + standard. Libraries that have proved valuable and important for C + + application development will have a great chance to be included in the C + + standard one day.

  the Boost community appeared around 1998, when the first version of the C + + standard was just released. Since then, the community has been expanding and has now become an important role in C + + standardization. Although there is no direct relationship between the Boost community and the Standardization Committee, some developers are active on both sides at the same time. The next version of the C + + standard is likely to be adopted in 2011, which will expand a number of libraries, all of which originated in the Boost community.

  to enhance the productivity of C + + projects, in addition to the C + + standard, Boost C + + library is a good choice. Since the current version of the C + + standard was revised in 2003, C + + has a new development, so the Boost C + + library provides many new features. With the Boost C + + library, we can enjoy the latest progress in the evolution of C + + without waiting for the next version of the C + + standard.

  Boost C + + libraries have a good reputation, which has proved to be very valuable based on their use. It is not common to ask about the knowledge of Boost C + + libraries in the interview, because developers who know these libraries usually know the latest innovations of C + + and can write and understand modern C + + code.

1.2. development process

  it is precisely because of the support and participation of a large number of independent developers and organizations that the development of Boost C + + library is possible. Because Boost only accepts libraries that meet the following conditions: solving real problems, showing convincing design, developing with modern C + + and providing documents in an understandable way, there is a lot of work behind each Boost C + + library.

  C + + developers can join the Boost community and propose their own new libraries. However, to turn an idea into a Boost C + + library requires a lot of time and effort. The most important thing is to discuss needs and possible solutions with other developers and potential users in the Boost mailing list.

  in addition to these new libraries that seem to emerge from nowhere, you can also nominate some existing C + + libraries to Boost. However, since the requirements for these libraries are the same as those developed specifically for Boost, they may need a lot of modifications.

  whether a library is connected to Boost depends on the results of the review process. Developers of the library can apply for review, which usually takes 10 days. During this time, other developers were invited to rate the library. Based on the number of positive and negative comments, the review manager will decide whether the library is accepted into Boost. Since some developers only disclose the code of the library for the first time in the review stage, it is not uncommon to be asked to modify the library during the review.

  if a library is rejected for technical reasons, it may also apply for a new review of the updated version after modification. However, if a library is rejected because it cannot solve practical problems or provide convincing solutions, another review is likely to be rejected.

  because new libraries may be accepted at any time, the Boost C + + library will release a new version every three months. The libraries involved in this book are based on version 1.42.0 released in February 2010.

  please note that there are other libraries that have been accepted but have not yet become part of the Boost C + + library release. They must be installed manually before they can be included in the release.

1.3. install

  Boost C + + libraries are provided with source code. Most of these libraries only contain header files and can be used directly, but some libraries need to be compiled. To make installation as easy as possible, you can use Boost Jam for automatic installation. Without checking and compiling libraries one by one, Boost Jam automatically installs the entire library set. It supports many operating systems and compilers and knows how to compile a single library based on the appropriate configuration file.

  in order to install automatically with the help of Boost Jam, use an application named bjam, which also comes with source code. For some operating systems, including Windows and Linux, there are also pre compiled bjam binaries.

  to compile bjam itself, execute a simple script called build, which also provides source code for different operating systems. For Windows, it is the batch file build bat. For Linux, the file name is build sh.

  if build is executed without any command line options, the script tries to find a suitable compiler to generate bjam. You can select a specific compiler by using a command-line switch called toolset. For Windows, build supports toolsets vc7, vc8 and vc9. You can choose different versions of Microsoft C + + compiler. To compile bjam from the C + + compiler of Visual Studio 2008, you need to specify the command build vc9. For Linux, tools GCC and Intel Linux are supported, and GCC and Intel C + + compilers are selected respectively.

  the application bjam must be copied to the local Boost Directory - whether it is compiled or downloaded precompiled binaries. Then you can execute bjam without any command-line options, compile and install the Boost C + + library. Since the default option - used in this case - is not necessarily the best choice, the following are the most important options for reference:

  declare that stage or install can specify whether the Boost C + + library is installed in a subdirectory named stage or system wide. The meaning of system scope depends on the operating system. In Windows, the target directory is C:\Boost; In Linux, / usr/local. The target directory can also be specified with the -- prefix option.

  if bjam is executed without any command line options, it will search for a suitable C + + compiler by itself. You can specify a specific compiler with the -- toolset option. To specify the Microsoft C + + compiler for Visual Studio 2008 in Windows, bjam should be executed with -- toolset=msvc-9.0 option. To specify the gcc compiler in Linux, give the -- toolset=gcc option.

  the command line option -- build type determines how to create the library. By default, this option is set to minimal, that is, only the release is created. This can be a problem for developers who want to build a debug version of their project with Visual Studio or GCC. Because these compilers will automatically try to link the debug version of the Boost C + + library, which will give an error message. In this case, the -- build type option should be set to complete to generate the debug and release versions of the Boost C + + library at the same time. Of course, it will take longer.

  to use the C + + compiler of Visual Studio 2008 to generate the debug version and release version of Boost C + + library at the same time and install them in the directory D:\Boost, the command to be executed is bjam -- toolset = msvc-9.0 -- build type = complete -- prefix = D:\Boost install To create them using the default directory in Linux, the command to execute is bjam -- toolset = GCC -- build type = complete install

Several other details of how the C + + library is compiled can be specified on the command line. I usually use the following command under Windows: bjam -- toolset = msvc-9.0 debug release link=static runtime link = shared install Debug and release enable both debug and release versions to be generated. link=static, only static libraries are created. Runtime link = shared specifies that the C + + runtime library is dynamically linked, which is the default setting for C + + projects in Visual Studio 2008.

1.4. summary

  version 1.42.0 of Boost C + + library contains more than 90 libraries. This book only discusses the following libraries in detail:

Boost C + + Library C + + standard Brief description
Boost.Any Boost.Any provides a data type called boost::any, which can store any type. For example, a variable of type boost::any can store a value of type int and then replace it with a string of type std::string.
Boost.Array TR1 Boost.Array can treat C + + arrays as C + + standard containers.
Boost.Asio TR2 Boost.Asio can be used to develop applications that process data asynchronously, such as network applications.
Boost.Bimap Boost.Bimap provides a class called boost::bimap, which is similar to std::map The main difference is that boost::bimap can search from both keys and values.
Boost.Bind TR1 Boost.Bind is an adapter that can take a function as a template parameter, even if the signature of the function is incompatible with the template parameter.
Boost.Conversion Boost.Conversion provides three transformation operators, which perform downward transformation, cross transformation, and value conversion between different number types.
Boost.DateTime Boost.DateTime can be used to process, read in and write out date and time values in a flexible format.
Boost.Exception Boost.Exception can add additional data to the thrown exception to provide more information in catch processing. This helps to debug and respond better to exceptions.
Boost.Filesystem TR2 Boost.Filesystem provides a class to handle path information, and also contains several functions to access files and directories.
Boost.Format Boost.Format replaces the std::printf() function with a type safe and extensible boost::format class.
Boost.Function TR1 Boost.Function simplifies the definition of function pointers.
Boost.Interprocess Boost.Interprocess allows multiple applications to communicate in a fast and efficient manner through shared memory.
Boost.Lambda Boost.Lambda can define anonymous functions. The code is declared and executed inline, avoiding separate function calls.
Boost.Multiindex Boost.Multiindex defines some new containers that can support multiple interfaces at the same time, such as the interface of std::vector and std::map.
Boost.NumericConversion Boost.NumericConversion provides a transformation operator, which can safely convert values between different number types without generating up overflow or down overflow conditions.
Boost.PointerContainer Boost.PointerContainer provides a container optimized specifically for dynamically allocating objects.
Boost.Ref TR1 Boost. The adapter of ref can pass the reference of non replicable object to the function to be copied.
Boost.Regex TR1 Boost.Regex provides functions for text search through regular expressions.
Boost.Serialization Via boost Serialization, objects can be serialized, such as saved in a file and re imported later.
Boost.Signals Boost.Signal is an event processing framework based on the so-called signal/slot concept. The function is associated with the signal and is called automatically when the signal is triggered.
Boost.SmartPoiners TR1 Boost. Smartpointers provides multiple smart pointers to simplify the management of dynamically allocated objects.
Boost.Spirit Boost.Spirit can generate a lexical analyzer with a syntax similar to EBNF (extended barkos paradigm).
Boost.StringAlgorithms Boost.StringAlgorithms provides several independent functions to facilitate string processing.
Boost.System TR2 Boost.System provides a framework for handling system related or application related error codes.
Boost.Thread C++0x Boost.Thread can be used to develop multithreaded applications.
Boost.Tokenizer Boost.Tokenizer can iterate over the components of a string.
Boost.Tuple TR1 Boost.Tuple provides a generalized version of std::pair, which can group any number of data together.
Boost.Unordered TR1 Boost.Unordered extends the C + + standard container and adds boost::unordered_set and boost::unordered_map.
Boost.Variant Boost.Variant can define multiple data types, similar to union, which groups multiple data types together. Boost. The advantage of variant over union is that it can use classes.

  Technical Report 1 was released in 2003. Some details about C++0x standard and Technical Report 2 can reflect the current status. Since neither the next version of C + + standard nor Technical Report 2 has been approved, they may still change in the future.

Chapter 2 smart pointer

2.1. Overview

  the first edition of C + + standard revised in 1998 only provides one kind of smart pointer: std::auto_ptr . It's basically like a normal pointer: accessing a dynamically allocated object through an address. std::auto_ptr is regarded as a smart pointer because it will call the delete operator to automatically release the contained objects when destructing. Of course, this requires that it be given the address of the object returned by the new operator during initialization. Since STD:: Auto_ The destructor of PTR will call the delete operator, and the memory of the object it contains will be guaranteed to be released. This is an advantage of smart pointers.

  this is even more important when associated with exceptions: there is no STD:: Auto_ With Smart Pointers such as PTR, every function that dynamically allocates memory needs to catch all possible exceptions to ensure that the memory is released before the exception is passed to the caller of the function. The Boost C + + library Smart Pointers provides many Smart Pointers that can be used in various occasions.

2.2. RAII

  the principle of smart pointer is based on a common idiom called RAII: resource application is initialization. Smart pointer is just one example of this idiom - of course, a very important one. Smart pointers ensure that dynamically allocated memory can be released correctly in any case, freeing developers from this task. This includes scenarios where the program is interrupted due to an exception and the code originally used to free memory is skipped. This is ensured by initializing the smart pointer with the address of a dynamically allocated object and freeing memory during deconstruction. Because the destructor will always be executed, the contained memory will always be released.

  RAII is applicable whenever there must be a second instruction to release the resources allocated by another instruction. Many C + + applications need to dynamically manage memory, so smart pointer is a very important type of RAII. However, RAII itself is applicable to many other scenarios.

#include <windows.h> 

class windows_handle 
{ 
  public: 
    windows_handle(HANDLE h) 
      : handle_(h) 
    { 
    } 

    ~windows_handle() 
    { 
      CloseHandle(handle_); 
    } 

    HANDLE handle() const 
    { 
      return handle_; 
    } 

  private: 
    HANDLE handle_; 
}; 

int main() 
{ 
  windows_handle h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId())); 
  SetPriorityClass(h.handle(), HIGH_PRIORITY_CLASS); 
} 

The name of the above example is defined as_ The class of handle whose destructor calls the CloseHandle() function. This is a Windows API function, so this program can only run on windows. On windows, many resources are required to be opened before use. This implies that resources should be shut down once they are no longer in use. windows_ The mechanism of the handle class ensures this.

  windows_ An instance of the HANDLE class is initialized with a HANDLE. Windows uses handles to uniquely identify resources. For example, the OpenProcess() function returns a HANDLE of type HANDLE, through which the process in the current system can be accessed. In the sample code, the process itself is accessed -- in other words, the application itself.

  we raise the priority of the process through this returned handle, so that it can get more CPU time from the scheduler. This is for demonstration purposes only and has no practical effect. It is important to note that resources opened through OpenProcess() do not need to be closed by calling CloseHandle() as shown. Of course, resources will be shut down when the application terminates. However, in more complex applications, windows_ The handle class ensures that a resource can be properly closed when it is no longer in use. Once a resource leaves its scope - the scope of h in the above example is at the end of the main() function - its destructor will be called automatically and the corresponding resource will be released.

2.3. Scope pointer

  a scope pointer monopolizes a dynamically allocated object. The corresponding class name is boost::scoped_ptr is defined in boost/scoped_ptr.hpp. Not like std::auto_ptr a scope pointer cannot pass ownership of the objects it contains to another scope pointer. Once initialized with an address, the dynamically allocated object will be released in the deconstruction phase.

  because a scope pointer simply saves and monopolizes a memory address, boost:: scoped_ The implementation of PTR is better than std::auto_ptr is simple. Boost:: scoped should be used first when ownership transfer is not required_ ptr . In these cases, compared with std::auto_ptr is a better choice because it can avoid inadvertent transfer of ownership.

#include <boost/scoped_ptr.hpp> 

int main() 
{ 
  boost::scoped_ptr<int> i(new int); 
  *i = 1; 
  *i.get() = 2; 
  i.reset(new int); 
} 

  once initialized, the smart pointer boost:: scoped_ The objects contained in PTR can be accessed through an interface similar to ordinary pointers. This is because the related operators operator * (), operator - > () and operator bool() are overloaded. In addition, there are get() and reset() methods. The former returns the address of the contained object, and the latter reinitializes the smart pointer with a new object. In this case, the newly created object will be automatically released before assignment.

  boost::scoped_ The destructor of PTR uses the delete operator to release the contained objects. This pair of boost:: scoped_ There is an important limitation on the types contained in PTR. boost::scoped_ptr cannot be initialized with dynamically allocated array, because it needs to call delete [] to release. In this case, you can use boost: scoped, which will be introduced below_ Array class.

2.4. Scope array

  scope arrays are used in a similar way to scope pointers. The key difference is that the destructor of the scope array uses the delete [] operator to release the contained objects. Because this operator can only be used for array objects, the scope array must be initialized by dynamically allocated arrays.

  the corresponding scope array class name is boost:: scoped_ It's defined in / scoped, boost array_ array. In HPP.

#include <boost/scoped_array.hpp> 

int main() 
{ 
  boost::scoped_array<int> i(new int[2]); 
  *i.get() = 1; 
  i[1] = 2; 
  i.reset(new int[3]); 
} 

  boost:scoped_ The array class overloads the operators operator [] () and operator bool(). You can access specific elements in the array through the operator [] () operator, so boost:: scoped_ An object of type array behaves like the array it contains.

  just like boost::scoped_ptr, boost:scoped_array also provides get() and reset() methods to return and reinitialize the address of the contained object.

2.5. Shared pointer

  this is the most widely used smart pointer, but it is missing in the first version of the C + + standard. It has been added to the standard as part of technical report 1 (TR 1). If the development environment supports it, you can use STD:: shared defined in memory_ ptr. In the Boost C + + library, this smart pointer is named boost::shared_ptr, defined in boost / shared_ ptr. In HPP.

  smart pointer boost::shared_ptr is basically similar to boost::scoped_ptr. The key difference is boost::shared_ptr does not have to be exclusive. It can work with other boost::shared_ptr type smart pointers share ownership. In this case, the object will not be released until the last smart pointer of the reference object is destroyed.

  because the ownership can be in boost:: shared_ PTRs are shared, and any shared pointer can be copied, which is similar to boost::scoped_ptr is different. This allows you to store smart pointers in standard containers - you can't store STD:: Auto in standard containers_ PTRs, because they pass ownership when copying.

#include <boost/shared_ptr.hpp> 
#include <vector> 

int main() 
{
	std::vector<boost::shared_ptr<int> > v; 
	v.push_back(boost::shared_ptr<int>(new int(1))); 
	v.push_back(boost::shared_ptr<int>(new int(2))); 
} 

  thanks to boost::shared_ptr, we can safely use dynamically allocated objects in the standard container as shown in the above example. Because boost::shared_ptr can share the ownership of the objects it contains, so the copies kept in the container (including the additional copies created by the container when needed) are the same as the original. As mentioned earlier, std::auto_ptr cannot do this, so they should never be saved in containers.

  similar to boost::scoped_ptr, boost::shared_ The PTR class overloads the following operators: operator * (), operator - > (), and operator bool(). In addition, there are get() and reset() functions to obtain and reinitialize the address of the contained object.

#include <boost/shared_ptr.hpp> 

int main() 
{ 
	  boost::shared_ptr<int> i1(new int(1)); 
	  boost::shared_ptr<int> i2(i1); 
	  i1.reset(new int(2)); 
} 

  in this example, two shared pointers i1 and i2 are defined, both of which refer to the same object of type int. i1 is initialized by the address display returned by the new operator, and i2 is constructed by copying i1. i1 then calls reset(), and the address of the integer it contains is reinitialized. However, the object it contained was not released because i2 still references it. Smart pointer boost::shared_ptr records how many shared pointers refer to the same object. The object will be released only when the last shared pointer is destroyed.

  by default, boost::shared_ptr uses the delete operator to destroy the contained objects. However, the specific method of destruction can be specified, as shown in the following example:

#include <boost/shared_ptr.hpp> 
#include <windows.h> 

int main() 
{ 
	  boost::shared_ptr<void> h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId()), CloseHandle); 
	  SetPriorityClass(h.get(), HIGH_PRIORITY_CLASS); 
} 

  boost::shared_ The second parameter of PTR constructor is an ordinary function or function object, which is used to destroy the contained object. In this case, this parameter is the Windows API function CloseHandle(). When the variable h is out of its scope, this function is called instead of the delete operator to destroy the contained object. In order to avoid compilation errors, the function can only take one parameter of HANDLE type, and CloseHandle () just meets the requirements.

  this example works the same as the example used when describing RAII idioms earlier in this chapter. However, this example does not define a single windows_ Instead, the handle class uses boost::shared_ptr's feature passes a method to its constructor, which will be called automatically when the shared pointer exceeds its scope.

2.6. Shared array

  shared arrays behave like shared pointers. The key difference is that when the shared array is destructed, the delete [] operator is used by default to release the contained objects. Because this operator can only be used for array objects, shared arrays must be initialized by dynamically allocated array addresses.

  the corresponding type of shared array is boost::shared_array, which is defined in boost / shared_ array. In HPP.

#include <boost/shared_array.hpp> 
#include <iostream> 

int main() 
{ 
	  boost::shared_array<int> i1(new int[2]); 
	  boost::shared_array<int> i2(i1); 
	  i1[0] = 1; 
	  std::cout << i2[0] << std::endl; 
} 

  just like shared pointers, the ownership of the contained objects can be shared with other shared arrays. In this example, two variables i1 and i2 are defined, which refer to the same dynamically allocated array. i1 through operator The operator holds an integer 1 -- an integer that can be i2 referenced, such as printing to standard output.

  like all smart pointers in this chapter, boost::shared_array also provides get() and reset() methods. operator bool() is also overloaded.

2.7. Weak pointer

  various intelligent pointers introduced so far can be used independently in different situations. In contrast, weak pointers make sense only when used in conjunction with shared pointers. Weak pointer boost::weak_ptr is defined in boost / weak_ ptr. In HPP.

#include <windows.h> 
#include <boost/shared_ptr.hpp> 
#include <boost/weak_ptr.hpp> 
#include <iostream> 

DWORD WINAPI reset(LPVOID p) 
{ 
	  boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p); 
	  sh->reset(); 
	  return 0; 
} 

DWORD WINAPI print(LPVOID p) 
{ 
	  boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p); 
	  boost::shared_ptr<int> sh = w->lock(); 
	  if (sh) 
	    std::cout << *sh << std::endl; 
	  return 0; 
} 

int main() 
{ 
	  boost::shared_ptr<int> sh(new int(99)); 
	  boost::weak_ptr<int> w(sh); 
	  HANDLE threads[2]; 
	  threads[0] = CreateThread(0, 0, reset, &sh, 0, 0); 
	  threads[1] = CreateThread(0, 0, print, &w, 0, 0); 
	  WaitForMultipleObjects(2, threads, TRUE, INFINITE); 
} 

  boost::weak_ptr must always pass through boost::shared_ptr to initialize. Once initialized, it basically provides only one useful method: lock(). Boost:: shared returned by this method_ PTR shares ownership with the shared pointer used to initialize the weak pointer. If the shared pointer does not contain any objects, the returned shared pointer will also be empty.

  weak pointers can be used when a function needs an object managed by a shared pointer, and the lifetime of the object does not depend on the function. As long as there is a shared pointer in the program that controls the object, the function can use the object. If the shared pointer is reset, even if a shared pointer can be obtained in the function, the object does not exist.

  in the main() function of the above example, two threads are created through the Windows API. Therefore, this example can only be compiled and run on Windows platform.

  the parameter of the first thread function reset() is the address of a shared pointer. The parameter of the second thread function print() is the address of a weak pointer. This weak pointer was previously initialized through a shared pointer.

  once the program starts, reset() and print() begin to execute. However, the order of execution is uncertain. This leads to a potential problem: when the reset() thread destroys the object, the print() thread may be accessing it.

  this problem can be solved by calling the lock() function of weak pointer: if the object exists, the shared pointer returned by the lock() function points to the legal object. Otherwise, the returned shared pointer is set to 0, which is equivalent to the standard null pointer.

  the weak pointer itself has no effect on the lifetime of the object. lock() returns a shared pointer, and the print() function can safely access the object. This ensures - even if another thread wants to release the object - that the object still exists because we have a returned shared pointer.

2.8. Intrusive pointer

  in general, intrusive pointers work exactly like shared pointers. boost::shared_ptr internally records the number of shared pointers that refer to an object, but for intrusive pointers, programmers have to make their own records. This is particularly useful for framework objects because they record the number of times they are referenced.

  intrusive pointer boost::intrusive_ptr is defined in boost / intrusive_ ptr. In HPP.

#include <boost/intrusive_ptr.hpp> 
#include <atlbase.h> 
#include <iostream> 

void intrusive_ptr_add_ref(IDispatch *p) 
{ 
  	p->AddRef(); 
} 

void intrusive_ptr_release(IDispatch *p) 
{ 
  	p->Release(); 
} 

void check_windows_folder() 
{ 
	  CLSID clsid; 
	  CLSIDFromProgID(CComBSTR("Scripting.FileSystemObject"), &clsid); 
	  void *p; 
	  CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), &p); 
	  boost::intrusive_ptr<IDispatch> disp(static_cast<IDispatch*>(p)); 
	  CComDispatchDriver dd(disp.get()); 
	  CComVariant arg("C:\\Windows"); 
	  CComVariant ret(false); 
	  dd.Invoke1(CComBSTR("FolderExists"), &arg, &ret); 
	  std::cout << (ret.boolVal != 0) << std::endl; 
} 

void main() 
{ 
	  CoInitialize(0); 
	  check_windows_folder(); 
	  CoUninitialize(); 
} 

  the above example uses the functions provided by com (component object model), so it can only be compiled and run on Windows platform. COM objects use boost::intrusive_ptr is a great example because a COM object needs to record how many pointers currently reference it. By calling AddRef() and Release() functions, the internal reference count increases or decreases by 1 respectively. When the reference count is 0, the COM object is automatically destroyed.

  in intrusive_ptr_add_ref() and intrusive_ptr_release() internally calls AddRef() and release() functions to increase or decrease the reference count of corresponding COM objects. The COM object used in this example is called 'filesystembobject', which is available by default on Windows. Through this object, you can access the underlying file system, such as checking whether a given directory exists. In the above example, we check whether the C:\Windows directory exists. How to implement it internally is similar to boost:: intrusive_ The function of PTR is independent and completely depends on COM. The key point is that once the intrusive pointer disp leaves its scope - check_ Windows_ At the end of the folder() function, the function is intrusive_ptr_release() will be called automatically. This will reduce the internal reference count of the COM object 'FileSystemObject' to 0, and the object is destroyed.

2.9. Pointer container

You should be able to use the array of C + + objects and the dynamic pointer library to allocate all kinds of C + + objects. Most of the time, these objects are stored in containers -- as described above -- using boost::shared_ptr and boost::shared_array this is quite simple.

#include <boost/shared_ptr.hpp> 
#include <vector> 

int main() 
{ 
  std::vector<boost::shared_ptr<int> > v; 
  v.push_back(boost::shared_ptr<int>(new int(1))); 
  v.push_back(boost::shared_ptr<int>(new int(2))); 
} 

  of course, the code in the above example is correct. Smart pointers can be used in this way. However, for some reasons, they are not used in practice. First, repeatedly declare boost::shared_ptr requires more input. Secondly, boost::shared_ptr copy in, copy out, or copy inside the container requires frequent increase or decrease of internal reference count, which is certainly inefficient. For these reasons, the Boost C + + library provides a pointer container specifically for managing dynamically allocated objects.

#include <boost/ptr_container/ptr_vector.hpp> 

int main() 
{ 
  boost::ptr_vector<int> v; 
  v.push_back(new int(1)); 
  v.push_back(new int(2)); 
} 

  boost::ptr_ The vector class is defined in boost / PTR_ container/ptr_ vector. In HPP, it uses boost:: shared as in the previous example_ Containers initialized with PTR template parameters have the same working mode. boost::ptr_vector is specially used for dynamically allocated objects, which is easier and more efficient to use. boost::ptr_vector monopolizes the objects it contains, so shared pointers outside the container cannot share ownership, which is the same as STD:: vector < boost::shared_ptr > opposite.

  except boost:: PTR_ In addition to vector, the container dedicated to managing dynamically allocated objects also includes: boost::ptr_deque, boost::ptr_list, boost::ptr_set, boost::ptr_map, boost::ptr_unordered_set and boost::ptr_unordered_map. These containers are equivalent to those provided in the C + + standard. The last two containers correspond to std::unordered_set and std::unordered_map, which are added to the C + + standard as part of technical report 1. If the C + + standard implementation used does not support technical report 1, you can also use boost:: unordered implemented in Boost C + + library_ Set and boost::unordered_map.

Chapter 3 function object

3.1. summary

  this chapter introduces the function object, which may be better referred to as' higher-order function '. It actually refers to a class of functions that can be passed into or returned from other functions. In C + +, higher-order functions are implemented as function objects, so this title is still meaningful.

  in this whole chapter, we will introduce several Boost C + + libraries for processing function objects. Among them, boost Bind replaces the well-known std::bind1st() and std::bind2nd() functions from the C + + standard, while boost Function provides a class for encapsulating function pointers. Finally, boost Lambda introduced a method to create anonymous functions.

3.2. Boost.Bind

  Boost.Bind is a library that simplifies a mechanism provided by the std::bind1st() and std::bind2nd() template functions in the C + + standard: by using these functions with almost unlimited parameters, you can get the function with the specified signature. One of the best examples of this situation is the multiple different algorithms defined in the C + + standard.

#include <iostream> 
#include <vector> 
#include <algorithm> 

void print(int i) 
{ 
  	std::cout << i << std::endl; 
} 

int main() 
{
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 

	  std::for_each(v.begin(), v.end(), print); 
} 

  algorithm std::for_each() requires its third argument to be a function or function object that accepts exactly one argument. If std::for_each() is executed, specifying that all elements in the container - in the above example, these elements are of type int - will be passed into the print() function in order. However, if you want to use a function with different signatures, things are complicated. For example, if you want to pass in the following function add(), it will add a constant value to each element in the container and display the result.

void add(int i, int j) 
{ 
 	 std::cout << i + j << std::endl; 
} 

  due to std::for_each() requires a function that accepts only one parameter, so you cannot pass in the add() function directly. The source code must be modified.

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <functional> 

class add 
  : public std::binary_function<int, int, void> 
{ 
public: 
	  void operator()(int i, int j) const 
	  { 
	    std::cout << i + j << std::endl; 
	  } 
}; 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), std::bind1st(add(), 10)); 
} 

  the above procedure adds a value of 10 to each element of container v and displays the results using the standard output stream. The source code must be significantly modified to achieve this function: the add() function has been converted to a function derived from std::binary_function object.

  Boost.Bind simplifies binding between different functions. The boost() function contains only one template HPP. Using this function, the above example can be realized as follows:

#include <boost/bind.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

void add(int i, int j) 
{ 
	  std::cout << i + j << std::endl; 
} 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1)); 
} 

  functions like add() are no longer needed in order to be used for std::for_each() is converted to a function object. Using boost::bind(), this function can be used regardless of its first parameter.

  because the add() function requires two parameters, both parameters must be passed to boost::bind(). The first parameter is a constant value of 10, while the second parameter is a strange one_ 1.

  _ 1 is called a placeholder and is defined in boost Bind. Except_ 1,Boost.Bind also defines_ 2 and_ 3. By using these placeholders, boost::bind() can become a unary, binary, or ternary function. For_ 1. boost::bind() becomes a unary function -- that is, a function requiring only one parameter. This is required because std::for_each() requires a unary function as its third parameter.

  when this program is executed, std::for_each() calls the unary function on the first element in the container v. The value of the element is passed through a placeholder_ 1 is passed into a unary function. This placeholder and constant value are further passed to the add() function. By using this mechanism, std::for_each() only sees the unary function defined by boost::bind(). boost::bind() itself simply calls another function and passes it a constant value or placeholder as a parameter.

  the following example defines a binary function through boost::bind() for the std::sort() algorithm, which requires a binary function as its third parameter.

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

int main() 
{ 
  std::vector<int> v; 
  v.push_back(1); 
  v.push_back(3); 
  v.push_back(2); 

  std::sort(v.begin(), v.end(), boost::bind(compare, _1, _2)); 
} 

  because two placeholders are used_ And 1_ 2. So boost::bind() defines a binary function. std::sort() algorithm calls this function with two elements of container v and sorts the containers according to the return value. Based on the definition of the compare() function, the containers will be arranged in descending order.

  however, since compare() itself is a binary function, it is redundant to use boost::bind().

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

int main() 
{ 
  std::vector<int> v; 
  v.push_back(1); 
  v.push_back(3); 
  v.push_back(2); 

  std::sort(v.begin(), v.end(), compare); 
} 

  however, it makes sense to use boost::bind(). For example, if the containers are arranged in ascending order and you cannot modify the definition of the compare() function.

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

int main() 
{ 
  std::vector<int> v; 
  v.push_back(1); 
  v.push_back(3); 
  v.push_back(2); 

  std::sort(v.begin(), v.end(), boost::bind(compare, _2, _1)); 
} 

  this example only changes the order of placeholders:_ 2 is passed as the first parameter, and_ 1 is passed as the second parameter to compare(), which can change the sorting order.

3.3. Boost.Ref

  this library boost Ref is usually associated with boost Bind works together, so I write them next to each other. It provides two functions - boost::ref() and boost::cref() - defined in boost/ref.hpp.

  when the function to be used for boost::bind() has at least one reference parameter, boost Ref is very important. Since boost::bind() will copy its parameters, references must be handled specially.

#include <boost/bind.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

void add(int i, int j, std::ostream &os) 
{ 
 	 os << i + j << std::endl; 
} 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1, boost::ref(std::cout))); 
} 

  the above example uses the add() function in the previous section. This time, however, the function needs a reference to the stream object to print the information. Because the parameter passed to boost::bind() is passed by value, std::cout cannot be used directly, otherwise the function will try to create a copy of it.

  by using the template function boost::ref(), streams like std::cout can be passed by reference, and the above example can be successfully compiled.

  to pass constant objects by reference, you can use the template function boost::cref().

3.4. Boost.Function

  to encapsulate function pointers, boost Function provides a class called boost::function. It is defined in boost / function HPP, the usage is as follows:

#include <boost/function.hpp> 
#include <iostream> 
#include <cstdlib> 
#include <cstring> 

int main() 
{ 
	  boost::function<int (const char*)> f = std::atoi; 
	  std::cout << f("1609") << std::endl; 
	  f = std::strlen; 
	  std::cout << f("1609") << std::endl; 
} 

  boost::function can define a pointer to a function with a specific signature. The above example defines a pointer f, which can point to a function that accepts a parameter of type const char * and returns a value of type int. After the definition is completed, the function matching this signature can be assigned to this pointer. This routine first assigns std::atoi() to f, and then reassigns it to std::strlen().

  note that a given data type does not require an exact match: Although std::strlen() is based on std::size_t is the return type, but it can also be assigned to f.

  because f is a function pointer, the assigned function can be called through the overloaded operator()() operator. Depending on which function is currently assigned, std::atoi() or std::strlen() will be called in the above example.

  if f is called without a function, a boost:: bad will be thrown_ function_ Call exception.

#include <boost/function.hpp> 
#include <iostream> 

int main() 
{ 
	  try 
	  { 
	    boost::function<int (const char*)> f; 
	    f(""); 
	  } 
	  catch (boost::bad_function_call &ex) 
	  { 
	    std::cout << ex.what() << std::endl; 
	  } 
} 

  note that assigning the value 0 to a function pointer of type boost::function will release the currently assigned function. Calling it after release will also result in boost::bad_function_call exception is thrown. To check whether a function pointer is assigned to a function, you can use the empty() function or the operator bool() operator.

  by using boost Function, class member functions can also be assigned to objects of type boost::function.

#include <boost/function.hpp> 
#include <iostream> 

struct world 
{ 
	  void hello(std::ostream &os) 
	  { 
	    os << "Hello, world!" << std::endl; 
	  } 
}; 

int main() 
{ 
	  boost::function<void (world*, std::ostream&)> f = &world::hello; 
	  world w; 
	  f(&w, boost::ref(std::cout)); 
} 

  when calling such a function, the first parameter passed in represents the specific object to which the function is called. Therefore, the first parameter after the left parenthesis in the template definition must be a pointer to the specific class. The next parameter is the signature representing the corresponding member function.

  this program also uses the data from boost boost::ref() of ref library, which provides a convenient mechanism to boost Function passes a reference.

3.5. Boost.Lambda

  anonymous functions - also known as lambda functions - already exist in many programming languages, except C + +. But at boost With the help of lambda library, they can now be used in C + + applications.

  the goal of the lambda function is to make the source code more compact and easier to understand. Take the code example in the first section of this chapter as an example.

#include <iostream> 
#include <vector> 
#include <algorithm> 

void print(int i) 
{ 
  	std::cout << i << std::endl; 
} 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), print); 
} 

  this program accepts the elements in the container v and writes them out to the standard output stream using the print() function. Since print() just writes out a simple int, the implementation of this function is quite simple. Strictly speaking, it is so simple that if you can_ It is more convenient to define each () algorithm directly; This eliminates the need to add a function. Another advantage is that the code is more compact, so that the algorithm is not locally separated from the function responsible for data output. Boost.Lambda just made it a reality.

#include <boost/lambda/lambda.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), std::cout << boost::lambda::_1 << "\n"); 
} 

  Boost.Lambda provides several structures to define anonymous functions. The code is placed where it is executed, eliminating the overhead of wrapping it as a function and then calling the corresponding function. As in the original example, this program writes out all the elements of container v to the standard output stream.

  and boost Bind is similar to boost Lambda also defines three placeholders called_ 1, _ 2 and_ 3. But with boost Unlike bind, these placeholders are defined in separate namespaces. Therefore, the first placeholder in this example is through boost::lambda::_1. In order to meet the requirements of the compiler, you must include the corresponding header file boost / lambda / lambda hpp .

  although the location of the code is in STD:: for_ The third parameter of each () looks weird, but boost Lambda can write normal C + + code. By using placeholders, the elements of container v can be written out to the standard output stream by < < passing to std::cout.

  although boost Lambda is very powerful, but it also has some disadvantages. To insert newline in the above example, you must use "\ n" instead of std::endl to compile successfully. Because the type required by the unary std::endl template function is different from the lambda function STD:: cout < < boost:: lambda::_ 1, so it cannot be used here.

  the next version of the C + + standard is likely to add lambda functions as part of the C + + language itself, eliminating the need for separate libraries. However, it may take several years for the next version to arrive and be adopted by different compiler manufacturers. Before that, boost Lambda has proved to be a perfect substitute. As can be seen from the following example, this example only writes elements greater than 1 to the standard output stream.

#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/if.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

int main() 
{ 
	  std::vector<int> v; 
	  v.push_back(1); 
	  v.push_back(3); 
	  v.push_back(2); 
	
	  std::for_each(v.begin(), v.end(), 
	  boost::lambda::if_then(boost::lambda::_1 > 1, 
	  std::cout << boost::lambda::_1 << "\n")); 
} 

  header file boost / lambda / if HPP defines several structures that allow if statements to be used inside lambda functions. The most basic structure is boost::lambda::if_then() template function, which requires two parameters: the first parameter evaluates the condition - if true, the second parameter is executed. As shown in the example, each parameter itself can be a lambda function.

  except boost::lambda::if_then(), Boost.Lambda also provides boost::lambda::if_then_else() and boost::lambda::if_then_else_return() template functions - they all require three parameters. It also provides template functions for implementing loops, transformation operators, and even throw - allowing lambda functions to throw exceptions.

  although these template functions can be used to construct complex lambda functions in C + +, you must consider other aspects, such as readability and maintainability. Because others need to learn and understand additional functions, such as boost::lambda::if_then() to replace the known C + + keywords if and else. The benefits of lambda functions usually decrease with its complexity. In most cases, it is more reasonable to define a single function with the familiar C + + structure.

Chapter 4 event handling

4.1. summary

  many developers think of GUI when they hear the term event processing: click a button and the associated functions will be executed. Click itself is an event, and the function is the corresponding event handler.

  the use of this mode is certainly not limited to GUI. In general, any object can call special functions based on specific events. This chapter introduces boost The signals library provides a simple way to apply this pattern in C++.

  strictly speaking, boost The function library can also be used for event handling. However, boost Function and boost A major difference between signals is that boost Signals can associate more than one event handler to a single event. Therefore, boost Signals can better support event driven development. When event processing is needed, it should be the first choice.

4.2. Signals

  although the name of this library may seem misleading at first glance, it is not. Boost. The mode implemented by signals is named 'signal to slot', which is based on the following concept: when the corresponding signal is sent, the associated slot is executed. In principle, you can replace the words' signal 'and' socket 'with' event 'and' event handler ', respectively. However, since signals can be sent at any given time, this concept abandons the name of 'event'.

  therefore, boost Signals does not provide any class similar to 'event'. Instead, it provides a class called boost::signal, which is defined in boost / signal hpp . In fact, this header file is the only one you need to know, because it will automatically contain other related header files.

  Boost.Signals defines other classes in the boost::signals namespace. Since boost::signal is the most commonly used class, it is located in the namespace boost.

#include <boost/signal.hpp> 
#include <iostream> 

void func() 
{ 
  	std::cout << "Hello, world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  s.connect(func); 
	  s(); 
} 

  boost::signal is actually implemented as a template function with a signature used as a function of the event handler, which is also its template parameter. In this example, only the function with the signature void () can be successfully associated to the signal s.

  the function func() is associated to the signal s through the connect() method. Since func () meets the required void () signature, the association is successfully established. Therefore, when the signal s is triggered, func () will be called.

  signals are triggered by calling s, just like ordinary function calls. The signature of this function corresponds to the signature passed in as a template parameter: since void () does not require any parameters, the parentheses are empty.

  calling s will trigger a trigger and then execute the corresponding func() function -- previously associated with connect().

  for the same example, you can also use boost Function.

#include <boost/function.hpp> 
#include <iostream> 

void func() 
{ 
  	std::cout << "Hello, world!" << std::endl; 
} 

int main() 
{ 
	  boost::function<void ()> f; 
	  f = func; 
	  f(); 
} 

  similar to the previous example, func() is associated with F. When f is called, func() is executed accordingly. Boost.Function is only applicable in this case, and boost Signals provides many more ways, such as associating multiple functions to a single specific signal, as shown below.

#include <boost/signal.hpp> 
#include <iostream> 

void func1() 
{ 
  	std::cout << "Hello" << std::flush; 
} 

void func2() 
{ 
  	std::cout << ", world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  s.connect(func1); 
	  s.connect(func2); 
	  s(); 
} 

  boost::signal can assign multiple functions to a single specific signal by repeatedly calling the connect() method. When the signal is triggered, these functions are executed in the order they were previously associated with connect().

  in addition, the order of execution can also be explicitly specified through another overloaded version of the connect() method, which requires an int type value as an additional parameter.

#include <boost/signal.hpp> 
#include <iostream> 

void func1() 
{ 
  	std::cout << "Hello" << std::flush; 
} 

void func2() 
{ 
 	 std::cout << ", world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  s.connect(1, func2); 
	  s.connect(0, func1); 
	  s(); 
} 

  as in the previous example, func1() is executed before func2().

  to release the association between a function and a given signal, you can use the disconnect() method.

#include <boost/signal.hpp> 
#include <iostream> 

void func1() 
{ 
  std::cout << "Hello" << std::endl; 
} 

void func2() 
{ 
  std::cout << ", world!" << std::endl; 
} 

int main() 
{ 
  boost::signal<void ()> s; 
  s.connect(func1); 
  s.connect(func2); 
  s.disconnect(func2); 
  s(); 
} 

  this example only outputs Hello, because the association with func2() has been released before the trigger signal.

  in addition to connect() and disconnect(), boost::signal provides several methods.

#include <boost/signal.hpp> 
#include <iostream> 

void func1() 
{ 
 	 std::cout << "Hello" << std::flush; 
} 

void func2() 
{ 
  	std::cout << ", world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  s.connect(func1); 
	  s.connect(func2); 
	  std::cout << s.num_slots() << std::endl; 
	  if (!s.empty()) 
	    s(); 
	  s.disconnect_all_slots(); 
} 

  num_slots() returns the number of associated functions. If no function is associated, num_slots() returns 0. In this particular case, the empty() method can be used instead. disconnect_ all_ What the slots () method does is actually what its name says: release all existing associations.

  after seeing how functions are associated with signals and understanding what happens when signals are triggered, there is another question: where are the return values of these functions? The following example answers this question.

#include <boost/signal.hpp> 
#include <iostream> 

int func1() 
{ 
  	return 1; 
} 

int func2() 
{ 
  	return 2; 
} 

int main() 
{ 
	  boost::signal<int ()> s; 
	  s.connect(func1); 
	  s.connect(func2); 
	  std::cout << s() << std::endl; 
} 

  both func1() and func2() have return values of type int. s will process the two return values and write them out to the standard output stream. So what will happen?

  the above example will actually write 2 to the standard output stream. Both return values are s correctly received, but all values except the last one are ignored. By default, of all associated functions, only the last return value is actually returned.

  you can customize a signal so that each return value is processed accordingly. To do this, pass something called a combiner as the second parameter to boost::signal.

#include <boost/signal.hpp> 
#include <iostream> 
#include <algorithm> 

int func1() 
{ 
   return 1; 
} 

int func2() 
{ 
   return 2; 
} 

template <typename T> 
struct min_element 
{ 
	  typedef T result_type; 
	
	  template <typename InputIterator> 
	  T operator()(InputIterator first, InputIterator last) const 
	  { 
	    return *std::min_element(first, last); 
	  } 
}; 

int main() 
{ 
	  boost::signal<int (), min_element<int> > s; 
	  s.connect(func1); 
	  s.connect(func2); 
	  std::cout << s() << std::endl; 
} 

  synthesizer is a class that overloads the operator()() operator. This operator is automatically called, passing in two iterators that point to all the return values of a particular signal. The above example uses the standard C + + algorithm std::min_element() to determine and return the minimum value.

  unfortunately, we can't put things like STD:: min_ An algorithm such as element () is directly passed to boost::signal as a template parameter. boost::signal requires the synthesizer to define a result_type, which is used to describe the type of return value of operator()(). Due to the lack of this type in the standard C + + algorithm, a corresponding error will be generated at compile time.

  in addition to analyzing the return values, the synthesizer can also save them.

#include <boost/signal.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

int func1() 
{ 
  	return 1; 
} 

int func2() 
{ 
  	return 2; 
} 

template <typename T> 
struct min_element 
{ 
	  typedef T result_type; 
	
	  template <typename InputIterator> 
	  T operator()(InputIterator first, InputIterator last) const 
	  { 
	    return T(first, last); 
	  } 
}; 

int main() 
{ 
	  boost::signal<int (), min_element<std::vector<int> > > s; 
	  s.connect(func1); 
	  s.connect(func2); 
	  std::vector<int> v = s(); 
	  std::cout << *std::min_element(v.begin(), v.end()) << std::endl; 
} 

  this example saves all the returned values in a vector and returns them by s().

4.3. Connections

  functions can be managed with the help of connect() and disconnect() methods provided by boost::signal. Because connect() returns a value of type boost::signals::connection, they can be managed by other methods.

#include <boost/signal.hpp> 
#include <iostream> 

void func() 
{ 
 	 std::cout << "Hello, world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  boost::signals::connection c = s.connect(func); 
	  s(); 
	  c.disconnect(); 
} 

  the disconnect() method of boost::signal needs to pass in a function pointer, while directly calling the disconnect() method on the boost::signals::connection object omits this parameter.

  in addition to the disconnect() method, boost::signals::connection provides other methods, such as block() and unblock().

#include <boost/signal.hpp> 
#include <iostream> 

void func() 
{ 
	  std::cout << "Hello, world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  boost::signals::connection c = s.connect(func); 
	  c.block(); 
	  s(); 
	  c.unblock(); 
	  s(); 
} 

  the above program will only execute func () once. Although signal s is triggered twice, func () will not be called at the first trigger, because connection c has actually been blocked by the call of block(). Since unblock() was called before the second trigger, func() was executed correctly.

  in addition to boost::signals::connection, there is another one called boost:: signals:: scoped_ Class of connection, which will automatically release the connection when destructed.

#include <boost/signal.hpp> 
#include <iostream> 

void func() 
{ 
  	std::cout << "Hello, world!" << std::endl; 
} 

int main() 
{ 
	  boost::signal<void ()> s; 
	  { 
	    boost::signals::scoped_connection c = s.connect(func); 
	  } 
	  s(); 
} 

  because the connection object c is destroyed before the signal is triggered, func() will not be called.

  boost::signals::scoped_connection is actually derived from boost::signals::connection, so it provides the same method. The only difference between them is that in destruct boost:: signals:: scoped_ The connection is automatically released when the connection is.

  although boost::signals::scoped_connection does make it easier to automatically release connections, but objects of this type still need to be managed. It would be better if the connection could be released automatically in other cases and there was no need to manage these objects.

#include <boost/signal.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 
#include <memory> 

class world 
{ 
public: 
	  void hello() const 
	  { 
	    	std::cout << "Hello, world!" << std::endl; 
	  } 
}; 

int main() 
{ 
	  boost::signal<void ()> s; 
	  { 
	    std::auto_ptr<world> w(new world()); 
	    s.connect(boost::bind(&world::hello, w.get())); 
	  } 
	  std::cout << s.num_slots() << std::endl; 
	  s(); 
} 

  the above program uses boost Bind associates an object's method with a signal. This object is destroyed before the signal is triggered, which can cause problems. Instead of passing the actual object w, we just pass a pointer to boost::bind(). When s() is actually called, the object pointed to by this pointer no longer exists.

  this program can be modified as follows, so that once the object w is destroyed, the connection will be automatically released.

#include <boost/signal.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 
#include <memory> 

class world : 
  public boost::signals::trackable 
{ 
public: 
    void hello() const 
    { 
      	std::cout << "Hello, world!" << std::endl; 
    } 
}; 

int main() 
{ 
  boost::signal<void ()> s; 
  { 
	  std::auto_ptr<world> w(new world()); 
	  {
	  		s.connect(boost::bind(&world::hello, w.get())); 
	  } 
	  std::cout << s.num_slots() << std::endl; 
	  s(); 
} 

  if you execute it now, num_slots() returns 0 to ensure that no attempt is made to call methods on destroyed objects. The only modification is to make the world class inherit from boost::signals::trackable. boost::signals::trackable can significantly simplify the management of connections when associating functions to signals using pointers to objects rather than copies of objects.

Chapter 5 string processing

5.1. preface

  in standard C + +, the std::string class is used to process strings. It provides many string operations, including functions to find specified characters or substrings. Although std::string includes more than 100 functions and is one of the most bloated classes in standard C + +, it can not meet the needs of many developers in their daily work. For example, Java and Net provides a function that can convert strings to uppercase letters, while std::string has no corresponding function. The Boost C + + library tries to make up for this shortcoming.

5.2. Regional settings

  before entering the topic, it is necessary to examine the problem of locale. Many functions mentioned in this chapter need an additional locale parameter.

  locale encapsulates the contents related to cultural customs in standard C + +, including currency symbols, date and time formats, symbols separating integer parts and fraction parts (cardinal sign) and separators (thousands sign) when there are more than three numbers.

  in string processing, locale is related to the description of character order and special characters in a specific culture. For example, whether the alphabet contains variant vowels and their position in the alphabet are determined by language and culture.

  if a function is used to convert a string to uppercase, its implementation steps depend on the specific locale.

  when using the class std::string, the locale can be ignored because its functions are not language dependent. However, in this chapter, in order to use the Boost C + + library, the knowledge of locale is essential.

  in the C + + standard, the class std::locale is defined in the locale file. Each C + + program automatically has an instance of this class, that is, a global locale that cannot be accessed directly. If you want to access it, you need to use the default constructor to construct an object of class std::locale and initialize it with the same properties as the global locale.

#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale loc; 
	  std::cout << loc.name() << std::endl; 
} 

  the above program outputs C in the standard output stream, which is the name of the basic locale. It includes the description used by default in the program written in C language.

  this is also the default global locale for every C + + application. It includes descriptions used in American culture. For example, the currency symbol uses the dollar symbol, the base character is the English period, and the month in the date is written in English.

  the global locale can be changed using the static function global() in class std::locale.

#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::locale loc; 
	  std::cout << loc.name() << std::endl; 
} 

  the static function global() accepts an object of type std::locale as its unique parameter. Another version of this class's constructor accepts strings of type const char * and can create locale objects for a particular culture. However, except that the C locale is named "C" accordingly, the names of other locales are not standardized, so this depends on the C + + standard library that accepts locale names. In the case of Visual Studio 2008, the language string document indicates that the language string "German" can be used.

5.3. String algorithm library boost StringAlgorithms

  Boost C + + string algorithm library Stringalgorithms provides many string manipulation functions. The type of string can be std::string, std::wstring or any other template class STD:: basic_ An instance of string.

  these functions are classified and defined in different header files. For example, the case conversion function is defined in the file boost/algorithm/string/case_conv.hpp. Because boost The stringalgorithms class includes more than 20 categories and the same number of header files. For convenience, the header file boost / algorithm / string HPP includes all other header files. This header file will be used in all subsequent examples.

  as mentioned in the previous section, boost Many functions in the stringalgorithms library can accept an object of type std::locale as an additional parameter. This parameter is optional. If it is not set, the default global locale will be used.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 
#include <clocale> 

int main() 
{ 
	  std::setlocale(LC_ALL, "German"); 
	  std::string s = "Boris Schäling"; 
	  std::cout << boost::algorithm::to_upper_copy(s) << std::endl; 
	  std::cout << boost::algorithm::to_upper_copy(s, std::locale("German")) << std::endl; 
} 

  function boost::algorithm::to_upper_copy() is used to convert a string to uppercase. Naturally, there is a function that provides the opposite function - boost::algorithm::to_lower_copy() converts the string to lowercase. Both functions return the converted String as the result. If the string passed in as a parameter itself needs to be converted to large (small) write form, you can use the function boost::algorithm::to_upper() or boost::algorithm::to_lower ().

  the above example uses the function boost::algorithm::to_upper_copy() converts the string "Boris Sch ä ling" to uppercase. The first call uses the default global locale, and the second call explicitly sets the locale to German culture.

  obviously, the conversion of the latter is correct, because the upper case form 'Ä' corresponding to the lower case letter 'ä' exists. In the C locale, 'ä' is an unknown character, so it cannot be converted. In order to get the correct results, you must explicitly pass the correct locale parameters or call boost:: algorithm:: to_ upper_ Change the global locale before copy().

  it can be noted that the program uses the function std::setlocale() defined in the header file clocale to set the locale for the C function, because std::cout uses the C function to display information on the screen. Only after the correct area is set can the vowel letters such as' ä 'and' Ä 'be displayed correctly.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  std::cout << boost::algorithm::to_upper_copy(s) << std::endl; 
	  std::cout << boost::algorithm::to_upper_copy(s, std::locale("German")) << std::endl; 
} 

  the above program sets the global locale to German culture, which makes the function boost:: algorithm:: to_ upper_ The call of copy() can convert 'ä' to 'Ä'.

  notice that this example does not call the function std::setlocale(). After setting the global locale with the function std::locale::global(), the C locale is also set automatically. In fact, C + + programs almost always use the function std::locale::global() for global locale, rather than using the function std::setlocale() as in the previous example.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  std::cout << boost::algorithm::erase_first_copy(s, "i") << std::endl; 
	  std::cout << boost::algorithm::erase_nth_copy(s, "i", 0) << std::endl; 
	  std::cout << boost::algorithm::erase_last_copy(s, "i") << std::endl; 
	  std::cout << boost::algorithm::erase_all_copy(s, "i") << std::endl; 
	  std::cout << boost::algorithm::erase_head_copy(s, 5) << std::endl; 
	  std::cout << boost::algorithm::erase_tail_copy(s, 8) << std::endl; 
} 

  Boost. The stringalgorithms library provides several functions to delete individual letters from a string, which can clearly specify where and how to delete them. For example, you can use the function boost::algorithm::erase_all_copy() deletes a specific character from the entire string. If you delete only when this character first appears, you can use the function boost::algorithm::erase_first_copy() . If you want to delete several characters at the beginning or end of a string, you can use the function boost::algorithm::erase_head_copy() and boost::algorithm::erase_tail_copy() .

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  boost::iterator_range<std::string::iterator> r = boost::algorithm::find_first(s, "Boris"); 
	  std::cout << r << std::endl; 
	  r = boost::algorithm::find_first(s, "xyz"); 
	  std::cout << r << std::endl; 
} 

  the following different functions boost::algorithm::find_first(), boost::algorithm::find_last(), boost::algorithm::find_nth(), boost::algorithm::find_head() and boost::algorithm::find_tail() can be used to find substrings in a string.

  what all these functions have in common is that the return type is boost:: iterator_ A pair of iterators of the range class. This class originates from the boost.com of Boost C + + Range library, which defines the "range" in the concept of iterator. Because the operator < < by boost:: iterator_ The range class is overloaded, and the results of a single search algorithm can be directly written to the standard output stream. The above program outputs Boris as the first result and the second result is an empty string.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 
#include <vector> 

int main() 
{ 
  std::locale::global(std::locale("German")); 
  std::vector<std::string> v; 
  v.push_back("Boris"); 
  v.push_back("Schäling"); 
  std::cout << boost::algorithm::join(v, " ") << std::endl; 
} 

  the function boost::algorithm::join() takes the container of a string as the first parameter and connects these strings according to the second parameter. Accordingly, this example will output Boris Sch ä ling.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  std::cout << boost::algorithm::replace_first_copy(s, "B", "D") << std::endl; 
	  std::cout << boost::algorithm::replace_nth_copy(s, "B", 0, "D") << std::endl; 
	  std::cout << boost::algorithm::replace_last_copy(s, "B", "D") << std::endl; 
	  std::cout << boost::algorithm::replace_all_copy(s, "B", "D") << std::endl; 
	  std::cout << boost::algorithm::replace_head_copy(s, 5, "Doris") << std::endl; 
	  std::cout << boost::algorithm::replace_tail_copy(s, 8, "Becker") << std::endl; 
} 

  Boost. The stringalgorithms library not only provides functions to find substrings or delete letters, but also provides functions to replace substrings with strings, including boost::algorithm::replace_first_copy(), boost::algorithm::replace_nth_copy(), boost::algorithm::replace_last_copy(), boost::algorithm::replace_all_copy(), boost::algorithm::replace_head_copy() and boost::algorithm::replace_tail_copy() and so on. They are used in the same way as the find and delete functions, except that an alternative string is required as an additional parameter.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "\t Boris Schäling \t"; 
	  std::cout << "." << boost::algorithm::trim_left_copy(s) << "." << std::endl; 
	  std::cout << "." <<boost::algorithm::trim_right_copy(s) << "." << std::endl; 
	  std::cout << "." <<boost::algorithm::trim_copy(s) << "." << std::endl; 
} 

  you can use the pruning function boost::algorithm::trim_left_copy(), boost::algorithm::trim_right_copy() and boost::algorithm::trim_copy() automatically removes the spaces in the string or the terminator of the string. What characters are spaces depends on the global locale.

  Boost. The function of the stringalgorithms library can accept an additional predicate parameter to determine which characters of the string the function acts on. The pruning function of the predicate version is named boost:: algorithm:: trim accordingly_ left_ copy_ if(), boost::algorithm::trim_right_copy_if() and boost::algorithm::trim_copy_if() .

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "--Boris Schäling--"; 
	  std::cout << "." << boost::algorithm::trim_left_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; 
	  std::cout << "." <<boost::algorithm::trim_right_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; 
	  std::cout << "." <<boost::algorithm::trim_copy_if(s, boost::algorithm::is_any_of("-")) << "." << std::endl; 
} 

  the above program calls an auxiliary function boost::algorithm::is_any_of(), which is used to generate a predicate to verify whether the character passed in as a parameter exists in the given string. Use the function boost:: algorithm:: is_ any_ After of, as in the example, the characters of the trimmed string are specified as hyphens. ()

  Boost. There are also many auxiliary functions returned by algorithmstrings.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
  std::locale::global(std::locale("German")); 
  std::string s = "123456789Boris Schäling123456789"; 
  std::cout << "." << boost::algorithm::trim_left_copy_if(s, boost::algorithm::is_digit()) << "." << std::endl; 
  std::cout << "." <<boost::algorithm::trim_right_copy_if(s, boost::algorithm::is_digit()) << "." << std::endl; 
  std::cout << "." <<boost::algorithm::trim_copy_if(s, boost::algorithm::is_digit()) << "." << std::endl; 
} 

  function boost:: algorithm:: is_ The predicate returned by digit() returns a Boolean value of true when the character is a number. The auxiliary functions to check whether the characters are uppercase or lowercase are boost::algorithm::is_upper() and boost::algorithm::is_lower() . All of these functions use the global locale by default, unless you specify a different locale in the parameter.

  in addition to predicates that verify individual characters, boost The stringalgorithms library also provides functions to process strings.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
  std::locale::global(std::locale("German")); 
  std::string s = "Boris Schäling"; 
  std::cout << boost::algorithm::starts_with(s, "Boris") << std::endl; 
  std::cout << boost::algorithm::ends_with(s, "Schäling") << std::endl; 
  std::cout << boost::algorithm::contains(s, "is") << std::endl; 
  std::cout << boost::algorithm::lexicographical_compare(s, "Boris") << std::endl; 
} 

  function boost::algorithm::starts_with(), boost::algorithm::ends_with(), boost::algorithm::contains() and boost:: algorithm:: graphical_ Compare() can compare two strings.

  the following describes a string cutting function.

#include <boost/algorithm/string.hpp> 
#include <locale> 
#include <iostream> 
#include <vector> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  std::vector<std::string> v; 
	  boost::algorithm::split(v, s, boost::algorithm::is_space()); 
	  std::cout << v.size() << std::endl; 
} 

  after the delimiter is given, use the function boost::algorithm::split() to split a string into a string container. It needs to give a predicate as the third parameter to determine where the string should be split. This example uses the auxiliary function boost::algorithm::is_space() creates a predicate that splits the string at each space character.

  many functions in this section have versions that ignore the case of the string. These versions generally have names similar to the original functions, except for 'i' start. For example, with the function boost::algorithm::erase_all_copy() corresponds to the function boost::algorithm::ierase_all_copy().

  finally, it is worth noting that the class boost Many functions in stringalgorithms support regular expressions. The following program uses the function boost::algorithm::find_regex() searches for regular expressions.

#include <boost/algorithm/string.hpp> 
#include <boost/algorithm/string/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
  std::locale::global(std::locale("German")); 
  std::string s = "Boris Schäling"; 
  boost::iterator_range<std::string::iterator> r = boost::algorithm::find_regex(s, boost::regex("\\w\\s\\w")); 
  std::cout << r << std::endl; 
} 

  in order to use regular expressions, this program uses boost::regex in the Boost C + + library, which will be introduced in the next section.

5.4. Regular expression library boost Regex

  regular expression library of Boost C + + Regex can apply regular expressions to C + +. Regular expressions greatly reduce the burden of searching for specific pattern strings and are powerful in many languages. Although C + + still needs to provide this function in the form of Boost C + + library, regular expressions will enter the C + + standard library in the future. Boost. The regex library is expected to be included in the next version of the C + + standard.

  Boost. The two most important classes in the regex library are boost::regex and boost::smatch, both of which are in boost / regex Defined in the HPP file. The former is used to define a regular expression, while the latter can save search results.

  the following will introduce boost The regex library provides three functions for searching regular expressions.

#include <boost/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  boost::regex expr("\\w+\\s\\w+"); 
	  std::cout << boost::regex_match(s, expr) << std::endl; 
} 

  function boost::regex_match() is used to compare strings with regular expressions. When the whole string matches the regular expression, its return value is true.

  function boost::regex_search() can be used to search for regular expressions in strings.

#include <boost/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  boost::regex expr("(\\w+)\\s(\\w+)"); 
	  boost::smatch what; 
	  if (boost::regex_search(s, what, expr)) 
	  { 
		    std::cout << what[0] << std::endl; 
		    std::cout << what[1] << " " << what[2] << std::endl; 
	  } 
} 

  function boost::regex_search() can accept a reference parameter of type boost::smatch to store the result. Function boost::regex_search() is only used for classified search. This example actually returns two results, which are based on regular expression grouping.

  the class boost::smatch that stores the results actually holds the type boost:: sub_ The container of match elements can be accessed through an interface similar to the class std::vector. For example, an element can be represented by the operator operator visit.

  on the other hand, the class boost::sub_match saves iterators in locations that correspond to regular expression groupings. Because it inherits from the class std::pair, the substring referenced by the iterator can be accessed using first and second. If only the substring is written to the standard output stream as in the above example, this can be done directly by overloading the operator < < and there is no need to access the iterator.

  please note that the results are saved in the iterator and boost:: sub_ The match class does not copy them, which means that they are only accessible when the relevant string referenced by the iterator exists.

  in addition, it should be noted that the reference stored in the first element of the container boost::smatch points to the entire string matching the regular expression, and the first substring matching the first group is accessed by index 1.

  Boost. The third function provided by regex is boost::regex_replace().

#include <boost/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = " Boris Schäling "; 
	  boost::regex expr("\\s"); 
	  std::string fmt("_"); 
	  std::cout << boost::regex_replace(s, expr, fmt) << std::endl; 
} 

  in addition to the string and regular expression to be searched, boost:: regex_ The replace () function also requires a format parameter that determines how substrings and groups that match regular expressions are replaced. If the regular expression does not contain any grouping, the relevant substrings will be replaced one by one with the given format. In this way, the output of the above program is Boris_Schäling .

  boost::regex_ The replace () function always searches the entire string for regular expressions, so the program actually replaces all three spaces with underscores.

#include <boost/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  boost::regex expr("(\\w+)\\s(\\w+)"); 
	  std::string fmt("\\2 \\1"); 
	  std::cout << boost::regex_replace(s, expr, fmt) << std::endl; 
} 

  format parameters can access substrings grouped by regular expressions. This example uses this technology to exchange the position of last name and first name, so the result is displayed as Sch ä ling Boris.

  it should be noted that there are different standards for regular expressions and formats. All three functions can accept an additional parameter for selecting specific criteria. You can also specify whether to interpret special characters in a specific format or replace the entire string that matches the regular expression.

#include <boost/regex.hpp> 
#include <locale> 
#include <iostream> 

int main() 
{ 
	  std::locale::global(std::locale("German")); 
	  std::string s = "Boris Schäling"; 
	  boost::regex expr("(\\w+)\\s(\\w+)"); 
	  std::string fmt("\\2 \\1"); 
	  std::cout << boost::regex_replace(s, expr, fmt, boost::regex_constants::format_literal) << std::endl; 
} 

  this program will boost:: regex_ constants::format_ Pass the fourth parameter as the boost:: flag to the regeral function_ Replace(), which suppresses the processing of special characters in format parameters. Because the whole string matches the regular expression, the output result of the format parameter replacement in this example is \ 2 \ 1.

  as pointed out at the end of the previous section, regular expressions can be compared with boost Stringalgorithms library. It passes through boost Regex library provides functions such as boost::algorithm::find_regex() , boost::algorithm::replace_regex() , boost::algorithm::erase_regex() and boost::algorithm::split_regex() and so on. Due to boost The regex library is likely to be part of the upcoming next version of the C + + standard, away from boost Stringalgorithms library, skilled use of regular expressions is a wise choice.

5.5. Vocabulary splitter library boost Tokenizer

  Boost. After traversing a string library, tokenizer can be specified as a character separator.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::char_separator<char> > tokenizer; 
	  std::string s = "Boost C++ libraries"; 
	  tokenizer tok(s); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    	std::cout << *it << std::endl; 
} 

  Boost.Tokenizer library is in boost / tokenizer The template class boost::tokenizer is defined in the HPP file, and its template parameters are classes that support related expressions. In the above example, boost:: char is used_ As template parameters, the separator class treats spaces and punctuation as separators.

  the vocabulary splitter must be initialized by a string of type std::string. By using the begin() and end() methods, the word splitter can be accessed like a container. By using iterators, you can get some expressions of the preceding string. The type of template parameter determines how to achieve partial expression.

  because boost:: char_ The separator class treats spaces and punctuation as separators by default, so the results shown in this example are boost, C, +, + and libraries. To identify these separators, boost:: char_ The separator function calls the std::isspace() function and the std::ispunct function. ()Boost. The tokenizer library distinguishes between separators to hide and separators to display. By default, spaces are hidden and punctuation is displayed, so two plus signs are shown in this example.

  if you don't need punctuation as a separator, you can initialize boost:: char accordingly before passing it to the word splitter_ Separator object. The following example formally does so.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::char_separator<char> > tokenizer; 
	  std::string s = "Boost C++ libraries"; 
	  boost::char_separator<char> sep(" "); 
	  tokenizer tok(s, sep); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    	std::cout << *it << std::endl; 
} 

  class boost:: char_ The constructor of separator can accept three parameters. Only the first one is required. It describes the delimiter that needs to be hidden. In this case, spaces are still treated as separators.

The second separator that is required is displayed. When this parameter is not provided, no delimiters are displayed. Execute the program, and Boost, C + + and libraries will be displayed.

  if the plus sign is used as the second parameter, the result of this example will be the same as that of the previous example.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::char_separator<char> > tokenizer; 
	  std::string s = "Boost C++ libraries"; 
	  boost::char_separator<char> sep(" ", "+"); 
	  tokenizer tok(s, sep); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    std::cout << *it << std::endl; 
} 

  the third parameter determines whether to display empty partial expressions. If two separators are found in succession, part of the expression between them will be empty. By default, these empty expressions are not displayed. The third parameter can change the default behavior.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::char_separator<char> > tokenizer; 
	  std::string s = "Boost C++ libraries"; 
	  boost::char_separator<char> sep(" ", "+", boost::keep_empty_tokens); 
	  tokenizer tok(s, sep); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    	std::cout << *it << std::endl; 
} 

  after executing the above program, the empty expressions of the other two will be displayed. The first one is between the two plus signs, and the second one is between the plus sign and the space after it.

The word splitter can also be used for different string types.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::char_separator<wchar_t>, std::wstring::const_iterator, std::wstring> tokenizer; 
	  std::wstring s = L"Boost C++ libraries"; 
	  boost::char_separator<wchar_t> sep(L" "); 
	  tokenizer tok(s, sep); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    	std::wcout << *it << std::endl; 
} 

  this example traverses a string of type std::wstring. In order to use this type of string, you must initialize the vocabulary splitter with another template parameter, which is called boost:: char_ The same is true for the separator class. They all need the parameter wchar_t initialization.

  except boost:: char_ In addition to the separator class, boost Tokenizer also provides two other classes to identify partial expressions.

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

int main() 
{ 
	  typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer; 
	  std::string s = "Boost,\"C++ libraries\""; 
	  tokenizer tok(s); 
	  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
	    	std::cout << *it << std::endl; 
} 

  Boost::escaped_ list_ The separator class is used to read multiple values separated by commas. This format file is usually called CSV (comma separated values). It can even handle double quotation marks and escape sequences. So the output of this example is Boost and C++ libraries.

  the other is boost::offset_separator class, which must be illustrated by an instance. The object of this class must be passed as the second parameter to the constructor of the boost::tokenizer class.

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

int main() 
{ 
  typedef boost::tokenizer<boost::offset_separator> tokenizer; 
  std::string s = "Boost C++ libraries"; 
  int offsets[] = { 5, 5, 9 }; 
  boost::offset_separator sep(offsets, offsets + 3); 
  tokenizer tok(s, sep); 
  for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it) 
    std::cout << *it << std::endl; 
} 

  boost::offset_separator specifies where part of the expression should end in the string. In the above procedure, the first partial expression shall end after 5 characters, the second string shall end after another 5 characters, and the third and last string shall end after the next 9 characters. The output results are Boost, C + + and libraries.

5.6. Format output library boost Format

  Boost. The format library can be used as an alternative to the function std::printf() defined in the file cstdio. The std::printf() function originally appeared in the C standard and provides the function of formatting data output, but it is neither type safe nor extensible. Therefore, in C + + applications, boost The format library is usually a good choice for data formatted output.

  Boost. The format library is in the file boost / format The class boost::format is defined in HPP. Similar to the function std::printf, the parameter passed to the constructor of () boost::format is also a string, which is composed of special characters that control the format. The actual data is connected through the operator% to replace special characters in the output, as shown in the following example.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
  	std::cout << boost::format("%1%.%2%.%3%") % 16 % 9 % 2008 << std::endl; 
} 

  Boost. The format class uses a number placed between two percent signs as a placeholder, which is later connected to the actual data through the% operator. The above program uses the numbers 16, 9 and 2009 to form a date string and output it in the format of 16.9.2008. If you want the month to appear before the date, that is, in American style, you just need to exchange placeholders.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
  	std::cout << boost::format("%2%/%1%/%3%") % 16 % 9 % 2008 << std::endl; 
} 

  now the result displayed by the program becomes 9 / 16 / 2008.

  if you want to format data using the C + + operator, boost The format library provides the function boost::io::group().

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
  	std::cout << boost::format("%1% %2% %1%") % boost::io::group(std::showpos, 99) % 100 << std::endl; 
} 

  the result of this example is + 99 100 + 99. Because the operator std::showpos() is connected to the number 99 through boost::io::group(), as long as 99 is displayed, a plus sign will be automatically added in front of it.

If you want the plus sign to appear only on 99 the first output, you need to transform the formatting placeholder.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
 	 std::cout << boost::format("%|1$+| %2% %1%") % 99 % 100 << std::endl; 
} 

  in order to change the output format to + 99 100 99, you need to not only change the reference symbol of the data from 1 $to 1%, but also add an additional pipe symbol on both sides, that is, replace the placeholder% 1% with% | 1 $+ |.

  please note that although the reference to data is generally not necessary, all placeholders must be set to specified and non specified at the same time. The following example will have errors in execution because it sets references to the second and third placeholders, but ignores the first.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
	  try 
	  { 
	   	 std::cout << boost::format("%|+| %2% %1%") % 99 % 100 << std::endl; 
	  } 
	  catch (boost::io::format_error &ex) 
	  { 
	   	 std::cout << ex.what() << std::endl; 
	  } 
} 

  this program threw a message of type boost::io::format_error exception. Strictly speaking, boost The exception thrown by the format library is boost::io::bad_format_string. However, because all exception classes inherit from boost::io::format_error class, it will be easier to catch this type of exception.

  the following example demonstrates the method of not referencing data.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
  	std::cout << boost::format("%|+| %|| %||") % 99 % 100 % 99 << std::endl; 
} 

  the pipe symbols of the second and third placeholders can be safely omitted because in this case, they do not specify the format. This syntax looks like std::printf().

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
 	 std::cout << boost::format("%+d %d %d") % 99 % 100 % 99 << std::endl; 
} 

  although this looks like std::printf(), boost The format library has the advantage of type safety. The use of the letter'd 'in the format string does not represent the output number, but represents the std::dec() operator on the internal flow object used by the boost::format class. It can use some format strings that are meaningless to the std::printf() function. If std::printf() is used, the program will crash at run time.

#include <boost/format.hpp> 
#include <iostream> 

int main() 
{ 
  	std::cout << boost::format("%+s %s %s") % 99 % 100 % 99 << std::endl; 
} 

  although in the std::printf() function, the letter's' is only used to represent a string of type const char *, the above program works normally. Because in boost In the format library, this does not mean that it is forced to be a string. It will adjust the operation mode of the internal flow in combination with the appropriate operator. So even in this case, it's OK to add numbers to the internal flow.

Chapter 6 multithreading

6.1. summary

  threads are discrete processing queues that allow different functions to be executed in the same program at the same time. This makes it very important that a function that performs a special operation for a long time does not hinder other functions. Threads actually allow two functions to be executed at the same time, and the two functions do not have to wait for each other.

  once an application starts, it contains only one default thread. This thread executes the main() function. The functions called in main () are executed sequentially according to the context of this thread. Such programs are called single threaded programs.

  conversely, programs that create new threads are multithreaded programs. Not only can they execute multiple functions at the same time, but this is particularly important in today's era of multi-core prevalence. Since multi-core allows multiple functions to be executed at the same time, it puts forward requirements for developers to use this processing ability accordingly. However, threads have always been used to execute multiple functions concurrently, and developers now have to carefully build applications to support this concurrency. Therefore, multi-threaded programming knowledge becomes more and more important in the era of multi-core systems.

  this chapter will introduce the C++ Boost library Thread, which can develop platform independent multithreaded applications.

6.2. Thread management

  the most important class in this library is boost::thread, which is in boost / thread Defined in HPP to create a new thread. The following example illustrates how to use it.

#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

void thread() 
{ 
	  for (int i = 0; i < 5; ++i) 
	  { 
	    wait(1); 
	    std::cout << i << std::endl; 
	  } 
} 

int main() 
{ 
	  boost::thread t(thread); 
	  t.join(); 
} 

  the name of the function executed in the new thread is passed to the constructor of boost::thread. Once the variable t in the above example is created, the thread() function is executed immediately in its thread. At the same time, the thread() is executed concurrently in main().

  to prevent program termination, you need to call the join() method on the new thread. The join() method is a blocking call: it can pause the current thread until the thread calling join() finishes running. This causes the main() function to wait until the thread() runs.

  as seen in the above example, a specific thread can be accessed through a variable such as t, which waits for its use of the join() method to terminate. However, even if t is out of bounds or destructed, the thread will continue to execute. A thread is always bound to a variable of type boost::thread at the beginning, but once created, it no longer depends on it. There is even a method called detach(), which allows variables of type boost::thread to be separated from their corresponding threads. Of course, a method like join() cannot be called later, because this variable is no longer a valid thread.

  what can be done in any function can also be completed in one thread. In the final analysis, a thread is just a function, except that it executes at the same time. In the above example, five numbers are written to the standard output stream using a loop. To slow down the output, calling the wait() function in each loop delays the execution by one second. wait() can call a function called sleep(), which also comes from boost Thread, located in boost::this_thread name space.

  sleep() either allows the thread to continue execution after an expected period of time or a specific point in time. By passing a type of boost::posix_time::seconds. In this example, we specify a period of time. boost::posix_time::seconds from boost Datetime library, which is called boost Thread is used to manage and process time data.

  although the previous example shows how to wait for a different thread, the following example shows how to interrupt a thread through the so-called interrupt point.

#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

void thread() 
{ 
	  try 
	  { 
	    for (int i = 0; i < 5; ++i) 
	    { 
	      	wait(1); 
	      	std::cout << i << std::endl; 
	    } 
	  } 
	  catch (boost::thread_interrupted&) 
	  { 
	  } 
} 

int main() 
{ 
	  boost::thread t(thread); 
	  wait(3); 
	  t.interrupt(); 
	  t.join(); 
} 

  calling interrupt() on a thread object will interrupt the corresponding thread. In this regard, an interrupt means a type of boost::thread_interrupted exception, which will be thrown in this thread. This then only happens when the thread reaches the breakpoint.

  if a given thread does not contain any breakpoints, a simple call to interrupt() will not work. Whenever a thread breaks, it checks whether interrupt() has been called. Only after being called, boost:: thread_ The interrupted exception will be thrown accordingly.

  Boost.Thread defines a series of breakpoints, such as the sleep() function. Since sleep() was called five times in this example, the thread checked five times whether it should be interrupted. However, the call between sleep() cannot interrupt the thread.

  once the program is executed, it will only print three numbers to the standard output stream. This is because the interrupt() method is called after 3 seconds in main. Therefore, the corresponding thread is interrupted and a boost:: thread is thrown_ Interrupted exception. This exception is also correctly caught in the thread, although the catch processing is empty. Since the thread() function returns after the handler, the thread is also terminated. This, in turn, will terminate the entire program because main() waits for the thread to terminate it with join ().

  Boost. The thread definition includes ten interrupts of the sleep() function described above. With these breakpoints, threads can be easily interrupted in time. However, they are not always the best choice because breakpoints must be read in in advance to check boost::thread_interrupted exception.

  in order to provide a pair of boost Thread provides an overall overview of various functions. The following examples will introduce two more.

#include <boost/thread.hpp> 
#include <iostream> 

int main() 
{ 
	  std::cout << boost::this_thread::get_id() << std::endl; 
	  std::cout << boost::thread::hardware_concurrency() << std::endl; 
} 

  use boost::this_thread namespace, which can provide independent functions to apply to the current thread, such as sleep(). The other is get_id(): it returns the ID number of the current thread. It is also provided by boost::thread.

  the boost::thread class provides a static method hardware_concurrency(), which can return the number of threads running on the physical machine at the same time based on the number of CPUs or CPU cores. Call this method on a commonly used dual core machine, and the return value is 2. In this way, the theoretical maximum number of threads that can run simultaneously in a multi-core program can be determined.

6.3. synchronization

  although the use of multithreading can improve the performance of applications, it also increases complexity. If threads are used to execute several functions at the same time, access to shared resources must be synchronized accordingly. Once the application reaches a certain scale, it involves considerable work. This paragraph introduces boost Thread provides a class for synchronizing threads.

#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

boost::mutex mutex; 

void thread() 
{ 
	  for (int i = 0; i < 5; ++i) 
	  { 
		    wait(1); 
		    mutex.lock(); 
		    std::cout << "Thread " << boost::this_thread::get_id() << ": " << i << std::endl; 
		    mutex.unlock(); 
	  } 
} 

int main() 
{
	  boost::thread t1(thread); 
	  boost::thread t2(thread); 
	  t1.join(); 
	  t2.join(); 
} 

  multithreaded programs use so-called mutex objects to synchronize. Boost.Thread provides multiple mutually exclusive classes, and boost::mutex is the simplest one. The basic principle of mutual exclusion is to prevent other threads from seizing ownership of resources when a particular thread owns them. Once released, other threads can take ownership. This causes the thread to wait until another thread completes processing some operations, releasing ownership of the mutex object accordingly.

  the above example uses a mutex global mutex object of type boost::mutex. Only after the thread() function obtains the ownership of this object can it be written to the standard output stream using the lock() method in the for loop. Once the information is written, use the unlock() method to release ownership.

  main() creates two threads and executes the thread() function at the same time. Using the for loop, each thread counts to 5, and an iterator writes a message to the standard output stream. Unfortunately, the standard output stream is a global object shared by all threads. This standard does not provide any guarantee that std::cout can be accessed safely from multiple threads. Therefore, access to the standard output stream must be synchronized: at any time, only one thread can access std::cout.

  since two threads try to get the mutex before writing to the standard output stream, only one thread can access std::cout at a time. No matter which thread successfully calls the lock() method, all other threads must wait until unlock() is called.

  getting and releasing mutexes is a typical mode, which is controlled by boost Thread is supported by different data types. For example, instead of calling lock() and unlock() directly, use boost:: lock_ The guard class is also OK.

#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

boost::mutex mutex; 

void thread() 
{ 
	  for (int i = 0; i < 5; ++i) 
	  { 
		    wait(1); 
		    boost::lock_guard<boost::mutex> lock(mutex); 
		    std::cout << "Thread " << boost::this_thread::get_id() << ": " << i << std::endl; 
	  } 
} 

int main() 
{ 
	  boost::thread t1(thread); 
	  boost::thread t2(thread); 
	  t1.join(); 
	  t2.join(); 
} 

  boost::lock_guard automatically calls lock() and unlock() in its internal constructor and destructor respectively. Accessing a shared resource requires synchronization because it is explicitly called by two methods. boost::lock_ The guard class is another RAII term that appears in smart pointers in Chapter 2.

  except for boost::mutex and boost:: lock_ In addition to guard, boost Thread also provides other classes to support various synchronization. One of the important is boost::unique_lock, compared with boost:: lock_ For guard, it provides many useful methods.

#include <boost/thread.hpp> 
#include <iostream> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

boost::timed_mutex mutex; 

void thread() 
{ 
	  for (int i = 0; i < 5; ++i) 
	  { 
		    wait(1); 
		    boost::unique_lock<boost::timed_mutex> lock(mutex, boost::try_to_lock); 
		    if (!lock.owns_lock()) {
		      lock.timed_lock(boost::get_system_time() + boost::posix_time::seconds(1)); 
		    }
		    std::cout << "Thread " << boost::this_thread::get_id() << ": " << i << std::endl; 
		    boost::timed_mutex *m = lock.release(); 
		    m->unlock(); 
	  } 
} 

int main() 
{ 
	  boost::thread t1(thread); 
	  boost::thread t2(thread); 
	  t1.join(); 
	  t2.join(); 
} 

  the above example demonstrates boost:: unique in different ways_ Lock function. Of course, the usage of these functions does not necessarily apply to a given scenario; boost::lock_ The usage of guard in the last example is quite reasonable. This example is to demonstrate boost:: unique_ Functions provided by lock.

  boost::unique_lock provides different ways to obtain mutexes through multiple constructors. The function expecting to get the mutex simply calls the lock() method until it gets the mutex. So its behavior is similar to that of boost:: lock_ The one on guard is the same.

  if the second parameter is passed in a boost:: try_ to_ For the value of lock type, the corresponding constructor will call try_lock() method. This method returns a bool type value: if the mutex can be obtained, it returns true; otherwise, it returns false. Compared with the lock() function, try_lock() returns immediately and is not blocked until the mutex is obtained.

  the above program sends a message to boost:: unique_ The second parameter of the lock constructor is passed into boost::try_to_lock. Then through owns_lock() can check whether mutexes are available. If not, owns_lock() returns false. This also uses boost::unique_lock provides another function: timed_lock() waits a certain amount of time to get the mutex. Given that the program waits for up to 1 second, it should take more time to get more mutexes.

  in fact, this example shows three methods to obtain a mutex: lock() will wait until a mutex is obtained. try_lock() will not wait, but if it can only be obtained when the mutex is available, otherwise it returns false. Finally, timed_lock() attempts to get the mutex within a certain period of time. And try_ Like lock(), the return value of bool type means whether it is successful or not.

  although boost::mutex provides lock() and try_lock() has two methods, but boost::timed_mutex only supports timed_lock(), which is why the above example is used. If timed is not used_ Lock(), you can also use boost::mutex as in the previous example.

  like boost:: lock_ Like guard, boost:: unique_ The destructor of lock will release the mutex accordingly. In addition, you can manually release the mutex with unlock(). You can also call release() to release boost:: unique, as in the above example_ The association between lock and mutex. However, in this case, you must explicitly call the unlock() method to release the mutex, because boost::unique_lock's destructor doesn't do this anymore.

  boost::unique_lock, the so-called exclusive lock, means that a mutex can only be obtained by one thread at the same time. Other threads must wait until the mutex is released again. In addition to exclusive locks, there are also non exclusive locks. Boost. There is a boost:: shared in the thread_ Lock's class provides a non exclusive lock. As in the following example, this class must be the same as boost::shared_mutex type mutex.

#include <boost/thread.hpp> 
#include <iostream> 
#include <vector> 
#include <cstdlib> 
#include <ctime> 

void wait(int seconds) 
{ 
	  boost::this_thread::sleep(boost::posix_time::seconds(seconds)); 
} 

boost::shared_mutex mutex; 
std::vector<int> random_numbers; 

void fill() 
{ 
	  std::srand(static_cast<unsigned int>(std::time(0))); 
	  for (int i = 0; i < 3; ++i) 
	  { 
		    boost::unique_lock<boost::shared_mutex> lock(mutex); 
		    random_numbers.push_back(std::rand()); 
		    lock.unlock(); 
		    wait(1); 
	  } 
} 

void print() 
{ 
	  for (int i = 0; i < 3; ++i) 
	  { 
		    wait(1); 
		    boost::shared_lock<boost::shared_mutex> lock(mutex); 
		    std::cout << random_numbers.back() << std::endl; 
	  } 
} 

int sum = 0; 

void count() 
{ 
	  for (int i = 0; i < 3; ++i) 
	  { 
		    wait(1); 
		    boost::shared_lock<boost::shared_mutex> lock(mutex); 
		    sum += random_numbers.back(); 
	  } 
} 

int main() 
{ 
	  boost::thread t1(fill); 
	  boost::thread t2(print); 
	  boost::thread t3(count); 
	  t1.join(); 
	  t2.join(); 
	  t3.join(); 
	  std::cout << "Sum: " << sum << std::endl; 
} 

  boost::shared_ A lock type non exclusive lock can be used when a thread has read access to only one resource. The resources modified by a thread need write access, so an exclusive lock is required. This is also obvious: threads that only need read access do not need to know whether other threads access at the same time. Therefore, non exclusive locks can share a mutex.

  in a given example, both print() and count() can access random read-only_ numbers . Although the print() function takes random_ The last number in numbers is written to the standard output, and the count() function counts it to the sum variable. Because there is no function to modify random_numbers, all can use boost:: shared at the same time_ A non exclusive lock of type lock accesses it.

  in the fill() function, you need to use a boost:: unique_ A non exclusive lock of type lock because it inserts a new random number into random_numbers. After unlock() explicitly calls unlock() to release the mutex, fill () waits a second. Compared with the previous situation, call wait() at the end of the for loop to ensure that there is at least one random number in the container, which can be accessed by print() or count(). Correspondingly, these two functions call wait() at the beginning of the for loop.

  considering that each individual calls wait() in different places, a potential problem becomes obvious: the order of function calls is directly determined by the order in which the CPU executes each individual process. Using the so-called condition variables, which independent threads can be synchronized, so that each element of the array can be immediately added to random by different threads_ numbers .

#include <boost/thread.hpp> 
#include <iostream> 
#include <vector> 
#include <cstdlib> 
#include <ctime> 

boost::mutex mutex; 
boost::condition_variable_any cond; 
std::vector<int> random_numbers; 

void fill() 
{ 
	  std::srand(static_cast<unsigned int>(std::time(0))); 
	  for (int i = 0; i < 3; ++i) 
	  { 
		    boost::unique_lock<boost::mutex> lock(mutex); 
		    random_numbers.push_back(std::rand()); 
		    cond.notify_all(); 
		    cond.wait(mutex); 
	  } 
} 

void print() 
{ 
	  std::size_t next_size = 1; 
	  for (int i = 0; i < 3; ++i) 
	  { 
		    boost::unique_lock<boost::mutex> lock(mutex); 
		    while (random_numbers.size() != next_size) 
		      cond.wait(mutex); 
		    std::cout << random_numbers.back() << std::endl; 
		    ++next_size; 
		    cond.notify_all(); 
	  } 
} 

int main() 
{ 
	  boost::thread t1(fill); 
	  boost::thread t2(print); 
	  t1.join(); 
	  t2.join(); 
} 

  the program in this example deletes wait() and count(). Instead of waiting a second in each loop iteration, the thread executes as fast as possible. In addition, the total amount was not calculated; The number is completely written to the standard output stream.

  to ensure that random numbers are handled correctly, you need a condition variable that allows you to check specific conditions between multiple threads to synchronize each independent thread.

  as mentioned above, the fill() function is used to generate a random number in each iteration and then put it in random_numbers container. In order to prevent other threads from accessing the container at the same time, an exclusive lock should be used accordingly. Instead of waiting for a second, this example actually uses a condition variable. Call notify_all() wakes up every thread that is waiting for this notification by calling wait().

  by looking at the for loop in the print() function, you can see that the same condition variable is called by the wait() function. If this thread is notified_ When all () wakes up, it will try to this mutex, but it will not succeed until the fill() function is fully released.

  the trick here is that calling wait () will release the corresponding mutex passed in by the parameter. After calling notify_ After all(), the fill() function releases the thread through wait(). It then blocks and waits for other threads to call notify_all(), which happens in print() once the random number has been written to the standard output stream.

  notice that calling wait() in the print() function actually occurs in a separate while loop. The purpose of this is to handle that the random number has been put into the container before the wait() function is called for the first time in the print() function. By comparing random_ The number of elements in numbers is the same as the expected value. It is found that this successfully handles writing random numbers to the standard output stream.

6.4. Thread local storage

  thread local storage (TLS) is a special storage area that can only be accessed by one thread. The variable of TLS can be regarded as a global variable visible only to a specific thread rather than the whole program. The following example shows the benefits of these variables.

#include <boost/thread.hpp> 
#include <iostream> 
#include <cstdlib> 
#include <ctime> 

void init_number_generator() 
{ 
	  static bool done = false; 
	  if (!done) 
	  { 
		    done = true; 
		    std::srand(static_cast<unsigned int>(std::time(0))); 
	  } 
} 

boost::mutex mutex; 

void random_number_generator() 
{ 
	  init_number_generator(); 
	  int i = std::rand(); 
	  boost::lock_guard<boost::mutex> lock(mutex); 
	  std::cout << i << std::endl; 
} 

int main() 
{ 
	  boost::thread t[3]; 
	
	  for (int i = 0; i < 3; ++i) {
	    t[i] = boost::thread(random_number_generator); 
	  }
	
	  for (int i = 0; i < 3; ++i) {
	    t[i].join(); 
	  }
} 

  this example creates three threads, and each thread writes a random number to the standard output stream. random_ number_ The generator () function will create a random number using the std::rand() function defined in the C + + standard. However, the random number generator for std::rand() must first be properly initialized with std::srand(). If not, the program always prints the same random number.

  random number generator, which returns the current time through std::time(), in init_ number_ Complete initialization in the generator() function. Since the generator is initialized with different values each time, it can always generate different random values. Because the generator only needs to be initialized once, init_number_generator() uses a static variable done as the conditional quantity.

  if the program runs multiple times, two-thirds of the random numbers written will obviously be the same. In fact, this program has a defect: the generator used by std::rand() must be initialized by each thread. So init_ number_ The implementation of generator () is actually wrong because it only calls std::srand() once. Using TLS, this defect can be corrected.

#include <boost/thread.hpp> 
#include <iostream> 
#include <cstdlib> 
#include <ctime> 

void init_number_generator() 
{ 
	  static boost::thread_specific_ptr<bool> tls; 
	  if (!tls.get()) {
	    tls.reset(new bool(false)); 
	  }
	  if (!*tls) 
	  { 
	    *tls = true; 
	    std::srand(static_cast<unsigned int>(std::time(0))); 
	  } 
} 

boost::mutex mutex; 

void random_number_generator() 
{ 
	  init_number_generator(); 
	  int i = std::rand(); 
	  boost::lock_guard<boost::mutex> lock(mutex); 
	  std::cout << i << std::endl; 
} 

int main() 
{ 
	  boost::thread t[3]; 
	
	  for (int i = 0; i < 3; ++i) {
	    t[i] = boost::thread(random_number_generator); 
	  }
	
	  for (int i = 0; i < 3; ++i) {
	    t[i].join(); 
	  }
} 

  replacing static variable done with a tls variable tls is based on boost:: thread instantiated with bool type_ specific_ ptr . In principle, tls works like done: it can be used as a condition to indicate whether the random number generator is initialized. But the key difference is that the value stored in tls is only visible and available to the corresponding thread.

  once a boost::thread_specific_ptr type variables are created and can be set accordingly. However, it expects the address of a bool variable, not itself. Using the reset() method, you can save its address to tls. In the given example, a bool type variable will be dynamically allocated, and its address will be returned by new and saved in tls. To avoid calling init every time_ number_ Generator () sets tls, which checks whether an address has been saved through the get() function.

  due to boost::thread_specific_ptr holds an address that behaves like an ordinary pointer. Therefore, both operator * () and operator - > () are overloaded for ease of use. This example uses * tls to check whether the condition is currently true or false. Then, according to the current conditions, the random number generator decides whether to initialize.

  as you can see, boost::thread_specific_ptr allows you to save the address of an object for the current process, and then only allow the current process to obtain this address. However, when one thread has successfully saved this address, other threads may fail.

  if the program is executing, it may be strange: Despite the variables of TLS, the generated random numbers are still equal. This is because three threads are created at the same time, causing the random number generator to initialize at the same time. If the program is executed several times, the random number will change, which indicates that the generator initialization is correct.

Notes and new records

time content
2020-12-14 Create notes

Tags: C C++ boost

Posted by cavemaneca on Mon, 02 May 2022 07:19:58 +0300