MasterReDWinD said:
- index stores the last magic number on the magic numbers vector m_MagicNumbers.
No, m_MagicNumbers.size() is the length of the "m_MagicNumbers" vector.
Since vector indices start at 0, the length is also the index of the next element that gets appended in the push_back method (C++ speak for appending an element) 3 lines down.
In other words, "index" is the index of the new element that gets created in the storage vectors. That index is stored in the handle as well. New data is created at 'index' (since it didn't exist yet) and the created magic number is also stored at that index in a different vector.
MasterReDWinD said:
Block A seems to give an existing handle a valid index and magic number and default DATA object and then by returning a DATA* for that handle allow the user to set its stored DATA.
The caller makes an empty handle (probably simply making a variable "Handle myhandle;" is sufficient), and passes it into the Acquire function with “HANDLE& handle”. This means that writes into handle within Acquire actually end up in the myhandle variable of the caller. The Acquire function fills the handle with data (index where the data is stored + a magic number to make sure 2 handles with the same index number can be distinguished).
MasterReDWinD said:
Block B updates the index and magic number without default initialising the DATA object.
Block B has the same exit invariants as above, except it re-uses an already existing entry.
In Block A, it creates a new entry with "push_back DATA())". In B, the entry already exists, no need to create it. (Just like malloc doesn't give garantuees about contents of memory, it only makes sure some area is allocated so it won't be used for other purposes. Caller has the responsibility to initialize the acquired space.)
It's like a new house. You get space, but it's literally completely blank, concrete walls and floors. No carpet, no furniture, no decorations. "malloc" gives you the empty house, you have to decorate (initialize) it to your liking.
MasterReDWinD said:
- check if the index and magic number for the handle are 0, if so return null
The null-handle means "unused handle":
bool IsNull(void) const { return ( !m_Handle ); }
If m_handle is 0, !0 is 'true', so the handle "Is Null" (ie "not used" or “not valid").
MasterReDWinD said:
Dereference seems to be the function to call when you want to retrieve the data in a handle (i.e. read).
Not exactly, you get a pointer to the memory, so you can do both read and write.
The checks before it are sanity checks (don't try to access a non-existing handle index, and check that the magic number of the handle matches with the number that was set during Acquire).
The magic number is for catching the following scenario:
- acquire a handle (this will be stored at some index i)
- release that handle but keep it around (makes index i available again as last free entry)
- acquire a second handle (gets index i as well since it's the last free index)
- try to dereference using the first handle.
4 fails, because the magic number of step 1 doesn't match the magic number of step 3.
MasterReDWinD said:
Acquire should be called when you create a handle (but it doesn't have a valid index/magic number yet) and the objective is to assign data to it (write).
Acquire gives you empty space (a fully empty house). It may be an existing (empty) house, or a new one. Caller doesn't care if it's new or not. You get entrance to the house in both cases (a pointer to the data memory), and you can decorate the house as you like (or not, although it's likely you want to decorate, not much fun living in a fully empty house). As a side-effect, you also get a handle (an address of the house).
Dereference takes the address, checks that you are the last person that acquired the house (ie you are the rightful owner as you haven't release it back to the pool of empty houses yet), and then it gives you entrance again so you can read what you stored before, and/or write new things.
MasterReDWinD said:
- if the insert succeeded, Acquire is called on the handle of the newly inserted element. The DATA template parameter in the Texture example is deduced to have the type Texture, therefore Acquire here returns a Texture*.
Success on insertion in a map means the key (the name) didn't exist (it could be inserted as new entry), ie you queried a texture with that name for the first time. So you need a new storage place (a new empty house) for the new texture.
template parameter deduction is correct.
MasterReDWinD said:
- load is called with the name passed into the GetTexture function as its argument.
Yes, so apparently, name is also the filename of the texture.
MasterReDWinD said:
- if loading fails call DeleteTexture on the handle we inserted into NameIndex.
The "m_Textures.Acquire( rc.first->second );" gave us an empty house with entrance rights. Since we failed to find the texture, we have no need for that space, so we return it to the pool for other users.
MasterReDWinD said:
- return the handle inserted into NameIndex.
The "return" path is used both by new entries and by already existing entries. It gives the handle connected to the texture, so other code can get entrance, and (probably) use the texture.
For a new entry that failed loading, the null-handle is associated with the new entry and returned, meaning "there is no data for this name". (And there literally isn't, as the empty house was returned to the handle manager.)
This also means that if you try to query the texture with the name again, you simply again get the null-handle, it doesn't try to load the texture from the file system again.
MasterReDWinD said:
NameIndex stores name -> handle mappings, allowing a user to retrieve a handle based on the name of a Texture. A user would then use the returned handle in a call to Acquire, Dereference or Release.
Acquire is silly. You ask the texture manager for a decorated house (a handle that points to the loaded texture), and then you ask for a new empty house. It's simpler to create a new handle yourself and access the handle manager directly.
Release is probably a bad idea. The handle manager manages a handle globally, it has no knowledge how many copies of handles exist. If one person says "I don't need this space anymore", the handle manager invalidates the space and all handles to that space (that house) become invalid. Obviously, the latter includes the handle in the name->handle map.