Adapters

An Adapter is a component that modifies the interface of another component.

One example is a reverse_iterator, which adapts an iterator type into a type of iterator with all the capabilities of the original, but with the direction of traversal reversed.

You can obtain a reverse_iterator by calling a container’s rbegin() or rend() methods.

//
//  main.cpp
//  STL Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <vector>      // For vector
#include <string>      // For string
#include <algorithm>   // For for_each()

void display(std::string x)
{
  std::cout << x << std::endl;
}

int main(int argc, const char * argv[]) 
{
  std::string values[] =
    {"Rose ", "Daffodil ", "Orchid ", "Tulip "};
  size_t len = sizeof(values)/sizeof(values[0]);
  
  // Initialize vector using pointer iterator
  std::vector<std::string> v(&values[0], &values[len]);
  
  // Use a regular iterator
  for_each(v.begin(), v.end(), display);
  std::cout << std::endl;
  
  // Use a reverse_iterator
  for_each(v.rbegin(), v.rend(), display);
  
  return 0;
}

… which outputs the following:

Rose 
Daffodil 
Orchid 
Tulip 

Tulip 
Orchid 
Daffodil 
Rose 
Program ended with exit code: 0

The for_each Loop

Note the use of a for_each loop:

for_each (InputIterator first, InputIterator last, Function fn)

first : The beginning position from where function operations are to be executed.
last : The ending position until where function has to be executed.
fn : The 3rd argument is a function or an object function specifying the operation to be applied to each element.

for_each is defined in

#include<algorithm>

The calls to rbegin() and rend() return iterators of type vector<string>::reverse_iterator, which is defined as part of the vector interface.

We could have used the following directly in the program:

reverse_iterator< vector<string>::iterator > 
      start( v.end() ), finish( v.begin() );
for_each(start, finish, display);

Container Adapters

With a Container Adapter, you can choose the interface you wish, and also the underlying structure.

STL supplies three Container Adapters:

  • stack : A Last-In, First-Out (LIFO) structure
  • queue : Supports insertions at the back, and removals from the front.
  • priority_queue : Supports insertions in sorted order, and removals from the front.

The stack Container Adapter

Here’s an example of the use of a stack container adapter:

//
//  main.cpp
//  STL Container Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <stack>

int main(int argc, const char * argv[])
{
  std::stack< const char * > s;  
      // Uses default deque implementation
  s.push("Nashua");
  s.push("Manchester");
  s.push("Hollis");
  s.push("Milford");
  s.push("Amherst");
  
  while (!s.empty())
  {
    std::cout << s.top() << std::endl;
    s.pop();  // returns no value
  }
  
  return 0;
}

… which outputs:

Amherst
Milford
Hollis
Manchester
Nashua
Program ended with exit code: 0

Or you can override the default implementation to use any of the sequence containers (vector, deque, or list):

//
//  main.cpp
//  STL Container Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <vector>
#include <stack>

int main(int argc, const char * argv[])
{
  //std::stack< const char * > s;  
      // Uses default deque implementation
  std::stack< const char *, std::vector<const char *> > s; 
      // uses vector
  s.push("Nashua");
  s.push("Manchester");
  s.push("Hollis");
  s.push("Milford");
  s.push("Amherst");
  
  while (!s.empty())
  {
    std::cout << s.top() << std::endl;
    s.pop();  // returns no value
  }
  
  return 0;
}

… which outputs:

Amherst
Milford
Hollis
Manchester
Nashua
Program ended with exit code: 0

The queue Container Adapter

A queue container adapter may be implemented using either list or deque (the default). 

Here we use a list:

//
//  main.cpp
//  STL Container Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <list>
#include <queue>

int main(int argc, const char * argv[])
{
  std::queue< const char *, std::list<const char *> > q;  
      // Uses list
  q.push("Nashua");
  q.push("Manchester");
  q.push("Hollis");
  q.push("Milford");
  q.push("Amherst");
  
  while (!q.empty())
  {
    std::cout << q.front() << std::endl;
    q.pop();  // returns no value
  }
  
  return 0;
}

… which outputs:

Nashua
Manchester
Hollis
Milford
Amherst
Program ended with exit code: 0

The priority_queue Container Adapter

A priority_queue container adapter may be implemented using a deque or a vector (the default is vector):

//
//  main.cpp
//  STL Container Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <vector>
#include <queue>
#include <string>

int main(int argc, const char * argv[])
{
  std::priority_queue< std::string > q;  // Uses vector, less<T>
  q.push("Nashua");
  q.push("Manchester");
  q.push("Hollis");
  q.push("Milford");
  q.push("Amherst");
  while (!q.empty())
  {
    std::cout << q.top() << std::endl;
    q.pop();
  }
  return 0;
}

… which outputs:

Nashua
Milford
Manchester
Hollis
Amherst
Program ended with exit code: 0

Or we can change the priority ordering:

//
//  main.cpp
//  STL Container Adapters
//
//  Created by Bryan Higgs on 10/23/24.
//

#include <iostream>
#include <vector>
#include <queue>
#include <string>

int main(int argc, const char * argv[])
{
  //std::priority_queue< std::string > q;  // Uses vector, less<T>
  std::priority_queue<std::string,
                      std::vector<std::string>, 
                      std::greater<std::string> > q;
  q.push("Nashua");
  q.push("Manchester");
  q.push("Hollis");
  q.push("Milford");
  q.push("Amherst");
  while (!q.empty())
  {
    std::cout << q.top() << std::endl;
    q.pop();
  }
  return 0;
}

… which outputs:

Amherst
Hollis
Manchester
Milford
Nashua
Program ended with exit code: 0
Index