Clicky

Centrally Distributed from c++ to fixed-income markets, and everything in between

Pointer to Member Function as Delegates

png

Over the past few days, I’ve been trying to find a way to store different PMF (pointer to member functions) in a container as delegates. And I wanted to type erase the PMF so I can store it in a container along with other typed PMF delegates. The only thing common tying the delegates together would be its call signature.

Straight to the use case:

int main() {
    auto delegate_container = DelegateContainer<void()>{};
    auto p1 = PlusOne();
    auto p2 = PlusTwo();

    delegate_container.insert(&PlusOne::plus_one, &p1);
    delegate_container.insert(&PlusTwo::plus_two, &p2);

    std::cout << "p1.val=" << p1.val << std::endl;
    std::cout << "p2.val=" << p2.val << std::endl;

    delegate_container.call();

    std::cout << "p1.val=" << p1.val << std::endl;
    std::cout << "p2.val=" << p2.val << std::endl;
}

You can see here, I’m able to store two different PMF’s in the same container. The idea here is to capture the function pointer of each callable type as void * and invoke it when asked. Implementation below:

#include <iostream>
#include <vector>

struct PlusOne {
    void plus_one() { val += 1; }
    int val = 0;
};

struct PlusTwo {
    void plus_two() { val += 2; }
    int val = 0;
};

struct Delegate {
    void *fp;
    void *obj;
};

template <typename CallSignature>
struct DelegateContainer;

template <typename... Args>
struct DelegateContainer<void(Args...)> {
    using call_signature = void (*)(void * /*obj*/, Args...);

    template <typename T>
    void insert(void (T::*fp)(Args...), T *obj) {
        _delegates.push_back({reinterpret_cast<void *>(fp),
                              reinterpret_cast<void *>(obj)});
    }

    void call(Args &&... args) {
        for (auto &d : _delegates) {
            (reinterpret_cast<call_signature>(d.fp))
                (d.obj, std::forward(args)...);
        }
    }

    std::vector<Delegate> _delegates;
};

So the magic here is how we reinterpreted cast both the PMF and object pointer to void *, and then stored the call structure in a typedef. This Delegate model can also be extended for functors, lambdas (capturing and non-capturing), and ofc, standalone function pointers. Just some tinkering will be needed. Happy coding.

The idea came from the Type Erasure CppCon talk by Arthur O’Dwyer, full source code can be found here, useful blog referenced here.