LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Function pointer and functors problem (https://www.linuxquestions.org/questions/programming-9/function-pointer-and-functors-problem-678526/)

lali.p 10-23-2008 07:20 AM

Function pointer and functors problem
 
Hi

Sorry for the stupid heading of the thread, i could not think any better.

I am having a doubt about function pointers and functors. I know that the signatures of a function pointer and a pointer to a class member function are different. However signature of a global function and signature of a static class function are compatible.

Here is what i want to achieve:

There are 2 unrelated classes say A and B ( they are unrelated and hence i don't want to make them part of any inheritance structure)

I have written a timer class in which i can register multiple timers, it takes a timeout value and a function pointer that would be invoked when the timer times out i.e basically i want a callback mechanism on timeout. So internally the timer is using a queue to store all these function pointer and all these function pointers have signature void (*ptr)( );

So the queue would be like:

Code:

typedef void (*ptr)( );
queue<ptr> timer_queue;

The problem is i want to invoke the member function of classes A and B when a timer expires so that i can change the state of object of class A or B.

Let me make it clear( i really suck in communication, sorry for that)

Code:

class A
{
        public:
                A( ):a(5)
                {
                }
                void timerExpired( )
                {
                        cout<<a;
                }

        private:
        int a
};

class B
{
        public:
                B():name("JOHN")
                {
                }
                void timerExpired( )
                {
                        cout<<name;
                }
        private:
                string name;
};

So i want a timer that can help me code the below code :( the following code may not be syntactically correct )

Code:

Timer timer;
A obj_of_a;
B obj_of b

// please remember that timer uses a queue of function pointers
timer.registerTimer(A::timerExpired,6)// expires after 5 seconds

timer.registerTimer(B::timerExpired,2) // expires after 2 seconds

Problem is that these two functions are not static and if i make them static how would i change the state of the objects ( static functions don't have this pointer)
Is it even possible i.e to store pointers to member functions (of different classes) in a queue and invoke them.

It can be done if i make A and B derived from class C containing a virtual function timerExpired and then storing a functor instead of a function pointer but i don't want to do that as in my case A and B are not at all related.

Can it be done somehow using wrapper static functions of classes A and B ??
Please provide some hint or guidance.

Thanks a lot for your patience
Regards
lali

ntubski 10-23-2008 11:38 AM

Quote:

Can it be done somehow using wrapper static functions of classes A and B ??
Yes, but only if you give the timer a pointer to the object as well. eg
Code:

typedef void (*ptr)( void *arg );
typedef pair<void*, ptr> callee;
queue<callee> timer_queue;

class A {
  ...
  static void callTimerExpired(void *arg);
};
void A::callTimerExpired(void *arg) {
      A *_this = (A*) arg;
      _this->timerExpired();
}

timer.registerTimer(make_pair(&obj_of_a, A::callTimerExpired),6)// expires after 5 seconds


dmail 10-23-2008 03:32 PM

Personally I would use an existing functor library like Loki, Boost or an implementation of C++0x. Using one of these it is possible to register any of the following function, this example uses Loki.
Code:

typedef Functor<void,NullType> ptr;
typedef queue<ptr> timer_queue;

struct foo
{
        void timeout(){}
        static void do(){}
};

struct bar
{
        void timer(){}
        void operator()(){}
};

void baz(){}

timer_queue q;
q.push_back(ptr(new foo,&foo::timeout));
q.push_back(ptr(&foo::do));
q.push_back(ptr(new bar,&bar::timer));
q.push_back(ptr(&baz));


ta0kira 10-23-2008 04:07 PM

Quote:

Originally Posted by ntubski (Post 3319845)
Yes, but only if you give the timer a pointer to the object as well. eg
Code:

typedef void (*ptr)( void *arg );
typedef pair<void*, ptr> callee;
queue<callee> timer_queue;

class A {
  ...
  static void callTimerExpired(void *arg);
};
void A::callTimerExpired(void *arg) {
      A *_this = (A*) arg;
      _this->timerExpired();
}

timer.registerTimer(make_pair(&obj_of_a, A::callTimerExpired),6)// expires after 5 seconds


void* is a terrible idea in C++. That's what templates and abstract classes are for!
Code:

#include <iostream>


struct function_wrapper
{
    virtual void function() = 0;
};


template <class Type>
class function_template : public function_wrapper
{
public:
    function_template(Type *pPointer) : pointer(pPointer) {}

    void function()
    { if (pointer) pointer->function(); }

private:
    Type *const pointer;
};


struct structure1
{
    void function()
    { std::cout << "structure1\n"; }
};

struct structure2
{
    void function()
    { std::cout << "structure2\n"; }
};


int main()
{
    structure1 one;
    structure2 two;

    function_wrapper *wrapper = NULL;

    wrapper = new function_template <structure1> (&one);
    wrapper->function();
    delete wrapper;

    wrapper = new function_template <structure2> (&two);
    wrapper->function();
    delete wrapper;
}

The code in main can be encapsulated into yet another class derived from function_wrapper so that it can encapsulate any object. This is a very reliable pattern, but one problem with it is that the name of the function must be fixed. In any case, there is absolutely no way that class1::function and class2::function can be derived from a single function pointer; you need to use something like this.
ta0kira

dmail 10-23-2008 08:08 PM

Whilst the code posted by ta0kira would work for this instance I would have to say it is not a nice implementation due to it not being generic enough. It is the fundamentals of how functors are done yet any change to the function signature requires a change of the base class or worst still if you have many signatures in a project then many different base "wrappers" hand coded, for example if you wanted a return type or parameters. Added to this that all functions have to be member functions and have the same name and it is reinventing a well defined wheel which good libraries with good support all ready implement.

ta0kira 10-23-2008 08:31 PM

Yeah, I didn't read the thread very well I guess. I actually wrote something several years ago that did what ptr in your post does and I sort of misremembered how I did it. Regardless, it's often better to use something already built and maintained.
ta0kira

lali.p 10-24-2008 01:16 AM

Thanks for your replies, i had installed boost recently on my system. For the time being i am implementing the idea provided by ntubski as i need this thing done by the end of day. However, soon i would go through boost::function and would use that in future.


@ta0kira But don't you think that such libraries would also be using void* pointer and casting in their implementation ?
Quote:

void* is a terrible idea in C++. That's what templates and abstract classes are for!
Are templates an answer to explicit casting ?( correct me if i am wrong )

One more thing i would like to mention. I have found the boost libraries to be a source of extremely good learning material, making the life of an application programmer quite easy.Boost libraries come with documentation on how to *use* the library but no information about how it is implemented(boost after all is not meant for tutoring or mentoring)

However i would also like to learn how things are implemented( and designed ) in such expertly written libraries. I don't want to be a mere user of such libraries and so want to improve my skill by reading code written by experienced people and then compare with what i would have thought about implementing the same thing and thereby learn in the process.


Although boost libraries come with source code, if i begin reading a library say asio, it is dependent on other boost libraries like boost::system ,boost::function(i am not sure about that)
and so most of the time i get lost in details. There are no pointers as how to read code, i mean where to start first.

I can comprehend most of the code that i read except these library dependencies. Any hints on how i can tackle that problem ?

I know that learning programming is not like following a list of sequentional steps from beginning to end and is not a straight path. It takes time and patience. However if any of you has ever tried learning from code written by others, please shed some light on your experience on how to do it.

dwhitney67 10-24-2008 05:11 AM

Here's a timer class (Timer.hpp) that I tinkered with a couple of years ago. I never used it for anything; maybe it can provide you with some ideas. (Btw, sorry for the lack of documentation; hopefully the TestTimer.cpp test-program will serve as a guide).

Timer.hpp (provides for blocking or non-blocking timer, with option to repeat):
PHP Code:

/*
# Copyright (C) 2008 David M. Whitney (aka "dwhitney67")
#
# This program is free software: you can redistribute it and/or modify it under 
# the terms of the GNU General Public License as published by the Free Software 
# Foundation, either version 3 of the License, or (at your option) any later 
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT 
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with 
# this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef TIMER_HPP
#define TIMER_HPP

#include <sys/types.h>
#include <sys/select.h>
#include <unistd.h>       // for fork()
#include <cstdlib>        // for exit()
#include <iostream>


template<class T>
class 
Timer
{
  public:
    
enum TimerType
    
{
      
ONE_SHOTREPEATINGCONTINUOUS
    
};

    
enum TimerRunType
    
{
      
BLOCKINGNON_BLOCKING
    
};

    
Timer(int secondsTimerType timerType ONE_SHOTint repeats 1)
    {
      
setupTimer(seconds0timerTyperepeats);
    }

    
Timer(int secondsint microseconds 0TimerType timerType ONE_SHOTint repeats 1)
    {
      
setupTimer(secondsmicrosecondstimerTyperepeats);
    }

    
inline void start(TimerRunType runType BLOCKING)
    {
      
pid_t childPid 0;

      if (
runType == NON_BLOCKING)
      {
        
childPid fork();
      }

      if (
childPid == -1)
      {
        
std::cerr << "failed to start timer process" << std::endl;
      }
      else if (
childPid == 0)
      {
        switch (
type)
        {
          case 
ONE_SHOT:
              
struct timeval tv;
              
tv.tv_sec  seconds;
              
tv.tv_usec microseconds;
              
select(0000, &tv);
              
functor();
              break;

          case 
REPEATING:
          case 
CONTINUOUS:
              
int r repeats;
              while ((
r-- > 0) || (type == CONTINUOUS))
              {
                
struct timeval tv;
                
tv.tv_sec  seconds;
                
tv.tv_usec microseconds;
                
select(0000, &tv);
                
functor();
              }
              break;
        }

        if (
runType == NON_BLOCKING)
        {
          exit(
childPid);
        }
      }
      else
      {
        
// in parent context... just return
      
}
    }

  private:
    
void setupTimer(int secondsint microsecondsTimerType timerTypeint repeats)
    {
      
this->seconds      seconds;
      
this->microseconds microseconds;
      
this->type         timerType;
      
this->repeats      repeats;
    }

    
int       seconds;
    
int       microseconds;
    
TimerType type;
    
int       repeats;
    
T         functor;
};

#endif 

WaitTimer.hpp (a blocking timer):
PHP Code:

/*
# Copyright (C) 2008 David M. Whitney (aka "dwhitney67")
#
# This program is free software: you can redistribute it and/or modify it under 
# the terms of the GNU General Public License as published by the Free Software 
# Foundation, either version 3 of the License, or (at your option) any later 
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT 
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with 
# this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef WAIT_TIMER_HPP
#define WAIT_TIMER_HPP

#include "Timer.hpp"
#include <iostream>


class WaitTimer
{
  private:
    class 
CallbackForTimer
    
{
      public:
        
CallbackForTimer() {}
        
void operator()() { /* nothing to do */ }
    };

    
typedef Timer<CallbackForTimerWaitTimerDef;

  public:
    
WaitTimer(int secondsint milliseconds 0)
        : 
internalTimer(secondsmillisecondsWaitTimerDef::REPEATING1)
    {
    }

    
void start()
    {
      
internalTimer.start(WaitTimerDef::BLOCKING);
    }

  private:
    
WaitTimerDef internalTimer;
};

#endif 

TimerTest.cpp (a test program):
PHP Code:

/*
# Copyright (C) 2008 David M. Whitney (aka "dwhitney67")
#
# This program is free software: you can redistribute it and/or modify it under 
# the terms of the GNU General Public License as published by the Free Software 
# Foundation, either version 3 of the License, or (at your option) any later 
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT 
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with 
# this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "Timer.hpp"
#include "WaitTimer.hpp"
#include <iostream>
#include <string>


class CallbackOne
{
  public:
    
CallbackOne() {}

    
void operator()()
    {
      
std::cout << "CallbackOne operator() has been called" << std::endl;
    }
};


class 
CallbackTwo
{
  public:
    
CallbackTwo() {}

    
int operator()()
    {
      
std::cout << "CallbackTwo operator() has been called" << std::endl;
      return 
10;
    }
};


int main(int argcchar **argv)
{
  
int seconds      1;
  
int microseconds 0;

  
typedef TimerCallbackOne CallbackOneTimer;
  
typedef TimerCallbackTwo CallbackTwoTimer;

  
CallbackOneTimer timerOne(secondsmicrosecondsCallbackOneTimer::REPEATING6);
  
CallbackTwoTimer timerTwo(seconds500         CallbackTwoTimer::REPEATING6);

  
timerOne.start(CallbackOneTimer::NON_BLOCKING);
  
timerTwo.start(CallbackTwoTimer::NON_BLOCKING);

  
// block for 10 seconds so that the callback-timers started above can finish.
  
WaitTimer wt(10);
  
wt.start();

  return 
0;


To compile/link:
Code:

g++ TimerTest.cpp -o timer
Let me know if you have any questions or comments.

dw

ta0kira 10-24-2008 08:58 AM

Quote:

Originally Posted by lali.p (Post 3320493)
@ta0kira But don't you think that such libraries would also be using void* pointer and casting in their implementation ?

If it can be done safely, it can be done without void*. Sure, that's really what's used internally, but there is almost always a way to make things work with templates. I've created some very elaborate function pointer systems without using a single void*. Though not documented, here is my experiment with function pointers from many years ago: http://sourceforge.net/projects/afunct-ta0kira/. No coding standards, I admit, but it's built almost entirely partial specialization. I hope my skills have improved since then ;)
ta0kira


All times are GMT -5. The time now is 05:33 PM.