Skip to main content
GameDev.net gamedev.net
🔒 Locked

Function pointer as unique [system] id

Started by Strewya Jun 12, 2013 at 6:42 PM 11 replies 3.1k views
Original Post
Strewya
Strewya
Hi all.

I have a question that i honestly forgot why i wanted to ask biggrin.png
Say you have an ECS framework which has Systems that do work on Components.

Is it smart or even safe to make the ID of these systems be the address of their Update method?
Something like this (no good coding practices applied for simplicity):

class System
{
public:
   uint id;
   list entities;
   void Update(float dt) = 0;
};

class SpecificSystem : public System
{
public:
   SpecificSystem() { id = (uint)Update; //or &Update, not sure for the syntax
   void Update(float dt) { /* some specific update code*/ }
};

typedef void(*Logic)(float dt, list entities);

class CustomSystem : public System
{
public:
   CustomSystem(Logic fn) { id = (uint)fn; }
   void Update(float dt) { fn(dt, entities); }
};
devstropo.blogspot.com - Random stuff about my gamedev hobby
frob
frob

No, it is not.

Here are several reasons:

Just because it is pure virtual in the base does not mean every class will implement their own; it only means it must be implemented at some point in the hierarchy. You can make a subclass that implements it, and that subclass can be used as the parent to many other objects.

The compiler can do fun things like merge duplicate functions. Two objects that have different functions can still end up having the same function address.

The location can change between builds. You cannot rely on it for any persistent or transferable use.

Those are the reasons that immediately jump to mind. I'm sure a creative person could come up with many more reasons why it is a bad idea.

SiCrane
SiCrane
The first immediate problem is that a pointer to a member function isn't convertible to an int. Then there's the fact that you can't take the address of a specific implementation of a virtual function. A pointer to a virtual member function may not even contain the function address. It can be just an index into the virtual function table, meaning that even if you did some horrible hacks to get at the contents of the memory of a pointer to member function, you'd end up with the same data if you tried getting the address of different class implementations.
Strewya
Strewya
Thanks.
I'd like to just ask some questions about this, i don't think i'll actually use this biggrin.png

The idea was to use that ID only for the purpose of distinguishing different systems in runtime (so no persistance), where two instances of the same systems should have the same ID (not that you'd have two instances of a system, but...)

But i didn't know you can't take the address of a virtual functions implementation. Does that mean you can't pass the Update method of a derived class around as a pointer to member function neither?
devstropo.blogspot.com - Random stuff about my gamedev hobby
SiCrane
SiCrane

If you create a pointer to a member function for a virtual function what you'll get is a member function pointer that always calls the most derived function implementation for that virtual function.

frob
frob

Expanding on that a bit, the whole pointer-to-member-function (PMF) thing in c++ is fun.

A PMF is very different than a regular pointer.

Exactly how they work is compiler specific.

In both Microsoft and GCC implementations, a PMF is actually a larger data structure. Both systems are slightly different, but they basically contain an implementation-defined collection of pointers and offsets.

The exact magic that makes them work is entirely compiler-specific, and can get extremely complicated in practice. The GCC documentation actually says "If you are using PMFs in an inner loop, you should really reconsider that decision."

Thanks to single inheritance, casting a PMF can change its value. And thanks to multiple inheritance, casting one can even change its size.

In your code sample you have a regular pointer, not a PMF. The typedef for the pointer would need to be SpecificSystem::* instead.

SiCrane
SiCrane

Thanks to single inheritance, casting a PMF can change its value.

Are you sure about that? I can't think of any situation where single inheritance would require a change in value when casting.

Fun fact: MSVC actually has three different kinds of pointers to member functions: one for classes with no inheritance or only single inheritance, one for classes with multiple inheritance and one for classes with virtual inheritance.
L. Spiro
L. Spiro

Also, this isn’t what you asked, but it is something you should know anyway.

Do not cast pointers to “uint”, “unsigned int”, “unsigned long”, “int”, “long”, “__int32”, “u32”, “DWORD”, or any variation thereof.
Pointers are not 32 bits.
Pointers are not 64 bits.

Pointers are 32 bits on some architectures, 64 bits on others, and 16 bits in still others, so it doesn’t make sense to cast them to “uint” or any other type that is not always exactly the same size as a pointer.
The Windows® SDK exposes a type called UINT_PTR. Other platforms define uintptr_t (in ).
They exist for a reason. Never cast pointers to “uint”.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
Khatharr
Khatharr
The GCC documentation actually says "If you are using PMFs in an inner loop, you should really reconsider that decision."

I love GNU documentation, lol.

It's true, though. If you walk through it disassembled it can get pretty wild, and it's not always the same flavor of wildness, even in the same module.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Strewya
Strewya

Also, this isn’t what you asked, but it is something you should know anyway.

Do not cast pointers to “uint”, “unsigned int”, “unsigned long”, “int”, “long”, “__int32”, “u32”, “DWORD”, or any variation thereof.
Pointers are not 32 bits.
Pointers are not 64 bits.

Pointers are 32 bits on some architectures, 64 bits on others, and 16 bits in still others, so it doesn’t make sense to cast them to “uint” or any other type that is not always exactly the same size as a pointer.
The Windows® SDK exposes a type called UINT_PTR. Other platforms define uintptr_t (in ).
They exist for a reason. Never cast pointers to “uint”.

L. Spiro

Does this apply to all pointers, or just function pointers?

I thought that the pointer size is always 4 bytes on 32bit systems (8bytes on 64bit, all depending on the target build settings), but what you're saying is that this is wrong?

devstropo.blogspot.com - Random stuff about my gamedev hobby
L. Spiro
L. Spiro

It applies to all pointers.

And it is not about the size of the pointer, it is about the size of “uint” and friends. There are 4 64-bit data models.

The size of “uint” on 64-bit target platforms will vary depending on the compiler. It will be 32 bits in any version of Microsoft® Visual Studio®, which means you will clearly be losing data by casting any pointer to “uint”.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
Paradigm Shifter
Paradigm Shifter

Nah, it doesn't apply to pointer to member functions, which can be larger than regular pointers, as others have said.

EDIT: And there is some stuff where you can use 32 bit pointers on a 64 bit system, but that is a bad idea in the long run usually. Although on the PS3 we used 32 bit pointers all the time since they are mapped into a 4GB virtual address space smaller than the available physical memory anyway, they are just extended into the correct address space (trivially by 0-extending the high 32 bits) by the compiler. Probably best to forget I ever said that now 4GB+ of physical memory is becoming the norm...

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.