开发者

How to pass a Function pointer without exposing class details

I'm creating a library that needs to allow the user to set a callback function. The interface of this library is as below:

// Viewer Class Interface Exposed to user
/////////////////////////////
#include "dataType_1.h"
#include "dataType_2.h"

class Viewer
{
    void SetCallbackFuntion( dataType_1* (Func) (dataType_2* ) );
  private:
    dataType_1* (*CallbackFunction) (dataType_2* );
}

In a typical usage, the user needs to access an object of dataType_3 within the callback. However, this object is only known only to his program, like below.

// User usage
#include "Viewer.h"
#include "dataType_3.h"

// Global Declaration needed
dataType_3* objectDataType3;

dataType_1* aFunction( dataType_2* a)
{
    // An operation on object of type dataType_3
    objectDa开发者_StackOverflow中文版taType3->DoSomething();
}

main()
{
    Viewer* myViewer;
    myViewer->SetCallbackFunction( &aFunction ); 
}

My Question is as follows: How do I avoid using an ugly global variable for objectDataType3 ? (objectDataType3 is part of libraryFoo and all the other objects dataType_1, dataType_2 & Viewer are part of libraryFooBar) Hence I would like them to remain as separate as possible.


Don't use C in C++.

Use an interface to represent the fact you want a notification.
If you want objects of type dataType_3 to be notified of an event that happens in the viewer then just make this type implement the interface then you can register the object directly with the viewer for notification.

// The interface
// Very close to your function pointer definition.
class Listener
{ 
    public:  virtual dataType_1* notify(dataType_2* param) = 0;
};
// Updated viewer to use the interface defineition rather than a pointer.
// Note: In the old days of C when you registered a callback you normally
//       also registered some data that was passed to the callback 
//       (see pthread_create for example)
class Viewer
{
    // Set (or Add) a listener.
    void SetNotifier(Listener* l)       { listener = l; }
    // Now you can just inform all objects that are listening
    // directly via the interface. (remember to check for NULL listener)
    void NotifyList(dataType_2* data)   { if (listener) { listener->notify(data); }

  private:
    Listener*   listener;
};

int main()
{
    dataType_3  objectDataType3;  // must implement the Listener interface

    Viewer      viewer;
    viewer.SetNotifier(&objectDataType3);
}


Use Boost.Function:

class Viewer
{
  void SetCallbackFuntion(boost::function<datatype_1* (dataType_2*)> func);
private:
  boost::function<datatype_1* (dataType_2*)> CallbackFunction;
}

Then use Boost.Bind to pass the member function pointer together with your object as the function.


If you don't want or can't use boost, the typical pattern around callback functions like this is that you can pass a "user data" value (mostly declared as void*) when registering the callback. This value is then passed to the callback function.

The usage then looks like this:

dataType_1* aFunction( dataType_2* a, void* user_ptr )
{
    // Cast user_ptr to datatype_3
    // We know it works because we passed it during set callback
    datatype_3* objectDataType3 = reinterpret_cast<datatype_3*>(user_ptr);

    // An operation on object of type dataType_3
    objectDataType3->DoSomething();
}

main()
{
    Viewer* myViewer;
    dataType_3 objectDataType3;  // No longer needs to be global

    myViewer->SetCallbackFunction( &aFunction, &objectDataType3 );      
}

The implementation on the other side only requires to save the void* along with the function pointer:

class Viewer
{
    void SetCallbackFuntion( dataType_1* (Func) (dataType_2*, void*), void* user_ptr );
private:
    dataType_1* (*CallbackFunction) (dataType_2*, void*);
    void* user_ptr;
}


boost::/std:: function is the solution here. You can bind member functions to them, and in addition functors and lambdas, if you have a lambda compiler.

struct local {
    datatype3* object;
    local(datatype3* ptr)
        : object(ptr) {}
    void operator()() {
        object->func();
    }
};

boost::function<void()> func;
func = local(object);
func(); // calls object->func() by magic.


Something like this is simple to do:

class Callback
{
public:
  virtual operator()()=0;
};

template<class T>
class ClassCallback
{
  T* _classPtr;
  typedef void(T::*fncb)();
  fncb _cbProc;
public:
  ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){}
  virtual operator()(){
    _classPtr->*_cbProc();
  }
};

Your Viewer class would take a callback, and call it using the easy syntax:

class Viewer
{
  void SetCallbackFuntion( Callback* );

  void OnCallCallback(){
    m_cb->operator()();
  }
}

Some other class would register the callback with the viewer by using the ClassCallback template specialization:

// User usage
#include "Viewer.h"
#include "dataType_3.h"


main()
{
  Viewer* myViewer;
  dataType_3 objectDataType3;
  myViewer->SetCallbackFunction( new ClassCallback<dataType_3>(&objectDataType3,&dataType_3::DoSomething));
}


You're asking several questions mixed up in here and this is going to cause you lots of confusion in your answers.

I'm going to focus on your issue with dataType_3.

You state:

I would like to avoid declaring or including dataType_3 in my library as it has huge dependencies.

What you need to do is make an interface class for dataType_3 that gives the operations -- the footprint -- of dataType_3 without defining everything in it. You'll find tips on how to do that in this article (among other places). This will allow you to comfortably include a header that gives the footprint for dataType_3 without bringing in all of its dependencies. (If you've got dependencies in the public API you may have to reuse that trick for all of those as well. This can get tedious, but this is the price of having a poorly-designed API.)

Once you've got that, instead of passing in a function for callback consider having your "callback" instead be a class implementing a known interface. There are several advantages to doing this which you can find in the literature, but for your specific example there's a further advantage. You can inherit that interface complete with an instantiated dataType_3 object in the base class. This means that you only have to #include the dataType_3 interface specification and then use the dataType_3 instance provided for you by the "callback" framework.


If you have the option of forcing some form of constraints on Viewer, I would simply template that, i.e.

template <typename CallBackType>
class Viewer
{
public:
  void SetCallbackFunctor(CallBackType& callback) { _callee = callback; }

  void OnCallback()
  {
    if (_callee) (*_callee)(...);
  }

private:
  // I like references, but you can use pointers
  boost::optional<CallBackType&> _callee;
};

Then in your dataType_3 implement the operator() to do as needed, to use.

int main(void)
{
  dataType_3 objectDataType3;
  // IMHO, I would construct with the objectDataType3, rather than separate method
  // if you did that, you can hold a direct reference rather than pointer or boost::optional!
  Viewer<dataType_3> viewer;
  viewer.SetCallbackFunctor(objectDataType3);
}

No need for other interfaces, void* etc.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜