Reputation: 400
Se original question in the bottom.
I think I understand what you guys are saying now – that because the internal structure of the member function pointer is compiler/machine specific it is really not possible to do that I am trying to. So even though it works when I test it – I have no guarantee that it will on other compilers/machines.
Is there another way to go about what I want then?
I have a template class and a base template class for that class, and I have a delegate class which contains a std::map of all the events the delegate class should invoke, when invoked.
The reason i need a map is, both to insure that the same member function (event pointing to member function) is not adde more that once, and to make it possible to remove events from the map using the object and member function originally used when instantiating the event object.
template <class T_arg1> struct EventBase1
{
public:
bool operator < (const EventBase1 &event1) const { return _key < event1._key; };
virtual void operator()(T_arg1 t1) const {};
std::pair<intptr_t, intptr_t> _key;
};
template <class T, class T_arg1> struct Event1: public EventBase1<T_arg1>
{
template <class T_arg1> friend class Delegate1;
typedef typename void (T::*Method)(T_arg1);
private:
Method _method;
T* _object;
public:
Event1(T* object, Method method): _object(object), _method(method)
{
_key = std::pair<intptr_t, intptr_t>(reinterpret_cast<intptr_t>(object), reinterpret_cast<intptr_t>( reinterpret_cast<void*&>(method)));
};
virtual void operator()(T_arg1 t1) const {
(_object->*_method)(t1);
};
};
template <class T_arg1> class Delegate1
{
public:
typedef typename EventBase1<T_arg1> T_event;
void operator += (T_event* observer)
{
assert(observer);
_observers[*observer] = observer;
};
void operator -= (const T_event &observer)
{
std::map<T_event, T_event*>::iterator i = _observers.find(observer);
if(i != _observers.end()) {
delete i->second;
_observers.erase(i);
}
};
void operator()(T_arg1 t1)
{
for(std::map<T_event, T_event*>::iterator i = _observers.begin(); i != _observers.end(); i++) {
(*(i->second))(t1);
}
};
private:
std::map<T_event, T_event*> _observers;
};
Original question:
I am storing function pointers in a std::map
, and I am generating my key for the map as follows: std::pair<int, int>( (int)((int*)object), (int)(static_cast<const void*>(&method)) )
.
method
is a function (method) pointer, and object
is a pointer to the object of the method.
It works, however I have a sneaky suspicion that the way I get the second part of the key isn’t entirely correct.
I have never fully understood function pointers, but I guess that I am getting the address of the pointer and not the address of the function, and the compiler won’t let me do like this ((int)(static_cast<const void*>(method)))
.
So my question is - how do I get a unique key from the function pointer which will the same if I later get a get the key from another function pointer pointing the same method?
Thanks in advance, Martin
Upvotes: 2
Views: 2610
Reputation: 49976
You should be fine writing:
reinterpret_cast<uintptr_t>(method)
[edit] - for pointers to methods you will have to stay with c-style casts, as is explained in this SO: reinterpret_cast to void* not working with function pointers
&method is as you suspec pointer to pointer, so its not what you want
uintptr_t is better over int because it is guaranteed to be the same size as a pointer
Upvotes: 2
Reputation: 153909
The second isn't legal: formally, you cannot convert a pointer to a
function to a pointer to data (and a void*
is a pointer to data).
Also, you're not guaranteed to be able to convert any pointer into an
int
; the conversion is only legal if int
is at least as large as a
pointer (which means that your code should fail to compile on most 64
bit systems).
There are several ways around this. First, on most (all?) modern
machines, poitners to functions and pointers to data do have the same
size and representation. (Posix requires it, in fact. Even if it
wasn't the case on the first Unix machines I used.) If we assume this,
you can guarantee a large enough integral type by using intptr_t
, and
"trick" the compiler using an additional level of indirection:
std::pair<intptr_t, intptr_t>(
reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( object ) ),
reinterpret_cast<intptr_t>( reinterpret_cast<void*&>( method ) ) )
(This supposes that object
and method
are your pointers to the
object and the function.)
Note that this does not work for pointers to member functions. Pointer to member functions are completely different beasts, and I don't think that there is any effective way to use them as a key in this way (since they may, and often do, contain padding, or unset fields, in certain cases).
For that matter, formally, this isn't really guaranteed even for normal pointers. The standard allows pointers to have don't care bits, or for several different pointer representations to compare equal. In practice, however, it is safe on most (all?) modern machines.
Upvotes: 4