The Crystal Programming Language Forum

How do classes interact with macros?

Example code:

class Player
  property health = 10
end

p = Player.new


macro adjust_nodes(option, client)
  {{client}}.health {{option.id}} 10
end

adjust_nodes("+=", p)


5.times do |x|
  pp adjust_nodes("+=", p)
end


5.times do |x|
  pp adjust_nodes("-=", p)
end

My questions:

  • How many adjust_nodes methods are being created? 1 or 10?
  • Is the Player being passed by reference, or passed by value?

Do realize macros != methods. Macros just define code that will be expanded at compile time. I.e. your example is essentially no different than:

class Player
  property health = 10
end

p = Player.new

p.health += 10

5.times do |x|
  pp p.health += 10
end

5.times do |x|
  pp p.health -= 10
end

The Player class isnt being passed at all. You’re just interacting with the getter.

1 Like

Damn, that makes sense. I still can’t believe why macros are so damn complex for me, thanks for being patient.

I still am not unsure if I should use them for my use case (in that github thread). I’m going to fiddle around with them some more and just refer back to this thread.

macro adjust_skill_nodes(nodes, operator, client)
  {{nodes}}.each do |sid|
    if (skill_node = TreeData[sid]?) && (main_node_type = NodeTypes[skill_node["type"]]?)
      main_node_type.modifications.each do |mod, value|
        case mod
        when "strength"
          {{client}}.strength {{operator.id}} value
        when "increased_attack_speed"
          {{client}}.increased_attack_speed  {{operator.id}} (value / 100.00)
        when "health"
          {{client}}.health  {{operator.id}} value
        when "health_regen"
          {{client}}.health_regen  {{operator.id}} (value / 100.00)
        end
      end

      puts {{client}}
    else
      raise "Skill tree error.. this is bad. skill id: #{sid} does not exist on the server!"
    end
  end
end

Does this code stand out to anyone (any improvements maybe?)

I use

        adjust_skill_nodes(nodes_added, "+=", client)

When a player creates/joins a game or when they allocate points on their passive tree.

So far it’s working good. Any possible issues?

You know my opinion on this. There are some improvements you could make but it would require some not so trivial changes.

For this use case it seems like an okay solution as you’re able to reuse some seemly complex logic. The only issue you would run into is when/if logic is required for one adjustment and not the other.

Yeah, I did pp {{client}} and my console was bombarded with messages. I originally thought it was passing all that data into the macro (by copy) instead of by reference, each time the method is executed ;S. However, pp expands and prints out all the client’s properties (even properties that reference other classes). So it would have acted the same even if it wasn’t in a macro. This is why I was worried, but I shouldn’t have been.

“expanded at compile time” is what helped. I always thought it was just duplicating a method that has different functionality based on how you set up the macro. But in reality, wherever a macro is located at in your code, just expands at compile time.

Kind of like throwing darts at a dartboard. Then, those darts expand into code. Those expanded darts are now your macros.