delphivariants

how to call _varOp (_varAdd etc) from my program?


I'm writing custom variant PhysUnit which is something like VarConv but more advanced, allowing not only adding and subtracting, but also multiplying and dividing units, with real or complex quantities, it works fine, but extremely slow.

Problem is, this custom variant wraps around some other variant (simple types like integer or double, or another custom like VarComplex), so when performing DoAdd, DoSubtract, it firstly checks if both operands have same family (length for example), and then adds quantities converting one of them if neccesary:

Left:=Left + Right*multiplier;

something like that, here Left and Right are variants.

Compiler turns this line into series of calls:

_varCopy(tmp,Left);
_varAdd(tmp,Right*multiplier);
_varClear(Left);
_varCopy(Left,tmp);
_varClear(tmp);

while in fact, it would be enough of _varAdd, without allocating/deallocating memory for temporary variant and all these workarounds.

The sad part is: I can't just write _varAdd(Left,Right), it's not linked in VCL.

So the question is: is it possible to call it anyway and make it as "clean" as possible, without nasty calls to direct memory address which might change when compiled with different options or other libraries added?


Solution

  • You can't call the underscored functions because the compiler turns the underscore into @ making it impossible to use it as an identifier.

    But assembler functions are allowed to call them. You can use the original declaration and change the TVarData into Variant so you don't have to cast the Variants all the time.

    procedure _VarAdd(var Left: Variant; const Right: Variant);
    asm
      jmp System.Variants.@VarAdd
    end;
    
    procedure _VarSub(var Left: Variant; const Right: Variant);
    asm
      jmp System.Variants.@VarSub
    end;
    
    begin
      _VarAdd(Left, Right);
    end;
    

    But using Variants if you want to be fast, isn't the right way. They are very slow and don't have that much help from the compiler's optimizer like integer arithmetic, where i := i + 1; is compiled without the need for a temporary variable/cpu-register.

    You can make Variants faster by using special handing for common cases:

    if TVarData(Left).VType = TVarData(Right).VType then
    begin
      case TVarData(Left).VType of
        varSingle:
          begin
            TVarData(Left).VSingle := TVarData(Left).VSingle + TVarData(Right).VSingle * M;
            Exit;
          end;
        varDouble:
          begin
            TVarData(Left).VDouble := TVarData(Left).VDouble + TVarData(Right).VDouble * M;
            Exit;
          end;
      end;
    end;
     // use the slow function for all other cases
    _VarAdd(Left, Right * M);