KillianKemps
KillianKemps

Reputation: 123

How do I find a instance variable through its "actual" class?

The program I'm writing is storing elements in a hash called grid of type Position => LivingBeing | Thing. This grid is stored on a Map and I would like this Map to return the position of an element of class Apple which is a subclass of Thing.

However, when using typeof() to get the class, I get LivingBeing | Thing instead of the subclass Apple

Here is the Map class:

class Map
  @@grid = {} of Position => LivingBeing | Thing

  def initialize()
  end

  # Add an entity to the grid
  def add_entity(new_entity : LivingBeing | Thing)
    @@grid[new_entity.position] = new_entity
  end

  # Return the position of an object of class "something"
  def self.where_is?(something : Class)
    # First attempt was to get the key by the value
    # @@grid.key(something)

    @@grid.each do |position, thing|
      # Returns "thing #<Apple:0x55f1772085c0> at Position(@x=1, @y=2) is (LivingBeing | Thing)"
      puts "thing #{thing} at #{position} is #{typeof(thing)}"
      position if typeof(thing) == something
    end
  end

Here the Thing class:

abstract class Thing
  getter position
  @name = "Unkown object"

  def initialize(@position : Position)
  end
end

class Apple < Thing
  @name = "Apple"
end

Here the Position struct:

struct Position
  getter x, y

  def initialize(@x : Int32, @y : Int32)
  end
end

And here is the test I try to make it pass:

it "gives a random thing location based on its class" do
  world = Map.new()
  apple = Apple.new(Position.new(1, 2))
  puts "Apple type : #{typeof(apple)}" # Returns "Apple type : Apple"
  world.add_entity(apple)
  position = Map.where_is?(Apple)
  position.should eq Position.new(1, 2)
end

Is there some class method or function which could give the Apple class? Or is it a design issue?

Thank you for your answer !

Upvotes: 2

Views: 75

Answers (3)

Stephie
Stephie

Reputation: 3175

You can use forall to solve this:

  # Return the position of an object of class "something"
  def self.where_is?(something : T.class) forall T
    @@grid.each do |position, thing|
      return position if thing.is_a?(T)
    end
  end

and call it using Map.where_is? Apple just like you wish.

This works because the type variable T (introduced using forall T) can be inferred to be Apple from passing in the constant Apple which matches the T.class type restriction. T is then a constant you can use with is_a?.

Upvotes: 1

peak
peak

Reputation: 116880

As @ RX14 said, it looks like you want to check the run-time "type", i.e. .class. Here's an example:

class Apple
  @name = "Apple"
end

def check(obj : Object)
  obj.class == Apple
end

a=Apple.new
p check(a)

Upvotes: 0

KillianKemps
KillianKemps

Reputation: 123

One solution I have is this for my function:

  # Return the position of an object of class "something"
  def self.where_is?(something)
    @@grid.each do |position, thing|
      return position if thing.is_a?(typeof(something))
    end
  end

And this for the test:

  it "gives a random thing location" do
    world = Map.new(4)
    apple = Apple.new(Position.new(1, 2))
    world.add_entity(apple)
    position = Map.where_is?(Apple.new(Position.new(0, 0)))
    position.should eq Position.new(1, 2)
  end

I will use it like this if there is no other solution. But I would prefer to be able to search directly the class Apple instead of creating an instance of Apple

I would like to be able to do position = Map.where_is?(Apple) instead of position = Map.where_is?(Apple.new(Position.new(0, 0)))

Upvotes: 1

Related Questions