Understanding the Boost.Bind library

We have been able to use the io_service object and initialize the work object. What we should know after this is how to give some work to the io_service object. But before we progress to giving work to the io_service service, we need to understand the boost::bind library.

The Boost.Bind library is used to ease the invocation of a function pointer. It converts the syntax from something that is abstruse and confusing to something that is easy to understand.

Wrapping a function invocation

Let's look at the following code in order to understand how to wrap a function invocation:

/* uncalledbind.cpp */
#include <boost/bind.hpp>
#include <iostream>

void func() {
  std::cout << "Binding Function" << std::endl;
}

int main(void) {
  boost::bind(&func);
  return 0;
}

Save the preceding code as uncalledbind.cpp and then compile it using the following command:

g++ -Wall -ansi -I ../boost_1_58_0 uncalledbind.cpp -o uncalledbind

We will not get any line of text as output since we just created a function invocation but haven't actually called it. We have to add it to the () operator to call the function as follows:

/* calledbind.cpp */
#include <boost/bind.hpp>
#include <iostream>

void func() {
  std::cout << "Binding Function" << std::endl;
}

int main(void) {
  boost::bind(&func)();
  return 0;
}

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

g++ -Wall -ansi -I ../boost_1_58_0 calledbind.cpp -o calledbind

Now, we will get the line of text as the output if we run the program, and of course, we will see the bind() function as an output:

boost::bind(&func)();

As we can see in the entire code, the change is only in one line, as shown in the preceding code snippet.

Now, let's use the function that has arguments to pass. We will use boost::bind for this purpose in the following code:

/* argumentbind.cpp */
#include <boost/bind.hpp>
#include <iostream>

void cubevolume(float f) {
  std::cout << "Volume of the cube is " << f * f * f << std::endl;
}

int main(void) {
  boost::bind(&cubevolume, 4.23f)();
  return 0;
}

Run the following command to compile the preceding argumentbind.cpp file:

g++ -Wall -ansi -I ../boost_1_58_0 argumentbind.cpp -o argumentbind

We successfully call the function with the argument using boost::bind because of which we obtain the following output:

Volume of the cube is 75.687

You need to remember that if the function has more than one argument, we have to match the function signature exactly. The following code will explain this in more detail:

/* signaturebind.cpp */
#include <boost/bind.hpp>
#include <iostream>
#include <string>

void identity(std::string name, int age, float height) {
  std::cout << "Name   : " << name << std::endl;
  std::cout << "Age    : " << age << " years old" << std::endl;
  std::cout << "Height : " << height << " inch" << std::endl;
}

int main(int argc, char * argv[]) {
  boost::bind(&identity, "John", 25, 68.89f)();
  return 0;
}

Compile the signaturebind.cpp code by using the following command:

g++ -Wall -ansi -I ../boost_1_58_0 signaturebind.cpp -o signaturebind

The signature of an identity function are std::string, int, and float. So, we have to fill the bind parameter with std::string, int, and float, respectively.

Because we have matched the function signature exactly, we will obtain an output as follows:

Wrapping a function invocation

We have already been able to call the global() function in boost::bind. Now, let's continue to call the function inside a class in boost::bind. The code for this looks as follows:

/* classbind.cpp */
#include <boost/bind.hpp>
#include <iostream>
#include <string>

class TheClass {
public:
  void identity(std::string name, int age, float height) {
    std::cout << "Name   : " << name << std::endl;
    std::cout << "Age    : " << age << " years old" << std::endl;
    std::cout << "Height : " << height << " inch" << std::endl;
  }
};

int main(void) {
  TheClass cls;
  boost::bind(&TheClass::identity, &cls, "John", 25, 68.89f)();
  return 0;
}

Compile the preceding classbind.cpp code by using following command:

g++ -Wall -ansi -I ../boost_1_58_0 classbind.cpp -o classbind

The output for this will be exactly the same as the signaturebind.cpp code since the content of the function is exactly the same as well:

boost::bind(&TheClass::identity, &cls, "John", 25, 68.89f)();

As we can see in the preceding code snippet, we have to pass the boost:bind arguments with the class and function name, object of the class, and parameter based on the function signature.

Working with the Boost.Bind library

So far, we have been able to use boost::bind for the global and class functions. However, when we use the io_service object with boost::bind, we will get a non-copyable error because the io_service object cannot be copied.

Now, let's take a look at multithreads.cpp again. We will modify the code to explain the use of boost::bind for the io_service object and we will still need the help of the shared_ptr pointer. Let's take a look at the following code snippet:

/* ioservicebind.cpp */
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

void WorkerThread(boost::shared_ptr<boost::asio::io_service> iosvc, int counter) {
  std::cout << counter << ".
";
  iosvc->run();
  std::cout << "End.
";
}

int main(void) {
  boost::shared_ptr<boost::asio::io_service> io_svc(
    new boost::asio::io_service
  );

  boost::shared_ptr<boost::asio::io_service::work> worker(
    new boost::asio::io_service::work(*io_svc)
  );

  std::cout << "Press ENTER key to exit!" << std::endl;

  boost::thread_group threads;
  for(int i=1; i<=5; i++)
    threads.create_thread(boost::bind(&WorkerThread, io_svc, i));

  std::cin.get();

  io_svc->stop();

  threads.join_all();

  return 0;
}

We name the preceding code ioservicebind.cpp and compile it using the following command:

g++ -Wall -ansi -I ../boost_1_58_0 ioservicebind.cpp -o ioservicebind –L ../boost_1_58_0/stage/lib -l boost_system-mgw49-mt-1_58 -l ws2_32 -l boost_thread-mgw49-mt-1_58

When we run ioservicebind.exe, we obtain the same output as multithreads.exe, but of course, the program will randomize the order of all threads:

boost::shared_ptr<boost::asio::io_service> io_svc(
  new boost::asio::io_service
);

We instantiate the io_service object in the shared_ptr pointer to make it copyable so that we can bind it to the worker thread() function that we use as a thread handler:

void WorkerThread(boost::shared_ptr<boost::asio::io_service> iosvc, int counter)

The preceding code snippet shows us that the io_service object can be passed to the function. We do not need to define an int global variable as we did in the multithreads.cpp code snippet, since we can also pass the int argument to the WorkerThread() function:

std::cout << counter << ".
";

Now, instead of incrementing the int variable to be shown to the user. We can use the preceding code snippet because we passed the counter from the for loop in the main block.

If we look at the create_thread() function, we see the different arguments that it gets in the ioservicebind.cpp and multithreads.cpp files. We can pass a pointer to the void() function that takes no arguments as the argument to the create_thread() function, as we can see in the multithreads.cpp file. We can also pass a binding function as an argument to the create_thread() function, as we can see in the ioservicebind.cpp file.

Synchronizing data access with the Boost.Mutex library

Have you ever got the following output when you ran the multithreads.exe or ioservicebind.exe executable files?

Synchronizing data access with the Boost.Mutex library

We can see in the preceding screenshot that there is a formatting issue here. Because the std::cout object is a global object, writing to it from different threads at once can cause output formatting issues. To solve this issue, we can use a mutex object that can be found in the boost::mutex object provided by the thread library. Mutex is used to synchronize access to any global data or shared data. To understand more about Mutex, take a look at the following code:

/* mutexbind.cpp */
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread(boost::shared_ptr<boost::asio::io_service> iosvc, int counter) {
  global_stream_lock.lock();
  std::cout << counter << ".
";
  global_stream_lock.unlock();

  iosvc->run();

  global_stream_lock.lock();
  std::cout << "End.
";
  global_stream_lock.unlock();
}

int main(void) {
  boost::shared_ptr<boost::asio::io_service> io_svc(
    new boost::asio::io_service
  );

  boost::shared_ptr<boost::asio::io_service::work> worker(
    new boost::asio::io_service::work(*io_svc)
  );

  std::cout << "Press ENTER key to exit!" << std::endl;

  boost::thread_group threads;
  for(int i=1; i<=5; i++)
    threads.create_thread(boost::bind(&WorkerThread, io_svc, i));

  std::cin.get();

  io_svc->stop();

  threads.join_all();

  return 0;
}

Save the preceding code as mutexbind.cpp and then compile it using the following command:

g++ -Wall -ansi -I ../boost_1_58_0 mutexbind.cpp -o mutexbind -L ../boost_1_58_0/stage/lib -l boost_system-mgw49-mt-1_58 -l ws2_32 -l boost_thread-mgw49-mt-1_58

Now, run the mutexbind.cpp file and we will not face the formatting issue anymore:

boost::mutex global_stream_lock;

We instantiate the new mutex object, global_stream_lock. With this object, we can call the lock() and unlock() functions. The lock() function will block other threads that access the same function to wait for the current thread to be finished. The other threads can access the same function if only the current thread has called the unlock() function. One thing to remember is that we should not call the lock() function recursively because if the lock() function is not unlocked by the unlock() function, then thread deadlock will occur and it will freeze the application. So, we have to be careful when using the lock() and unlock() functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset