Using stateful functors

For another example posts on functors (function objects) see here. A functor is an instance of a C++ class that has the operator() defined. One big advantage of functors is that when you define the operator() in C++ classes you not only get objects that can act like functions, but can also store state as well.

Non-stateful functor

Many of the example functors in C++ you see online are used as predicates, or comparison functions in STL algorithms. For example:

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

class Foo
{
public:
    void operator () (int i) { std::cout << i << " "; }
};

int main()
{
	// Initialize example vector arrays and output
	int vals[] = { 1, 2, 2, 3, 2, 5, 77, 4, 16, 11 };	
	std::vector<int> v( vals, vals + sizeof( vals ) / sizeof( vals[ 0 ] ) ) ;			
	for_each( v.begin(), v.end(), Foo() );
	
	return 0;
}

Giving the following output:

1 2 2 3 2 5 77 4 16 11

Stateful functor: collecting even numbers

Say in your application you had (say) a vector array of integers and you wished to find the indexes of all the even ones. This would be a perfect job for a functor and for_each algorithm. The EvenNumberCollect functor takes care to ensure that it’s instances are handled appropriately, hence the use of the copy constructor and assignment operator in addition to the default constructor:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>  
#include <sstream>

class EvenNumberCollect
{
public:
	std::vector<int> v;		
	int index;

	// Default constructor
	EvenNumberCollect()  : index( 0 ) {}

	// Copy constructor
	EvenNumberCollect( const EvenNumberCollect& e ) : index( e.index )
	{		
		v.clear();
		v = e.v;
	}

	// Assignment operator
	EvenNumberCollect& EvenNumberCollect::operator =( const EvenNumberCollect &e )  
	{  
		if ( &e != this )  
		{  
			index = e.index;  
			v.clear();
			v = e.v;
		}  
		return *this;  
	}  

	void Clear()
	{
		v.clear();
		index = 0;
	}

	void operator() ( int i )
	{ 
		if ( i % 2 == 0 ) 
		{
			v.push_back( index );			
		}
		index++;
	}

	void PrintIndexes()
	{
		std::copy( v.begin(),   
                   v.end(),   
                   std::ostream_iterator<int>( std::cout, "\t" ) ); 
		std::cout << "\n";
	}
};

int main()
{
	// Initialize example vector arrays and functors
	int vals1[] = { 1, 2, 2, 3, 2, 5, 77, 4, 16, 11 };
	int vals2[] = { 5, 5, 3, 5, 5, 76, 5, 19 };
	std::vector<int> v1( vals1, vals1 + sizeof( vals1 ) / sizeof( vals1[ 0 ] ) ) ;
	std::vector<int> v2( vals2, vals2 + sizeof( vals2 ) / sizeof( vals2[ 0 ] ) ) ;
	EvenNumberCollect evenNoColl1;
	EvenNumberCollect evenNoColl2;

	// Display the indexes of all even numbers 
	evenNoColl1 = for_each( v1.begin(), v1.end(), evenNoColl1 );
	evenNoColl1.PrintIndexes();	
	evenNoColl2 = evenNoColl1;
	evenNoColl2.PrintIndexes();

	// Reset 1st functor and get all indexes of even numbers in second array
	evenNoColl1.Clear();
	evenNoColl1 = for_each( v2.begin(), v2.end(), evenNoColl1 );
	evenNoColl1.PrintIndexes();

	return 0;
}

Giving the following output showing the indexes of even numbers in the vector arrays:

1 2 4 7 8
1 2 4 7 8
5 8 9

Stateful functor: Appending strings from container items

This collects each ‘txt‘ item in vector of ‘Token‘ structs, appending them to the ‘txt‘ member of the Appender functor, which has the Word() member function to return the complete concatenated string:

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

struct Token
{
	std::string txt;
	Token( std::string s ) : txt( s ) {}	
};

class Append
{
private:
	std::string txt;
public:
	Append() : txt("") {}

	 // for_each calls operator() for each container element
    void operator() (const Token &x)
    { txt.append( x.txt + " " ); }

	std::string Word() const 
	{ return txt; }
};

int main()
{
	std::vector<Token> tokens;
	tokens.push_back( Token( "JJ" ) );
	tokens.push_back( Token( "NN" ) );
	tokens.push_back( Token( "NN" ) );

	std::string txt = for_each( tokens.begin(), 
		                        tokens.end(), 
								Append() ).Word();
	
	std::cout << txt << std::endl;
	return 0;
}

Giving the following output:

JJ NN NN

Leave a Reply