I wrote a class to store either a function pointer or a member function pointer (not both at a time). When I store the member function pointer, I store an object pointer too (the receiver).
The problem is: I don't know in advance neither the object's type nor the function signature, so I use a template typename. For arguments I use variadic templates.
I have a code similar to this:
template <typename... Args>
class A
{
public:
template <typename Object, typename Ret>
A (Object *receiver, Ret (Object::*mem)(Args...)); // store the member function pointer
template <typename Ret>
A (Ret (*function)(Args...)); // store the global function pointer
void operator()(Args... args) const; // to call the function pointer
// ... more public stuff ...
private:
class UndefinedClass;
typedef void (Undefined::*MemFunPtrType)(Args...)
union
{
struct
{
MemFunPtrType memFunPtr; // the stored member function
void *receiverPtr; // the receiver object
void (*call)(void *, MemFunPtrType, Args...);
} member;
void (*funPtr)(Args...); // the stored global function
};
enum class Type {Function, Member} type;
};
Since a need just one, global function or member function, I put everything inside union
.
In the constructor I cast the member function mem
to a void (Undefined::*)(Args...)
and store it. I took this trick from implementation of std::function.
Using a lambda with no capture, I cast again to the original type, both the object and the function and I call them:
typedef Ret (Object::*OriginalMemberType)(Args...);
// ...
member.call = [] (void *receiver, MemFunPtrType memFun, Args... args)
{
(static_cast<Object*>(receiver)->*reinterpret_cast<OriginalMemberType>(memFun))(args...);
}
I store this lambda in the call
function pointer to call it inside operator()
. With an if-else sentence I compare the type data, and I call the correct pointer.
I know this is a little annoying, but it works. I have a lot of tests and all of them pass. But I am worried about the strict aliasing thing. I do a lot of pointer casting and I am not sure if this falls unto undefined behaviour.
Question: Is it allowed to cast from Ret (Object::*)(Args...)
to void (UndefinedClass::*)(Args...)
? (Ret, Object and Args are template arguments)
Note I never call the object without cast again to the original type. It is for storing only.
Question:: Must I compile with -fno-strict-aliasing
? If I must, and sice this is a template class, should I put -fno-strict-aliasing
in every project using this class? Maybe I could use an array of char
of length sizeof(void (UndefinedClass::*)(Args...))
or something like that instead.
Notes:
- I use gcc 4.8.1 in Archlinux. I use C++11.
- I did not use std::function here just for the thing of using placeholders with std::bind (I don't know the number of placeholders required). And this is a good practice and learning after all. If you know how to do this with std::function, answer is very welcome.
- Actually, I use this class in a std::vector for calling many "callbacks" of the same signature (a signal/slot kind framework).
Thank you so much. I will clarify any aspect of this mess, if is required :)