Understanding the C++ Utility Components in the C++ Standard Library

Utility components in a standard programming language refers to those entities without which we can do away with in everyday programming but fit a specific need. They are basically built to provide some programming convenience rather than being an absolute necessity. The utility components provided by the standard C++ library are so commonly used that they are difficult to be isolated and tag as part of any major standard library. These components are, in fact, strewn all over the standard library and merged to the mainstream C++ language in a cohesive manner. This article, therefore, attempts to pick a few commonly used components and explore their utility and usages with appropriate examples and background details.

C++ Utility Components

Components are built to ensure that they will perform the required function and will fit into the architectural style specified for the system and will exhibit the qualities such as performance, usability, and reliability required for the application. In short, a reusable entity is one that can plug in with the existing code and be happy because it does exactly what is is supposed to do. That's it.

Keeping this view, if you look at the C++ standard library, you'll find many classes fit into the category. C++ documentation broadly classifies them into two categories:

  • Language support utility library
  • General purpose utility library

Language Support Utility Libraries

Language support utility libraries provide type definition, classes, and functions that are closely bound to the C++ language features. Many of them you may have used knowingly or unknowingly because they are so common that they almost have merged with the language idiom. They may be categorized further, as follows.

Type Support

Type supports are very common and often we use them without thinking that they are actually a utility. For example, std::size_t is a type defined or alias of an unsigned integer. The return value of the sizeof operator is size_t, which is nothing but an unsigned integer. Similarly, there are others, such as std::nullptr_t, which represents a null pointer literal of nullptr, std::rank, std::type_info, and so forth.

Dynamic Memory Management

Dynamic memory management categorizes many pointer types, such as std::shared_ptr, std::auto_ptr, std::shared_ptr, std::malloc, std::allocator, and so on. These types are used in relation to memory management. Examples are:

char *cptr=(char *)std::malloc(sizeof(char));

or

#include <iostream>
#include <memory>

struct Shape {
   Shape() { std::cout<<"Shape Constructor"<<std::endl; }
   ~Shape() { std::cout<<"Shape Constructor"<<std::endl; }
};

struct Triangle:public Shape{
   Triangle() { std::cout<<"Triangle Constructor"<<std::endl; }
   ~Triangle() { std::cout<<"Triangle Constructor"<<std::endl; }
};

int main(){
   std::shared_ptr<Shape> sptr=std::make_shared<Triangle>();
   std::cout<<sptr.get();
   std::cout<<sptr.use_count();
}

Error Handling

Error handling comprises exceptions as well as errors. The utility classes and functions for exception handling in C++ are defined in the header <exception>. Unlike languages such as Java and C++, exception handling is not enforcing. It stands as a additional utility library; usability depends on the discretion of the programmer. Also, there are several macros to define POSIX error codes, such as E2BIG, EACESS, EADDRINUSE, and the like, in the header <cerrno>. There are types and functions defined in the header <system_error>, which is used to report error conditions. Errors can originate from the operating system, IO streams, or from some other low-level APIs. The error and exception handling utility routines can be used to get all the required information to understand the root cause of the problems and take appropriate measures to rectify them.

Variadic Functions

Variadic functions are those that take a variable number of arguments. The most common variadic functions are printf and scanf. These two functions take a variable number of arguments. For example, the prototype of the printf function is:

printf(const char *, ...)

Here, the elipses (...) define the variable arguments. The utility macro functions, such as va_start, va_arg, va_copy, va-end, and the typedef va_list are used to get the requisite information passed as an argument.

Let's create a custom printf function to illustrate this.

#include <iostream>
#include <cstdarg>
#include <string>

using namespace std;

void toBinary(int num){
   if(num>1)
      toBinary(num/2);
   printf("%d",num%2);
}

void custom_printf(const char *format, ...){
   va_list args;
   va_start(args, format);
   const char *ptr;
   for(ptr=format;*ptr;++ptr){
      if(*ptr=='%')
         switch(*++ptr){
            case 'd':
               printf("%d",va_arg(args, int));
               break;
            case 'f':
               printf("%f",va_arg(args, double));
               break;
            case 'b':
               toBinary(va_arg(args, int));
               break;

         }else{
            putchar(*ptr);
         }
         va_end(args);
   }
}

int main(){
   int a=20;
   double b=22/7;
   int c=14;
   custom_printf("value of a = %d\n", a);
   custom_printf("value of b = %f\n", b);
   // prints binary of c
   custom_printf("value of c = %b\n", c);
   return 0;
}

General Purpose Utility Library

The general purpose utility library provides a variety of functions, such as managing program execution, resource cleanup, communicating with the underlying platform, date and time utilities, function object, and so forth.

Date and Time

The date and time utilities support two types of time manipulation, such as using the chrono library; another is C-style time manipulation using std::time_t, std::difftime, and CLOCKS_PER_SEC. The functions and types of C-style time manipulation are defined in the header <ctime>. The chrono library provides the facilities for dealing with duration and time points. It is defined in the std::chrono namespace. As a result, we either have to use explicitly qualify with chrono:: or add using directive as: using namespace std::chrono;.

Sometimes, we can use both the C-style and chrono library together to deal with the date and time utility. The following example is a simple illustration to the scenario.

#include <iostream>
#include <ctime>
#include <chrono>

using namespace std;

int gcd( int a, int b )
{
   if ( a>=b && a%b==0 )
      return( b );
   else if ( a<b )
      return(gcd( b, a ) );
   else
      return(gcd( b, a%b ) );
}

int main(){

   chrono::time_point<chrono::system_clock> t1, t2;
   t1=chrono::system_clock::now();
   cout<<gcd(192,3680)<<endl;
   t2=chrono::system_clock::now();
   chrono::duration<double> t3=t2-t1;
   time_t etime=chrono::system_clock::to_time_t(t2);
   cout << "GCD computation ended at " << ctime(&etime)
        << "time elapsed: " <<t3.count() << " sec"<<endl;
   return 0;
}

Pair and Swap Utility

C++ provides a structure called pair that treats two values as a single unit. It is defined as the following:

template < class T1, class T2 > struct pair;

Apart from many generic functions, the container classes such as map and multimap use pairs to manage their key-value pair elements. It is defined in the <utility> header as a template structure. The generic swap() methods defined in <utility> also use pair and are defined as:

template <class T1, class T2>
void swap (pair <T1, T2> &lhs, pair <T1, T2> &rhs)

This method swaps the contents of lhs to rhs. A simple example to illustrate this is as follows.

#include <iostream>
#include <vector>
#include <utility>

using namespace std;

void display(vector<int> v){
   for(int elem:v){
      cout<<elem<<' ';
   }
   cout<<endl;
}

int main(){
   vector<int> odd{1,3,5,7};
   vector<int> even{2,4,6,8};
   cout<<"ODD before swap"<<endl;
   display(odd);
   cout<<"EVEN before swap"<<endl;
   display(even);
   swap(odd,even);
   cout<<"ODD after swap"<<endl;
   display(odd);
   cout<<"EVEN after swap"<<endl;
   display(even);
   return 0;
}

Similarly, there are many other utility generic function templates defined in the <utility> header, such as exchange, which is used to replace the value of a parameter with a new value and return the old value of the object; tuple class template, which are actually a generalization of pair and represents a fixed sized collection of heterogeneous values; and the template function get to extract elements from the pair.

Conclusion

This article has only scratched the surface in delineating the utility components provided by the C++ standard library. The best way to understand them is to use them in your code. Soon, you'll see that the fine line of difference in using a utility type and a primitive type vanishes. The utility functions, classes, and typedefs are all streamlined in the mainstream C++ code.



About the Author

Manoj Debnath

manojdebnath@fastmail.fm

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date