Lazarus

Free Pascal => General => Topic started by: TCH on October 15, 2021, 12:43:08 pm

Title: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 15, 2021, 12:43:08 pm
I have the following C structs:
Code: C  [Select][+][-]
  1. typedef struct
  2. {
  3.         char *a;
  4.         void *b;
  5. } entry;
  6.  
  7. typedef struct
  8. {
  9.         int size;
  10.         entry *entries;
  11. } table;
So when i say
Code: C  [Select][+][-]
  1. t->entries[0]
it will directly point to the entry, with the two pointers (*a and *b). However, when i try to convert this to Pascal:
Code: Pascal  [Select][+][-]
  1. table_entry = record
  2.         a: AnsiString;
  3.         b: Pointer;
  4. end;
  5. atable_entry = array of table_entry;
  6. patable_entry = ^atable_entry;
  7.  
  8. table = record
  9.         size: integer;
  10.         entries: patable_entry;
  11. end;
  12. ptable = ^table;
and i try to pass a list from a Pascal executable to a C library, then it does not work, because
Code: C  [Select][+][-]
  1. t->entries[0]
will not point to the item with the two pointers directly, but to a pointer to the item. So, this list in C
Code: C  [Select][+][-]
  1. table_entry t_table_entries[] =
  2. {
  3.         {"a", (void *)&a},
  4.         {"b", (void *)&b},
  5.         {"c", (void *)&c},
  6.         {"d", (void *)&d},
  7. };
  8. table t_table =
  9. {
  10.         sizeof(t_table_entries) / sizeof(table_entry),
  11.         &t_table_entries[0],
  12. };
can be passed to the C library, but this very same list in Pascal
Code: Pascal  [Select][+][-]
  1. t_table_entries: atable_entry =
  2. (
  3.         (symbol: 'a'; funcptr: @a),
  4.         (symbol: 'b'; funcptr: @b),
  5.         (symbol: 'c'; funcptr: @c),
  6.         (symbol: 'd'; funcptr: @d),
  7. );
  8. t_table: table =
  9. (
  10.         size: 4;
  11.         entries: @t_table_entries;
  12. );
cannot. What i am doing wrong? Is it possible to store an array of Record the same way as it is stored in C; the array directly containing the records and not pointers to each one? (I am aware that i could allocate memory directly for it and copy the items into it, but what i need is the identical initialization of the list from const or var.)
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Zvoni on October 15, 2021, 01:09:18 pm
I would have expected something like this
Code: Pascal  [Select][+][-]
  1. Type
  2.   TEntry = Record
  3.       a:PChar;
  4.       b:Pointer;
  5.   End;
  6.   PEntry = ^TEntry;
  7.  
  8.   TTable = Record
  9.       Size:Integer;
  10.       Entries:PEntry;
  11.   End;
  12.  
  13. Var
  14.   arrEntries:Array[0..3] Of TEntry; //Or dynamic array
  15.  
  16.  
  17.  
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: alpine on October 15, 2021, 01:09:55 pm
You should make difference between:
Code: [Select]
atable_entry = array of table_entry; and
Code: [Select]
atable_entry = array [0..n-1] of table_entry;The first array is known as dynamic.

P.S. Also AnsiStrings are not the same as C asciiz strings, use PChar instead.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Zvoni on October 15, 2021, 01:36:12 pm
Forgot:
This is using "native" Pascal-types.
When interfacing with a c-library the usage of the ctypes-unit (and its types) is recommended.

And i can never remember, which Packrecords-directive to use
{$PACKRECORDS C} ?
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 15, 2021, 01:38:30 pm
Okay guys, thank you all. This setup works:
Code: Pascal  [Select][+][-]
  1. table_entry = record
  2.         symbol: PChar;
  3.         funcptr: Pointer;
  4. end;
  5. ptable_entry = ^table_entry;
  6.  
  7. table = record
  8.         size: integer;
  9.         entries: ptable_entry;
  10. end;
  11. ptable = ^table;
Code: Pascal  [Select][+][-]
  1. t_table_entries: array[0..3] of table_entry =
  2. (
  3.         (symbol: 'a'; funcptr: @a),
  4.         (symbol: 'b'; funcptr: @b),
  5.         (symbol: 'c'; funcptr: @c),
  6.         (symbol: 'd'; funcptr: @d)
  7. );
  8. t_table: table =
  9. (
  10.         size: length(t_table_entries);
  11.         entries: @t_table_entries[0];
  12. );
Is it possible to omit the [0..3] part somehow? Or in Pascal i have to pre-declare the size of a constant static array, the compiler cannot calculate the size by the entries?
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: marcov on October 15, 2021, 02:03:37 pm
Is it possible to omit the [0..3] part somehow? Or in Pascal i have to pre-declare the size of a constant static array, the compiler cannot calculate the size by the entries?

Add {$pointermath on}  after your {$mode } line.

Then use a pointer to the table entry. ( entries: Ptable_entry;    with ptable_entry = ^table_entry; ) and overindex it like in C
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 15, 2021, 03:00:59 pm
Then use a pointer to the table entry. ( entries: Ptable_entry;    with ptable_entry = ^table_entry; )
It is already that way.
and overindex it like in C
I don't understand this. I did not overindex anything in C. In the C version, the number of items was calculated by
Code: C  [Select][+][-]
  1. sizeof(t_table_entries) / sizeof(table_entry)
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Kays on October 15, 2021, 03:08:06 pm
Code: Pascal  [Select][+][-]
  1. table = record
  2.         size: integer;
  3.         entries: ptable_entry;
  4. end;
You should really use CInt (https://www.freepascal.org/docs-html/rtl/ctypes/cint.html): In FPC integer (https://wiki.freepascal.org/Integer) depends on the used compiler compatibility mode.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 15, 2021, 03:36:00 pm
Got it, thanks.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: marcov on October 15, 2021, 03:49:18 pm
and overindex it like in C
I don't understand this. I did not overindex anything in C. In the C version, the number of items was calculated by
Code: C  [Select][+][-]
  1. sizeof(t_table_entries) / sizeof(table_entry)

The overindexing is if you access t_table_entries via the  table.entries pointer. That pointer is ^table_index; so talbe.entries[3] is overindexing (as in indexing more than declared, since you declare it as pointing to one item, but treat it as an array where you acccess the fourth item ). Note that overindexing is not entirely the same as out-of-bounds access. It is merely accessing more items that you declare, even if you ensure by other means that they are there.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 15, 2021, 05:29:36 pm
I know what is overindexing, but why should i do that here? I have the code
Code: Pascal  [Select][+][-]
  1. t_table_entries: array[0..3] of table_entry =
  2. (
  3.         (symbol: 'a'; funcptr: @a),
  4.         (symbol: 'b'; funcptr: @b),
  5.         (symbol: 'c'; funcptr: @c),
  6.         (symbol: 'd'; funcptr: @d)
  7. );
  8.  
  9. t_table: table =
  10. (
  11.         size: length(t_table_entries);
  12.         entries: @t_table_entries[0];
  13. );
and i would like to avoid specifiying the boundaries of the array at the declaration ([0..3]), because if i add more elements, then i have to re-specify that too each time. (I know, not a big deal, i can live with it, but would be better.)
So, i don't understand how overindexing comes into the picture here...
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: ASerge on October 16, 2021, 10:31:31 am
Is it possible to omit the [0..3] part somehow? Or in Pascal i have to pre-declare the size of a constant static array, the compiler cannot calculate the size by the entries?
If initialize during execution:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. type
  4.   TEntry = record
  5.     A: PAnsiChar;
  6.     B: Pointer;
  7.   end;
  8.  
  9.   TTable = record
  10.     Count: Int32;
  11.     Entries: array of TEntry;
  12.   end;
  13.  
  14. const
  15.   CEntry1: TEntry = (A: 'a'; B: nil);
  16.   CEntry2: TEntry = (A: 'b'; B: nil);
  17.   CEntry3: TEntry = (A: 'c'; B: nil);
  18.   CEntry4: TEntry = (A: 'd'; B: nil);
  19. var
  20.   Tbl: TTable;
  21. begin
  22.   Tbl.Entries := [CEntry1, CEntry2, CEntry3, CEntry4];
  23.   Tbl.Count := Length(Tbl.Entries);
  24. end.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 17, 2021, 11:38:17 am
This will result in a dynamical array too:
Code: Pascal  [Select][+][-]
  1. WriteLn(Char(Pointer(Pointer(@(Tbl.Entries[0]))^)^));
Code: [Select]
a
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Thaddy on October 17, 2021, 12:49:24 pm
yes, but that is not complete.
An example that fits the C syntax, and that can be nested too:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. type
  3.   TMyrec = packed record
  4.    a:integer;
  5.    b:single;
  6.   end;
  7.   TMyRecArray = packed array[0..2] of TMyRec;
  8. const  
  9.   constantarr:TMyRecArray = (
  10.   (a:1;b:1.0),
  11.   (a:2;b:2.2),
  12.   (a:3;b:3.3)
  13.   );
  14. var
  15.   rec:TMyRec;
  16. begin
  17.   for rec in constantarr do
  18.     writeln(rec.a, rec.b);
  19. end.
I used that a lot for really fixed array structures.
[edit:] I just saw ASerge gave about the same answer. And is just as correct.
That pointer rubbish is not necessary at all.

Point is you just have to know the proper Pascal syntax to do it like in C. Focus on pointers is not necessary at all..
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Zvoni on October 17, 2021, 03:10:34 pm
Thaddy, are you sure?
OP stated in his first post, that he wants to pass those records to a c-lib
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: Thaddy on October 17, 2021, 04:38:29 pm
Thaddy, are you sure?
OP stated in his first post, that he wants to pass those records to a c-lib
Yes, of course. You can simply match the memory layout both ways. On the C side, pointers are required, on the Pascal side not. That is the point.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: TCH on October 30, 2021, 10:50:22 pm
Whoops, did not see, that you've replied. Thanks for the insight.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: SymbolicFrank on October 31, 2021, 03:56:30 pm
Isn't it the case that PChar and AnsiString are basically the same, with their length just before it (MyString[-4]), but with a ref count and some more stuff before that? And that the length of a dynamic array is also stored in the 4 bytes just before where the pointer points?

So, shouldn't it work to just declare that dynamic array on the right location and be done with it?

This:

Code: Pascal  [Select][+][-]
  1. table: array of entries;

should be exactly the same as:

Code: C  [Select][+][-]
  1. typedef struct
  2. {
  3.         int size;
  4.         entry *entries;
  5. } table;


On second thought: it isn't. "entry" is a pointer.

Is there a BufferLength before the Length as well?
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: SymbolicFrank on October 31, 2021, 07:32:27 pm
a PChar has not data for length other than a NULL character in the string at some point.

In C(++) it doesn't, that is true.

Quote
Ansistring is a managed string which means all data before the actual start of the first character is for use of that string.

 A Pchar can wrap around a manage string because even managed strings insert a #0 at the end just to make sure but is not counted as part of the string. So casting around the managed string will behave like a Pchar with no regard of what info is before it.

 At least that is my take on it.. been that way since I can remember with Delphi.

So, the only difference is that a PChar string has no length? I'm pretty sure it does have that as well. They're exactly the same in FPC and Delphi, as far as I know.

Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: winni on October 31, 2021, 08:05:26 pm
Hi!

Take care if you cast an AnsiString to a Pchar:

Ansistrings can contain any char including #0
If you cast it without checking on #0 the Pchar wil stop at the first position of #0

Winni
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: PascalDragon on November 02, 2021, 01:57:10 pm
Isn't it the case that PChar and AnsiString are basically the same, with their length just before it (MyString[-4]), but with a ref count and some more stuff before that? And that the length of a dynamic array is also stored in the 4 bytes just before where the pointer points?

You can relatively cheaply cast a AnsiString to a PChar, because an AnsiString has the string data followed by a NUL and the pointer points to the first character (just like a PChar). There is special handling however for an empty string as that is Nil for an AnsiString, but a pointer to a NUL character for a PChar.

So, shouldn't it work to just declare that dynamic array on the right location and be done with it?

This:

Code: Pascal  [Select][+][-]
  1. table: array of entries;

should be exactly the same as:

Code: C  [Select][+][-]
  1. typedef struct
  2. {
  3.         int size;
  4.         entry *entries;
  5. } table;


On second thought: it isn't. "entry" is a pointer.

Is there a BufferLength before the Length as well?

The header record of a dynamic array contains the highest element as well as the reference count.

But if you have a C-struct like this:

Code: C  [Select][+][-]
  1. typedef struct {
  2.   // whatever
  3. } Element;
  4.  
  5. typedef struct {
  6.   Element* entries;
  7. } Table;

You can abuse the fact that a dynamic array points to the first element as long as the C code does not modify the data (the header record won't be accessed by C code):

Code: Pascal  [Select][+][-]
  1. type
  2.   TElement = record
  3.     // whatever
  4.   end;
  5.  
  6.   TTable = record
  7.     entries: array of TElement;
  8.   end;

That is probably what Thaddy had meant with matching the memory layout.
Title: Re: Pascal const Record array stored identically as a C const struct array?
Post by: SymbolicFrank on November 03, 2021, 11:48:49 am
Yes, I expect the memory management is the real problem.You might be able to make the array smaller (when RefCount = 1), but expanding requires copying it to a new location. And C++ doesn't know the size of the buffer.

So, calling functions is probably the best way to make it multi-language.
TinyPortal © 2005-2018