Thread: Flexible callback system

  1. #1
    Registered User
    Join Date
    Dec 2006
    Posts
    69

    Flexible callback system

    I want to create a callback system that can call back (member or non-member) functions that take (int, ...) as parameters and return void. For this to work, it must be possible to create an array of function pointers where every index of the array may contain either a member or non-member function with the specified parameters and returntype. And it must be possible to call the functions without knowing if it is a member function or not.

    Here is some pseudocode to explain my idea:
    Code:
    // flexibleFunctionType is a type that can store function pointers and pointer-to-member functions
    flexibleFunctionType funcArray[100]; 
    
    class X {void foo(int, ...);};
    X x;
    void bar(int, ...);
    
    // Syntax probably wouldn't look like that, but the following means:
    // funcArray now contains a pointer to the member function "foo",
    //  that is invoked on the object "x".
    funcArray[10] = &x.foo;
    // Store a normal function pointer to function bar:
    funcArray[20] = &bar;
    
    (*(funcArray[10])) (1, 5); // Calls: x.foo(1, 5);
    (*(funcArray[20])) (2, 10, 67); // Calls: bar(2, 10, 67);
    
    // BTW, the above calls use literal values, but it is not required that that is possible.
    I've looked at boost::function and boost::bind, but I found it somewhat difficult to understand and I don't know if this could be a solution. I still consider myself a newbie in C++, so please explain in simple terms.

    Thanks

  2. #2
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    You might be able to pull something like this off if the member functions were static. Non-static member functions require an object to invoke them on, and they have a distinctly different data type than ordinary functions. For example:

    Code:
    void Funk(int, ...);
    Is of type void (*)(int, ...)

    Code:
    class a
    {
        void Funk(int, ...);
    };
    
    class b
    {
        void Funk(int, ...);
    };
    These are of type void (a::*)(int, ...) and void (b::*)(int, ...), so just storing them in the same array would be either impossible, or require some dangerous hackery.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  3. #3
    Registered User
    Join Date
    Dec 2006
    Posts
    69
    Yes I am aware of that. Is there no elegant way to store non-static member functions of an arbitrary class in a such an array? Possibly using some class that can store both normal function pointers and pointer-to-member functions, and/or boost::function or something..

    http://www.boost.org/doc/html/function.html
    http://www.boost.org/libs/bind/bind.html

  4. #4
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    Quote Originally Posted by C+/- View Post
    Yes I am aware of that. Is there no elegant way to store non-static member functions of an arbitrary class in a such an array? Possibly using some class that can store both normal function pointers and pointer-to-member functions, and/or boost::function or something..

    http://www.boost.org/doc/html/function.html
    http://www.boost.org/libs/bind/bind.html
    Not that I know of, and even if there were, there is another problem.

    Pointers to member functions can't be used in the same way that ordinary function pointers can. You need an object to invoke them on. If x is an object, and memptr points to a member function of x's class, you have to invoke it like so:
    Code:
    (x.*memptr)(5, 7)
    Or if x is a pointer to an object:
    Code:
    (x->*memptr)(5, 7)
    So even if you had such a magical function pointer container, you would still need both a means of telling which were member pointers, and an appropriate object upon which to invoke the pointers.

    It may be possible to achieve something like this effect using function objects to wrap both ordinary function pointers and member function pointer/object pairs in a common interface. That may be something to investigate.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  5. #5
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    Here is a general outline that might do what you want:
    Code:
    class FunctionWrapper
    {
    public:
        virtual ~FunctionWrapper() { }
        virtual void Execute(int, ...) = 0;
    };
    
    class Function : public FunctionWrapper
    {
    private:
        void (*TheFunction)(int, ...);
    
    public:
        Function(void (*f)(int, ...)) : TheFunction(f) { }
        void Execute(int x, ...)
        {
            //vararg magic
            TheFunction(x, /*varargs*/);
        }
    };
    
    template <typename t>
    class MemberFunction : public FunctionWrapper
    {
    private:
        t *TheObject;
        void (t::*TheFunction)(int, ...);
    
    public:
        MemberFunction(t *obj, void (t::*f)(int, ...)) : TheObject(obj), TheFunction(f) { }
        void Execute(int x, ...)
        {
            //vararg magic
            (TheObject->*TheFunction)(x, /*varargs*/);
        }
    };
    Now you can make an array of FunctionWrapper pointers, and the Execute method should do what you want in either case.

    Note: I have neither tested nor compiled this code, but hopefully you get the general idea.
    Last edited by AverageSoftware; 06-19-2007 at 11:42 AM.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  6. #6
    Registered User
    Join Date
    Dec 2006
    Posts
    69
    Thanks! This looks like it does exactly what I need.

    It is pretty much what I had in mind but I'm still learning C++ and getting my head around the details (use of inheritance and templates in such a way) was difficult. So thanks for that, now I can hopefully implement the missing parts myself.

  7. #7
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    you say you don't want to use boost.function or boost.bind and yet you're essentially re-implementing them. I don't consider myself a C++ newbie, but this is a task I wouldn't like to attempt.

    bind and function are actually quite simple to use as callbacks (I use them everyday)
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If you want multicast callbacks (i.e. more than one callback can register on the same slot) use the Boost.Signals library. It's very much like Function in its usage, but with slightly different aims.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User
    Join Date
    Dec 2006
    Posts
    69
    I didn't say I don't want to use boost.Function and boost.Bind, I sayd I find them hard to understand and don't know how to use them in this context.

    I'll take a look at boost.Signals.
    EDIT: I undestand what multicast callbacks are now, and that's not necesary. It's still nice to have, so I'll look further into boos.Signals. What I really need is a way to have an array (or other container) of callbacks, wherein each callback may have a different type (non-member function, member of X, member of Y, etc.).


    Thanks
    Last edited by C+/-; 06-21-2007 at 03:43 AM.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    That's possible with Boost.Function, but only as long as all callbacks have the same signature. (E.g. they all return void and have one int parameter.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Using system icons
    By @nthony in forum Windows Programming
    Replies: 1
    Last Post: 01-13-2007, 07:56 PM
  2. Linux database system needed
    By BobS0327 in forum Tech Board
    Replies: 7
    Last Post: 06-11-2006, 03:56 PM
  3. measuring system resources used by a function
    By Aran in forum C Programming
    Replies: 1
    Last Post: 03-13-2006, 05:35 PM
  4. typesafe callback system
    By gandalf_bar in forum C++ Programming
    Replies: 1
    Last Post: 05-22-2004, 06:35 AM
  5. BIOS system and memory allocation problem
    By beely in forum Tech Board
    Replies: 9
    Last Post: 11-25-2003, 07:12 AM