Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic function pointer in C++11

I am currently in the process of writing a method execution queue in C++x0. I have implemented and verified the basic queue mechanism but want to amend it with an option to have push() automatically remove all previous calls to a specific method:

queue.push(this, &Obj::foo, 1);
queue.push(this, &Obj::foo, 2);
queue.push(this, &Obj::foo, 3);

should be the same as merely calling

queue.push(this, &Obj::foo, 3);

My code thus far looks like this:

Queue.h:

#pragma once

#include <functional>
#include <vector>

using std::vector;
using std::function;
using std::bind;

class Queue
{

    private:
        struct functioncall {
            std::function<void()> call;
        };

        vector<functioncall> queue;

    public:
        Queue();
        ~Queue();

        template<typename T, typename F, typename... Args>
        int push(T, F , Args... args);

        int pop();
        bool empty();
        size_t size();
};


template<typename T, typename F, typename... Args>
int Queue::push(T instance, F func, Args... args)
{
    functioncall newelem = { bind(func, instance, args...) };
    queue.push_back(newelem);
    return queue.size();
}

Queue.cpp:

#include "Queue.h"

Queue::Queue() : queue()
{
}

Queue::~Queue()
{
    delete &queue;
}

int Queue::pop()
{
    if(!queue.empty())
    {
        queue.front().call();
        queue.erase(queue.begin());
        return queue.size();
    }
    return 0;
}

bool Queue::empty()
{
    return queue.empty();
}

size_t Queue::size()
{
    return queue.size();
}

I have already prepared the vector queue to take a struct in wich I want to not only save the result of std::bind but also the pointer to the method being called so I can look for that pointer and remove the old entries.

The issue is that the functions passed to push() can take an arbitrary amount of arguments. Is there a generic pointer type (it doesn't have to be executable, just be the same when I repeatedly push the same function to the queue) that can do that?

like image 686
Nils Werner Avatar asked Nov 29 '12 09:11

Nils Werner


2 Answers

Per 5.2.10p10, you can cast a pointer to member function T::*(A1, A2, ...) to another pointer to member function type U::*(B1, ...) and back with no loss of information; std::less can compare pointers to member functions so by casting to a dummy pointer-to-member type void (Impl::*)() you can compare pointer to member functions with the same signature.

However, it is not guaranteed that pointer to member functions with different signatures will compare different when cast to the same pointer to member type, so you will need to encode the signature in your comparable type. typeid will work here:

auto key = std::make_pair(&typeid(F), reinterpret_cast<void (Queue::*)()>(func));

This assumes that F is indeed a pointer to member function; if the user attempts to pass some other callable object then this will break.

like image 193
ecatmur Avatar answered Sep 28 '22 12:09

ecatmur


std::function::target<>() can be used to check wrapped function type:

template <class F> bool is_same_call(const functionalcall& prevCall, F newCall)
{
   const F* pf = prevCall.call.target<F>();
   return pf ? *pf == newCall : false;
}

Note that std::function::target() will return nullptr if function wrapped with std::function object has type different from F.

like image 31
Rost Avatar answered Sep 28 '22 10:09

Rost