QMacros宏的值稳定性选项详解

QMacros宏的稳定性,系统给了一个枚举类型TQMacroVolatile来表示,它定义的正确与否,会影响整体替换操作的效率。我们先看其定义:

  /// <summary>宏定义值的稳定性定义</summary>
  /// <param name="mvImmutable">值是固定不变的,是一种常量状态</param>
  /// <param name="mvStable">值是相对不变的,在一次替换操作过程中,我们可以认为其等价发mvImmutable</param>
  /// <param name="mvVolatile">值是易变的,每次获取宏的值时,返回的值都可能是不同的</param>
  TQMacroVolatile = (mvImmutable, mvStable, mvVolatile);

这三种不同的选择,影响替换时,QMacros如何优化操作过程:

1、不变的(mvImmutable)

此时,值是固定不变的,不管宏什么时候调用,返回值始终是固定的。如定义宏PI返回π的值,它无论何时调用都返回固定的值。

默认通过下面的Push函数入栈的宏都是mvImmutable类型的宏。

procedure Push(const AName, AValue: QStringW);

如果通过为宏提供了事件处理函数并指明其值类型为mvImmutable,宏在实际入栈时,会缓存一次宏的值,供以后调用。无论后面多少次替换,都只使用第一次的初始值,这样可以减少不必要的调用开销,加快替换操作的完成速度。

procedure Push(const AName: QStringW; AOnFetchValue: TQMacroValueFetchEvent;AVolatile: TQMacroVolatile = mvVolatile; ATag: IntPtr = 0);

我们看一下这个函数入栈时的部分代码:

function NewValue: PQMacroValue;
  begin
  New(Result);
  Result.OnFetchValue := AOnFetchValue;
  Result.Prior := nil;
  Result.Next := nil;
  Result.Tag := ATag;
  Result.SavePoint := FSavePoint;
  Result.Volatile := AVolatile;
  case AVolatile of
    mvImmutable: // 始终返回固定值的宏,我们直接取一次值,以便优化其替换过程
      AOnFetchValue(Self, AItem, #0, Result.Value);
    mvStable:
      Inc(FStableCount);
    mvVolatile:
      Inc(FVolatileCount);
  end;
  end;

可以看到mvImmutable类型的函数入栈时直接获取了其值。而在内部获取宏的取值的过程中,代码检查到宏的稳定性为muImmutable时,会直接返回缓存的Value值。

    if AItem.Value.Volatile = mvImmutable then
      AValue := AItem.Value.Value
    else if Assigned(AItem.Value.OnFetchValue) then
      begin
      if (AItem.Value.Volatile = mvVolatile) or (FReplaceRef = 0) then //
        AItem.Value.OnFetchValue(Self, AItem, AQuoter, AValue)
      else // mvStable
        begin
        if AItem.Value.SavePoint <> FSavePoint then // 缓存当前值
          begin
          New(AItem.Value.Next);
          AItem.Value.Next.Tag := AItem.Value.Tag;
          AItem.Value.Next.OnFetchValue := nil;
          AItem.Value.Next.SavePoint := FSavePoint;
          AItem.Value.Next.Volatile := mvImmutable;
          AItem.Value.Next.Prior := AItem.Value;
          AItem.Value.Next.Next := nil;
          AItem.Value.OnFetchValue(Self, AItem, #0, AValue);
          AItem.Value.Next.Value := AValue;
          AItem.FValue := AItem.Value.Next;
          end;
        end;
      end

2、稳定的(mvStable)

这类宏的值在同一次替换过程中,多次调用会返回同一个值,如取当前时间类函数,在替换过程中,一般我们是不变的,所以它的稳定性我们一般设置为mvStable。

因为这类值在单次替换时,其值是固定不变的,所以,替换操作对此的优化是发现是第一次访问该宏时,缓存该值为并入栈为mvImmutable,这样子后面读取该宏的值时,都和mvImmutable的处理完全一样,从而不再需要进行函数调用。

3、易变的(mvVolatile)

这类宏的值是易变的,每次调用都会返回完全不同的值。这类函数由于每次宏替换时都返回不同的值,因此,在替换时我们需要按最糟糕的情况来考虑。每次需要替换时,都会调用OnFetchValue对应的回调函数来获取该值。

综上所述,我们可以总结如下:

(1)对于mvImmutable类型的宏,其OnFetchValue只在注册时触发一次,以后不再触发。

(2)对于mvStable类型的宏,其OnFetchValue在每次执行Replace函数时触发,在同一次替换操作时,不会重复触发。

(3)对于mvVolatile类型的宏,其OnFetchValue在每次遇到需要解析该宏的值时都会触发。

分享到: