Does Crystal have something like C's #define

begin
    File.open("Googoo.txt")
rescue e 
    puts "\n*** \"#{e}\" ***\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"
end

Is it possible to " #define" a string like

 "\n*** \"#{e}\" ***\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"

to avoid copy/pasting it?

You can do these things with Crystal’s macro language.

https://crystal-lang.org/reference/syntax_and_semantics/macros/index.html

3 Likes

I tried:

macro msg
    "#{ {{"\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"}}}"
end  

begin
    File.open("Googoo.txt")
rescue err 
    puts err.to_s
    puts msg
end

``
But I must have done something wrong...

If I use a macro, LINE and FILE are those where the Macro is defined, not those where the macro is inserted (unlike C’s “#define”):

macro msg
    {{"\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"}}
end  

begin
    File.open("Googoo.txt")
rescue err 
    puts err.to_s
    puts msg
end

That’s because you expand these magic constants in the macro context.
For what you want to do, you don’t need to do anything in the macro body. It’s just the original string literal:

macro msg
  "\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"
end  
1 Like

Thank you. I’m new to macros (I’ve been using mostly Golang for a long time).

Do you know about a tutorial about this topic?

Here is a convenient formulation :

# testing.cr

require "./error_message"

begin
    File.open("Googoo.txt")
rescue err 
    puts errorMessage(err)
end

# error_message.cr

macro errorMessage(err)
    "\n*** \"#{err}\" ***\n-> in file: #{__FILE__} \n-> on line: #{__LINE__}\n\n"
end

Result:

>>crystal testing.cr 

*** "Error opening file with mode 'r': 'Googoo.txt': No such file or directory" ***
-> in file: /Users/sergehulne/Documents/code/Crystal/fibers/logic.cr 
-> on line: 15

Note that for this example using a plain def with default values is enough

begin
    File.open("Googoo.txt")
rescue err 
    puts errorMessage(err)
end

def errorMessage(err, *, file = __FILE__, line = __LINE__)
    "\n*** \"#{err}\" ***\n-> in file: #{file} \n-> on line: #{line}\n\n"
end
2 Likes

No, it’s not: lt would yield the wrong line (and file, if stored in another file).

Pretty sure it would be fine because __FILE__ and __LINE__ would default to the file and line of the invocation of the method, not where the method is defined.

Try.

bar.cr

def errorMessage(err, *, file = __FILE__, line = __LINE__)
  "\n*** \"#{err}\" ***\n-> in file: #{file} \n-> on line: #{line}\n\n"
end

foo.cr

require "./bar"

begin
  File.open("Googoo.txt")
rescue err
  puts errorMessage(err)
  puts errorMessage(err)
  puts errorMessage(err)
end

Output:

*** "Error opening file with mode 'r': 'Googoo.txt': No such file or directory" ***
-> in file: /home/george/foo.cr
-> on line: 6


*** "Error opening file with mode 'r': 'Googoo.txt': No such file or directory" ***
-> in file: /home/george/foo.cr
-> on line: 7


*** "Error opening file with mode 'r': 'Googoo.txt': No such file or directory" ***
-> in file: /home/george/foo.cr
-> on line: 8

Works fine.

1 Like

Ok! Perhaps the pb.was that my earliest version erroneously contained double curly braces : {{“…”}}.