Is it safe to speed up a function pointer function?

I have base classes Object

andEvent

class Object
{
//...
};

class Event
{
};

      

And the typedef for the function pointer

typedef void (Object::*PF) (Event*);

      

And an unbound class that stores two pointers

class Dispatcher
{
public:
    Dispatcher (Object* obj, PF pf) : _obj (obj), _pf (pf)

    void call (Event* event)
    {
        _obj->*pf (event);
    }

    Object* _obj;
    PF _pf;
};

      

Then I have a specific object and a specific event

class ConcreteEvent : public Event
{
};

class ConcreteObject : public Object
{
public:
   void handle (ConcreteEvent* event)
   {
      // Do something specific for ConcreteEvent
   }
};

      

And then call it like this:

ConcreteObject* obj = new ConcreteObject();
Dispatcher dispatcher (obj, static_cast<PF>(&ConcreteObject::handle));

ConcreteEvent* event = new ConcreteEvent ();
dispatcher.call (event);

      

I guarantee that the dispatcher will always be called with the correct event, i.e. I will not call the dispatcher and pass it ConcreteEvent

when the function pointer it encapsulates actually acceptsSomeOtherConcreteEvent

Question: Is this guaranteed to work? Of course it works fine in gcc 4.7 on both Linux and mingw.

+3


source to share


2 answers


From the C ++ 11 standard, section 4.11.2:

A pointer of type "pointer to member B of type cv T", where B is a class type, can be converted to a prvalue of type "pointer to member D of type cv T", where D is a derived class (section 10) B. If B is not available ( 11), an ambiguous (10.2) or virtual (10.1) base class D, or a virtual base class D base class, the program that requires such a transformation is poorly formed. The result of the conversion is to the same member as the pointer to the member before the conversion, but it refers to the base of the member of the class as if it were a member of a derived class.

So it should be safe.



Edit: so if you actually meant downcasting:, which is also legal, as per C ++ 11 5.2.9.12:

A value of type "pointer to member D of type cv1 T" can be converted to prvalue of type "pointer to member B" of type cv2 T, where B is the base class (clause 10) D if valid standard conversion from "pointer to member B of type T "to" a pointer to a member D of type T "exists (4.11) and cv2 is the same cv-qualification as or a higher cv-qualification than cv1. 69

+4


source


I would argue that this is not secure for two reasons.

First, a pointer to member functions can only be safely propagated downward (because you, the class Derived

, have made sure to inherit the function from the base class, while the opposite is not true).

class Base {
public:
   void foo();
}; // class Base

class Derived: public Base {
public:
   void bar();
};

using PBase = void (Base::*)();
using PDerived = void (Derived::*)();

int main() {
   PBase pb = &Base::foo;
   PDerived pd = &Derived::bar;

   pb = pd; // error: cannot convert 'PDerived {aka void (Derived::*)()}'
            //                    to 'PBase {aka void (Base::*)()}' in assignment
   pd = pb;
}

      

(as seen here )

Second, you cannot change the type of the argument in this way. To illustrate the problem, use ConcreteObject: public virtual Object

and you will see that it does not work as you hoped.




Now, this does not mean that what you would like to do is not possible, it just takes a little more.

Ideally, instead of using the member functions you just fix the signature to take both Object

and Event

then, if necessary, enable the use of hand-cast:

using Function = std::function<void(Object*,Event*)>;

void handle(Object* o, Event* e) {
    ConcreteObject* co = dynamic_cast<ConcreteObject*>(o);
    ConcreteEvent* ce = dynamic_cast<ConcreteEvent*>(e);

    if (co and ce) { co->handle(ce); }
}

      

Or whatever you do, it is more convenient for you.

Note: use std::function

for compatibility with lambdas / functors.

+1


source







All Articles