代码之家  ›  专栏  ›  技术社区  ›  Sancarn

Crystal Lang-交叉宏变量

  •  0
  • Sancarn  · 技术社区  · 6 年前

    所以我正在构建一个数据类型 可选自动铸造 . 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. 我相信水晶编译器不能确定 @@auto_cast 每当我打算自动转换时都是正确的(公平地说,当禁用自动转换时,我希望出现语法错误)。
    2. 出现编译错误,因为 @@自动铸造 在编译时未知。
    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
    

    我认为这是可行的,因为只有在 @@自动铸造 已设置。然而,我忘记的是前提2。即 @@自动铸造 在编译时未知。最终,为了实现这一目标,我需要一个变量,它可以是:

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

    不过这感觉很糟糕…我想知道是否还有其他的选择(或者,甚至,如果这根本不起作用的话)?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Johannes Müller    6 年前

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

    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
    

    不过,这很老套;)