recently while studying STL i came thru functors and facades

http://en.wikipedia.org/wiki/Function_object]
A typical use of a functor is in writing more intelligent callback functions. A callback in procedural languages, such as C, may be accomplished by using function pointers. However it can be difficult or awkward to pass state into or out of the callback function. This restriction also inhibits more dynamic behavior of the function. A functor solves those problems since the function is really a façade for a full object, thus it carries its own state.

I am not able to figure out what does state mean while they are saying it is difficult to pass the information into callback functions.
Moreover in facades is there something like interfaces with wrapovers written for them.

please give me some good examples as I am really interested to learn this aspect of C++

Recommended Answers

All 5 Replies

State means that info persists between calls. If you want to keep a running total of some statistic, that's state, and it's hard to maintain with functions. You have to use a global variable or a parameter.

int count1 = 0;

void function( int& count2 ) {
  // do work
  ++count1;
  ++count2;
}

But global variables are always bad, and with callbacks the parameter is harder to work with because you have to pass it along whenever you use the callback.

void callback( int& count ) {
  // do work
  ++count;
}

void function( void (*cb)( int& ), int& count ) {
  cb( count );
}

Add a few more functions in the callback chain and a few more state parameters and it gets really messy. Functors make the problem go away because they're objects that can have inside information.

struct functor {
  int count;
  functor(): count( 0 ) {}
  void operator()() {
    // do work
    ++count;
  }
};

void function( functor& f ) {
  f();
}

Add as many functions to the callback chain and as many state variables as you want and it stays simple. :) That's why functors are part of the facade design pattern. They take a lot of complicated stuff and make it look like a simple function call.

So you mean that functors can overcome the shortcoming of data integrity and can benefit to better visibility of code. I am still thinking on thing. in case u have a callback function you can pass the this pointer to make use of the class members and thus avoiding the global variable maintainance.

Where will my (dumb ;)) thinking get falsified please let me know.

Member Avatar for iamthwee

yes you are correct. Functors help to increase readability as does obfuscation.

the total store of c++ functions is fixed. these are the functions which were written when the code was compiled; there is simply no way to create new functions at run time. what differentiates function objects from functions is that they are objects (variables). in principle we can do everything with them that we can do with any other kind of variable. among other things, thay can hold state, they can be modified at run time and they can be created at run time. simply put, function objects give us a means to bundle a function call with implicit arguments to pass to that function. this allows us to build up complicated expressions using surprisingly simple syntax.

perhaps an example would help. we want to sort a sequence of integers; the sort criteria is a single digit of the integer at a position N (digit 0 being the least significant one). the value of N is known only at run time.
here is a version using c library qsort which requires a pointer to a comparison function (we avoid global variables for all the usual reasons - they encourage tight coupling, are a recipe for disaster in the presence of concurrency etc.):

#include <cstdlib>
#include <vector>
#include <iostream>
#include <boost/random.hpp>
#include <iterator>
#include <cassert>
using namespace std ;
using namespace boost ;

int compare_digit( int first, int second, int digit )
{
  int div = 1 ;
  for( int i=0 ; i<digit ; ++i ) div *= 10 ;
  int digit1 = (first/div) % 10 ;
  int digit2 = (second/div) % 10 ;
  if( digit1 < digit2 ) return -1 ;
  return digit1==digit2 ? 0 : +1 ;
}

int cmp_digit_0( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 0 ) ;
}

int cmp_digit_1( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 1 ) ;
}

int cmp_digit_2( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 2 ) ;
}

int cmp_digit_3( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 3 ) ;
}

int cmp_digit_4( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 4 ) ;
}

int cmp_digit_5( const void* first, const void* second )
{
  return compare_digit( *static_cast<const int*>(first),
                        *static_cast<const int*>(second), 5 ) ;
}

int main()
{
  vector<int> sequence ;
  mt19937 twister ;
  variate_generator< mt19937&, uniform_int<> > 
                         rng( twister, uniform_int<>(1,1024*1024) ) ;
  generate_n( back_inserter(sequence), 1024*1024*8, rng ) ;
  int digit ; cout << "sort on digit? " ; cin >> digit ;
  assert( digit>=0 && digit<=5 ) ;

  switch(digit)
  {
    case 0:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_0 );
       break ;
    case 1:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_1 );
       break ;
    case 2:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_2 );
       break ;
    case 3:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_3 );
       break ;
    case 4:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_4 );
       break ;
    case 5:
       qsort( &sequence.front(), sequence.size(), sizeof(int), cmp_digit_5 );
       break ;
    default:
       assert(false) ;
   }
}

and here is the version using std::sort which accepts function objects:

#include <algorithm>
#include <vector>
#include <iostream>
#include <boost/random.hpp>
#include <iterator>
#include <cassert>
using namespace std ;
using namespace boost ;

struct compare_digit_t
{
  explicit compare_digit_t( int digit ) : div(1)
  { for( int i=0 ; i<digit ; ++i ) div *= 10 ; }

  bool operator() ( int first, int second ) const
  {
    int digit1 = (first/div) % 10 ;
    int digit2 = (second/div) % 10 ;
    return digit1 < digit2 ;
  }
  private: int div ;
};

int main()
{
  vector<int> sequence ;
  mt19937 twister ;
  variate_generator< mt19937&, uniform_int<> > 
                          rng( twister, uniform_int<>(1,1024*1024) ) ;
  generate_n( back_inserter(sequence), 1024*1024*8, rng ) ;
  int digit ; cout << "sort on digit? " ; cin >> digit ;
  assert(digit>=0) ;
 
  std::sort( sequence.begin(), sequence.end(), compare_digit_t(digit) ) ;
}
commented: You are the best to explain...Cheers to you +1
// and here is an example of operating on function objects
// where f1 and f2 are unary function objects;
// we define an operation fnew = f1 & f2 
// fnew is a new function equivalent to f1(f2(T))
// see "Ruminations on C++" - andy koenig & barbara moo 
// pp. 241-261 for a full discussion

#include <algorithm>
#include <iostream>
#include <functional>
#include <boost/function.hpp>
#include <math.h>
#include <float.h>
using namespace std ;
using namespace boost ;

template < typename FN1, typename FN2 > struct chain_t 
 : unary_function< typename FN2::argument_type, typename FN1::result_type >
{
  chain_t( FN1 f1, FN2 f2 ) : fn1(f1), fn2(f2) {}
  typename FN1::result_type operator() ( 
                                 typename FN2::argument_type arg ) const
  { return fn1( fn2( arg ) ) ; }
  FN1 fn1 ;
  FN2 fn2 ;
};

template < typename FN1, typename FN2 > inline chain_t<FN1,FN2>  
operator& ( FN1 fn1, FN2 fn2 ) { return chain_t<FN1,FN2>(fn1,fn2) ; }

int main()
{
  typedef function<double(double)> fun_type ;

  fun_type new_fun = bind2nd( plus<double>(), 10.4 ) & 
                     bind2nd( multiplies<double>(), 2.3 ) ;
  cout << "(5.6*2.3)+10.4 == " << new_fun(5.6) << '\n' ;

  fun_type another_fun = ptr_fun(sqrt) & new_fun ;
  cout << "sqrt( (5.6*2.3)+10.4 ) == " << another_fun(5.6) << '\n' ;

  fun_type a_third_fun = ptr_fun(ceil) & another_fun ;
  cout << "ceil( sqrt( (5.6*2.3)+10.4 ) ) == " << a_third_fun(5.6) << '\n' ;
}

> g++ -std=c++98 -Wall -I/usr/local/include functor_chain.cpp && ./a.out
(5.6*2.3)+10.4 == 23.28
sqrt( (5.6*2.3)+10.4 ) == 4.82494
ceil( sqrt( (5.6*2.3)+10.4 ) ) == 5

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.