Doing callbacks

This is a discussion on Doing callbacks within the C++ Programming forums, part of the General Programming Boards category; I just wanted to see if someone has a good solution to this. Initially, I have this function named Synchronize: ...

  1. #1
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,182

    Doing callbacks

    I just wanted to see if someone has a good solution to this.
    Initially, I have this function named Synchronize:
    Code:
    	template<typename Class, typename Function> void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, Class pClass = NULL, Function pFunction = NULL)
    Which is a template function due to the fact that it can do a callback.
    However, I'm trying to make it possible to do both a callback to a single function and a function within a class, so a class function callback to speak.

    But as you've probably have guessed, the compiler won't allow that, since the syntax is incompatible for both. If it's a function only, the class callback code will bark and vice versa.
    So now the question is what the best solution is for this?

    The function itself can be a little problematic to split into different parts and a wrapper for the callback:
    Code:
    template<typename Class, typename Function> void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, Class pClass = NULL, Function pFunction = NULL)
    {
    	// Let's set it to update 50 frames/sec.
    	//const DWORD dwTimeFrame = 1000; // The time frame we're targeting (in ms)
    	//const DWORD dwFramesPerTimeFrame = 50; // Number of times per time frame cycle to do rendering
    	const UINT64 dwTickRate = dwTimeFrame / dwFramesPerTimeFrame; // Calculate amount of time to sleep each time
    	INT32 nSleepNeeded; // Stores the amount of time we need to sleep
    	UINT64 dwTimeElapsed; // Time elapsed since beginning of time frame
    	UINT64 dwNextSleepBoundary; // The next "target" time to sleep until.
    	DWORD dwTick; // Used to hold a snapshot of timeGetTime so we can calculate elapsed time
    	//DWORD Count = 1; // Simple counter just for cosmetics - just to print something
    	DWORD dwTick2 = timeGetTime(); // Snapshot of the beginning of the code sample
    
    	for(;;)
    	{
    		// Initialize variables
    		nSleepNeeded = 0;
    		dwTimeElapsed = 0;
    		dwNextSleepBoundary = dwTickRate; // Set the next sleep boundary to the next tick.
    		dwTick = timeGetTime(); // Get a snapshot of the current time
    		while (dwNextSleepBoundary <= dwTimeFrame)
    		{
    			// Do callback here
    			dwTimeElapsed = timeGetTime() - dwTick; // Calculate elapsed time
    			INT64 nTemp = dwNextSleepBoundary - dwTimeElapsed; // Calculate amount of time we need to sleep (at least)
    			ASSERT(nTemp <= 0xFFFFFFFF); // Must not exceed the precision of a UINT32
    			if (nTemp > 0) // Safesty check; we don't want to do Sleep(0) or sleep a negative value since Sleep takes a DWORD
    			{
    				nSleepNeeded = (UINT32)nTemp;
    				Sleep(nSleepNeeded); // Sleep for the amount of time necessary before next drawing
    			}
    			dwNextSleepBoundary += dwTickRate; // Increase sleep "boundary" to next time frame tick
    			//cout << Count++ << endl; // Print counter
    		}
    		//DWORD dwTick3 = timeGetTime() - dwTick2; // Calculate amount of time the entire loop took
    		//cout << "Took " << dwTick3 << " ms!\n"; // Print out the time it took
    		//__asm int 3; // Do a debug break
    	}
    }
    I already tried assembly, but it won't let me call other template functions via assembly. The other option I see is to do callback directly through asm, but that may be a little far fetched and error prone.

    Your thoughts?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.
    For information on how to enable C++11 on your compiler, look here.
    よく聞くがいい!私は天才だからね! ^_^

  2. #2
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,796
    Any problem can be solved with an extra level of indirection, as they say. Change your function to take a callback function object. The function object provides a consistent interface for the callback, and you can pass in a function object for a member function callback or for a regular function callback as long as they both maintain the interface.
    My best code is written with the delete key.

  3. #3
    Unregistered User Yarin's Avatar
    Join Date
    Jul 2007
    Posts
    1,534
    Seeing how you have a p before the arg names, wouldn't this work?
    Code:
    template<typename Class, typename Function>
    void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, const void *cvpClass = NULL, const void *cvpFunction = NULL)
    {
       Class pClass = &((Class*)cvpClass);
       Function pFunction = &((Class*)cvpFunction);
    A class that doesn't overload all operators just isn't finished yet. -- SmugCeePlusPlusWeenie
    A year spent in artificial intelligence is enough to make one believe in God. -- Alan J. Perlis

  4. #4
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,182
    Good idea. The only (minor) nuisance I see in this approach is that I have to use template classes, which means I have to define the types of the class when creating the class:
    Code:
    Stuff::Synchronize( Stuff::SecToMilliSec(nCountDownTime), Stuff::SecToMilliSec(60),
    	Stuff::CClassCallback<CCountdownApp*, void (CCountdownApp::*)()>(this, &CCountdownApp::TimerUpdate) );
    I believe this is as good as it's going to get? There's no way to get rid of that type unless I use a typedef.

    Quote Originally Posted by Yarin View Post
    Seeing how you have a p before the arg names, wouldn't this work?
    Code:
    template<typename Class, typename Function>
    void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, const void *cvpClass = NULL, const void *cvpFunction = NULL)
    {
       Class pClass = &((Class*)cvpClass);
       Function pFunction = &((Class*)cvpFunction);
    Yarin, no, 'fraid not.
    The problem lies in when calling the callback:
    Code:
    pFunction();
    (pClass->*pFunction)();
    One of those lines will fail depending on the type of the template.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.
    For information on how to enable C++11 on your compiler, look here.
    よく聞くがいい!私は天才だからね! ^_^

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,892
    You could just use Boost.Function.

    Code:
    template<typename Class>
    void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame,
      Class *pClass = NULL, boost::function<void ()> callback)
    Code:
    if(callback) callback();
    Code:
    Synchronize(..., &someFunc);
    Code:
    Synchronize(..., boost::bind(&myClass::member, myInstance))
    Code:
    Synchronize(..., functionObject());
    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

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,607
    Boost.Function is very nice and definitely the way to go if available.

    For what it's worth, here's what I do in my environment - which doesn't have boost and must compile under VC 6.

    I use polymorphic hierarchy for my callback objects. This allows me to have a base-callback object that doesn't have a templated type applied to it. The downside is that the callback signature is tightly coupled to all objects. Here's an example:
    Code:
    #include <iostream>
    using namespace std;
    
    //------------------------------------------------------------------------------
    
    struct BaseCallback
    {
        virtual void Invoke(int, int) = 0;
    };//BaseCallback
    
    // Functor object for non-member functions
    template <typename Functor_t>
    struct NonMemCallback : public BaseCallback
    {
        Functor_t m_cb;
    
        explicit NonMemCallback(const Functor_t &cb) : m_cb(cb) {}
    
        virtual void Invoke(int a, int b)
        {
            m_cb(a, b);
        }//Invoke
    };//NonMemCallback
    
    // Functor object for member functions
    template <typename Obj_t>
    struct MemCallback : public BaseCallback
    {
        typedef void (Obj_t::*MemberFunc_t)(int, int);
        MemberFunc_t m_cb;
        Obj_t *m_obj;
    
        MemCallback(Obj_t *obj, MemberFunc_t cb) : m_cb(cb), m_obj(obj) {}
    
        virtual void Invoke(int a, int b)
        {
            (m_obj->*m_cb)(a, b);
        }//Invoke
    };//MemFunctor
    
    
    // Helper for NonMemCallback<>
    template<typename Functor_t>
    BaseCallback* CreateCallback(const Functor_t &func)
    {
        return new NonMemCallback<Functor_t>(func);
    }//CreateCallback
    
    // Helper for MemCallback<>
    template <typename Obj_t>
    BaseCallback* CreateCallback(Obj_t *obj, void (Obj_t::*fun)(int, int))
    {
        return new MemCallback<Obj_t>(obj, fun);
    }//CreateCallback
    
    //------------------------------------------------------------------------------
    
    void foo(int a, int b)
    {
        cout << "::foo(" << a << ',' << b << ')' << endl;
    }//foo
    
    struct foo_functor
    {
        void operator()(int a, int b)
        {
            cout << "foo_functor(" << a << ',' << b << ')' << endl;
        }//operator()
    };//foo_function
    
    struct A
    {
        void foo(int a, int b)
        {
            cout << "A::foo(" << a << ',' << b << ')' << endl;
        }//foo
    };//A
    
    //------------------------------------------------------------------------------
    
    int main()
    {
        A a;
    
        BaseCallback *cb1 = CreateCallback(&foo);
        BaseCallback *cb2 = CreateCallback(foo_functor());
        BaseCallback *cb3 = CreateCallback(&a, &A::foo);
    
        cb1->Invoke(1, 1);
        cb2->Invoke(2, 2);
        cb3->Invoke(3, 3);
    
        delete cb1;
        delete cb2;
        delete cb3;
    
        return 0;
    }//main
    So whether you use boost::function or BaseCallback, you can remove the template<> off of Synchronize() and the pClass parameter (assuming you only needed pClass for callback purposes).

    gg

  7. #7
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,600
    a very simple way to do it:

    Code:
    template < typename Function >
    void
    Synchronize( const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, Function pFunction = Function( ) );
    
    you can then supply it with the function or function object (such as boost::function) of your choosing.
    Code:
    if( numeric_limits< byte >::digits != bits_per_byte )
        error( "program requires bits_per_byte-bit bytes" );
    24bbs.cpp

  8. #8
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,182
    Template classes is what I've done for now. The only downside is the syntax:
    Code:
    Stuff::Synchronize( Stuff::SecToMilliSec(nCountDownTime), nTimerTick, 
    	Stuff::CClassCallback<CCountdownApp*, void (CCountdownApp::*)()>(this, &CCountdownApp::TimerUpdate) );
    
    class CCallback
    {
    public:
    	virtual void DoCallback() { }
    };
    
    template<typename Function> class CNonClassCallback: public CCallback
    {
    public:
    	CNonClassCallback(Function pFunction) : m_pFunction(pFunction) { }
    	virtual void DoCallback() { m_pFunction(); }
    private:
    	Function m_pFunction;
    };
    
    template<typename Class, typename Function> class CClassCallback: public CCallback
    {
    public:
    	CClassCallback(Class pClass, Function pFunction) : m_pClass(pClass), m_pFunction(pFunction) { }
    	virtual void DoCallback() { (m_pClass->*m_pFunction)(); }
    private:
    	Class m_pClass;
    	Function m_pFunction;
    };
    
    void Synchronize(const UINT64 dwTimeFrame, const UINT64 dwFramesPerTimeFrame, CCallback& rCallback);
    
    rCallback.DoCallback();
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.
    For information on how to enable C++11 on your compiler, look here.
    よく聞くがいい!私は天才だからね! ^_^

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Input via callbacks or functions
    By zacs7 in forum Game Programming
    Replies: 3
    Last Post: 06-02-2008, 05:12 PM
  2. Callbacks to member functions
    By prog-bman in forum C++ Programming
    Replies: 0
    Last Post: 01-19-2008, 01:48 AM
  3. declaring function pointer types for callbacks
    By Pea in forum C Programming
    Replies: 6
    Last Post: 01-06-2005, 08:46 PM
  4. callbacks within a class?
    By btq in forum C++ Programming
    Replies: 5
    Last Post: 10-02-2002, 05:51 AM
  5. Callbacks
    By pradeepkrao in forum C++ Programming
    Replies: 5
    Last Post: 09-13-2002, 10:30 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21