views:

571

answers:

7

Following code does NOT work, but it expresses well what I wish to do. There is a problem with the template struct container, which I think SHOULD work because it's size is known for any template argument.

class callback {

  public:

  // constructs a callback to a method in the context of a given object
  template<class C>
  callback(C& object, void (C::*method)())
    : ptr.o(object), ptr.m(method) {}

  // calls the method
  void operator()() {
    (&ptr.o ->* ptr.m) ();
  }

  private:

  // container for the pointer to method
  template<class C>
  struct {
    C& o;
    void (C::*m)();
  } ptr;

};

Is there any way to do such a thing? I mean have a non-template class callback which wraps any pointer to method?

Thanks C++ gurus!

Edit:

Please see this:

http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2

+1  A: 

You didn't say what errors you found, but I found that this worked:

template<typename C>
class callback {

  public:

  // constructs a callback to a method in the context of a given object
  callback(C& object, void (C::*method)())
    : ptr(object,method) {}

  // calls the method
  void operator()() {
    (&ptr.o ->* ptr.m) ();
  }

  private:

  // container for the pointer to method
  // template<class C>
  struct Ptr{
  Ptr(C& object, void (C::*method)()): o(object), m(method) {}
    C& o;
    void (C::*m)();
  } ptr;

};

Note that Ptr needs a constructor as it has a reference member.

You could do without struct Ptr and have the raw members.

Tested with VS2008 express.

quamrana
That type won't be polymorphic, which I assume is largely the point of the exercise. Now, if you added a base class with a virtual `operator()`, then you'd be doing well.
Barry Kelly
yeah but now callback is a template... is there any way to do this without callback being a template?
Helltone
Also one has to consider object slicing etc. Best to store the data via a pointer.
Barry Kelly
+2  A: 

You need to use polymorphism. Use an abstract base class with a virtual invocation method (operator() if you please), with a templated descendant that implements the virtual method using the correct type signature.

The way you have it now, the data holding the type is templated, but the code meant to invoke the method and pass the object isn't. That won't work; the template type parameters need to flow through both construction and invocation.

Barry Kelly
good idea, look at my try below.
Helltone
+1  A: 

@Barry Kelly

#include <iostream>

class callback {
  public:
  virtual void operator()() {};
};

template<class C>
class callback_specialization : public callback {
  public:
  callback_specialization(C& object, void (C::*method)())
    : o(object), m(method) {}

  void operator()() {
    (&o ->* m) ();
  }

  private:
  C& o;
  void (C::*m)();

};

class X {
  public:
  void y() { std::cout << "ok\n"; }
};

int main() {
  X x;
  callback c(callback_specialization<X>(x, &X::y));
  c();
  return 0;
}

I tried this, but it does not work (print "ok")... why?

Edit: As Neil Butterworth mentioned, polymorphism works through pointers and references,

  X x;
  callback& c = callback_specialization<X>(x, &X::y);
  c();

Edit: With this code, I get an error:

invalid initialization of non-const reference of type ‘callback&’
from a temporary of type ‘callback_specialization<X>’

Now, I don't understand that error, but if I replace callback& c with const callback& c and virtual void operator()() with virtual void operator()() const, it works.

Helltone
You instantiate a callback object and then call its operator() method, which is defined to do nothing. Why should it do anything?
anon
It's virtual, shouldn't it call the callback_specialization version? At least that's what I wanted to do...
Helltone
quamrana
No it shouldn't - the object c is of type callback, and so you get callbacks operator(). virtual methods only work via a pointer or a reference.
anon
A: 

Improving the OP's answer:

int main() {
  X x;
  callback_specialization<X> c(x, &X::y);
  callback& ref(c);
  c();
  return 0;
}

This prints "ok".

Tested on VS2008 express.

quamrana
+2  A: 

I recently implemented this:

#define UNKOWN_ITEM 0xFFFFFFFF

template <typename TArg>
class DelegateI
{
public:
    virtual void operator()(TArg& a)=0;
    virtual bool equals(DelegateI<TArg>* d)=0;
};


template <class TArg>
class Event
{
public:    
    Event()
    {
    }

    ~Event()
    {
 for (size_t x=0; x<m_vDelegates.size(); x++)
  delete m_vDelegates[x]; 
    }

    void operator()(TArg& a)
    {
     for (size_t x=0; x<m_vDelegates.size(); x++)
     {
      m_vDelegates[x]->operator()(a);
     }
    }

    void operator+=(DelegateI<TArg>* d)
    {
     if (findInfo(d) != UNKOWN_ITEM)
     {
      delete d;
      return;
     }

     m_vDelegates.push_back(d);
    }

    void operator-=(DelegateI<TArg>* d)
    {
     uint32 index = findInfo(d);

     delete d;

     if (index == UNKOWN_ITEM)
      return;

     m_vDelegates.erase(m_vDelegates.begin()+index);
    }

protected:
    int findInfo(DelegateI<TArg>* d)
    {
     for (size_t x=0; x<m_vDelegates.size(); x++)
     {
      if (m_vDelegates[x]->equals(d))
       return (int)x;
     }

     return UNKOWN_ITEM;
    }

private:
    std::vector<DelegateI<TArg>*> m_vDelegates;
};

template <class TObj, typename TArg>
class ObjDelegate : public DelegateI<TArg>
{
public:
    typedef void (TObj::*TFunct)(TArg&); 

    ObjDelegate(TObj* t, TFunct f)
    {
     m_pObj = t;
     m_pFunct = f;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
     ObjDelegate<TObj,TArg> *d = dynamic_cast<ObjDelegate<TObj,TArg>*>(di);

     if (!d)
      return false;

     return ((m_pObj == d->m_pObj) && (m_pFunct == d->m_pFunct));
    }

    virtual void operator()(TArg& a)
    {
     if (m_pObj && m_pFunct)
     {
      (*m_pObj.*m_pFunct)(a);
     }
    }

    TFunct m_pFunct;   // pointer to member function
    TObj* m_pObj;     // pointer to object
};

template <typename TArg>
class FunctDelegate : public DelegateI<TArg>
{
public:
    typedef void (*TFunct)(TArg&); 

    FunctDelegate(TFunct f)
    {
     m_pFunct = f;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
     FunctDelegate<TArg> *d = dynamic_cast<FunctDelegate<TArg>*>(di);

     if (!d)
      return false;

     return (m_pFunct == d->m_pFunct);
    }

    virtual void operator()(TArg& a)
    {
     if (m_pFunct)
     {
      (*m_pFunct)(a);
     }
    }

    TFunct m_pFunct;   // pointer to member function
};


template <typename TArg>
class ProxieDelegate : public DelegateI<TArg>
{
public:
    ProxieDelegate(Event<TArg>* e)
    {
     m_pEvent = e;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
     ProxieDelegate<TArg> *d = dynamic_cast<ProxieDelegate<TArg>*>(di);

     if (!d)
      return false;

     return (m_pEvent == d->m_pEvent);
    }

    virtual void operator()(TArg& a)
    {
     if (m_pEvent)
     {
      (*m_pEvent)(a);
     }
    }

    Event<TArg>* m_pEvent;   // pointer to member function
};


template <class TObj, class TArg>
DelegateI<TArg>* delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArg&))
{
    return new ObjDelegate<TObj, TArg>(pObj, NotifyMethod);
}

template <class TArg>
DelegateI<TArg>* delegate(void (*NotifyMethod)(TArg&))
{
    return new FunctDelegate<TArg>(NotifyMethod);
}

template <class TArg>
DelegateI<TArg>* delegate(Event<TArg>* e)
{
    return new ProxieDelegate<TArg>(e);
}

use it like so:

define:

Event<SomeClass> someEvent;

enlist callbacks:

someEvent += delegate(&someFunction);
someEvent += delegate(classPtr, &class::classFunction);
someEvent += delegate(&someOtherEvent);

trigger:

someEvent(someClassObj);

You can also make your own delegates and overide what they do. I made a couple of others with one being able to make sure the event triggers the function in the gui thread instead of the thread it was called.

Lodle
cool :-) ... could Event not be a template?
Helltone
it needs to be a template to allow only delegates that can handle that type of object to register.
Lodle
Ah, I understood, thanks a lot. But there is some dynamic allocation under the hood, **new** in the delegate functions. Please see my other question here: http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2
Helltone
Yeah i know, thats the only bit i wasnt happy with but it avoids copying objects and the end user doesnt need to know about it. If you have any better ways of doing it with out copys let me know.
Lodle
See my other question... perhaps someone knows a better way :-) Thansk again Lodle for your very good answer.
Helltone
See this: http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2/1284431#1284431
Helltone
+2  A: 

This is a complete working example that does what I think you're trying to do:

#include <iostream>
#include <memory>

// INTERNAL CLASSES

class CallbackSpecBase
{
  public:
    virtual ~CallbackSpecBase() {}
    virtual void operator()() const = 0;
};

template<class C>
class CallbackSpec : public CallbackSpecBase
{
  public:
    CallbackSpec(C& o, void (C::*m)()) : obj(o), method(m) {}
    void operator()() const { (&obj->*method)(); }

  private:
    C& obj;
    void (C::*method)();
};

// PUBLIC API

class Callback
{
  public:
    Callback() {}

    void operator()() { (*spec)(); }

    template<class C>
      void set(C& o, void (C::*m)()) { spec.reset(new CallbackSpec<C>(o, m)); }

  private:
    std::auto_ptr<CallbackSpecBase> spec;
};

// TEST CODE

class Test
{
  public:
    void foo() { std::cout << "Working" << std::endl; }
    void bar() { std::cout << "Like a charm" << std::endl; }
};

int main()
{
  Test t;
  Callback c;
  c.set(t, &Test::foo);
  c();
  c.set(t, &Test::bar);
  c();
}
Ropez
Yeah cool answer, too bad I can't choose two of them to be the best... yet you have some dynamic allocation. See http://stackoverflow.com/questions/1284239/callback-in-c-template-member-2
Helltone