Crystal-Lang - Cross-Macro Macro-variables
Crystal-Lang - Cross-Macro Macro-variables
所以我正在构建一个数据类型,我想 可选 auto-casting。 The last question I asked is related to this also.
我目前拥有的代码可以在下面找到:
class Test(T)
@@auto_cast = false
def initialize(var : T)
@var = var
end
def self.auto_cast
@@auto_cast
end
def self.auto_cast=(val)
@@auto_cast = val
end
def self.auto_cast(forced_value=true,&block)
#Force value, but store initial value:
ac = @@auto_cast
@@auto_cast = forced_value
block.call
@@auto_cast = ac
end
def +(val)
var = @var
if @@auto_cast
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
else
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
end
end
end
当我尝试测试数据类型时:
Test.auto_cast do
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
end
它在 return var + val
处抛出错误:
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
ERROR! --> return var + val
end
起初我很困惑为什么,但现在有道理了。
- 我相信 Crystal 编译器无法确定
@@auto_cast
会在我打算 auto_cast 时为真(公平地说,当 auto-casting 被禁用时,我想要语法错误)。
- 发生编译错误,因为
@@auto_cast
的值在编译时未知。
- 由于身体的矛盾性质:
.
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
和
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
每个定义只应在用户明确声明时使用。因此这更适合宏。
鉴于这些原因,我开始尝试将功能构建到宏中:
def +(val)
var = @var
{%if @@auto_cast%}
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
{%else%}
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
{%end%}
end
我认为这可行,因为只有在设置了 @@auto_cast
时才会生成代码。但是我忘记了前提#2。 IE。 @@auto_cast
的值在编译时是未知的。最终,为了完成这项工作,我需要一个变量,它可以是:
- 在编译时设置。
- 编译时在宏中全局使用。
最终我认为我可以按照以下方式做一些事情:
SET_AUTOCAST_VARIABLE true
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
SET_AUTOCAST_VARIABLE false
然后在+()
定义中:
{%if autocast_variable %}
...
{%else%}
...
{%end%}
问题是,我认为这样的 宏全局变量 不存在...我正在考虑解决这个问题的方法,这是目前唯一的解决方案提出在编译时使用一些外部文件:
{{File.write("/tmp/cct","1")}}
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
{{File.write("/tmp/cct","")}}
并且在方法定义中:
{%if File.read("/tmp/cct")=="1" %}
...
{%else%}
...
{%end%}
虽然这感觉真的很老套...我想知道是否还有其他选择,(或者,即使这根本行不通)?
这行不通。方法只实例化一次,不可能有具有相同参数类型的同一方法的两个实现。在下面的示例中,两个 +
方法将不可避免地具有相同的实现。
Test.auto_cast do
Test.new(1) + "1"
end
Test.new(1) + "1"
您不能根据词法范围对同一方法有不同的实现。该方法只被实例化一次,其中的宏也是如此。
我不了解您的整体用例,但也许还有其他方法可以实现您的需求。
为了完整性:您可以将常量用作宏全局变量。常量无法重新定义,但可以通过宏表达式进行更改。这可用于存储宏之间的状态。例如:
FOO = [true]
{{ FOO[0] }} # => true
{% FOO.clear; FOO << false %}
{{ FOO[0] }} # => false
虽然这很老套 ;)
所以我正在构建一个数据类型,我想 可选 auto-casting。 The last question I asked is related to this also.
我目前拥有的代码可以在下面找到:
class Test(T)
@@auto_cast = false
def initialize(var : T)
@var = var
end
def self.auto_cast
@@auto_cast
end
def self.auto_cast=(val)
@@auto_cast = val
end
def self.auto_cast(forced_value=true,&block)
#Force value, but store initial value:
ac = @@auto_cast
@@auto_cast = forced_value
block.call
@@auto_cast = ac
end
def +(val)
var = @var
if @@auto_cast
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
else
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
end
end
end
当我尝试测试数据类型时:
Test.auto_cast do
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
end
它在 return var + val
处抛出错误:
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
ERROR! --> return var + val
end
起初我很困惑为什么,但现在有道理了。
- 我相信 Crystal 编译器无法确定
@@auto_cast
会在我打算 auto_cast 时为真(公平地说,当 auto-casting 被禁用时,我想要语法错误)。 - 发生编译错误,因为
@@auto_cast
的值在编译时未知。 - 由于身体的矛盾性质:
.
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
和
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
每个定义只应在用户明确声明时使用。因此这更适合宏。
鉴于这些原因,我开始尝试将功能构建到宏中:
def +(val)
var = @var
{%if @@auto_cast%}
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
{%else%}
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
{%end%}
end
我认为这可行,因为只有在设置了 @@auto_cast
时才会生成代码。但是我忘记了前提#2。 IE。 @@auto_cast
的值在编译时是未知的。最终,为了完成这项工作,我需要一个变量,它可以是:
- 在编译时设置。
- 编译时在宏中全局使用。
最终我认为我可以按照以下方式做一些事情:
SET_AUTOCAST_VARIABLE true
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
SET_AUTOCAST_VARIABLE false
然后在+()
定义中:
{%if autocast_variable %}
...
{%else%}
...
{%end%}
问题是,我认为这样的 宏全局变量 不存在...我正在考虑解决这个问题的方法,这是目前唯一的解决方案提出在编译时使用一些外部文件:
{{File.write("/tmp/cct","1")}}
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
{{File.write("/tmp/cct","")}}
并且在方法定义中:
{%if File.read("/tmp/cct")=="1" %}
...
{%else%}
...
{%end%}
虽然这感觉真的很老套...我想知道是否还有其他选择,(或者,即使这根本行不通)?
这行不通。方法只实例化一次,不可能有具有相同参数类型的同一方法的两个实现。在下面的示例中,两个 +
方法将不可避免地具有相同的实现。
Test.auto_cast do
Test.new(1) + "1"
end
Test.new(1) + "1"
您不能根据词法范围对同一方法有不同的实现。该方法只被实例化一次,其中的宏也是如此。
我不了解您的整体用例,但也许还有其他方法可以实现您的需求。
为了完整性:您可以将常量用作宏全局变量。常量无法重新定义,但可以通过宏表达式进行更改。这可用于存储宏之间的状态。例如:
FOO = [true]
{{ FOO[0] }} # => true
{% FOO.clear; FOO << false %}
{{ FOO[0] }} # => false
虽然这很老套 ;)