Calling C++ class methods via a function pointer

前端 未结 10 826
醉酒成梦
醉酒成梦 2020-11-22 06:47

How do I obtain a function pointer for a class member function, and later call that member function with a specific object? I’d like to write:

class Dog : A         


        
相关标签:
10条回答
  • 2020-11-22 07:17

    How do I obtain a function pointer for a class member function, and later call that member function with a specific object?

    It's easiest to start with a typedef. For a member function, you add the classname in the type declaration:

    typedef void(Dog::*BarkFunction)(void);
    

    Then to invoke the method, you use the ->* operator:

    (pDog->*pBark)();
    

    Also, if possible, I’d like to invoke the constructor via a pointer as well. Is this possible, and if so, what is the preferred way to do this?

    I don't believe you can work with constructors like this - ctors and dtors are special. The normal way to achieve that sort of thing would be using a factory method, which is basically just a static function that calls the constructor for you. See the code below for an example.

    I have modified your code to do basically what you describe. There's some caveats below.

    #include <iostream>
    
    class Animal
    {
    public:
    
        typedef Animal*(*NewAnimalFunction)(void);
    
        virtual void makeNoise()
        {
            std::cout << "M00f!" << std::endl;
        }
    };
    
    class Dog : public Animal
    {
    public:
    
        typedef void(Dog::*BarkFunction)(void);
    
        typedef Dog*(*NewDogFunction)(void);
    
        Dog () {}
    
        static Dog* newDog()
        {
            return new Dog;
        }
    
        virtual void makeNoise ()
        {
            std::cout << "Woof!" << std::endl;
        }
    };
    
    int main(int argc, char* argv[])
    {
        // Call member function via method pointer
        Dog* pDog = new Dog ();
        Dog::BarkFunction pBark = &Dog::makeNoise;
    
        (pDog->*pBark)();
    
        // Construct instance via factory method
        Dog::NewDogFunction pNew = &Dog::newDog;
    
        Animal* pAnimal = (*pNew)();
    
        pAnimal->makeNoise();
    
        return 0;
    }
    

    Now although you can normally use a Dog* in the place of an Animal* thanks to the magic of polymorphism, the type of a function pointer does not follow the lookup rules of class hierarchy. So an Animal method pointer is not compatible with a Dog method pointer, in other words you can't assign a Dog* (*)() to a variable of type Animal* (*)().

    The static newDog method is a simple example of a factory, which simply creates and returns new instances. Being a static function, it has a regular typedef (with no class qualifier).

    Having answered the above, I do wonder if there's not a better way of achieving what you need. There's a few specific scenarios where you would do this sort of thing, but you might find there's other patterns that work better for your problem. If you describe in more general terms what you are trying to achieve, the hive-mind may prove even more useful!

    Related to the above, you will no doubt find the Boost bind library and other related modules very useful.

    0 讨论(0)
  • 2020-11-22 07:17

    I did this with std::function and std::bind..

    I wrote this EventManager class that stores a vector of handlers in an unordered_map that maps event types (which are just const unsigned int, I have a big namespace-scoped enum of them) to a vector of handlers for that event type.

    In my EventManagerTests class, I set up an event handler, like this:

    auto delegate = std::bind(&EventManagerTests::OnKeyDown, this, std::placeholders::_1);
    event_manager.AddEventListener(kEventKeyDown, delegate);
    

    Here's the AddEventListener function:

    std::vector<EventHandler>::iterator EventManager::AddEventListener(EventType _event_type, EventHandler _handler)
    {
        if (listeners_.count(_event_type) == 0) 
        {
            listeners_.emplace(_event_type, new std::vector<EventHandler>());
        }
        std::vector<EventHandler>::iterator it = listeners_[_event_type]->end();
        listeners_[_event_type]->push_back(_handler);       
        return it;
    }
    

    Here's the EventHandler type definition:

    typedef std::function<void(Event *)> EventHandler;
    

    Then back in EventManagerTests::RaiseEvent, I do this:

    Engine::KeyDownEvent event(39);
    event_manager.RaiseEvent(1, (Engine::Event*) & event);
    

    Here's the code for EventManager::RaiseEvent:

    void EventManager::RaiseEvent(EventType _event_type, Event * _event)
    {
        if (listeners_.count(_event_type) > 0)
        {
            std::vector<EventHandler> * vec = listeners_[_event_type];
            std::for_each(
                begin(*vec), 
                end(*vec), 
                [_event](EventHandler handler) mutable 
                {
                    (handler)(_event);
                }
            );
        }
    }
    

    This works. I get the call in EventManagerTests::OnKeyDown. I have to delete the vectors come clean up time, but once I do that there are no leaks. Raising an event takes about 5 microseconds on my computer, which is circa 2008. Not exactly super fast, but. Fair enough as long as I know that and I don't use it in ultra hot code.

    I'd like to speed it up by rolling my own std::function and std::bind, and maybe using an array of arrays rather than an unordered_map of vectors, but I haven't quite figured out how to store a member function pointer and call it from code that knows nothing about the class being called. Eyelash's answer looks Very Interesting..

    0 讨论(0)
  • 2020-11-22 07:23

    Reason why you cannot use function pointers to call member functions is that ordinary function pointers are usually just the memory address of the function.

    To call a member function, you need to know two things:

    • Which member function to call
    • Which instance should be used (whose member function)

    Ordinary function pointers cannot store both. C++ member function pointers are used to store a), which is why you need to specify the instance explicitly when calling a member function pointer.

    0 讨论(0)
  • 2020-11-22 07:26

    I came here to learn how to create a function pointer (not a method pointer) from a method but none of the answers here provide a solution. Here is what I came up with:

    template <class T> struct MethodHelper;
    template <class C, class Ret, class... Args> struct MethodHelper<Ret (C::*)(Args...)> {
        using T = Ret (C::*)(Args...);
        template <T m> static Ret call(C* object, Args... args) {
            return (object->*m)(args...);
        }
    };
    
    #define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>
    

    So for your example you would now do:

    Dog dog;
    using BarkFunction = void (*)(Dog*);
    BarkFunction bark = METHOD_FP(&Dog::bark);
    (*bark)(&dog); // or simply bark(&dog)
    

    Edit:
    Using C++17, there is an even better solution:

    template <auto m> struct MethodHelper;
    template <class C, class Ret, class... Args, Ret (C::*m)(Args...)> struct MethodHelper<m> {
        static Ret call(C* object, Args... args) {
            return (object->*m)(args...);
        }
    };
    

    which can be used directly without the macro:

    Dog dog;
    using BarkFunction = void (*)(Dog*);
    BarkFunction bark = MethodHelper<&Dog::bark>::call;
    (*bark)(&dog); // or simply bark(&dog)
    

    For methods with modifiers like const you might need some more specializations like:

    template <class C, class Ret, class... Args, Ret (C::*m)(Args...) const> struct MethodHelper<m> {
        static Ret call(const C* object, Args... args) {
            return (object->*m)(args...);
        }
    };
    
    0 讨论(0)
提交回复
热议问题