【问题标题】:Crystal-Lang - Cross-Macro Macro-variablesCrystal-Lang - 跨宏宏变量
【发布时间】:2018-06-21 19:01:39
【问题描述】:

所以我正在构建一种数据类型,我希望在其中可选自动转换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

起初我很困惑为什么,但现在它是有道理的。

  1. 我相信 Crystal 编译器无法确定 @@auto_cast 在我打算进行 auto_cast 时是否为真(公平地说,当自动转换被禁用时,我想要语法错误)。
  2. 因为@@auto_cast 的值在编译时未知,所以出现编译错误。
  3. 由于身体的矛盾性质:

.

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 的值在编译时是未知的。最终,为了完成这项工作,我需要一个变量,它可以是:

  1. 在编译时设置。
  2. 编译时在宏内全局使用。

最终我认为我可以按照以下方式做一些事情:

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%}

这感觉真的很hacky...我想知道是否还有其他选择,(或者,甚至,如果这根本行不通)?

【问题讨论】:

    标签: crystal-lang


    【解决方案1】:

    这行不通。方法只实例化一次,不可能有两个具有相同参数类型的相同方法的实现。在下面的示例中,两个+ 方法将不可避免地具有相同的实现。

    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
    

    不过,这很 hacky ;)

    【讨论】:

    • 我确实想到方法只能实例化一次。我后来实际上用我上次建议的方法澄清了它。这是一个耻辱,但它也很好。它只适用于“语法糖”。我确实有的另一个想法是拥有 2 个独立的类,它们扩展同一个基类,但以不同的方式实现 +(),然后在编译时根据变量选择要实例化变量的类。但是话又说回来,用户直接使用该类可能会更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-05
    • 1970-01-01
    • 2015-02-16
    相关资源
    最近更新 更多