Click here to Skip to main content
15,891,943 members
Articles / Programming Languages / C++
Article

An object-oriented wrapper around function pointers

Rate me:
Please Sign up or sign in to vote.
3.89/5 (15 votes)
9 Sep 20042 min read 70.5K   395   19   18
Function pointers provide a handy mechanism for 1<sup>st</sup> order functions but are rather complex in usage. This article gives a wrapper around those function pointers making the use of them easier.

Introduction

Sometimes functions only differ in a few things you cannot express by using variables, what you really love to do is passing a function that can handle the 'special' case and keep your abstraction nice. In some languages like Scheme, you can pass functions as arguments; in C++, you have to use function pointers.

Function pointers are a good system for C, but not for C++; you have to define a non-class member or static class member... So, we cannot access non-static data members of a class without using global variables.

The solution is creating a wrapper around the function pointers that contains the function pointer and a pointer to the class.

Note: an alternative used by STL are function objects; I don't like this approach since it causes too much type-work ;).

Usage

I'll describe a short guide on how to incorporate/use the function pointer wrappers.

  1. Add the following statements to your header file:
    C++
    ...
    #include "functor.h"
    ...
  2. (optional) Do a typedef of the templated function pointer, this makes it much easier to read :).

    It is recommended to make this typedef inside your class definition, to prevent polluting your global namespace. I usually place them in the private declarations of my class.

    C++
    typedef Functor<Test, void, int> Function;

    In this example, Test is the class that contains the function, void is the return value of the function, and int is the only argument of the function.

  3. Define your class, I added a quick sample of a class here:
    C++
    /* 
     * header file
     */
    class Test
    {
    public:
      void TestFunc();
    
    private:
      void Add(int x);
      void Mul(int x);
      void DoSomething(int x, Function f);
    
      int value_;
    };
    
    /* 
     * source file
     */
    void Test::Add(int x)
    {
      value_ += x;
    }
    
    void Test::Mul(int x)
    {
      value_ *= x;
    }
    
    void Test::DoSomething(int x, Function f)
    {
      // some very complicated code
      ...
      f(x);
      ...
      // lots of other code
    }

    In the example, you should note f(x), this is the calling of the function pointer wrapper. Looks exactly like a regular function call, but the f is an argument of the function.

  4. Create the function pointer wrapper:
    C++
    void Test::TestFunc()
    {
      // create the function pointer wrapper (functor)
      Function myadd(this, &Test::Add);
      Function mymul(this, &Test::Mul);
    
      // now do the action!
      DoSomething(5, myadd);
    }

    You create a wrapper object just by passing two arguments to the constructor: the host class of the functions, usually a this, and the name of the method that the wrapper should use.

    You can now pass the function to any method and call it as it was a normal function :)

Technical Stuff

The system of the wrapper is based on the usage of templates and operator overloading. Templates are used to make it more generic (i.e., to be able to create a wrapper for any function with a specified number of arguments). The operator overloading is used to simulate a regular procedure call, we overload the operator() to achieve this.

Known Limitations

The source code only contains a wrapper for 1 and 2 arguments. If you need more, you'll need to extend the classes or pass a struct/class that contains the data as single argument.

Todo

  • Compatibility with const member functions
  • Create an example project
  • Create an inline version for compatibility with VC++ 6 or lower.

History

  • Version 1.0.1 - September 12, 2004

    Some corrections based on suggestions of Roland Pibinger and KevinHall. Thanks!

  • Version 1.0.0 - September 9, 2004

    Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Belgium Belgium
Kenny Moens is a student computer science at Vrije Universiteit Brussel (VUB), a university located in Brussels, Belgium.

He's been programming for about 10 years with experience in Pascal, Basic, Perl, PHP, C, C++, Java, Scheme, Lisp and Smalltalk.

Comments and Discussions

 
GeneralCompile Error Pin
haiwenliao11-Mar-09 16:24
haiwenliao11-Mar-09 16:24 
GeneralSome Ideas Pin
Roland Pibinger13-Sep-04 10:33
Roland Pibinger13-Sep-04 10:33 
GeneralSome Remarks Pin
Roland Pibinger11-Sep-04 8:08
Roland Pibinger11-Sep-04 8:08 
First let me say that I find your work instructive and comprehensible.
There are more sophisticated approaches
(eg. http://www.cuj.com/documents/s=8464/cujcexp0308sutter/)
which I'm not really fond of (typical Boost code).


Hints to make Functor compile with VC++ 6.0:
- #include <stdexcept>
- void return types don't work with VC++ 6.0 (probably a compiler bug)
- templates must be inline (in VC++ 6.0), see: http://www.decadentplace.org.uk/womble/cplusplus/template-faq.html#vc6-non-inline-mem


I don't quite understand your rationale with respect to the one-argument
constructor (ie. Functor(Class* cls) : class_(cls), function_(NULL)) and operator=
(ie. operator=(typename Functor::FunctionPointer ptr)). I left them out in
my destilled version that now looks like this (default copy ctor and operator= are ok):


#include <stdexcept>

template <typename Class, typename Return, typename Arg1>
class Functor {
public:
  typedef Return (Class::*FunctionPointer)(Arg1);
  Functor() : object_(0), function_(0) {}
  Functor(Class* obj, FunctionPointer ptr) : object_(obj), function_(ptr) {}
  Return operator()(Arg1 arg1)  {
    if(! (object_ && function_)) {
      throw std::runtime_error("XX");
    }
    return (object_->*function_)(arg1); 
  }
private:
  Class* object_;
  FunctionPointer function_;
};


Unfortunately const functions cannot be used with the Functor template. You need an
extra template for const functions (maybe this can be overcome with template tricks):

template <typename Class, typename Return, typename Arg1>
class FunctorConst {
public:  
  typedef Return (Class::*FunctionPointer)(Arg1) const;
  FunctorConst() : object_(0), function_(0) {}
  FunctorConst(const Class* obj, FunctionPointer ptr) 
     : object_(obj), function_(ptr) {}
  Return operator()(Arg1 arg1)  {
    if(! (object_ && function_)) {
       throw std::runtime_error("XX");
    }
    return (object_->*function_)(arg1); 
  }
private:
  const Class* object_;
  FunctionPointer function_;
};


ToDos (for you Smile | :) ):
- Examples, especially examples with const and/or refernce argument and return types
- Functor templates for 0 - 8 arguments


I hope you find time to refactor code and article (keep it simple, don't boost the code).
It's a "Nice topic" and there's "a lot of room for improvement though" (Kevin Hall).


Best wishes,
Roland Pibinger


PS.: This has been my test code (compiles with VC++ 6.0, BCC 5.5, gcc 3.2(MingW)):

#include <stdio.h>
#include "functor.h"

class Test {
public:
 Test() : value_(0) {}

 int Add (const int& x) { printf ("Add\n"); value_ += x; return value_; }
 int Mul (const int& x) { printf ("Mul\n"); value_ *= x; return value_; }
 int Foo (const int& x) const { printf ("Foo\n"); return value_; } // const!
private:
 int value_;
};

typedef Functor<Test, int, const int&> Function;
typedef FunctorConst<Test, int, const int&> FunctionConst;

void DoSomething (int x, Function f) {f(x);}
void DoSomething (int x, FunctionConst f) {f(x);}

void TestFunc1() {
   printf ("TestFunc1\n");
   Test t1;
   Function myadd (&t1, &Test::Add);
   Function mymul (&t1, &Test::Mul);

   Function myfuncTest;

   DoSomething (5, myadd);
   DoSomething (5, mymul);
   
   Function myfunc (myadd);
   DoSomething (5, myfunc);
   myfunc = mymul;
   DoSomething (5, myfunc);
}

void TestFunc2() {
   printf ("TestFunc2\n");
   Test t2;
   FunctionConst myconst (&t2, &Test::Foo);

   DoSomething (5, myconst);

   FunctionConst myfuncConst;
   myfuncConst = myconst;
   DoSomething (5, myfuncConst);
}

int main() {
   TestFunc1();
   TestFunc2();

   return 0;
}

GeneralSome small corrections to the article Pin
KevinHall10-Sep-04 5:16
KevinHall10-Sep-04 5:16 
GeneralRe: I don't agree with everything you say. Pin
KevinHall9-Sep-04 13:41
KevinHall9-Sep-04 13:41 
GeneralRe: I don't agree with everything you say. Pin
WREY10-Sep-04 3:21
WREY10-Sep-04 3:21 
GeneralRe: I don't agree with everything you say. Pin
KevinHall10-Sep-04 5:06
KevinHall10-Sep-04 5:06 
GeneralI didn't know that. Pin
WREY10-Sep-04 5:46
WREY10-Sep-04 5:46 
GeneralRe: I didn't know that. Pin
KevinHall10-Sep-04 5:59
KevinHall10-Sep-04 5:59 
GeneralThis might help. Pin
WREY10-Sep-04 8:10
WREY10-Sep-04 8:10 
GeneralRe: I don't agree with everything you say. Pin
Kenny Moens9-Sep-04 22:58
Kenny Moens9-Sep-04 22:58 
GeneralNice topic -- a lot of room for improvement though Pin
KevinHall9-Sep-04 10:57
KevinHall9-Sep-04 10:57 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
Emilio Garavaglia9-Sep-04 21:32
Emilio Garavaglia9-Sep-04 21:32 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
KevinHall10-Sep-04 2:36
KevinHall10-Sep-04 2:36 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
Emilio Garavaglia10-Sep-04 4:10
Emilio Garavaglia10-Sep-04 4:10 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
KevinHall10-Sep-04 4:50
KevinHall10-Sep-04 4:50 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
Kenny Moens9-Sep-04 23:02
Kenny Moens9-Sep-04 23:02 
GeneralRe: Nice topic -- a lot of room for improvement though Pin
KevinHall10-Sep-04 5:20
KevinHall10-Sep-04 5:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.