Computer Science Canada

[C++] Function Objects

Author:  wtd [ Tue Apr 06, 2004 11:09 pm ]
Post subject:  [C++] Function Objects

C++ templates and operator overloading allow for a lot of interesting programming techniques to be implemented. One of the interesting ones is creating functions that can be passed around like objects.

Normally, in C and C++, functions are just places in memory where certain chunks of code begin. They aren't first-class things that can be passed to other functions. So, if functions can't be made to behave like objects, why don't we make objects behave like functions?

We can overload the () operator like so:

code:
#include <string>
#include <iostream>

class Foo {
   public:
      void operator()(std::string input) {
         std::cout << input << std::endl;
      }
};

int main() {
   Foo write;
   write("hello");
}


Pretty slick, huh?

Now, apply it to a collection or array of things.

code:
#include <iostream>
#include <string>
#include <algorithm>

template <typename _t, typename _func>
void collect(_t * in_begin, _t * in_end, _t * dest_begin, _func * function);

class multiplier {
   private:
      int multiply_by;
   public:
      multiplier(int init_multiply_by) : multiply_by(init_multiply_by) { }

      int operator()(int input) {
         return input * multiply_by;
      }
};

int main() {
   int array1[5] = { 10, 5, 7, 1, 8 };
   int array2[5];

   collect(array1, array1 + 5, array2, new multiplier(3));

   for (int i = 0; i < 5; i++)
      std::cout << array2[i] << std::endl;
}

template <typename _t, typename _func>
void collect(_t * in_begin, _t * in_end, _t * dest_begin, _func * function) {
   for (_t * iter(in_begin); iter != in_end; iter++)
      *(dest_begin + (iter - in_begin)) = (*function)(*iter);
}

Author:  wtd [ Sat Jun 12, 2004 8:35 pm ]
Post subject: 

A simpler example:

code:
#include <vector>
#include <iostream>
#include <algorithm>

#include "advcmp3.h"

template <typename _t, bool _with_endline = false> class print
{
        private:
                std::ostream& stream;
        public:
                print(std::ostream& out) : stream(out) { }

                void operator()(_t data)
                {
                        stream << data;
                        if (_with_endline) stream << std::endl;
                }
};

template <typename _t> class println : public print<_t, true>
{
        public:
                println(std::ostream& out) : print<_t, true>(out) { }
};

int main()
{
        using namespace std;

        float a[] = { 1.23, 2.34, 4.65 };

        for_each(a, a + 3, println<float>(cout));
}

Author:  bugzpodder [ Sat Jun 12, 2004 9:38 pm ]
Post subject: 

thats some pretty neat sh*t ... i think functors exists in C/C++ (pointers to functions) that can be passed around as pointers

Author:  wtd [ Sun Jun 13, 2004 12:00 am ]
Post subject: 

Indeed. for_each and its like will also accept simple functions.

code:
#include <iostream>
#include <algorithm>

void print(float data)
{
   std::cout << data;
}

int main()
{
   using namespace std;

   float a[] = { 1.32, 4.98, 3.14159 };

   for_each(a, a + 3, print);
}


In C++ this operates on fairly simple polymorphism. The for_each function is templated, so the type of the third argument (the function pointer or function object) can be anything, so long as it responds to the () operator with a single argument.

Function pointers, especially as they must be dealt with in C and can be dealt with in C++ are powerful, but also quite dangerous and difficult and clumsy to wield. Function objects yield greater simplicity and yet greater flexibility as well.


: