10

Can I initialize a constant dynamic array of arrays?

If:

type
  tNamePair = array[1..2] of String;
  tPairList = array of tNamePair;

How can I create an initialized constant? I can't get the code below compile:

const
  PairList: tPairList = ( ('One', '1'), 
                          ('Two', '2'),
                          ('Three', '3'));

If that's not possible, can a constant fixed array be initialized with a fixed array:

 type
    tPairList: array[1..3] of tNamePair;

If that's not possible, can a constant dynamic array be initialized with a record:

tNamePair = record 
              English: String;
              Number: String;
            end;           
tPairList = array of tNamePair;

if that's not possible can a constant fixed array be initialized with a record:

tNamePair = record 
              English: String;
              Number: String;
            end;           
tPairList = array[1..3] of tNamePair;

If that's not possible, any suggestions other than just hardwiring assignments in the code, which frankly would have taken me less time than composing this question!

3
  • 3
    You might find this answer to a somewhat similar question useful. Or this one. Commented May 9, 2012 at 23:56
  • 1
    As the compiler cannot know the length of a dynamic array, I guess it is impossible initialize one. Commented May 10, 2012 at 0:19
  • constant dynamic is a nice oxymoron. :-) Commented May 10, 2012 at 7:11

5 Answers 5

27

Prior to XE7 you could not create dynamic array constants. Constants have to be known at compile time, but dynamic arrays are allocated at runtime.

A fixed array of fixed arrays can be declared at compile-time:

type
  tNamePair = array[1..2] of String;
  tPairList = array[1..3] of tNamePair;

const
  PairList: tPairList = ( ('One', '1'),
                          ('Two', '2'),
                          ('Three', '3'));

A fixed array of records can also be declared at compile-time:

type
  tNamePair = record
              English: String;
              Number: String;
            end;
  tPairList = array[1..3] of tNamePair;

const
  PairList: tPairList = ( (English: 'One'; Number: '1'),
                          (English: 'Two'; Number: '2'),
                          (English: 'Three'; Number: '3'));

If you need a dynamic array, you have to construct it at run-time. You can either build it up directly:

type
  tNamePair = array[1..2] of String;
  tPairList = array of tNamePair;

var
  PairList: tPairList;

initialization
  SetLength(PairList, 3);
  PairList[0][1] := 'One';
  PairList[0][2] := '1';
  PairList[1][1] := 'Two';
  PairList[1][2] := '2';
  PairList[2][1] := 'Three';
  PairList[2][2] := '3';
end.

Or you can define a compile-time constant fixed array and copy it into the dynamic array at run-time:

type
  tNamePair = array[1..2] of String;
  tPairList = array[1..3] of tNamePair;
  tPairListDyn = array of tNamePair;

const
  PairList: tPairList = ( ('One', '1'),
                          ('Two', '2'),
                          ('Three', '3'));

function MakePairListDyn(const Pairs: tPairList): tPairListDyn;
var
  I, J: Integer;
begin
  SetLength(Result, Length(Pairs));
  J := 0;
  for I := Low(Pairs) to High(Pairs) do begin
    Result[J] := Pairs[I];
    Inc(J);
  end;
end;

var
  Pairs: tPairListDyn;

initialization
  Pairs := MakePairListDyn(PairList);
end.

For the situation post XE7 see @LURD's answer.

Sign up to request clarification or add additional context in comments.

3 Comments

thanks for reminding me to the somewhat unusual syntax of declaring const arrays of records, which I will immediately forget again ;-)
Thank you, Remy, for taking the time to create such a useful response.
You should initialize variable J before the for loop if you don't want some AVs here and there ;)
14

In XE7 it is possible to declare dynamic array constants.

Simple case:

const
  a: TArray<String> = ['Delphi','XE7'];

In your example this compiles:

type
  tNamePair = TArray<String>;
  tPairList = TArray<tNamePair>;

const
  PairList: tPairList = [['One', '1'],['Two', '2'],['Three', '3']];

To create a dynamic array of a record, it can be done at runtime like this:

Type
  TNamePair = record
    English: String;
    Number: String;
    class function Define(_English,_Number: String): TNamePair; static;
  end;
  TPairList = TArray<TNamePair>;

class function TNamePair.Define(_English, _Number: String): TNamePair;
begin
  Result.English := _English;
  Result.Number := _Number;
end;

var
  pl : TPairList;
begin
  pl := [TNamePair.Define('A','1'),TNamePair.Define('B','2')];
  ...
end;

8 Comments

Wow! I was just wondering about what was new in XE7. You've got a good memory to have found this two year old thread! THANKS.
Not so much my memory, but a certain search engine (read Google) was helpful :)
@LURD Do you know of a way to use a dynamic array constant with the OP's original definition of TNamePair (a record)?
@Graymatter, no not a constant, but at runtime you can do as in my updated answer.
@LURD Thanks, that's quite cool. I tested something similar with a static class function and the Create(...) syntax that can be used in a const but got the same error that I did when trying to use a const record format. Ordinal value expected or something like that.
|
0

You can use this method :

const
  C_ARRAY_CODE : array ['a'..'d'] of string = ('01','02','03','04');
var Conter:Char;
begin
  //Use With Loop
  for Conter := Low(C_ARRAY_CODE) to high(C_ARRAY_CODE) do
    ShowMessage(C_ARRAY_CODE[Conter]);

  //Use Direct
  ShowMessage(C_ARRAY_CODE['a']);
end;

Comments

0

A bit of an old thread, and probably not an answer that's relevant to many people, but I frequently hit situations in parsing routines where I want constant arrays with each entry having a variable number of items in one of the fields. I much prefer using constants for the variable entries because it keeps the code cleaner and easier to understand (for me, at least). You can have a constant array where each entry has a variable number of entries in a sub-array if you're prepared to have an entry count field. You can use this same entry count field as a flag to allow you to also process true dynamic arrays as well:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  PSubStruct = ^SubStruct;
  SubStruct = record
    ValueName: string;
    ValueID: integer;
    class function NewSubStruct(aName: string; aID: integer): SubStruct;
                                static; inline;
  end;
  SubStructArray = array of SubStruct;

  Struct = record
    SuperName: string;
    // <0 means Dynamic array
    ValueCount: integer;
    Values: SubStructArray;
  end;
  StructArray = array of Struct;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure ClearDynamicArray(aArray: StructArray);
    function CreateDynamicArray: StructArray;
    procedure ShowSubStruct(Struct: Struct);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  Sub1: array[0..0] of SubStruct = (
              (ValueName: 'Sub1 Name';
                ValueID: 1;)
      );

  Sub2: array[0..1] of SubStruct = (
        (ValueName: 'Sub2.1 Name';
          ValueID: 3;),
        (ValueName: 'Sub2.2 Name';
          ValueID: 15)
  );

const
  ConstStructArray: array[0..1] of Struct = (
        (SuperName: 'Substructure 1';
          ValueCount: Length(Sub1);
          Values: SubStructArray(@Sub1);
        ),
        (SuperName: 'Substructure 2';
          ValueCount: Length(Sub2);
          Values: SubStructArray(@Sub2);
        )
  );

procedure TForm1.Button1Click(Sender: TObject);
var
  DynamicStructArray: StructArray;
  i: integer;
begin
  DynamicStructArray := CreateDynamicArray;

  for i := Low(ConstStructArray) to High(ConstStructArray) do begin
    ShowMessage(ConstStructArray[i].SuperName);
    ShowSubStruct(ConstStructArray[i]);
  end;
  for i := Low(DynamicStructArray) to High(DynamicStructArray) do begin
    ShowMessage(DynamicStructArray[i].SuperName);
    ShowSubStruct(DynamicStructArray[i]);
  end;

  ClearDynamicArray(DynamicStructArray);
  DynamicStructArray := nil;
end;

procedure TForm1.ClearDynamicArray(aArray: StructArray);
var
  i: integer;
begin
  for i := Low(aArray) to High(aArray)
      do if aArray[i].ValueCount < 0
          then aArray[i].Values := nil;
end;

function TForm1.CreateDynamicArray: StructArray;
begin
  SetLength(Result, 2);
  Result[0].SuperName := 'DynamicStructure';
  Result[0].ValueCount := -1;
  SetLength(Result[0].Values, 1);
  Result[0].Values[0] := SubStruct.NewSubStruct('DS.V1', 1001);

  Result[1].SuperName := 'DynamicStructure2';
  Result[1].ValueCount := -1;
  SetLength(Result[1].Values, 2);
  Result[1].Values[0] := SubStruct.NewSubStruct('DS2.V1', 2001);
  Result[1].Values[1] := SubStruct.NewSubStruct('DS2.V2', 2002);
end;

procedure TForm1.ShowSubStruct(Struct: Struct);
var
  HighVal: integer;
  i: integer;
  CurSubStruct: PSubStruct;
begin
  if Struct.ValueCount < 0 then begin
    // It's a dynamic array
    HighVal := High(Struct.Values);
    CurSubStruct := @Struct.Values[0];
  end
  else begin
    // It's a constant array
    HighVal := Struct.ValueCount-1;
    CurSubStruct := PSubStruct(Struct.Values);
  end;

  // process the array
  for i := 0 to HighVal do begin
    ShowMessage(CurSubStruct.ValueName
                    + '=' + IntToStr(CurSubStruct.ValueID));
    Inc(CurSubStruct);
  end;
end;

{ SubStruct }

class function SubStruct.NewSubStruct(aName: string;
  aID: integer): SubStruct;
begin
  Result.ValueName := aName;
  Result.ValueID := aID;
end;

end.

Using "ValueCount" as a flag also allows you to mix dynamic and constant sub-arrays in the same master array, if you have a need (I never have, but it's a built-in side-effect of the technique).

Comments

0

It is definitely possibile to declare a constant of type "array of array of string" in Delphi 7 using this ugly hack. But nobody should use it.

const

{ Doesn't compile in Delphi 7
  ArrayOfArrayOfString = [
    ['Zero', 'One'],
    ['2', '3', '4'],
    ['5', '6', '7', '8'],
    ['9', 'A', 'B', 'C', 'D']
  ];
}

  ConstArrayOfArrayOfStringRec: packed record
    R, C: Integer;
    A: array[0..3] of array of string;
    A0R: packed record R, C: Integer; A: array[0..1] of string end;
    A1R: packed record R, C: Integer; A: array[0..2] of string end;
    A2R: packed record R, C: Integer; A: array[0..3] of string end;
    A3R: packed record R, C: Integer; A: array[0..4] of string end;
  end = (R: -1; C: 4;
    A: (@ConstArrayOfArrayOfStringRec.A0R.A,
        @ConstArrayOfArrayOfStringRec.A1R.A,
        @ConstArrayOfArrayOfStringRec.A2R.A,
        @ConstArrayOfArrayOfStringRec.A3R.A
    );
    A0R: (R: -1; C: 2; A: ('Zero', 'One'));
    A1R: (R: -1; C: 3; A: ('2', '3', '4'{, 'Five' })); // 'Five' gives compile time error
    A2R: (R: -1; C: 4; A: ('5', '6', '7', '8'));
    A3R: (R: -1; C: 5; A: ('9', 'A', 'B', 'C', 'D'));
  );

  ConstArrayOfArrayOfString: array of array of string = @ConstArrayOfArrayOfStringRec.A;

procedure Test;
begin
  Assert(Length(ConstArrayOfArrayOfString) = 4);
  Assert(Length(ConstArrayOfArrayOfString[0]) = 2);
  Assert(Length(ConstArrayOfArrayOfString[1]) = 3);
  Assert(Length(ConstArrayOfArrayOfString[2]) = 4);
  Assert(Length(ConstArrayOfArrayOfString[3]) = 5);
  Assert(ConstArrayOfArrayOfString[0, 0] = 'Zero');
  Assert(ConstArrayOfArrayOfString[3, 4] = 'D');
  SetLength(ConstArrayOfArrayOfString[3], 6);
  ConstArrayOfArrayOfString[3][5] := 'E';
  SetLength(ConstArrayOfArrayOfString, 5000000); // Should realloc and copy content
  Assert(ConstArrayOfArrayOfString[3][5] = 'E');
  ConstArrayOfArrayOfString := nil;
end;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.