Recent

Author Topic: Is there some way to obtain the length of a character array at compile time ?  (Read 16387 times)

440bx

  • Hero Member
  • *****
  • Posts: 3921
I didn't say it's perfect. I just don't think you have a better solution at this moment. If it was for me I would make IDE script even more simple - just count commas in a selection and add 1 to the result and put it where it belongs in the code. Crude but should do the work.
Don't get me wrong.  I appreciate the suggestion.  The goal of the question is/was to find out if there was a reliable mechanism of determining the lengths at compile time so they could be used to define buffers of the right sizes, that would be automatically updated by the compiler if any changed.

At this point, I think the only "reasonable" solution is to determine the string length at run time.  Of course, not even that is as reliable as having the compiler make the size available at compile time but, it is a general solution that works every time (as long as it is bug-free, of course ;))
« Last Edit: August 12, 2018, 07:13:38 pm by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

440bx

  • Hero Member
  • *****
  • Posts: 3921
If You are looking for something else then it eludes me..

You have the right idea but, the compiler doesn't provide all the information I need to implement it.  Here is an example:

Code: Pascal  [Select][+][-]
  1. const
  2.   SomeText    : pchar = 'some text here';
  3.   MoreText    : pchar = ' more text here';
  4.   ALittleMore : pchar = 'and some more here';

at run time, I have to create a character array composed of those 3 "constants" and a some other text (to be determined at run time).  It would nice, not to mention useful, to declare a buffer to hold the end result.  Something along the lines of

type
  TheBuffer : array[0..length(SomeText) + length(MoreText) + length(ALittleMore) + "maximum size of text that will be added at run time"] of char;

That way, the size of "TheBuffer" automatically adapts to any changes made to the 3 constants and spares the programmer from having to define some artificial "maximum size" which could eventually become insufficient if the programmer changes the strings and doesn't notice that the "maximum size" defined is now too small, that while wasting memory (though the wasting of memory is not much of a concern, just a nice bonus if it can avoided.)

It's the same thing that is done with other structure sizes the compiler makes available at compile time.  No difference other than the type being a const pchar, which causes the compiler to be unwilling to give it size, even though the compiler knows the array is a constant.
 
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

440bx

  • Hero Member
  • *****
  • Posts: 3921
I just created 3 non-writable constants containing the text then finalized it with a writable constant of the whole that
works compile time and also allows for a max size...

Code: Pascal  [Select][+][-]
  1. Const
  2. SomeText = ' Some text';
  3. MoreText = 'MoreText';
  4. AlittleMore = ' AlittleMore';
  5.  TotalBuffer = Array[0..SomeMaxSize] of char = SomeText+MoreText+AlittleMore;
  6.  

So what ever you want with it at runtime afterwards.

TotalBuffer that is..
yes, that is no problem.  The problem is that the SomeMaxSize you declare is not based on the sizes of the three constants.  Someone can change the constants in such a way that they exceed SomeMaxSize then there is a problem.  If SomeMaxSize could be determined based on the sizes of the constants then the code would always work before the buffer size, calculated by the compiler, is always large enough.

Also, unlike in your example, the result will not simply be an addition of the constants.  There will be some dynamically determined text in addition to the character constants which will make up the "final" text to display/store.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9754
  • Debugger - SynEdit - and more
    • wiki
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. const
  3.   a = 'abcdefghi';
  4.   l = length(a);
  5. var
  6.   b: array[0..l] of char;
  7.  
  8. begin
  9.   move(a[1], b[0], l);
  10.   writeln(a);
  11.   writeln(l);
  12.   writeln(b[l-1]);
  13.   readln
  14. end.
  15.  

Compiles, runs and prints:
abcdefghi
9
i

440bx

  • Hero Member
  • *****
  • Posts: 3921
Martin, that is beautiful !!

I can use that.  I added some code in the example you provided to give a rough idea of how I will use it.  The actual code does more acrobatics with the text but, the essence is the same.

Code: Pascal  [Select][+][-]
  1. program lengthofcharacterarrayconstants2;
  2.  
  3. const
  4.   a = 'abcdefghi';
  5.  
  6.   l = length(a);
  7.  
  8. type
  9.   TINDEXES  = 0..3;
  10.  
  11. const
  12.   // if any string is made longer than the currently longest string then the
  13.   // constant "Longest" needs to be updated to be the length of the longest
  14.   // string.
  15.  
  16.   const_1   = 'this is const 1 - a';
  17.   const_2   = 'this is const 2 - ab';
  18.   const_3   = 'this is const 3 - cba';
  19.   const_4   = 'this is const 4 - 12';
  20.  
  21. const
  22.   // comment here informing that Longest should be set to the length of the
  23.   // longest string in the set of const_xx
  24.  
  25.   Longest   = length(const_3);    // const_3 randomly chosen for
  26.                                   // the example's sake
  27.  
  28.   constants : array[TINDEXES] of pchar = (const_1, const_2, const_3, const_4);
  29.  
  30. const
  31.   ADDITIONAL_SPACE_NEEDED = 16;   // for additional text in real life app.
  32.                                   // determined by a combination of text.
  33.   space     = ' ';
  34.   comma     = ',';
  35.   colon     = ':';
  36.  
  37.   // move wants variables for the above - I don't use move so that won't be a
  38.   // problem.  The variables below are only for this example.
  39.  
  40.   spacev    : pchar = space;
  41.   commav    : pchar = comma;
  42.   colonv    : pchar = colon;
  43.  
  44. type
  45.   // it accepts this!! -> excellent - this solves the problem!
  46.  
  47.   TEXT_BUFFER = array[0 ..
  48.                       (2 * Longest) + ADDITIONAL_SPACE_NEEDED] of char;
  49.  
  50. var
  51.   TheBuffer   : TEXT_BUFFER;
  52.   Index       : sizeint       = 0;
  53.  
  54. var
  55.   b : array[0..l] of char;
  56.   i : integer;
  57.  
  58. begin
  59.   // code below is similar/equivalent (and much simpler) to what I'm doing but,
  60.   // it is representative.  Not included in the code below is code to ensure
  61.   // that index doesn't go beyond high(TheBuffer).  With the definitions based
  62.   // on the length of the character arrays, it should never happen but,
  63.   // ensuring it doesn't, is so cheap, it's worth including in a real program.
  64.  
  65.   // initialize the buffer with spaces and null terminate it.
  66.  
  67.   FillChar(TheBuffer, sizeof(TheBuffer), space);
  68.   TheBuffer[high(TheBuffer)] := #0;
  69.  
  70.   // now the buffer is ready - use it :-)
  71.  
  72.   move(const_2, TheBuffer, length(const_2));
  73.   inc(index, length(const_2));
  74.  
  75.   // place a comma in there
  76.  
  77.   move(commav^, TheBuffer[Index], sizeof(comma));
  78.   inc(index, sizeof(comma));
  79.  
  80.   // a space after a comma.  could do space_comma in one shot but
  81.   // this example is just to show the mechanics.
  82.  
  83.   move(spacev^, TheBuffer[Index], sizeof(space));
  84.   inc(index, sizeof(space));
  85.  
  86.   // now put the next string in the buffer
  87.  
  88.   move(const_4, TheBuffer[Index], length(const_4));
  89.   inc(index, length(const_4));
  90.  
  91.   // null terminate it
  92.  
  93.   TheBuffer[Index] := #0;
  94.  
  95.   // write it out
  96.  
  97.   writeln(TheBuffer);
  98.  
  99.  
  100.   move(a[1], b[0], l);
  101.   writeln(a);
  102.   writeln(l);
  103.   writeln(b[l-1]);
  104.  
  105.   for i := low(constants) to high(constants) do writeln(constants[i]);
  106.  
  107.   readln
  108. end.
  109.  

Knowing the character array sizes will result in cleaner, simpler and safer code.   Thank you again. :)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

JdeHaan

  • Full Member
  • ***
  • Posts: 115
I know it's an old thread, but I also needed the string length at compile time. I found the new generic with constant parameter very helpful.

Code: Pascal  [Select][+][-]
  1. program gentest;
  2.  
  3. generic function ConstString<const P,Q,S: string>: PChar;
  4. var
  5.   size: Integer;
  6. begin
  7.   Size := SizeOf(P) + SizeOf(Q) + SizeOf(S);
  8.   writeln(Size);
  9.  
  10.   Result := P+Q+S;
  11. end;
  12.  
  13. var
  14.   s: PChar;
  15. begin
  16.   s := specialize ConstString<'Hello', ' world', ' !'>;
  17.   writeln(s);
  18. end.
  19.  

Only 1 minor detail: The length of the string must be > 1. If I remove the space in front of the ' !', it gives a type mismatch (expected 'char').

I'm using trunk for both Laz + FPC on MacOs Big Sur 11.4

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Only 1 minor detail: The length of the string must be > 1. If I remove the space in front of the ' !', it gives a type mismatch (expected 'char').

Please report that as a bug. That's a missing type check then as that should definitely be allowed.

JdeHaan

  • Full Member
  • ***
  • Posts: 115
Done: 0039030
My first one, so hope it's clear

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Done: 0039030
My first one, so hope it's clear

Looks good. Thank you.

 

TinyPortal © 2005-2018