Do dynamic arrays support a nonzero lower bound (for compatibility with VarArrayCreate)?

I will support and port a bunch of very old Delphi code in Delphi XE2 that is full of VarArrayCreate to fake dynamic arrays having a lower bound that is not zero.

Disadvantages of using Variant types:

  • rather slowly than native arrays (the code performs many complex financial calculations, so speed is important)
  • not a safe type (especially when the wrong var... constant is accidentally used, and the Variant system starts doing unwanted conversions or rounding)

Both can become controversial if I can use dynamic arrays .

The good thing about variant arrays is that they can have nonzero lower bounds.

What I recall is that dynamic arrays always started from the bottom of zero.

Is this still true? In other words: is it possible for dynamic arrays to start at a different boundary than zero?

As an illustration, the example before / after for a specific case (one-dimensional, but the code is full of multidimensional arrays and, in addition, varDouble , the code also uses various other varXXX data types that TVarData allows to use):

 function CalculateVector(aSV: TStrings): Variant; var I: Integer; begin Result := VarArrayCreate([1,aSV.Count-1],varDouble); for I := 1 to aSV.Count-1 do Result[I] := CalculateItem(aSV, I); end; 

The CalculateItem function returns Double . Borders from 1 to aSV.Count-1 .

The current replacement is this: trading the spatial zero element of the result to improve compile-time checking:

 type TVector = array of Double; function CalculateVector(aSV: TStrings): TVector; var I: Integer; begin SetLength(Result, aSV.Count); // lower bound is zero, we start at 1 so we ignore the zeroth element for I := 1 to aSV.Count-1 do Result[I] := CalculateItem(aSV, I); end; 
+4
source share
2 answers

Dynamic arrays always have a lower bound of 0 . So low(A) is 0 for all dynamic arrays. This is also true for empty dynamic arrays, i.e. nil .

In the documentation:

Dynamic arrays are always integer indices, always starting at 0.

+5
source

Answering your direct question, I also suggest you start a general class that you can use in your porting.

 type TSpecifiedBoundsArray<T> = class private FValues: TArray<T>; FLow: Integer; function GetHigh: Integer; procedure SetHigh(Value: Integer); function GetLength: Integer; procedure SetLength(Value: Integer); function GetItem(Index: Integer): T; procedure SetItem(Index: Integer; const Value: T); public property Low: Integer read FLow write FLow; property High: Integer read GetHigh write SetHigh; property Length: Integer read GetLength write SetLength; property Items[Index: Integer]: T read GetItem write SetItem; default; end; { TSpecifiedBoundsArray<T> } function TSpecifiedBoundsArray<T>.GetHigh: Integer; begin Result := FLow+System.High(FValues); end; procedure TSpecifiedBoundsArray<T>.SetHigh(Value: Integer); begin SetLength(FValues, 1+Value-FLow); end; function TSpecifiedBoundsArray<T>.GetLength: Integer; begin Result := System.Length(FValues); end; procedure TSpecifiedBoundsArray<T>.SetLength(Value: Integer); begin System.SetLength(FValues, Value); end; function TSpecifiedBoundsArray<T>.GetItem(Index: Integer): T; begin Result := FValues[Index-FLow]; end; function TSpecifiedBoundsArray<T>.SetItem(Index: Integer; const Value: T); begin FValues[Index-FLow] := Value; end; 

I think it is pretty obvious how this works. I intended to use record , but I believe that this is impossible. This refers to a combination of value type semantics for FLow and reference type semantics for FValues . So, I think the class is better here.

It also behaves rather strangely when you change Low .

No doubt you would like to expand this. You would SetBounds , copy, copy, etc. But I think you may find this helpful. This certainly shows how you can create an object that looks very much like an array with a nonzero lower bound.

+2
source

All Articles