对于那些不熟悉D字符串Mixins的人,它们基本上是编写时间的evals。您可以采用任何编译时字符串(是否由模板元素或编译时间函数评估的文字或生成)并将其编译为代码。如果您使用简单的字符串文字,它基本上是Compiler-Automated Copy-Paste。

你会认为它是一个反attern,用于使用文字的字符串mixins作为简单代码重用的手段,其中其他要素的方法不太适合?一方面,它基本上是编译器 - 自动的文字副本和粘贴,这意味着一旦混合在实例中,彼此无所作为。如果字符串Mixin中的符号与混合范围中的符号碰撞,则会发生坏事(虽然在编译时,而不是在运行时)。它相对非结构化在那个中可以,例如,将字符串混在一起的函数中间,如果且仅当范围中的变量根据某个约定而命名。 MixIns还可以声明外部范围可以使用的变量,然后在它们看到合适时使用。

另一方面,由于复制和粘贴是自动的编译器,因此源级别的代码有一个真相,并且如果需要修改,它只需要修改它一个地方,一切都保持同步。 String MixIns也大大简化了重用代码,这非常难以以任何其他方式因素,否则会有非常高的概率被手动切割和粘贴。

没有正确的解决方案

其他提示

你提出的所有批评都是真的。

无论如何,它仍然优于手动copypaste。

实际上,我在我的工具库中有类似运行的东西,字符串表扩展。示例代码,从路径跟踪器的动态值实现:

  T to(T)() {
    static if (!is(T == Scope)) {
      T value;
      if (flatType == FlatType.ScopeValue) value = sr.value().to!(T);
    }
    const string Table = `
                 | bool          | int         | string               | float   | Scope
      -----------+---------------+-------------+----------------------+---------+----------
      Boolean    | b             | b           | b?q{true}p:q{false}p | ø       | ø
      Integer    | i != 0        | i           | Format(i)            | i       | ø
      String     | s == q{true}p | atoi(s)     | s                    | atof(s) | ø
      Float      | ø             | cast(int) f | Format(f)            | f       | ø
      ScopeRef   | !!sr          | ø           | (sr?sr.fqn:q{(null:r)}p) | ø   | sr
      ScopeValue | value         | value       | value                | value   | sr`;
    mixin(ctTableUnrollColMajor(Table,
      `static if (is(T == $COL))
        switch (flatType) {
          $BODY
          default: throw new Exception(Format("Invalid type: ", flatType));
        }
      else `,
      `case FlatType.$ROW:
        static if (q{$CELL}p == "ø")
          throw new Exception(q{Cannot convert $ROW to $COL: }p~to!(string)~q{! }p);
        else return $CELL;
      `
    ).litstring_expand() ~ `static assert(false, "Unsupported type: "~T.stringof); `);
  }
.

我相信它很容易看出一个可怕的ifs和案例陈述的令人恐惧,冗余的混乱,这将没有弦乐器 - 这种方式,所有丑陋都集中在底部,实际的行为函数易于读取。

While other, more elegant solutions may be better to use if you can, string mixins can be extremely useful. They allow for both code re-use and code generation. They're checked at compile time. The code that results is exactly the same as if you'de written it yourself by hand, so it's not any less safe than if you had written it yourself by hand.

The problem with string mixins is that they're harder to control than hand-written code in the sense that it's not physically laid out in your source in the same manner with line numbers clearly traceable to errors, and it may be harder to debug. For instance, take hello world with a string mixin:

import std.stdio;

void main()
{
    mixin(hello());
}

string hello()
{
    return "
    writeln(\"hello world\");
";
}

If we were to remove the semicolon after writeln(), then the error we got would be

d.d(7): found 'EOF' when expecting ';' following statement

The mixin is done on line 5. Line 7 is a blank line. So, the line number is of limited usefulness here. Now, this mixin is short enough that we could have put it on a single line and gotten it to say that the error was on the same line as the mixin, but with more complicated mixins, that obviously won't work. So, by using a string mixin, your ability to figure out where an error is is impaired. If the code is generated using CTFE, then it would become that much more difficult to figure out exactly what the code even looks like in order to figure out what's wrong with it. It's a lot like figuring out what code a C-style macro turns into, except that it could be worse because they could be generated rather than a direct replacement. However, they don't replace except where you explicitly tell them to, so they're much safer than C-style macros.

String mixins are totally safe, and there's nothing particularly wrong with them, but they do make maintenance harder in some ways. The corresponding hand-written code would be easier to debug. However, string mixins are powerful enough that they can do a lot of code generation for you and save you a lot of maintainence costs in that sense, and they allow you to re-use code, which can be a big maintanence gain as well.

So, whether using a string mixin is a good idea in a particular situation depends on that situation. I don't see anything particularly wrong with them, and I certainly wouldn't call them an anti-pattern, but there are both pros and cons to using them such that whether they're a good idea depends on what you're doing. In many cases, there are more elegant, cleaner solutions which would be better. In others, they're exactly what the doctor ordered.

Personally, I think that they're fantastic if you're looking to generate code, saving yourself the effort of having to write that code by hand, and possibly making it easier to generate correct code for a variety of situations and avoiding risking creating new bugs like you might have had you written it yourself in each of those places where you used the mixin. It also is one of the ways to just outright re-use code without having to worry about the cost of a function call or issues with the limits of single-inheritance or anything else that makes code re-use by calling functions or inheritance harder. You're simply copying and pasting the code into each place in a manner which makes it so that if you change the code, the changes will be properly pasted everywhere without you having to worry about tracking them all down like if you had hand copy and pasted.

So, use string mixins where appropriate, and it's probably best not to use them if they're not needed, but there's nothing really wrong with using them.

String mixin is like goto: it should be avoided where ever possible and should be used wherever required.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top