Hi,
I've written this code to test inheritance for a game :
type
Player = object of RootObj
z: int
HumanPlayer = object of Player
x: int
ComputerPlayer = object of Player
y: float
proc echox(a: Player) {.inline.} =
echo HumanPlayer(a).x
proc collide(a: HumanPlayer) {.inline.} =
echo "HumanPlayer"
proc collide(a: ComputerPlayer) {.inline.} =
echo "ComputerPlayer"
var
a: HumanPlayer
b: ComputerPlayer
L = newSeq[Player]()
L.add(a)
L.add(b)
# Why this works ...
Player(a).echox
# ... and not this ?
L[0].collide # No method collide for Player
HumanPlayer(L[0]).collide # ObjectConversionError
How could I put HumanPlayer and ComputerPlayer objects in a sequence with type Player, but still being able to call procs or methods associated with HumanPlayer and ComputerPlayer ?
Thanks.
- Change object of to ref object of in the definition of the types Player, HumanPlayer & ComputerPlayer. This means you will be storing references rather than object values.
- Change proc collide to method collide. [Also, remove the {.inline.} pragma. I can't see how you could have inline virtual methods.]
- Define method collide(a: Player) for the base type. This will be overridden by the definitions for the derived types.
The idea is to use ref objects, because otherwise the seq would not have enough space to store the whole HumanPlayer and ComputerPlayer inside the space for a Player. With a ref object they're just references (which are all the same size, int) to the heap.
Also, you need to use multimethods instead of procs. For multimethods it's decided at runtime which type the object has and which actual method to call:
type
Player = ref object of RootObj
z: int
HumanPlayer = ref object of Player
x: int
ComputerPlayer = ref object of Player
y: float
proc newHumanPlayer: HumanPlayer =
new result
result.x = 10
proc newComputerPlayer: ComputerPlayer =
new result
result.y = 0.333
method collide(a: Player) =
echo "Player"
method collide(a: HumanPlayer) =
echo "HumanPlayer"
method collide(a: ComputerPlayer) =
echo "ComputerPlayer"
var
a = newHumanPlayer()
# OR: a = HumanPlayer(x: 10)
b = newComputerPlayer()
# OR: b = ComputerPlayer(y: 0.333)
L = newSeq[Player]()
L.add(a)
L.add(b)
a.collide
b.collide
L[0].collide
L[1].collide
I have still some problems to understand why we have to define method collide(a: Player) when we never want to call that...
And, I just looked at the "Nim by example" page again. (Unfortunately it still has the ugly typo "was as procs"). That example uses Dog(name: "Sparky", age: 10) while your example uses newHumanPlayer(). Are we free to use both variants, or is one deprecated?
I have still some problems to understand why we have to define method collide(a: Player) when we never want to call that...
What would happen if you have a Player and call collide on it?
That example uses Dog(name: "Sparky", age: 10) while your example uses newHumanPlayer(). Are we free to use both variants, or is one deprecated?
HumanPlayer(x: 10) is more for internal usage inside a module. If you used that from outside, you would have to know all fields the object has and how to properly initialize them, which is complicated and can break if something in the module changes. newHumanPlayer() is higher level and should be used from outside the module as code will keep working even if HumanPlayer s fields change.