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

Casting from const to non-const

Started by Kest Aug 26, 2005 at 11:40 PM 26 replies 2.9k views
Original Post
Kest
Kest
I've just recently realized it's possible to cast to override a const trait on a variable type. Seems a little too easy in fact..? Anyway, here's the situation I'm in. My characters allow const access to their inventory. The inventory menu uses this access to set up lists and such for the interface. But when it comes time to signal the character to make inventory changes, I need to send the equip/unequip item pointers to the character task function. The funny thing is that these pointers are const, and the character obviously can't change equipped status on them. So they need a non-const pointer to the item. From my knowledge, there are two things I could do. One is to simply cast it to non-const. But damn that seems like a backwards thing to do. The other would be to search for the item in their inventory locations and find the same item - obtaining a normal non-const pointer. That seems pretty backwards too! Any suggestions?
Lenox
Lenox
You might be better off to use cast it to a non-const. It'd probably take up less time to just cast it then to do a search for the item in the inventory then obtain the non-const pointer. The difference is probably unnoticable to the user.
Kest
Kest
Speed isn't that much of a concern. The game is single player and the menu will pause the action. But saftey isn't really a concern either, considering a debug-only inventory-search can be implimented even on top of the cast.

I guess there's no harm in casting it. Does this situation not come up often? The only way around it would be to allow non-const inventory access.

Thanks for your time :)
Saruman
Saruman
99% of the time if you are casting away const that you declared in your own application than you should rethink the design as this is a red flag. The reason for casting away const is not so you can throw out your own const contracts, but to be compatible with other libraries that don't follow proper constness and/or C libraries.
Saruman
Saruman
Quote:
Original post by Anonymous Poster
Quote:
Original post by Saruman
99% of the time if you are casting away const that you declared in your own application than you should rethink the design as this is a red flag. The reason for casting away const is not so you can throw out your own const contracts, but to be compatible with other libraries that don't follow proper constness and/or C libraries.


C has had const since C89. Only libraries written for K&R and older versions of C have an excuse for not following proper constness.

Oh I realize that C has had const in the standard, but there are many C programmers and libraries out there that are not following proper constness, I have actually seen it a lot.
Kest
Kest
Quote:
Original post by Anonymous Poster
It's usually an indication that something is const that shouldn't have been const.

I don't think that's true in my case. Equipment is pretty dynamic when characters are using them. They track ammunition counts, heat levels, and a few other dynamic states. So characters need non-const equipment. And the inventory doesn't modify the items. The only situation where they need non-const items is when telling the character what items to equip - which isn't needing non-const access, but the character needs to equip the items. It would have to make a const to non-const transition somewhere along the line to be equipped.

Quote:
Original post by Saruman
99% of the time if you are casting away const that you declared in your own application than you should rethink the design as this is a red flag. The reason for casting away const is not so you can throw out your own const contracts, but to be compatible with other libraries that don't follow proper constness and/or C libraries.

I'm not sure you understand what I'm saying. I'm not throwing out const. Imagine a simple list that gives only const access to its non-const nodes. What if the list could perform a task, such as 'remove', which obviously needs non-const access. The item could even have links to neighboring nodes and a parent link to the list itself - making it obvious that the list owns it. How do you signal to the list to remove an item which you have a const pointer to?

The only possible items the inventory can contain are the character's items. I could easily give all items unique IDs, pass that to the character from the inventory menu and have them search their inventories for it. Likewise, I can use the const pointer just like an ID and search for that. But is there really a point? If I know it's there and I know I have a non-const version of it, it seems pretty silly.

Don't get me wrong, I'm all open to suggestions for rethinking the design.
desertcube
desertcube
Quote:
Original post by Kest
Imagine a simple list that gives only const access to its non-const nodes. What if the list could perform a task, such as 'remove', which obviously needs non-const access. The item could even have links to neighboring nodes and a parent link to the list itself - making it obvious that the list owns it. How do you signal to the list to remove an item which you have a const pointer to?


Why does the list only provide const access? You can overload it to provide both a const and non-const pointer. Better yet, use std::list!

Basically, when you declare your function to accept a const object, you're indicating to the rest of your code that this function will not modify the object, i.e. the object will be in the exact same way after calling the function as it was before it entered the function. If you cannot make this promise, then you need to provide a non-const pointer.

Another solution might be to provide the inventory menu an index of the item, and then it can call various functinos form the character using that index.

HTH
Kest
Kest
Quote:
Original post by desertcube
Why does the list only provide const access? You can overload it to provide both a const and non-const pointer. Better yet, use std::list!

Because only the list (or parenting object) is allowed to modify nodes. It was just an example. You can pretend the class that gives only const access to nodes uses std::list internally if that makes any difference.

Quote:
Basically, when you declare your function to accept a const object, you're indicating to the rest of your code that this function will not modify the object, i.e. the object will be in the exact same way after calling the function as it was before it entered the function. If you cannot make this promise, then you need to provide a non-const pointer.

That's a pretty good point. It looks to me like the only solution would be to give inventory mode non-const access. Lesser of evils? Although less misleading, it's much less safe.

Quote:
Another solution might be to provide the inventory menu an index of the item, and then it can call various functinos form the character using that index.

Inventory mode needs to know everything about an item, for display to the user. That would be quite a hefty interface. Why should characters have an interface for equipment (other than adding, removing, and equipping) when equipment already has such a thing? Like I said before, I could use an index, an ID, or anything to identify the item, but it still accomplishes the same task. I suppose it would be better for clarity, even though a function named Equip() is obviously going to equip the item.
Kest
Kest
Quote:
Original post by Anonymous Poster
Imagine you ask if you can borrow my car to go to the grocery story. Imagine I say "Sure" but don't give you the keys.

Finally someone who understands. Now imagine I give you back the car. Look, you still have the keys! But the car is still const.

Quote:
Do you know that?

const Equipment *e = c1.getFirstInInventory();
c2.equip(e);

Items have link states that make it obvious where they are (they can only be in one place at a time), and characters verify this information (excessively), so you wouldn't be able to do that. Although I would become very worried about my judgement if I actually threw the error with your example code. Nothing but the inventory menu accesses character inventory data externally, and it only deals with one character at a time.

Quote:
By the way, how does the menu access the inventory?

Iterator.

Quote:
Also, for what it's worth, it almost sounds like the menu should be doing the equipping, not the character class.

AI characters do not have menues. Equipment swapping can also take place through animation events (like transferring items from sheaths to hands) and other dynamic game situations also deal with it. But all of it happens internally within the character class. The public equip function actually calls private methods to do the dirty work depending on what the item is and where it's going to need to go.

[Edited by - Kest on August 27, 2005 5:49:44 AM]
desertcube
desertcube
Quote:
Original post by Anonymous Poster
Imagine you ask if you can borrow my car to go to the grocery story. Imagine I say "Sure" but don't give you the keys.

That actually made me laugh! But it does kinda make a point.

Anyways, if inventory needs to know about the items and modify them, the only option is to provide it with a non-const pointer. That doesn't mean that you have to forget about const corectness for that class. You can still have methods which are const, as it can still help you find silly errors. Should GetCoolItem really call AddCoolItem if it doesn't exist or should it return NULL? The answer will determin if it's const or not.
Kest
Kest
Quote:
Original post by desertcube
Anyways, if inventory needs to know about the items and modify them

Inventory [mode] does not modify items.
Kest
Kest
Quote:
Original post by Anonymous Poster
Incorrect. If inventory mode needs to pass the item to a function that modifies the item, then inventory mode is modifying the items.

Wrong. If I send an index or ID to the character equip function, the same routine is happening - and inventory wouldn't be passing any items at all. In this case, inventory is truely not modifying anything. In both of these cases, the algorithm of both the character system and the inventory system work exactly the same way. Only the appearance of the code changes. And you're saying one modifies the items and one doesn't? Well, maybe if you're myopic.

Quote:
By the way, why isn't this a problem when you exchange equipment?

Exchange? Do you mean swap between sheaths and hands, dropping and picking up, buying and selling, or trading and looting dead bodies? Characters manage their own items. The inventory menu is nothing but a human-player interface into what the game characters already do. Other than adding, removing, or equipping, nothing else can take place from the public interface of the character class. And nothing except the inventory menu does this. Most item exhanging happens in real-time through animation. So characters control all of it.
hplus0603
hplus0603
An alternative is to not perform actions through direct method calls, but instead through posting messages. So, when time comes to perform inventory actions, your inventory menu would post an action to the message queue, addressed at the inventory item (probably by ID). The Inventory interface would have a const method that returns the ID, but the message dispatcher would have a MessageTargetByID interface registered that's non-const.
enum Bool { True, False, FileNotFound };
Nemesis2k2
Nemesis2k2
I would say the knowledge of which items are equipped belongs to the characters, not the items themselves. If you changed the equipped items through the character rather than the items, you wouldn't be getting this problem.

Quote:
99% of the time if you are casting away const that you declared in your own application than you should rethink the design as this is a red flag. The reason for casting away const is not so you can throw out your own const contracts, but to be compatible with other libraries that don't follow proper constness and/or C libraries.

I actually wrote some interesting classes recently where I used const_cast on code I had entirely written, and it was used in a capacity that not only was logical, it was completely safe. Granted the design was a little unusual however. I was getting around a language limitation more than anything else. I know you didn't say 100% of the time, but I thought it'd just confirm there are actually legitimate uses in unusual circumstances, other than interfacing with non-const correct code.
JohnBolton
JohnBolton
Here is a solution for you. First, items in a character's inventory are accessed externally via handles. For example:
    Handle Character::GetWeapon() const { ... }    void Character::SetWeapon( Handle hWeapon ) { ... } 
Next, you need a function that returns a reference to an inventory item so you can look at it:
    Item const * Character::GetItem( Handle handle ) const { ... } 
And privately:
    Item * Character::GetItem( Handle handle ) { ... } 
That should satisfy your requirements, right?

Now, how you implement this depends on how your inventory is stored and what kind of optimizations you want to make. You just need a way to convert between handles and pointers. It could be something as simple as using the pointer itself as the handle, or the handle could be an index, or it could be a GUID. The difference is that now the conversion (whether you use const_cast or not) is an explicit part of the design and implemented in one place, rather than done implicitly and scattered throughout the code.

[Edited by - JohnBolton on August 27, 2005 6:36:45 PM]
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
JohnBolton
JohnBolton
Here is a related problem. strchr bypasses const by returning a pointer-to-non-const. To be safe, it should return pointer-to-const, but then that would cause difficulties.
    void foo( char const * string, char c )    {        char * pEnd;                pEnd = strchr( string, c );        *pEnd = 0; // Shouldn't be possible because string is pointer-to-const    } 
If strchr returned pointer-to-const, then the above problem would be averted, but then you are forced to use const_cast as in this function:
    void TerminateLine( char * string )    {        char * pEnd;                pEnd = const_cast<char*>( strchr_non_const( string, '\n' ) );        *pEnd = 0;    } 


[Edited by - JohnBolton on August 27, 2005 7:53:25 PM]
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
helix
helix
Quote:
Original post by Saruman
99% of the time if you are casting away const that you declared in your own application than you should rethink the design as this is a red flag. The reason for casting away const is not so you can throw out your own const contracts, but to be compatible with other libraries that don't follow proper constness and/or C libraries.


I was just going to post the same thing. If you need to do something like this, then you probably should find a better way to solve your problem.
Kest
Kest
Quote:
Original post by Anonymous Poster
So, how do you tell it to go between sheaths and hands? Do that here.
How do you tell a character to drop or pick up? Do that here.
Perhaps you're a bit myopic?

Ermm, player characters have input commands to arm weapons and pickup and drop things. Input control is a sub routine of characters, as is AI control. Giving characters the ability to swap links between their own containers makes sense. Allowing them to arm and drop equipment makes sense. Giving characters the ability to create and manage a menu does not, IMHO. So all menues are external systems.

Quote:
Original post by Nemesis2k2
I would say the knowledge of which items are equipped belongs to the characters, not the items themselves. If you changed the equipped items through the character rather than the items, you wouldn't be getting this problem.

I'm not sure I follow? Characters do manage the data. The only data items manage is data that they need elsewhere as well. Like links to transforms. Bone transforms for characters, physics object transforms when they get tossed into the real world.

Quote:
Original post by JohnBolton
Here is a solution for you. First, items in a character's inventory are accessed externally via handles.

So you're saying..
typedef const Item * ITEMHANDLE; ?

Thanks for the replies. I'm really looking into how easily I could impliment hplus0603's idea. I like messaging (I like anything that avoids adding new public interface methods). It's just that currently, my game objects send messages to the system, because they are unaware of it, but the system has never sent messages to game objects (completely aware of them).

Quote:
Original post by helix
I was just going to post the same thing. If you need to do something like this, then you probably should find a better way to solve your problem.

Ummmm, thanks for your help.

edit: By the way, this is all currently working. I just recently realized the inventory menu can do with const access, so I changed the character's public inventory access. It was only when I needed to communicate back to the character that I had problems. You can see some snaps of weapons equipped and even a picture of the WIP inventory menu here. It has become a bit more complex since the snap shots, though. He's currently able to carry five equipment objects on his body at once (dual sidearms, big back weapon, shield, and quiver).
Kest
Kest
Quote:
Original post by Anonymous Poster
Your character class handles mouse/keyboard input? That sounds iffy...

Not exactly. It's allowed to read the state of device controls that have been mapped to it (edit: actually, that's not true. They have access to an input mapper object - which has access to input devices mapped to it). I'm all open to other ideas, though. Input and AI send the same commands to the same control system. In other words, they both work exactly the same way. They both need to know everything about the character's current state and be capable sending the data. It's better than having some other object send this information to the character, when it is indeed only the character that all of this data pertains to. Other than through certain menues, such as inventory and status type screens, no other running systems even know characters exist. They are handled along with all other objects in the same ways.

Quote:
And what's so strange about the character making a menu so the player can interact with the character, especially if it's already handling input in other areas?

Because the game contains a lot of menues, most of which are unrelated to characters. I would rather have them all belong to the same system.

Quote:
He's saying that you shouldn't have a "bool equipped;" member variable in your equipment class. Restructuring such that it doesn't would solve your problem.

How would something like that solve my problem? You're suggesting characters have nothing but const access to items? Do you realize that nothing else contains links to these items? They exist only in the character's inventory. If an item is sent as const and equipped as const, then it is completely const. If I have nothing but a const pointer, it becomes completely static. If I follow this route, I end up with a still picture instead of a video game :P

There was no particular reason to limit the inventory to const access. I simply realized it didn't need anything else. The idea that a character's inventory items cannot change outside of the character's own system is reassuring. In some games, there could be reasons the inventory really would need access, though, such as changing firing rates, ammo types, or reloading. I definitely wouldn't want to give characters an interface for this.

Why don't you register a name so everyone knows they're talking to the same person?
Way Walker
Way Walker
Quote:
Original post by Kest
Quote:
He's saying that you shouldn't have a "bool equipped;" member variable in your equipment class. Restructuring such that it doesn't would solve your problem.

How would something like that solve my problem? You're suggesting characters have nothing but const access to items? Do you realize that nothing else contains links to these items? They exist only in the character's inventory. If an item is sent as const and equipped as const, then it is completely const. If I have nothing but a const pointer, it becomes completely static. If I follow this route, I end up with a still picture instead of a video game :P


Ok, so the character is the only object with a pointer to the equipment, that's cool, ownership is plain. But, why does the character have to modify the equipment? Because you need somewhere to keep track of whether the equipment is actually equipped, but why does that have to be in the equipment itself? There are several other ways of storing this information. (e.g. Two inventory lists, a map from equipment to bool, list of pairs of equipment and bool, pointers representing equipment slots (hand, body, legs, etc.) on the character, etc.)

Quote:

Why don't you register a name so everyone knows they're talking to the same person?


I threw off the mask, happy? :)

Topic Locked

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

Sign in to reply to this topic.